최근 프로젝트에서 nginx 무중단 배포 설정을 하면서 겪은 일인데, 기록을 해두면 좋을 것 같아서 오랜만에 블로그를 쓰게 되었다.ㅎㅎ
우선 해당 프로젝트에서 github actions + aws code depoly + aws s3 + aws ec2를 사용하여 CI/CD 구축을 해놓은 상황이었다.
무중단 배포 설정은 해두지 않아서, 새 버전으로 배포를 할 시에 다운타임이 생긴다는 치명적인 문제점이 있었다..!
이를 해결할 수 있는 방법으로, 리버스 프록시 역할을 해줄 수 있는 nginx로 무중단 배포를 하고자 하였다.
TO-BE
- 하나의 EC2 혹은 리눅스 서버에 Nginx 1대와 스프링부트 jar를 2대를 사용
- 스프링부트1은 8081포트로, 스프링부트2는 8082포트(포트 번호는 자유)를 사용
Flow
1. 사용자는 서비스 주소로 접속한다. (80 혹은 443 포트)
2. Nginx는 사용자의 요청을 받아 현재 연결된 스프링부트로 요청을 전달한다.
- 현재 연결된 스프링부트는 스프링부트1이라고 가정하면, 8081포트로 요청을 전달하게 된다.
- 이때 Nginx는 8081포트로 띄워져있는 스프링부트1을 바라보고 있는것이다.
3. 신규 버전으로 배포하게 된다면, Nginx와 연결되지 않은 스프링부트2 (8082)로 배포한다.
- 이때, 배포하는 동안에 다운타임(서비스 중단)은 생기지 않는다.
- 아직 Nginx는 스프링부트1을 바라보고 있기 때문!
4. 배포가 끝나고 정상적으로 스프링부트2가 구동중인지 확인한다.
- 스프링부트2가 정상 구동중이면 nginx reload를 통해 8081 대신에 8082를 바라보도록 합니다.
- Nginx Reload는 1초 이내에 실행완료가 된다.
신규 배포가 일어날 때마다 위의 과정을 계속 반복하게 된다!!
<Nginx 무중단 배포는 아래의 두 블로그 글들을 참고했다!>
AWS CodeDeploy + Nginx로 무중단 배포
AWS CodeDeploy + Nginx 무중단 배포 아키텍처 01 0. 도입 배경 AWS Blue/Green 무중단 배포방식은 ELB와 EC2 인스턴스 하나 더 필요합니다. 비용이 많이 필요하다보니 또다른 방법인 Nginx를 이용한 무중단 배
www.sunny-son.space
Github Actions + CodeDeploy + Nginx 로 무중단 배포하기 (3)
Nginx 소개 Nginx는 널리 쓰이는 웹 서버 중 하나입니다. 동적 처리를 주로 담당하는 WAS(Web Application Server)와는 다르게 웹 서버(Web Server)는 정적 자원에 대한 응답을 내려주는 역할을 가지고 있는데
wbluke.tistory.com
이제부터는 이때 발생했던 트러블 슈팅을 기록해보고자 한다.
위와 블로그 글과 같이 설정을 해준뒤 배포를 시도했는데, 분명 code deploy에서는 배포 성공이라고 뜨는데, 접속하면 502 에러가 떴다...!!
터미널로 ec2에 접속해서, 8081또는 8082로 떠있는 jar 파일이 있는지 보았는데, 8082 포트로 실행중이었다...!
그럼 일단 다시 재배포를 하고나면 8081로 포트가 잘 바뀔까?!를 테스트해보았다.. 하지만 포트가 바뀌지 않았다..!
appspec.yml을 보면, run_new_was, health_check, switch 순으로 실행시키고 있다.. 그럼 switch에서 에러가 난걸까?! 라는 생각이 들었다..! 포트가 바뀌지 않았으니까..! 그 이전일수도 있고.
hooks:
ApplicationStart:
- location: scripts/run_new_was.sh
timeout: 180
runas: ubuntu
- location: scripts/health_check.sh
timeout: 180
runas: ubuntu
- location: scripts/switch.sh
timeout: 180
runas: ubuntu
우선 nginx 문제인 건 알겠고,, 에러 로그를 확인해보았다.
에러로그 확인 명령어 : tail -f /var/log/nginx/error.log
-> error.log 파일의 가장 마지막 부분을 보여준 다음, 새로운 로그 메시지가 추가될 때마다 실시간으로 로그를 갱신, 이를 통해 웹 서버인 Nginx의 에러 로그를 실시간으로 모니터링 하면서 새로운 오류 메시지를 확인할 수 있음
nginx 에러 로그를 살펴보니 에러메시지를 확인할 수 있었다. 살펴보니, Nginx가 127.0.0.1(로컬 호스트)의 8081 포트로 요청을 전달하려고 시도했지만, 서버가 띄워져 있지 않은 문제 등으로 거절되어 발생한 문제였다.
run_new_was.sh 스크립트를 보면, 현재 실행중인 포트를 찾고, 현재 실행중인 포트가 예를 들어 8082면 8081을 새롭게 배포를 진행할타켓 포트로 설정하고 있다.
# run_new_was.sh
#!/bin/bash
CURRENT_PORT=$(cat /home/ubuntu/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Current port of running WAS is ${CURRENT_PORT}."
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
fi
TARGET_PID=$(lsof -Fp -i TCP:${TARGET_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
if [ ! -z ${TARGET_PID} ]; then
echo "> Kill WAS running at ${TARGET_PORT}."
sudo kill ${TARGET_PID}
fi
nohup java -jar \
-Dserver.port=${TARGET_PORT} \
-Dspring.config.location=/home/ubuntu/app/application.yml \
/home/ubuntu/app/build/libs/* > /home/ubuntu/nohup.out 2>&1 &
echo "> Now new WAS runs at ${TARGET_PORT}."
sleep 10s
exit 0
그렇다면 위에서 확인했을 때 8082포트가 현재 실행중인 포트이므로, /home/ubuntu/service_url.inc에는
set $service_url http://127.0.0.1:8082가 적혀져 있어야 한다..!
참고) /home/ubuntu/service_url.inc 는 위의 run_new_was.sh에서 현재 실행중인 포트를 확인할 때 사용된다!
근데 확인해보니까 여기에 8081이 써져있었음..
아래 사진에서 보면 현재 8082포트에서 실행중인데, nginx는 8081을 가리키고 있음...
원인은, WAS(Web Application Server)가 8082 포트에서 실행 중이었지만, Nginx는 8081 포트를 바라보고 있었다. 이 경우, Nginx가 8081로 요청을 전달하려 했으나, 해당 포트에 WAS가 실행 중이지 않기 때문에 Connection refused 오류가 발생한것이었다..
해결)
Nginx 설정의 set $service_url을 8082로 바꾸면서 실제 실행 중인 WAS와 Nginx 설정이 일치하게 되었고, 502에러 잘 안뜨고 무중단 배포 된 것을 확인할 수 있었다~!