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

Jinseok Lee·2022년 4월 16일
0

사연

단일의 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개의 댓글