춤추는 개발자

[Spring boot] Nginx를 통한 무중단 배포 본문

Developer's_til/스프링 프레임워크

[Spring boot] Nginx를 통한 무중단 배포

Heon_9u 2021. 7. 28. 13:28
728x90
반응형

 

 지난 포스팅에서 Travics CI를 활용한 배포 자동화 환경을 구축했습니다. 코드를 커밋/푸쉬하면 자동으로 빌드&배포를 통해 완벽한 프로젝트를 유지할 수 있었습니다.

 하지만, 새로운 Jar 파일을 실행 및 배포하는 동안, 기존 Jar를 종료시키기 때문에 서비스가 잠시 중단된다는 문제가 남아있습니다. 이번 포스팅에서는 24시간동안 운영되어야하는 서비스를 위한 중단 없는 배포에 대해 알아보겠습니다.

 

✅ 무중단 배포

 무중단 배포 방식에는 몇 가지가 있습니다.

 

  • AWS에서 블루/그린 무중단 배포
  • 도커를 이용한 웹서비스 무중단 배포
  • L4스위치를 이용한 무중단 배포
  • Nginx를 이용한 무중단 배포

이 중 오픈소스 SW인 Nginx를 활용합니다.

 

✅ Nginx의 무중단 배포

 Nginx는 가벼움과 높은 성능을 목표로 하는 웹 서버 SW입니다. 웹 서버, 리버스 프록시, 캐싱, 로드 밸런식, 미디어 스트리밍 등의 기능을 갖고 있으며 트래픽이 많은 웹 사이트를 위해 확장성을 고려하여 설계한 비동기 이벤트 기반 구조를 가지고 있습니다.

 

  • 비동기 Event-Driven 기반 구조.
  • 다수의 연결을 효과적으로 처리.
  • 대부분의 코어 모듈이 Apache보다 적은 리소스로 더 빠르게 동작.
  • 더 작은 쓰레드로 클라이언트의 요청들을 처리.

 

Thread 방식
Event-Driven 방식

 

 Theard 기반의 경우, 하나의 커넥션 당 하나의 Thread가 필요합니다. 반면에, Event-Driven 방식은 여러 개의 커넥션을 전부 Event-Handler를 통해 비동기 방식으로 처리, 먼저 처리되는 것부터 로직이 진행됩니다. 

 

 Nginx의 기능 중 리버스 프록시외부의 요청을 받아 백엔드서버로 요청을 전달하는 행위를 말합니다. 즉, 리버스 프록시 서버는 요청을 전달하고, 실제 요청에 대한 처리는 뒷단의 웹 어플리케이션 서버들이 처리합니다.

 이러한 환경을 구축하기 위한 인프라 구조는 하나의 EC2 서버와 Nginx, Spring boot Jar를 2대 사용하는 것입니다. 여기서는 다음과 같은 구조로 진행합니다.

 

  • Nginx: 80(http), 443(https)
  • Spring boot1: 8081
  • Spring boot2: 8082

 

운영 과정

  1. 사용자는 서비스 주소로 접속합니다.(80 혹은 443)
  2. Ngnix는 사용자의 요청을 받아 현재 연결된 Spring boot1로 요청을 전달합니다. 
  3. 만약, 신규 배포가 필요한 경우, Nginx와 연결되지 않은 Spring boot2로 배포합니다.
  4. 배포 완료 및 정상적인 구동이 확인합니다.
  5. 확인이 완료되면, Nginx를 reload하여 요청 방향을 Spring boot2로 변경합니다.
  6. Nginx reload는 0.1초 이내에 완료됩니다.

 

 EC2(Linux)에서 Nginx 설치는 다음 명령어로 설치합니다.

sudo yum install nginx

 

 만약, install에 대한 권한이 없는 경우, 다음 명령어를 통해 권한을 추가합니다.

chmod +x ./install

 

 설치가 완료되면, Nginx의 포트번호인 80(http)을 [AWS의 보안 그룹]과 [구글 콘솔의 리디렉션 URI]에 추가 등록합니다. 

이외에도 아래와 같은 설정들이 추가됩니다.

 

  • 프록시 셋팅을 위한 nginx.conf의 location 설정
  • 무중단 배포 스크립트 Profile API
  • application-xxxx.properties (교재에서는 real1, real2)
  • 어플리케이션이 빌드 & 배포되는 과정에 실행되야할 스크립트 (appspec.yml)
스크립트명 역할
profile.sh 연결되지 않은 Profile과 Port 탐색
stop.sh Nginx와 연결되지 않은 Spring boot 종료
start.sh Nginx와 연결되어 있지 않은 Port로 새 버전의 Spring boot 시작
health.sh 새 Spring boot가 정상적으로 실행되는지 확인
switch.sh 배포와 구동 확인이 완료된 Port로 Nginx 전환

 

 전체적인 설정과 코드는 교재와 Github 저장소를 참고해주시기 바랍니다. 모든 과정을 첨부하기에는 교재 내용의 90%이상이 포함되기 때문에 일부러 제외했습니다.

 

추가적으로 Jar파일명이 겹칠 수 있기 때문에 build.gradle의 version명을 변경합니다.

version '1.0.1-SNAPSHOT-' + new Date().format("yyyyMMddHHmmss")

 

 여기까지 모든 과정을 완료했다면, 새로운 코드를 커밋/푸쉬했을 때, 중단없이 배포되는 서비스가 완성되었습니다. 배포할 때마다 URL주소 뒤에 /profile를 입력하면 profile이름이 real1에서 real2로, real2에서 real1으로 바뀌는 것을 확인할 수 있습니다. 이는 Nginx가 현재 바라보고 있는 Profile을 나타내며, 설정된 Port값이 전환됐다는 것을 의미합니다.

 

🙋‍♂️ 느낀점

 지금까지 5개의 포스팅을 올리며 다양한 개발 도구와 API 설계 방법 등을 배웠습니다.

 

  • IntelliJ, Gradle, Putty, Github
  • Spring boot, JPA, JUnit4
  • OAuth2.0 (Google, Naver)
  • AWS EC2, RDS, S3, IAM, CodeDeploy
  • Travis CI, Nginx

 프로젝트 초반에는 익숙한 Java로 코딩하며 빠르게 개발할 수 있었지만, 갈수록 인프라 부분들을 다루다보니 사소한 이슈들이 많았습니다. 이 과정에서 버전에 따른 문제, Annotation, Port설정, 오타 등을 접하며 온갖 삽질을 다했습니다.

 이슈가 터질 때마다 프로젝트 로그, CodeDeploy 로그, 구글 검색, Github의 issue탭을 번갈아 확인하였고, 아무리 해도 해결이 안되는 문제는 대체재를 찾으며 진행했습니다. 팀원, 사수도 없이 혼자 진행하는 터라 개발자로서 생존력(?)을 키우는 기분이 들었습니다.😂

 

 물론, 이 과정에서 즐거움을 느끼는 순간들이 많았습니다. 평소 접하고 싶었던 기술들을 활용해 볼 수 있었고, 관련 용어들이 나오는 게시글을 전보다 더 쉽게 이해할 수 있었습니다.

 AWS 연동과 빌드&배포 자동화, 무중단 배포까지 완성하며 단계별로 발생하는 문제를 해결하기 위해 다음 단계로 진행하는 것이 인상깊었고, 서버의 전체적인 구조를 보면서 사용자의 요청이 어떤 과정으로 응답되는지 이해할 수 있었습니다.

 

 다만, 포스팅을 진행할수록 교재의 너무 많은 내용이 포함되는 것 같아 진행 과정들을 최대한 제외시켰습니다. 혹여나 해당 포스팅을 보며 따라하시는(?) 분이 계셨다면 죄송하다는 말씀을 드리고 싶습니다. 반본적으로 교재를 언급하는 부분이 많았는데 홍보하는 것은 절대 아닙니다!

 취업을 준비하는 개발자로서, 개발자가 되고싶으신 분들에게 권장하는 도서라는 점만 알아주셨으면 좋겠습니다.😁

 

 

✅ Reference

https://m.blog.naver.com/jhc9639/220967352282

 

 

728x90
반응형