본문 바로가기

프로젝트/고민

Nginx를 이용해 무중단 배포하기

들어가며

저번 시간에 Jenkins를 이용해 CI/CD 파이프라인을 구축하였다. 따라서 빌드와 배포의 자동화를 이루었다. 하지만 배포의 과정속에서 어플리케이션이 종료된다는 문제가 남아있다. 이를 해결하기 위해 무중단 배포 서비스를 적용시켰다.

 

무중단 배포 방식에는 docker를 이용하거나, AWS의 블루 그린 무중단 배포를 이용하는 등 Nginx 외의 수단을 사용하더라도 배포 할 수 있지만 가장 저렴한 방법으로 서비스를 제공할 수 있기에 Nginx를 이용했다.

 

Nginx

Nginx란 리버스 프록시, 로드 밸런서, http 캐시로 쓰일 수 있는 웹서버 이다. 요청에 응답하기 위해 이벤트 기반 구조를 채택했고, 덕분에 현재 웹서버 분야에서 1등을 차지하고 있다. 이 중 리버스 프록시를 이용해 무중단 배포를 구현할 것이다.

 

리버스 프록시란 다음 그림과 같이 외부의 요청을 받아 백엔드 서버로 요청을 전달하는 행위를 이야기 한다. 즉 사용자와 실제 서버 간에 Nginx 서버를 두어 요청을 실제 서버에 전달하게 하는 것이다.

 

 

무중단 배포는 다음의 순서로 진행된다.

1. 새로운 버전의 프로젝트가 올라온다.

2. 해당 프로젝트를 기존 올라와있는 프로젝트와 다른 포트에 구동한다.

3. Nginx가 새로운 프로젝트를 가르키도록 설정하고 reload 시킨다.(reload 작업은 0.1초 이내에 완료됨)

4. 구 버전의 프로젝트를 종료시킨다.

 

이러한 과정을 거친다면 새로운 버전이 배포되는 중에도 사용자는 서비스를 계속 이요할 수 있다.

 

구현

1. ec2 인스턴스에 nginx를 설치한다. 나의 경우 linux2 AMI를 이용했다. 명령어는 다음과 같다.

sudo amazon-linux-extras install nginx1 // nginx를 설치한다.

sudo systemctl start ngingx // nginx를 실행한다.

sudo systemctl status nginx // nginx 상태를 확인한다.

 

2.  nginx reload시 include 파일의 변경사항만 변경되기 때문에 외부 설정 파일을 만들어 준다. 동적인 설정을 해주기 위해 다음의 명렁어로 파일을 만들었다. 나의 경우 8080포트에 Jenkins 서버가 구동되어 있기에 8088포트를 바라보게 해줬다.

sudo vim /etc/nginx/conf.d/service-url.inc

 

 

3. 다음으로 nginx가 스프링 프로젝트를 바라볼 수 있도록 프록시 설정을 해준다. sudo vim /etc/nginx/nginx.conf 를 통해 nginx 다음과 같이 설정하면 된다.

 

 

ec2의 ip로 접속하면 8088포트로 접속하지 않더라도 바로 $service_url 이 연결된다.

 

4. 이제 프로필 별로 서버 포트를 다르게 해줘야 하기에 두개의 프로필을 만들어 준다.

//application-first.yml
server:
	port: 8088

//application-second.yml
server:
	port: 8089

 

5. 다음으로 현재 서버가 어떤 port를 이용하고 있는지 알기 위해 다음의 컨트롤러를 추가해준다.

 

 

6. 이제 위에서 설명한 무중단 배포 순서에 맞게 다음과 같이 배포스크립트를 작성하면 된다. 

 

 

스크립트를 하나씩 설명하자면 다음과 같다.

1. curl -s http://localhost/profile을 통해 현재 구동죽인 서버의 프로필을 확인한다.

2. if문을 통해 현재 구동중인 서버의 프로필이 first 이면 새롭게 배포할 서버의 프로필을 second, 구동중인 서버의 프로필이 second 이면 새롭게 배포할 서버의 프로필을 first로 설정해준다.

3. 이후 새로운 버전의 프로젝트를 배포한다. 컴파일시 환경변수로 --spring.profiles.active=$IDLE_PROFILE을 추가하여 현재 구동중인 서버에 맞는 프로필로 작동할 수 있도록 해줬다.

4. 이후 echo "set \$service_url http://127.0.0.1:$IDLE_PORT;" |sudo tee /etc/nginx/conf.d/service-url.inc
sudo service nginx reload 명령어로 위에서 작성한 service-url을 변경하고 reload를 하여 nginx가 새로운 버전의 프로필에 맞는 포트를 바라볼 수 있도록 하였다.

5. 위에서 CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar) 를 통해 구 버전의 프로젝트의 pid를 얻어 구 버전의 서버를 종료시켰다.

 

이제 해당 프로젝트는 무중단 서비스를 제공하는 프로젝트가 되었다.

 

마치며

nginx의 리버스 프록시를 이용해 무중단 배포를 구현했다. 처음 적용해 보는 기술이기에 오랜 시간 걸렸지만 구현 하는데 성공하여 성장했다는 느낌을 받게 되었다. 추 후 health check 기능도 추가하여 안정성 있게 프로젝트를 관리할 생각이다.