무중단 배포와 로드 밸런스 Pipeline
현재 구성된 아키텍쳐 기준으로 작성하였습니다.
여기서 현재란 우아한테크코스 교육 때를 뜻합니다 :) …
해당 글은 22년 늦가을에 작성된 오래된 글을 재구성하여 작성되었습니다.
두 가지 관점에서 무중단 배포를 기획했습니다.
- 하나는 배포 과정 중에는 사용자의 요청이 중단되지 않아야 한다는 것. (무중단)
- 다른 하나는 배포 후에 두 WAS 모두 사용자 요청을 받을 수 있어야 한다는 것입니다 (부하 분산)
아래 파이프라인은 복잡하게 보이나 결국 롤링 배포를 하는 것일 뿐입니다
(단, 현재 아키텍처 구조상 스케줄러 역할을 교체하는 과정이 있어서 보다 더 복잡하게 되었습니다)
저때 실제로 배포가 이루어지는 곳, 즉 WAS의 재기동이 일어나는 곳은 아래와 같이 세 군데입니다
- Change WAS 2 to Master
- Deploy WAS1 (Scheduler)
- Deploy WAS2 (Non-Scheduler)
약 40 TPS로 꾸준히 부하를 주어서 모니터링을 하면 아래와 같은 그래프를 볼 수 있습니다.
저때 실제로 배포가 이루어지는 곳, 즉 WAS의 재기동이 일어나는 곳은 아래와 같이 세 군데입니다
- Change WAS 2 to Master
- Deploy WAS1 (Scheduler)
- Deploy WAS2 (Non-Scheduler)
약 40 TPS로 꾸준히 부하를 주어서 모니터링을 하면 아래와 같은 그래프를 볼 수 있습니다.
그래프는 2가지가 있습니다.
위쪽 그래프는 사용자의 요청을 처리하는 것을 나타낸 것인데, 요청에 대한 처리를 꾸준히 하고 있음을 알 수 있습니다.
아래는 응답 시간을 나타내는 것이며 앞서 설명한 3개의 배포 과정에서만 응답이 지연되는 것을 알 수 있습니다.
다행히 배포가 이루어지는 약 10~20초간 사용자 요청이 중단되는 경우는 거의 없었습니다.
또, WAS 내부에서 로그를 확인해 보면 각 배포가 진행 중인 WAS는 요청을 받는 것을 멈추고 반대편 WAS가 바쁘게 요청 처리하는 것을 볼 수 있습니다.
배포 과정 설명
비즈니스 로직상 스케줄러가 필요했는데 멀티모듈을 학습하기엔 일정이 여의치 않아 운영 중인 2대의 WAS중 1대를 스케줄러로 삼기로 했습니다.
팀에서는 스케줄러의 역할을 하는 WAS를 Master,
그렇지 않은 것을 Slave라고 했습니다
WAS 1번을 WAS#1,
WAS 2번을 WAS#2 이라고 하겠습니다.
기본적으로 WAS#1은 Master,
WAS#2는 Slave입니다.
큰 흐름에서 배포 과정은 WAS#1을 배포하고 WAS#2를 배포합니다.
단 여기서 스케줄링은 멈추어선 안됩니다.
따라서 WAS#2를 먼저 Master로 바꾸어주고 (동일 버전을 설정을 바꾸어 재실행)
WAS#1을 배포합니다.
그리고 WAS#2를 Slave로 바꾸어서 배포합니다.
한쪽 WAS가 배포가 진행되는 동안 리버스 프록시(NginX)는 반대편 WAS에만 연결을 합니다
모든 배포가 이루어진 후 프록시는 모든 WAS에 연결합니다 (부하 분산)
배포과정 중 자동 복구
헬스 체크를 통해 배포를 한 WAS가 정상 동작하는지를 확인할 수 있습니다.
첫 번째 WAS에 배포를 하는 과정에 문제가 생길 경우 Recovery 파이프라인을 통해 복구 과정을 실행합니다.
(정확히는 테스트 통과 → 빌드 이후 Run할 때 문제가 생긴 경우입니다)
Recovery가 동작하는 시점은
- old version의 WAS#2가 Master로 동작하는 중
- 스케줄링 정상 동작
- new version의 WAS#1이 Down
-입니다
따라서 이 상태에서는 Jenkins는 단지 WAS#1만을 old version으로 복구한다면 전체 시스템은 회복이 됩니다.
구체적으로 WAS#2를 Health Check 중에 문제가 생길 경우 Jenkins는 직전 버전을 git checkout 을 해서 가져옵니다.
(상세 과정, 생략해도 전체적인 이해에는 지장이 없습니다.)
WAS#1 배포 이후 health check 를 하여 특정 횟수만큼 응답이 없으면,
jenkins는 직전 버전(혹은 tag)을 git checkout을 합니다.
그리고 WAS#1을 배포한 후 헬스체크를 해서 이상이 없으면 WAS#2를 Slave로 재배포 합니다.
한쪽 WAS가 배포가 진행되는 동안 리버스 프록시(NginX)는 반대편 WAS에만 연결을 합니다
모든 배포가 이루어진 후 프록시는 모든 WAS에 연결합니다 (부하 분산)
복구과정 확인
본 파이프라인에서 서버 기동 중에 문제가 생길 경우 복구하는 시나리오입니다.
- 본 파이프라인
- 복구 파이프라인
문제점
위 아키텍처는 다음과 같은 문제가 있습니다.
아래와 같은 시간 동안 스케줄러가 동시에 작동하는 문제가 있습니다.
- WAS#2가 Master로 전환되는 구간
- WAS#1이 배포를 시작하기 전 시간 간격
지각 처리의 경우 멱등 연산이므로 문제가 되지 않지만 다른 비즈니스로 로직이 생기고 그것이 멱등하지 않다면 문제가 됩니다
따라서
- 스케줄러를 별도로 분리하거나
- 항상 동작하는 싱글 인스턴스인 젠킨스가 어느 한 WAS에게 트리거
-하는 방법으로 해결해야 합니다. (그러나 당시에는 팀프로젝트 마감되고 EC2 인스턴스를 모두 반납하여 구현하지는 못했습니다)
그외 개선 포인트
스케줄러 역할을 변경하는 방식
Master, Slave 활성화 여부는, 재배포를 통해서 이루어져 왔습니다. 하지만 Spring Actuator를 이용해서 bean만 refresh를 하는 방식도 있고
Rest API를 통해서 스케줄러의 동작에 대한 switch를 끄는 방식으로 로직을 구성하는 방식도 있었을 것 같습니다.
가장 좋은 방법은 스케줄러를 WAS 2대 중 한 대에게 역할을 부여하는 것이 아니라
별도의 책임으로 분리하는 것이라고 생각이 들었습니다.
왜냐하면 CI/CD 구축이 생각보다 큰 비용이 들었기 때문입니다.
아래의 방법들에 대해 생각해 보았습니다.
- Jenkins에서 스케줄 후 WAS에 요청
- 스케줄러 모듈 분리후 별도 서버로 배포
hi hello... World >< 가장 아름다운 하나의 해답이 존재한다
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!