현재 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의 코드를 확인해 보자.
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파일을 만드는 과정에서 몇가지 확인해야 할 사항이 있다.
자바 버전 변경 을 참조하여 디폴트 설정을 바꾸게 되었다.
"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으로 열어준다.


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
카나리 배포로 안한 이유도 궁금하네요