3단계 CD(지속적 배포) 파이프라인 설계 - 100-hours-a-week/KTB4-3rd-CLOUD GitHub Wiki

팀 운영 제약

V1은 6명 규모의 팀을 가정한다.

  • 인프라 운영 1명
  • 백엔드 2명
  • 프론트엔드 1명
  • AI 2명 팀의 운영 목표는 핵심 기능을 우선 구현하고, 짧은 주기로 배포해 사용자 피드백을 확보하는 것이다. 동시에 배포·모니터링·비용·백업 업무를 1명의 인프라 담당자가 감당할 수 있어야 한다. 따라서 V1에서는 다음을 배포 설계의 제약으로 둔다.
  • 운영 컴포넌트 수를 과도하게 늘리지 않는다.
  • 개발 환경과 운영 환경은 분리하되, 별도의 복잡한 플랫폼을 미리 도입하지 않는다.
  • 확장성보다 빠른 검증과 장애 발생 시 복구 가능성을 우선한다.
  • Dev는 수시 배포하고, Prod는 계획 배포한다. 1 인프라 예산을 3개월 90만 원으로 가정하고 있으며, ECS, EKS, ALB, RDS, Redis, Multi-AZ, Auto Scaling은 기본 구성에서 제외하고 단일 EC2와 Docker Compose를 사용한다. 이 사실은 무중단 배포와 고가용성을 기본값으로 선택하기 어렵게 만드는 근거다. 팀 운영 구조와 실행 제약 가정

운영 환경 구성별 역할

V1에서는 Dev와 Prod의 목적, 데이터, 사용자 영향, 배포 빈도, 승인 절차가 서로 다르다. Dev는 기능 통합과 다중 사용자 테스트를 위해 수시 자동 배포하고, Prod는 실제 사용자 데이터와 서비스 신뢰성에 직접 영향을 주므로 검증된 결과만 주 1~2회 계획 배포한다.

Pipeline 실행 환경 목적 실행 빈도
CI 환경 없음 코드·테스트 검증 PR마다
Dev CD Dev EC2 기능 통합·다중 사용자 테스트 수시
Prod CD Prod EC2 실제 사용자 서비스 반영 계획 배포
Rollback Dev 또는 Prod 장애 복구 필요 시

따라서 배포 대상 환경별로 트리거, 권한, 승인, 동시 실행 제어, 실패 처리 정책을 분리하기 위해 Dev CD Pipeline과 Prod CD Pipeline을 별도로 구성한다. 단, 환경별로 이미지를 다시 빌드하지 않고 CI에서 생성한 동일한 Release Artifact를 Dev에서 검증한 뒤 Prod로 승격한다. 공통 빌드·테스트 로직은 재사용하여 Pipeline 분리로 인한 중복과 설정 불일치를 방지한다.

공통 CI
  → Build
  → Artifact 생성

Dev CD Pipeline
  → Dev 자동 배포
  → Health Check
  → 다중 사용자 테스트
  → QA

Prod CD Pipeline
  → Dev 검증 결과 확인
  → 운영 승인
  → 동일 Artifact 배포
  → Health Check
  → Smoke Test
  → 관찰 또는 Rollback

배포 파이프라인

배포 파이프라인은 공통 CI Pipeline과 환경별 CD Pipeline으로 구분한다. CI Pipeline은 코드가 배포 가능한 상태인지 검증하고, Dev CD Pipeline은 기능을 통합하고 다중 사용자 시나리오를 확인한다. Prod CD Pipeline은 Dev에서 검증된 Release를 실제 사용자 환경에 반영한다.

Pull Request
  → CI 검증
  → 통합 브랜치 반영
  → Dev 자동 배포
  → Dev 기능·동시성·채팅 검증
  → Release 후보 생성
  → 운영 승인
  → Prod 계획 배포
  → 배포 후 검증

Pull Request 단계에서는 백엔드와 프론트엔드의 빌드·테스트·API 계약을 검증한다. 팟 참여 기능은 좌석 수 초과, 중복 참여, 동시 요청 상태 불일치와 같은 정합성 문제가 중요하므로 단순한 HTTP 응답 코드 검증만으로는 부족하다. 따라서 CI와 Dev 검증 과정에서 이러한 동시성 시나리오를 별도로 확인한다.

Dev 배포는 기능 단위로 수시 실행한다. 개발자가 각자의 로컬 환경에서만 작업하면 API 명세, 데이터베이스 스키마, 환경변수, WebSocket 연결 방식이 서로 달라질 수 있기 때문이다. Dev 환경은 이러한 차이를 조기에 발견하기 위한 공동 통합 환경으로 사용한다.

Prod 배포는 Dev 검증을 통과한 Release만 대상으로 하며, 주 1~2회 계획 배포한다. Prod에는 테스트 데이터가 아닌 실제 사용자 데이터가 존재하고, 배포 실패가 서비스 신뢰성과 사용자 경험에 직접적인 영향을 주기 때문이다. 환경마다 다시 이미지를 빌드하지 않고, CI에서 생성한 동일한 Container Image와 Release Manifest를 Dev와 Prod에 승격한다.

빌드 1회
→ 이미지 저장
→ Dev 배포·검증
→ 동일 이미지 Prod 배포

이를 통해 Dev에서 검증한 코드와 Prod에 반영된 코드가 달라지는 문제를 방지하고, 장애 발생 시 이전 Release를 정확하게 추적할 수 있다.

V1에서는 별도 Staging 환경을 추가하지 않는다. Dev가 기능 통합·다중 사용자 테스트·QA를 담당하고, 팀 규모와 운영 예산을 고려했을 때 Staging을 추가하면 EC2, 데이터베이스, Secret, 로그, 배포 관리 대상이 늘어나기 때문이다. 다만 이후 Prod 배포 실패가 반복되거나 DB migration 검증이 Dev만으로 부족해지면 Staging 환경을 추가할 수 있다.

배포 전략

V1 인프라는 단일 EC2와 Docker Compose를 기반으로 하며, ECS, EKS, ALB, RDS, Redis, Multi-AZ, Auto Scaling은 기본 구성에서 제외한다. 이 구조에서는 여러 애플리케이션 인스턴스를 동시에 실행하고 Load Balancer로 트래픽을 전환하는 형태의 배포를 기본적으로 보장하기 어렵다.

Recreate 배포

Recreate 배포는 기존 버전을 종료한 뒤 새 버전을 실행하는 방식이다.

이 방식은 단일 EC2와 Docker Compose에서 구현이 가장 단순하다. 신·구 버전을 동시에 실행할 필요가 없으므로 추가 인스턴스나 Load Balancer가 필요하지 않고, 배포 과정에서 발생할 수 있는 상태를 파악하기도 쉽다. 또한 운영 담당자가 1명인 V1 환경에서 배포 절차와 장애 대응 절차를 단순하게 유지할 수 있다.

반면 기존 컨테이너를 종료한 뒤 새 컨테이너를 실행하므로 짧은 서비스 중단이 발생한다. 진행 중인 HTTP 요청이 실패할 수 있고, WebSocket 연결도 종료될 수 있다. 따라서 사용자가 특정 시간대에 집중적으로 팟을 탐색하거나 채팅하는 서비스 특성상, 피크 시간대 배포를 피하고 배포 후 재접속과 상태 복구를 제공해야 한다.

얻는 것 잃는 것
구현과 운영이 단순함 배포 중 짧은 중단 발생
추가 인프라 비용이 낮음 WebSocket 연결 종료 가능
장애 원인을 파악하기 쉬움 진행 중인 요청 실패 가능
Rollback 절차가 단순함 즉시 무중단 전환 불가
1명의 인프라 담당자가 운영 가능 배포 시간대를 통제해야 함
Rolling 배포 (탈락)

Rolling 배포는 여러 개의 애플리케이션 인스턴스를 실행한 뒤 일부 인스턴스씩 순차적으로 교체하는 방식이다. 배포 중 일부 인스턴스가 기존 버전으로 요청을 처리하므로 서비스 중단 시간을 줄일 수 있다. 새 버전에 문제가 발생하더라도 아직 교체되지 않은 기존 인스턴스가 남아 있어 전체 장애로 확산되는 것을 줄일 수 있다. 하지만 Rolling 배포를 적용하려면 최소 두 개 이상의 실행 인스턴스, 트래픽 분배 장치, Readiness 기반 라우팅, 인스턴스별 상태 공유가 필요하다. 특히 채팅은 WebSocket 연결을 사용하므로 연결이 어느 인스턴스에 유지되는지와 재접속 시 메시지 상태를 어떻게 복구할지 추가로 설계해야 한다. 또한 신·구 버전이 일정 시간 함께 실행되기 때문에 DB 스키마가 두 버전과 동시에 호환되어야 한다. 단일 EC2에서 신·구 버전을 동시에 실행하면 CPU·메모리 여유가 부족할 수 있다. Rolling 배포는 장기적으로 유효하지만, 현재** V1의 단일 EC2·Docker Compose 구조와 1명의 인프라 담당자라는 조건에서는 운영 복잡도가 얻는 효과보다 크다.**

Blue-Green 배포 (보류)

Blue-Green 배포는 기존 버전인 Blue와 새 버전인 Green을 동시에 실행한 뒤, Green 검증이 완료되면 트래픽을 한 번에 전환하는 방식이다. 새 버전을 실제 사용자에게 노출하기 전에 별도 환경에서 Health Check와 Smoke Test를 수행할 수 있고, 문제가 발생하면 트래픽을 Blue로 되돌릴 수 있다. 따라서 Rollback 속도가 빠르고 배포 실패가 사용자에게 전파될 가능성을 줄일 수 있다. 그러나 신·구 버전을 동시에 유지해야 하므로 배포 시점에 더 많은 CPU·메모리·디스크 공간이 필요하다. 단일 EC2를 유지한 채 Blue-Green을 적용하면 한 호스트의 장애 범위는 줄어들지 않으며, 단지 같은 호스트 안에서 컨테이너를 두 개 실행하는 구조가 된다. 또한 다음 문제가 추가된다.

  • 트래픽 전환을 위한 Proxy 또는 Load Balancer 설정
  • 구·신 버전의 DB migration 호환성
  • 기존 WebSocket 연결을 어떻게 종료할지에 대한 Drain 정책
  • Blue 환경을 언제 제거할지에 대한 운영 정책
  • 동일 호스트에서 두 버전을 실행하기 위한 리소스 여유 따라서 Blue-Green은 향후 Prod의 중단 허용 범위가 줄어들고, 별도 실행 리소스와 트래픽 전환 계층을 확보했을 때 도입한다.
Canary 배포 (탈락)

Canary 배포는 전체 트래픽이 아니라 일부 트래픽만 새 버전으로 보내고, 오류율과 응답시간을 확인한 뒤 점진적으로 트래픽을 확대하는 방식이다. 가장 큰 장점은 새로운 버전의 문제가 전체 사용자에게 확산되기 전에 제한된 범위에서 발견할 수 있다는 점이다. 특히 매칭·정산과 같이 실패 비용이 큰 기능에는 효과적일 수 있다. 그러나 Canary 배포를 위해서는 다음 조건이 필요하다.

  • 사용자 또는 요청 단위 트래픽 분배
  • 버전별 오류율과 응답시간 측정
  • 충분한 사용자 트래픽
  • 자동 확대·중단 기준
  • 버전별 데이터와 이벤트 비교
  • WebSocket 연결의 버전 관리 현재 초기 HTTP 트래픽은 약 1.52 RPS로 추정되어 있으며, 실제 WebSocket 연결 수와 메시지량은 아직 별도로 검증되지 않았다. 트래픽이 적거나 특정 노선·시간대에 분산되면 일부 트래픽만으로 새 버전의 품질을 판단하기 어렵다. 따라서 V1에서는 Canary 배포를 적용하지 않고, 향후 실제 트래픽과 관측 지표가 확보된 뒤 검토한다.

V1 배포 전략 선택

각 전략을 비교한 결과** V1에서는 Dev와 Prod 모두 Recreate 배포를 기본**으로 사용한다. 다만 두 환경의 운영 목적은 다르게 적용한다. Dev는 기능 통합과 다중 사용자 테스트가 목적이므로 기능 단위로 수시 배포한다. 배포 중 짧은 중단이 발생하더라도 빠르게 기능을 확인하고 다음 수정으로 넘어가는 것이 중요하다. Prod는 실제 사용자와 운영 데이터가 존재하므로 주 1~2회 계획 배포를 수행한다. Prod 배포 전에는 Dev에서 동일 Artifact를 검증하고, 배포 후에는 Health Check·Readiness·핵심 사용자 흐름·WebSocket 재접속을 확인한다.

Dev:
수시 Recreate 배포
→ 빠른 통합 검증
→ 실패 시 직전 Dev 이미지 복구

Prod:
계획된 Recreate 배포
→ 운영 승인
→ 동일 Artifact 배포
→ Health Check·Smoke Test
→ 관찰 또는 Rollback

결과적으로 무중단을 포기한 것이 아니라, 현재 V1에서 추가 인프라와 운영 복잡도를 감수하면서까지 무중단 배포를 도입하지 않고, 계획된 중단을 통제 가능한 수준으로 관리하는 선택이다.

선택한 전략의 보완책

Recreate 배포의 단점인 배포 중단과 연결 종료를 줄이기 위해 다음을 적용한다.

  • 신규 이미지를 기존 컨테이너 실행 중에 미리 Pull
  • 배포 중 동시 실행을 막기 위한 환경별 배포 잠금
  • 신규 컨테이너의 Readiness 통과 전 외부 요청 차단
  • 기존 요청을 처리한 뒤 종료하는 Graceful Shutdown
  • 채팅 재접속 시 메시지 이력과 최신 팟 상태 복구
  • 배포 전 직전 정상 Release와 Rollback 명령 확인
  • Prod 배포 후 핵심 사용자 흐름과 오류 지표 관찰

무중단 배포 도입 여부는 초기 RPS만으로 결정하지 않는다. 실제 배포 중 요청 실패율, WebSocket 재접속률, 팟 참여 실패율, 데이터 불일치 건수, 배포 중단 시간을 수집한 뒤 재평가한다. 다음 조건이 확인되면 Blue-Green 또는 Rolling 배포로 전환한다.

  • 계획 배포 시간에도 매칭 수요가 지속적으로 발생함
  • 배포 중 팟 참여나 채팅 실패가 반복됨
  • 서비스 중단 허용 범위를 만족하지 못함
  • Prod 실행 인스턴스가 2개 이상으로 증가함
  • 트래픽 전환 계층과 충분한 리소스를 확보함
  • DB migration과 WebSocket Drain 정책을 검증함

배포 게이트와 성공 판정 기준

배포 게이트는 다음 단계로 진행하거나 배포 성공으로 확정하기 전에 반드시 통과해야 하는 검증 조건이다. 컨테이너가 실행되었다는 사실만으로 배포 성공으로 판단하지 않는다.

배포 전 검증
→ 컨테이너 기동
→ Startup Probe
→ Readiness Probe
→ Smoke Test
→ 배포 성공 확정
→ 운영 중 Health Check·Liveness 감시

배포 전 검증 게이트

  • CI 테스트 통과 여부
  • 배포 대상 Git SHA와 Release ID
  • 이미지 Digest 존재 여부
  • Dev 검증 완료 여부
  • 환경별 Secret과 환경변수
  • DB Migration의 하위 호환성
  • DB 백업 상태
  • 배포 대상 환경이 정확한지 여부
  • 동일 환경의 다른 배포가 진행 중인지 여부
  • Rollback 대상 Release 존재 여부 이 단계에서 하나라도 확인되지 않으면 배포를 시작하지 않는다.

컨테이너 기동 게이트

  • 프로세스가 실행 중인가?
  • 애플리케이션 포트가 열려 있는가?
  • 기본 HTTP 요청에 응답하는가?
  • 필수 환경변수가 정상적으로 로드되었는가?
  • 애플리케이션 초기화가 완료되었는가? 컨테이너가 실행 중이지만 애플리케이션이 초기화 중이거나 DB 연결을 완료하지 못했다면 기동 성공으로 판정하지 않는다.

Health Check와 Probe

Health Check는 컨테이너와 애플리케이션의 상태를 확인하는 장치다. Startup Probe, Liveness Probe, Readiness Probe는 서로 다른 목적을 가진다. Startup Probe는 애플리케이션이 최초 기동을 완료했는지 확인한다. 초기화 시간이 긴 애플리케이션을 Liveness가 너무 빨리 재시작하지 않도록 보호한다. Liveness Probe는 프로세스가 정상적으로 살아 있는지를 확인한다. Liveness 실패는 프로세스 재시작의 근거가 될 수 있지만, DB나 외부 API 장애를 무조건 Liveness 실패로 처리해서는 안 된다. Readiness Probe는 애플리케이션이 실제 요청을 처리할 준비가 되었는지 확인한다. 필수 설정, DB 연결, Migration 상태, 핵심 의존성의 준비 여부를 확인한다. Health Check는 배포 중뿐 아니라 운영 중에도 지속적으로 수행한다. 배포 성공 여부를 판정하는 데 사용되며, 운영 중 장애를 감지하는 역할도 수행한다.

Readiness 게이트

Readiness를 통과하기 전에는 새 컨테이너로 외부 요청을 전달하지 않는다.

Readiness 실패
→ 새 컨테이너 트래픽 차단
→ 원인 확인
→ 재기동 또는 Rollback

Readiness는 무중단 배포 자체를 제공하지 않는다. 단일 EC2에서는 대체 인스턴스로 트래픽을 넘길 수 없기 때문이다. 현재 구조에서 Readiness는 준비되지 않은 컨테이너에 요청이 전달되는 것을 방지하는 안전장치로 사용한다.

Smoke Test 게이트

Smoke Test는 다음과 같은 실제 핵심 사용자 흐름이 동작하는지 확인한다.

  • 로그인
  • 주변 팟 조회
  • 팟 생성
  • 팟 참여
  • 좌석 초과 처리
  • 중복 참여 처리
  • 채팅방 진입
  • WebSocket 메시지 송수신
  • 채팅 재접속
  • 정산 상태 조회 Health Check와 Readiness가 통과했더라도 Smoke Test가 실패하면 배포를 성공으로 확정하지 않는다.

배포 실패 처리

실패 상황 가능한 원인 처리
CI 테스트 실패 빌드 오류, 단위·통합 테스트 실패, API 계약 불일치 배포를 시작하지 않고 PR 수정 후 재검증
이미지 생성·Push 실패 Dockerfile 오류, 의존성 설치 실패, Registry 인증·네트워크 오류 기존 운영 버전 유지, 오류 수정 후 이미지 재생성
컨테이너 기동 실패 환경변수·Secret 누락, 포트 충돌, 애플리케이션 초기화 오류 외부 트래픽 차단, 로그 확인 후 재배포 또는 직전 이미지 Rollback
Startup Probe 실패 애플리케이션 기동 지연, DB 연결 실패, 초기화 로직 오류 기동 상태 확인, 설정 수정 또는 이전 정상 Release로 Rollback
Readiness Probe 실패 필수 DB 연결 실패, Migration 미완료, 필수 의존성 장애 트래픽 전달 중지, 원인 확인 후 재기동·재배포
Health Check 실패 프로세스 중단, 애플리케이션 포트 미응답, 컨테이너 비정상 상태 컨테이너 재시작, 반복 실패 시 배포 중단 및 Rollback
Smoke Test 실패 로그인·팟 조회·팟 참여·채팅 등 핵심 기능 오류 배포 실패 처리, Prod 반영 차단 또는 직전 Release Rollback
팟 참여 동시성 실패 정원 초과 승인, 중복 참여, 좌석 복구 오류 신규 배포 중지, 쓰기 기능 보호, 데이터 대사 후 Rollback 또는 수정 배포
WebSocket 실패 연결 불가, 메시지 저장 실패, 재접속 후 이력 복구 실패 채팅 기능 배포 차단, 연결·메시지 상태 확인 후 Rollback
DB Migration 실패 하위 호환되지 않는 스키마 변경, 권한·락·디스크 부족 애플리케이션만 즉시 Rollback하지 않고 DB 상태 확인 및 백업 복구 검토
배포 후 오류율 증가 신규 코드 예외, 성능 저하, 외부 의존성 오류 배포 중지, 직전 정상 Image Digest로 Rollback, 배포 후 지표 재확인
DB 데이터 정합성 오류 참여자·좌석·정산 상태 불일치, 부분 트랜잭션 자동 Rollback보다 쓰기 기능 차단, 데이터 대사·보정 후 서비스 복구
EC2·디스크 장애 호스트 장애, EBS 오류, Docker·운영체제 장애 컨테이너 재시작으로 해결하지 않고 백업·스냅샷 기반으로 새 환경 복구

현재 V1 구조의 한계

한계 지점 발생 원인 CD 파이프라인에 미치는 영향
단일 장애 지점 단일 EC2에서 Docker Compose로 서비스 운영 EC2 장애나 Docker 호스트 장애 발생 시 애플리케이션과 DB가 함께 영향을 받음
무중단 배포의 어려움 ECS, Kubernetes, ALB와 같은 트래픽 전환 계층이 없음 컨테이너 교체 시 일시적인 중단이 발생할 수 있어 Recreate 배포가 현실적임
운영 복구의 수동성 오케스트레이터의 자동 롤백 기능이 없음 이전 이미지로 재배포하는 명시적인 Rollback 스크립트가 필요함
Dev·Prod의 공통 장애 가능성 환경은 분리하지만 같은 클라우드 사업자·리전에 의존할 가능성 환경 분리는 배포 전파를 막지만 리전·사업자 장애까지 격리하지 못함
운영 담당자 부족 인프라 운영 담당자가 1명인 팀 구조 복잡한 배포 시스템보다 적은 수의 표준화된 Job과 자동화된 검증이 필요함
트래픽 산정의 불확실성 초기 HTTP RPS는 가정값이고 WebSocket 접속량은 별도 산정 필요 현재 트래픽만으로 고가용성이나 무중단 배포 필요성을 확정할 수 없음

V1은 사용자 피드백을 빠르게 얻기 위한 구조이므로 운영 복잡도를 낮추는 데 초점을 둔다. 대신 단일 EC2 구조에서는 고가용성이나 자동 장애 복구를 보장할 수 없다. 따라서 CD 파이프라인의 목표도 “무중단 배포”보다는 다음에 둔다.

  • Dev에서 빠르게 통합하고 검증한다.
  • Prod에는 검증된 동일 Artifact만 승격한다.
  • 배포 성공 여부를 컨테이너 실행 여부만으로 판단하지 않는다.
  • 장애 발생 시 직전 정상 버전으로 빠르게 복구한다.
  • DB 장애와 애플리케이션 장애를 동일한 방식으로 롤백하지 않는다. 이 판단은 팀 운영 제약, Dev·Prod 운영 목적, 핵심 사용자 흐름, 초기 트래픽 가정을 근거로 한다.
⚠️ **GitHub.com Fallback** ⚠️