Blue/GreenDeploy

장준영·2024년 5월 10일

개요

현재 jenkins를 통해 배포를 할때
shutdown후 startup을 하여 톰캣이 단절되는 현상이 있었다.
저번의 Renewal 배포 이후 무중단 배포의 중요성을 깨달았기 때문에 이번 무중단 배포에 대해 고민하게 되었다.

적용 실패 시나리오

이러한 니즈에 대응하기 위해 로컬에서 사용중인 intellij의 deploy server처럼 톰캣 시작명령어중 deploy.sh가 있는지 찾아보았다. 하지만 deploy.sh라는 명령어는 존재하지 않았고

여러검색을 통해 Stackoverflow에서

touch /usr/share/tomcat/webapps/<WEBAPP-NAME>/WEB-INF/web.xml

키워드를 통해 jenkins ssh에 적용을 하려 하였다. 문제는 이러한 방법으로는 배포를 할 수 없었기 때문에, (war파일이 바뀌지 않아서 touch명령어를 사용해도 변경이 안된 것 같다.)
직접 deploy.sh라는파일을 만들기로 하였다.

적용 시나리오

무중단 배포에는 여러가지 방식이 있지만 nginx를 활용한 그중 Blue/Gree배포를 선택하였다.

저번의 Koin_Renewal 배포때처럼 ci/cd를 할때마다 매번 새로운 인스턴스를 만들고 연결 호스트를 바꾸어 주기엔 너무 많은 리소스 및 비용이 낭비되기 때문에 한 인스턴스 안에서 2개의 was를 띄워 healthcheck후 이전 버전은 종료하는 방식으로 진행하기로 하였다.

현재 legacy was에서 8080포트를 사용하고 있기 때문에 8081,8082포트를 사용하기로 하였다.

Deploy.sh 작성

먼저 deploy.sh의 코드를 확인해 보자.

echo "> 현재 구동중인 Port 확인"

CURRENT_PORT=8081;
NEW_PORT=8082;

# CURRENT & NEW 포트 확인
if lsof -Pi :8082 -sTCP:LISTEN -t >/dev/null; then
    echo "현재 8082 포트가 사용 중입니다."
    CURRENT_PORT=8082
    NEW_PORT=8081
elif lsof -Pi :8081 -sTCP:LISTEN -t >/dev/null; then
    echo "현재 8081 포트가 사용 중입니다."
    CURRENT_PORT=8081
    NEW_PORT=8082
else
    echo "8082과 8081 포트 모두 사용 중이지 않습니다."
fi
# NEW 포트 사용중일 경우 종료
if lsof -Pi :$NEW_PORT -sTCP:LISTEN -t >/dev/null; then
  echo "NEW_PORT가 사용중입니다."
  PID=$(lsof -Pi :$NEW_PORT -sTCP:LISTEN -t)
  kill -9 $PID
  echo "사용 중인 NEW_PORT 종료했습니다.."
  sleep 3
else
  echo "$NEW_PORT가 사용 중이지 않습니다."
fi

#Spring ON
nohup java -jar "KOIN_API_V2.jar" --server.port=$NEW_PORT --spring.profiles.active=dev > log.txt 2>&1 &

echo 백그라운드 모드로 애플리케이션 실행 성공 !!

# 새로 가동하는 서버 상태 확인
sleep 20

for retry_count in {1..10}
do
  response=$(curl -s http://localhost:$NEW_PORT/actuator/health)
  up_count=$(echo $response | grep 'UP' | wc -l)

  if [ $up_count -ge 1 ]
 then # $up_count >= 1 ("UP" 문자열이 있는지 검증)
      echo "> Health check 성공"
      break
  else
      echo "> 새롭게 가동하는 서버의 상태가 UP이 아닙니다."
      echo "> Health check: ${response}"
  fi

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

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

# 새로 가동하는 서버로 전환

echo "> 전환할 Port: $NEW_PORT"

echo "> Port 전환"
echo "set \$service_url http://127.0.0.1:${NEW_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc

echo "> NGINX Reload"
sudo service nginx reload

echo "> CURRENT_PORT를 종료합니다"
CURRENT_PID=$(lsof -Pi :$CURRENT_PORT -sTCP:LISTEN -t)
kill -9 $CURRENT_PID

시나리오를 정리해보자면

현재 구동중인 포트(current)와 새롭게 배포할 포트(NEW)를 확인한다.
NEW를 점유중인 프로세스가 있을 시 강제 종료한다.
NEW에 애플리케이션을 구동한다
애플리케이션의 정상 작동을 확인한다(health check)
nginx의 포트 포워딩 설정을 NEW PORT로 변경 후, nginx를 재가동 한다.
기존 사용하던 구버젼 PORT를 내린다.

또한 위의 bash파일을 만드는 과정에서 몇가지 확인해야 할 사항이 있다.

  1. 자바 버전 설정
    update-java-alternatives 자바 버전 재설정을 해줘야 한다. 이유는 레가시 프로젝트는 java8을, 새로운 프로젝트는 java17을 사용하기 때문이다.

자바 버전 변경 을 참조하여 디폴트 설정을 바꾸게 되었다.

  1. nginx권한 설정
"set \$service_url http://127.0.0.1:${NEW_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc

위와같이 포트의 번호가 바뀌게 된다면 nginx의 설정도 다시 해주어야 한다.
이를 위해선 ssh로 동작하는 유저(tomcat)에게 nginx의 설정을 바꾸어줄수 있도록 권한을 주어야 한다.
/etc/sudoers에서

tomcat  ALL=NOPASSWD: ALL

위와 같이 비밀번호 없이도 nginx의 설정을 바꿀수 있도록 해야 한다.

위의 sudoers 설정은 chmod 440으로 열어준다.

  1. Spring Actuator
    위의 배치파일에서 health-check를 하는 과정이 있다. 따라서 Koin_Api_v2에 Spring Actuator 라이브러리를 추가하였다.
    https://github.com/BCSDLab/KOIN_API_V2/pull/276
    이외에 조금더 자세하게 HealthCheck로 “Up” 보다 더 자세한 모니터링을 할수 있었지만 datadog을 통해 로깅을 하고 있었기에 제외하기로 하였다.

jenkins설정


Send files or execute commands over SSH after the build runs
배너에서 현재 중단 배포로 되고 있는 명령을

위와 같이 쉘스크립트를 변경해 주었다.

후기



위와 같은 방식으로 1.0버전과 1.1버전을 동시에 띄워 Blue/Green형식으로 무중단 배포를 할수 있게 되었다...!
물론 2초 정도 중단되어도 크게 문제가 없겠지라고 생각할 수도 있지만 실 서비스를 운용할때는 2초도 큰 시간이기에 중요하다고 생각한다.

참고
https://www.quora.com/How-can-I-deploy-a-war-file-in-Tomcat-9
https://stackoverflow.com/questions/31245611/where-do-i-find-the-war-ear-build-by-jenkins
https://blog.pium.life/dont-stop-deploy/
https://www.baeldung.com/linux/java-choose-default-version
https://docs.aws.amazon.com/corretto/latest/corretto-17-ug/generic-linux-install.html
https://access.redhat.com/documentation/ko-kr/red_hat_enterprise_linux/8/html/configuring_basic_system_settings/granting-sudo-access-to-a-user_managing-sudo-access
https://brownbears.tistory.com/225
https://velog.io/@zenon8485/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%95%A1%EC%B6%94%EC%97%90%EC%9D%B4%ED%84%B0
https://github.com/BCSDLab/KOIN_API_V2/pull/276
https://rutgo-letsgo.tistory.com/entry/BlueGreen-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%A7%84%ED%96%89%ED%95%B4%EB%B3%B4%EC%9E%90Spring-Boot-Nginx

1개의 댓글

comment-user-thumbnail
2024년 11월 24일

카나리 배포로 안한 이유도 궁금하네요

답글 달기