Spring Boot 프로젝트 Nginx 무중단 배포 스크립트

Jinseok Lee·2022년 4월 16일

사연

단일의 aws ec2 인스턴스에서 무중단 배포를 해야 하는 상황이 드디어 온것이다. 시행착오를 거치면서 완성한 배포 스크립트를 공유한다. 프로젝트는 spring boot + gradle 기반의 프로젝트이고 nginx 서버를 사용하였다. 어플리케이션 사이드에서 포트 프로파일 정보와 헬스 체크에 관련한 컨트롤러를 생성하고 그 정보를 토대로 배포할때 참고한다.

해결

현재 구동중인 프로파일 응답 컨트롤러

@RestController
@RequiredArgsConstructor
public class ProfileController {

    private final Environment env;

    @GetMapping("/port-profile")
    public String profile() {
        List<String> profiles = Arrays.asList(env.getActiveProfiles());
        List<String> realProfiles = Arrays.asList("port-1","port-2");
        String defaultProfile = profiles.isEmpty() ? "port-1" : profiles.get(0);

        return profiles.stream()
            .filter(realProfiles::contains)
            .findAny()
            .orElse(defaultProfile);
    }
}

Health 체크를 위한 Hello 컨트롤러

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}

구동 서버 port정보를 담고있는 port-1, port-2 프로파일 프로퍼티

application-port-1.properties

server.port=8080

application-port-2.properties

server.port=8081

nginx 설정

/etc/nginx/conf.d/semoh-app-api-service-url.inc

set $service_url http://127.0.0.1:8080;

/etc/nginx/sites-enabled/semoh.conf

server {
        listen 80;
        server_name api-test.semoh.co.kr;
        include /etc/nginx/conf.d/semoh-app-api-service-url.inc;

        location / {
                proxy_pass $service_url;

        }
}

배포스크립트

./deploy.sh $env (예: ./deploy.sh prod)

CURRENT_PORT_PROFILE=$(curl -k http://api-test.semoh.co.kr/port-profile)

echo "> spring.profiles.active = $1"
if [ -z "$1" ]; then
  echo "profile을 입력하세요. (예: ./start-nostop.sh prod)"
  exit 1
fi

echo "> 현재 구동중인 Port 프로필"
echo "$CURRENT_PORT_PROFILE"

if [ "$CURRENT_PORT_PROFILE" == "port-1" ]; then
  CURRENT_RUNNING_PORT=8080
  WILL_RUNNING_PORT=8081
  WILL_RUNNING_PORT_PROFILE="port-2"
elif [ "$CURRENT_PORT_PROFILE" == "port-2" ]; then
  CURRENT_RUNNING_PORT=8081
  WILL_RUNNING_PORT=8080
  WILL_RUNNING_PORT_PROFILE="port-1"
else
  CURRENT_RUNNING_PORT=8081
  WILL_RUNNING_PORT=8080
  WILL_RUNNING_PORT_PROFILE="port-1"
fi

echo "> 현재 구동중인 Port $CURRENT_RUNNING_PORT"

JAR="./sangsang-noriteo-app-api/build/libs/sangsang-noriteo-app-api-0.0.1-SNAPSHOT.jar"

cd ../../
rm $JAR
./gradlew :sangsang-noriteo-app-api:test
./gradlew :sangsang-noriteo-core:clean
./gradlew :sangsang-noriteo-core:cleanQuerydslSourcesDir
./gradlew :sangsang-noriteo-app-api:bootJar

fuser -k $WILL_RUNNING_PORT/tcp

echo "> 구동 시 Port $WILL_RUNNING_PORT"
echo "> 구동 할 port profile $WILL_RUNNING_PORT_PROFILE"

nohup java -jar -Dspring.profiles.active="$1",$WILL_RUNNING_PORT_PROFILE $JAR &

echo "> Health check 시작합니다."
echo "> curl -s http://localhost:$WILL_RUNNING_PORT/hello"
sleep 3

for retry_count in {1..300}; do
  response=$(curl -s http://localhost:$WILL_RUNNING_PORT/hello)
  up_count=$(echo "$response" | grep -c "hello")

  if [ "$up_count" -ge 1 ]; then
    echo "> Health check 성공"
    break![](https://velog.velcdn.com/images/sonaky47/post/60c08f31-794d-4d6a-a8bf-9f0ad26dd064/image.png)

  else
    echo "> Health check: ${response}"
  fi

  if [ "$retry_count" -eq 100 ]; then
    echo "> Health check 실패. "
    echo "> Nginx에 연결하지 않고 배포를 종료합니다."
    exit 1
  fi

  echo "> Health check 연결 실패. 재시도..."
  sleep 1
done

echo "> 전환할 Port: $WILL_RUNNING_PORT"
echo "> Port 전환"
echo "set \$service_url http://127.0.0.1:$WILL_RUNNING_PORT;" | sudo tee /etc/nginx/conf.d/semoh-app-api-service-url.inc

sudo service nginx reload

sleep 10
echo "> 구동중이었던 프로세스 종료"
fuser -k $CURRENT_RUNNING_PORT/tcp
profile
전 위메프, 이직준비중

0개의 댓글