이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › 아키텍처와 운영 › 배포와 운영
배포 전략
서비스를 끊지 않고 새 버전으로 바꾸려면
서버 4대가 v1으로 돌고 있습니다. 이것을 v2로 바꾸는 방법에 따라, 배포 중에 서비스가 끊기는지, 서버가 얼마나 더 필요한지, 새 버전에 문제가 있을 때 몇 명이 영향을 받는지가 달라집니다.
문서·표준 대조 · Kubernetes와 스프링 부트 공식 문서
이 실습에 나오는 개념
- 롤링, 블루-그린, 카나리 배포
- 서비스 중단 없이 새 버전을 배포하는 방식들이다. 롤링은 서버를 일부씩 차례로 새 버전으로 교체한다. 블루-그린은 새 버전 환경을 통째로 따로 준비한 뒤 트래픽을 한 번에 전환한다. 카나리는 일부 사용자에게만 새 버전을 먼저 적용해 문제가 없는지 확인한 뒤 점차 확대한다.
- Graceful Shutdown
- 애플리케이션을 종료할 때 새 요청은 받지 않고 이미 처리 중인 요청은 끝까지 처리한 뒤 종료하는 것이다. 배포 등으로 서버를 내릴 때 진행 중이던 요청이 중간에 끊겨 오류가 나는 것을 막는다.
- 헬스 체크(Liveness, Readiness)
- 서버가 정상인지 주기적으로 확인하는 것이다. Liveness는 프로세스가 살아 있는지를 확인해 문제가 있으면 재시작하게 한다. Readiness는 요청을 받을 준비가 되었는지를 확인해 준비되지 않은 서버로는 트래픽을 보내지 않게 한다. 서버가 시작 중이거나 종료 중일 때 요청이 들어가는 것을 막는다.
- 로드밸런서
- 들어오는 요청을 여러 서버에 나눠 보내는 장치다. 서버의 상태를 주기적으로 확인(헬스 체크)해 문제가 있는 서버로는 요청을 보내지 않는다. 한 서버에 부하가 몰리는 것을 막고, 일부 서버에 장애가 나도 서비스를 유지하게 한다.
- CI/CD
- CI(지속적 통합)는 코드 변경을 자주 합치고, 합칠 때마다 자동으로 빌드와 테스트를 실행해 문제를 일찍 발견하는 것이다. CD(지속적 전달·배포)는 검증된 코드를 자동화된 절차로 배포 가능한 상태로 만들거나 운영 환경까지 배포하는 것이다. 사람이 수동으로 하던 빌드·테스트·배포를 자동화해 실수를 줄이고 배포 주기를 짧게 한다.
이렇게 바꿔 보세요
- 처음 설정으로 — 롤링 배포. 한 대씩 바꾸므로 서비스가 끊기지 않습니다.
- 전부 내리고 올리기 — 가장 단순하지만 그동안 서비스가 멈춥니다.
- 블루-그린 — 서버가 두 배 필요하지만 전환과 되돌리기가 한순간입니다.
- 버그가 있는 버전을 롤링으로 — 문제를 알아챌 때까지 영향 범위가 점점 커집니다.
- 버그가 있는 버전을 카나리로 — 일부 요청만 영향을 받고 되돌립니다.
- Graceful Shutdown이 없으면 — 서버를 내릴 때 처리 중이던 요청이 끊깁니다.
처음 설정으로 실행하면
- 현재 상태: 서버 4대가 모두 v1이고, 로드밸런서가 요청을 고르게 나눠 보냅니다.
- 서버 1: 로드밸런서가 이 서버로 새 요청을 보내지 않게 하고, 서버에는 종료 신호를 보냅니다. 처리 중이던 요청을 끝까지 마친 다음 종료합니다.
- 서버 1: v2로 올리는 중입니다. 준비 상태 확인을 통과하기 전까지 요청을 받지 않습니다. 그동안 3대로 버팁니다.
- 서버 1: 준비 완료. 다시 요청을 받습니다. 지금은 v1과 v2가 함께 서비스 중이며 요청의 25%가 v2로 갑니다.
- 서버 2: 로드밸런서가 이 서버로 새 요청을 보내지 않게 하고, 서버에는 종료 신호를 보냅니다. 처리 중이던 요청을 끝까지 마친 다음 종료합니다.
- 서버 2: v2로 올리는 중입니다. 준비 상태 확인을 통과하기 전까지 요청을 받지 않습니다. 그동안 3대로 버팁니다.
- 서버 2: 준비 완료. 다시 요청을 받습니다. 지금은 v1과 v2가 함께 서비스 중이며 요청의 50%가 v2로 갑니다.
- 서버 3: 로드밸런서가 이 서버로 새 요청을 보내지 않게 하고, 서버에는 종료 신호를 보냅니다. 처리 중이던 요청을 끝까지 마친 다음 종료합니다.
- 서버 3: v2로 올리는 중입니다. 준비 상태 확인을 통과하기 전까지 요청을 받지 않습니다. 그동안 3대로 버팁니다.
- 서버 3: 준비 완료. 다시 요청을 받습니다. 지금은 v1과 v2가 함께 서비스 중이며 요청의 75%가 v2로 갑니다.
- 서버 4: 로드밸런서가 이 서버로 새 요청을 보내지 않게 하고, 서버에는 종료 신호를 보냅니다. 처리 중이던 요청을 끝까지 마친 다음 종료합니다.
- 서버 4: v2로 올리는 중입니다. 준비 상태 확인을 통과하기 전까지 요청을 받지 않습니다. 그동안 3대로 버팁니다.
- 서버 4: 준비 완료. 다시 요청을 받습니다. 이제 모든 요청이 v2로 갑니다.
- 4대 모두 v2가 됐습니다. 배포하는 동안 서비스는 끊기지 않았습니다.
실행 결과
- 추가 서버 없이 무중단으로 배포합니다. 배포 중에는 처리 용량이 줄고, v1과 v2가 동시에 요청을 받으므로 DB 스키마나 API가 두 버전 모두와 호환돼야 합니다.
코드
# Kubernetes Deployment (replicas: 4)
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 한 번에 1대까지만 내림
maxSurge: 0
readinessProbe: # 준비됐을 때만 요청을 받음
httpGet: { path: /actuator/health/readiness, port: 8080 }
# application.yml
server.shutdown: graceful # 처리 중인 요청을 끝내고 종료
spring.lifecycle.timeout-per-shutdown-phase: 30s
이 모형의 전제
서버 4대, 한 번에 한 대씩 교체하는 단순한 구성입니다. 버그는 헬스 체크로는 걸러지지 않고 실제 요청을 받아 봐야 드러나는 종류라고 가정했고, 롤링에서는 2대를 바꾼 시점에 발견한다고 보았습니다. 서버를 내릴 때 처리 중인 요청은 서버당 2건으로 두었습니다. 각 방식의 정의는 Kubernetes와 스프링 부트 공식 문서의 설명을 따랐습니다.
앞 실습: CI/CD 파이프라인 · 다음 실습: 컨테이너와 가상 머신