24시간 365일 중단 없는 서비스를 만들자.

Zerodin·2021년 11월 21일
1
post-thumbnail
post-custom-banner

무중단 배포

현재는 한대의 EC2서버에서 하나의 어플리캐이션이 작동중이다.
그래서 배포상태로 들어가면 서비스가 종료되는 문제가 생긴다.
이를 해결하기 위한 무중단 배포방법을 도입해보자.

무중단 배포의 종류

책에서는 몇가지의 무중단 배포 방식을 예로 들고 있다.

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

블루 그린 무중단 배포

Blue 는 실제 프로덕션 환경을 말하고, Green 은 새롭게 배포할 환경을 말한다.
이 둘을 항상 띄워놓고 배포할때 사용하는것을 말한다.

구성방식을 설명하면
우선 기존의 서버그룹과 같은 서버그룹을 하나 더 만든다. 서버 버전도 동일하고 모든게 똑같지만 로드밸런서에 연결되어 있지는 않다.

복사한 서버그룹 내부의 서버들을 업데이트 진행한다.

업데이트가 진행된 복사한 서버그룹을 로드밸런서에 연결한다.

기존에 존재하던 서버그룹을 로드밸런서에서 연결 해제한다.
이렇게 되면 처음에 먼저 배포가 끝난 서버그룹으로 연결이 이어지고 배포가 완료된 새 버전의 서비스제공이 시작된다.

블루그린 배포의 장점

  • 새롭게 배포할 환경에만 배포하면 되기 때문에 속도가 빠르다.
  • 만약에 잘못된 버전으로 배포했을 경우에 빠른 롤백이 가능하다.

단점

  • 동일한 서버그룹을 복수로 유지하는 비용이 든다.

도커 무중단 배포

도커란,
컨테이너라는 가상의 공간을 만들어서 호스트OS와는 전혀 별개의 환경에서 프로세스들이 동작하는 기술이다.

이러한 컨테이너를 이미지로 만들어 저장하면 하나의 이미지에서 여러개의 컨테이너를 생성할 수 있고 독립된 가상 공간에서 실행이 된다.

도커 이미지를 만드는 방법은 Dockerfile로 만들어지고 이미지를 다운받고 관리할 수 있는 도커허브 사이트를 지원하고 있다.

장점

  • 확장성이 좋다. 이미지만 만들어 놓으면 컨테이너는 그냥 띄우기만 하면 된다.
  • 배포 방법에 대한 일관성 있는 표준성을 가질 수 있다.

단점

  • 리눅스 커널에 따라 이슈가 약간 있다.
  • 도커 버전업할때 컨테이너가 죽는다.
  • 포트 포워딩에 iptable을 사용하면서 생기는 보안상 제약사항이 있다.

L4 스위치를 이용한 무중단 배포

로드밸런싱을 통한 무중단 배포를 가능하게 한다.
로드밸런싱이란 부하부산을 말하는데,
동일한 역할을 수행하는 서버 그룹을 VIP로 연결하고 서버로 향하는 트래픽을 일단 VIP를 가진 L4 스위치로 수신한 후 분배정책에 따라 적절한 서버에 분배해 주는 것을 말한다.

징점

  • 보안성이 높고 고급 스위칭 설정이 가능하여 상황에 따른 적절한 설정을 할 수 있다.
  • 용량에 관계없이 네트워크의 성능 개선에 기여한다.

단점

  • 프로토콜에 의존적이며, 설정이 복잡하다.
  • 고가의 장비이다. 비용절약을 위한 L2, L3 스위치와 적절한 혼합 배치가 필요할 수 있다.

어떤 방식으로 구현할 것인가

전부 각각의 장단점이 확실하다.
하지만 나는 혼자 공부하는 입장이고 비용적인 측면과 난이도 적인 측면에서 절충안이 필요했다.

여기서 등장하는 것이 NGINX이다.

NGINX란?

기존에 절대 강자였던 웹서버 아파치를 밀어낸 고성능 오픈소서 웹서버이다.

NGINX는 스레드와 프로세스를 사용하는 Apache 와 달리, 비동기 이벤트 호출 방식을 사용한다.

외부의 요청을 받아 백앤드 서버로 요청을 전달하는 기능인 리버스 프록시를 지원하는게 특징이다.

장점

  • Apache 대비 성능이 뛰어나다.
  • 상대적으로 저렴하다.

단점

  • Apache 대비 호환성은 아직 좀 부족하다.
  • 일반적으로 사용하는 웹 서버 기능 외에 부가적인 기능에 떨어진다.

NGINX를 통한 무중단 배포 구조

일단 인프라 구성은 기존에 사용중인 EC2서버를 그대로 사용할 예정이다.
그러므로 추가적인 인스턴스를 필요로 하지 않는다.
(물론 별도로 구축도 가능하다. 하지만 비용이 발생할 것이다.)

기존에는 8080포트로 하나의 Jar를 실행했었다.
이를 8081,8082 두 포트로 각각의 Jar를 실행하는 구조로 변경할 것이다.

이중에 8081포트 Jar를 Nginx와 연결을 한 상태로 가정을 하자.
그리고 새로운 버전의 업데이트가 이루어져야 하는 상황이 발생했다.
이 업데이트를 연결하지 않은 8082부터 시작을 한다.
8082가 업데이트 되는 도중에는 8081포트로 연결을 해줌으로써 서비스를 지속하는 것이다.

그리고 8082포트쪽 업데이트가 마무리되고 배포가 완료되면 정상적으로 작동하는지를 테스트한 뒤 NGINX 설정을 8082 포트를 바라보도록 수정후 reload를 시킨다.

8081포트 Jar를 업데이트 한다.
8082포트때와 동일한 과정을 반복해서 진행후 완료되면 다시 NGINX의 설정을 8081로 바꾸고 reload를 시키면 끝난다.

엔진엑스 설치와 스프링 부트 연동하기

EC2 서버에 NGINX를 설치하자.

sudo yum install nginx

에러가 발생했다.
nginx is available in Amazon Linux Extra topic "nginx1"

nginx1로 다시 수행해보자.

책에서 사용하던 커맨드가 또 지원되지 않는 것 같다.
지원되는 커맨드를 구글링해서 찾았다.

sudo amazon-linux-extras install nginx1.2

이번에는 설치가 진행이 되었다. 과연 제대로 된 것인가..

정상적으로 설치가 되었는지 확인하자.

sudo service nginx start

책에서 나오는 메세지와 다른 내용이 출력되었다.
nginx의 설치버전을 확인하고 프로세스에 nginx가 있는지 확인해 보았다.

내용으로만 봐서는 설치는 제대로 된 것 같다.

EC2 보안그룹에 NGINX가 사용할 포트80에 대한 인바운드 규칙을 추가하자.

포트를 지운다음 어플리케이션에 접근해보자.

http://ec2-3-37-37-4.ap-northeast-2.compute.amazonaws.com/

NGINX가 정상적으로 호출되었다.

Google, Naver API 리디렉션 URL 수정

기존에 등록된 주소에서 포트 정보를 제거한 URL을 등록하도록 하자.

Google 도메인 추가

Naver 도메인 추가

NGINX와 스프링부트 연동

이제 NGINX에 프록시를 설정하도록 하자.

sudo vim /etc/nginx/nginx.conf
아래와 같이 location 항목을 추가해주면 된다.

작성이 끝났으면 NGINX을 재시작해줘야 한다.

sudo systemctl restart nginx

이제 포트정보없이 도메인으로 접속해보자.

정상적으로 접근이 되었다.

무중단 배포 스크립트 만들기

이제 무중단 배포를 위한 API를 추가해야 한다.
API를 통해서 8081 포트를 사용할지 8082포트를 쓸지를 판단하도록 만들자.

profile API 추가

활성화된 properties를 반환하는 API를 만들어 보자.
real, real1, real2 3가지 중 하나를 반환하도록 만들 것이다.

만들어진 컨트롤러에 대한 테스트코드를 작성하도록 하자.

생성자 주입방식으로 주입된 properties 별로 테스트코드를 작성하자.

security에서 permitAll 항목에 '/profile'을 추가하자.

등록된 /profile에 대한 접근 테스트코드를 작성하자.

이제 개발된 소스를 commit & push 후 PR로 반영하자.
설정이 반영되었다면 도메인에서 port를 제거하고 /profile로 접근해보도록 하자.

현재는 8081, 8082로 설정한 properties가 없기 때문에 real로만 연결해주는 것을 확인할 수 있었다.

real1, real2 profile 생성

이제 8081, 8082를 사용하기 위한 properties를 작성하자.

port 8081을 사용하기 위한 properties

port 8082을 사용하기 위한 properties

완성된 properties를 git에 반영하도록 하자.

엔진엑스 설정 수정

NGINX의 설정파일을 하나 생성해야 한다.

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

생성되면 아래와 같은 코드를 추가하고 저장하도록 하자.
set $service_url http://127.0.0.1:8080;

프록시가 바라보는 주소를 방금 생성한 service-url.inc를 include하고 proxy_pass값을 service_url를 찾도록 nginx.conf를 수정해자.

sudo vim /etc/nginx/nginx.conf

수정이 완료되면 NGINX를 재시작해야 한다.

sudo systemctl restart nginx

배포 스크립트들 작성

새로운 경로로 무중단 배포를 구성하도록 하자.
아래 명령어로 새롭게 배포 경로를 생성하겠다.

mkdir ~/app/step3 && mkdir ~/app/step3/zip

이제 배포를 위한 스크립트들을 작성해야 한다.

  • stop.sh : 실행중인 스프링 부트 종료.
  • start.sh : 배포할 신규 버전 스프링 부트 프로젝트를 stop.sh 로 종료한 'profile'로 실행.
  • health.sh : 'start.sh'로 실행시킨 프로젝트가 정상적으로 실행됐는지 확인.
  • switch.sh : NGINX가 바라보는 스프링 부트를 최신 버전으로 변경.
  • profile.sh : 위의 4개의 스크립프 파일에서 공통으로 사용할 'profile'과 포트 체크.

변경된 경로를 수정하고 각 단계별 실행 스크립트 설정을 위해 appspec.yml을 수정해야 한다.

이제 스크립트들을 작성해 보자.

profile.sh

stop.sh

start.sh

health.sh

switch.sh

무중단 배포 테스트

배포할때마다 버전을 알아서 갱신하도록 bundel.gradle에 아래와 같은 코드를 추가하자.

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

Groovy 기반의 빌드 툴이기 때문에 문법을 사용하는 것이 가능하기에 이러한 시간정보를 직접 넣을 수 있었다.

이제 CodeDeploy에서 정상적으로 스크립트들이 작동하는지 확인해봐야 한다.

깔금하게 확인하기 위해 EC2 인스턴스를 재시작한 후 배포를 진행했다.
그래서 처음에는 구동되는 어플리케이션이 한개만 확인되었고
간단한 텍스트수정을 반영하면서 2차 배포를 시행한 뒤,
한번 더 텍스트수정을 반영하면서 무중된 배포되는 과정을 확인했다.

tail -f /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
처음 어플리캐이션이 한개만 실행된 상태

두번째 어플리케이션이 실행되는 상태

스프링부트 로그 확인

vim ~/app/step3/nohup.out

어플리캐이션이 실행중인지 확인이 필요할 경우

ps -ef |grep java
처음에 어플리캐이션이 한개만 실행 되었을때

두개의 어플리캐이션이 모두 실행되었을때

Travis CI의 빌드과정을 모니터링하면서 브라우저로 실시간으로 refrech를 하면서 텍스가 변화하는 과정을 눈으로 확인했다.

실제로 2.31 버전으로 화면이 잘 조회가 되고 있었고 빌드가 종료되는 시점에서 재조회를 하자마자 2.32로 버전이 갱신된 것을 확인 할 수 있었다.

마무리

일련의 긴 과정을 통해서 무중단 배포까지 구현을 해보았다.

일련의 과정을 간단하게 요약해보았다.

  1. 반영대상 파일을 Git에 반영한다.
  2. Travis CI에서 해당 파일을 배포하도록 CodeDeploy로 배포요청과 동시에 S3에 Zip파일로 압축하여 전달한다.
  3. CodeDeploy에서 hooks에 설정된 수순으로 스크립트를 실행한다.
    3-1. NGINX와 연결되어 있지 않은 스프링 부트를 종료.
    3-1. NGINX와 연결되어 있지 않은 Port로 새버전의 스프링 부트를 시작.
    3-2. 새로 시작된 스프링 부트가 정상적으로 실행되는지 확인.
  4. 정상실행이 확인되면 NGINX를 새로 시작된 스프링 부트가 사용하는 Port로 reload

이러한 과정을 통해서 실시간으로 서비스가 중단되지 않고 배포가 진행되고 완료되는 순간 사용 Port가 스위칭 되면서 배포가 완료된 서비스로 자연스럽게 이어지는 것을 확인할 수 있었다.

나는 기존에 SI에서 일할때 L4스위치를 통한 무중단 배포는 경험을 해본적이 있었지만
NGINX를 통한 무중단 배포는 처음 경험해봤다.

두가지 경험 다 개념은 스위칭을 해준다는 점에서 공통적이였지만
L4스위를 작업했을때의 번거로움과 비싼 장비로 구현되는 점과 비교해도 손색이 없을 정도로 사용도 편했고 성능도 우수했다.

보통은 NGINX도 별도의 인스턴스 서버를 만들어서 병렬화된 어플리캐이션 서버를 연결하는 구성을 하지만 스터디과정에서는 비용적인 측면을 고려해서 단 한개의 인스턴스 서버에서 포트만으로 병렬화된 무중단 배포를 구현했다.

Git hub : https://github.com/kdh85/spring-aws-study.git

profile
멈추지 않는 사람이 되고 싶어요!
post-custom-banner

0개의 댓글