바이브코딩 서비스를 Docker 컨테이너로 실전서비스 론칭하기 · 3부 — CI/CD, IaC, 무중단 배포, 롤백

19장. 365일 24시간 무중단 서비스 만들기

18장에서 "죽지 않는 구조"를 봤습니다. 그런데 서비스가 가장 죽기 쉬운 순간이 언제인지 아세요? 역설적이게도 배포하는 순간입니다. 낡은 것을 새것으로 바꾸는 그 틈에 서비스가 끊기기 쉽습니다. 이번 장은 그 틈을 없애는 기술, 무중단 배포입니다.

[지금 여기]

우리는 22단계를 거쳐 "라이브 투표 서비스"를 만들고, 컨테이너에 담아, AWS 클라우드에 배포하는 여정을 지나고 있습니다. 아래가 그 전체 여정 지도입니다. 지금은 19단계, 무중단 배포 차례입니다.

  1. 1장개발 환경 준비 (터미널 / Node.js / Git / GitHub / VS Code / Claude Code)
  2. 2장Claude Code 사용법 익히기
  3. 3장무엇을 만들지 기획 (라이브 투표 서비스)
  4. 4장로컬에서 서비스 만들기 (React + Express + PostgreSQL)
  5. 5장Docker 개념
  6. 6장Dockerfile 작성
  7. 7장컨테이너 빌드·실행
  8. 8장AWS 시작 (계정 / IAM / 리전 / 비용)
  9. 9장이미지 태깅
  10. 10장ECR에 이미지 올리기
  11. 11장VPC 네트워크 구성
  12. 12장RDS 데이터베이스 생성
  13. 13장SSM 터널로 RDS 초기화
  14. 14장ECS Fargate + ALB로 배포 (기본 완성)
  15. 15장CI/CD (GitHub Actions)
  16. 16장IaC (Terraform)
  17. 17장배포 자동화 연결
  18. 18장현업 운영 구조 이해
  19. 19장무중단 배포
  20. 20장신규 버전 배포 & 롤백
  21. 21장(심화) 프론트를 Vercel로 분리 배포
  22. 22장마무리 (회고 + 리소스 삭제)

이번 장에서 완성되는 것

한 문장으로: 배포가 진행되는 내내 서비스에 1초 간격으로 요청을 보내며, 단 한 번도 실패 없이 200(성공)이 이어지는 것을 눈으로 확인하게 됩니다.

이렇게 되면 성공입니다: 감시 화면에 200이 끊김 없이 이어지는 동안, ECS 콘솔에서는 구버전 태스크가 새 태스크로 하나씩 갈아 끼워지고, 배포가 끝난 뒤 브라우저에는 바뀐 새 버전이 떠 있습니다. 즉 사용자는 아무것도 눈치채지 못한 채 서비스가 새 버전으로 교체된 것입니다.

사전 조건

이 장은 1~18장을 마친 상태에서 시작합니다. 아래가 갖춰져 있어야 합니다.

참고

이 장에서는 우리가 직접 명령을 외워 칠 일이 거의 없습니다. 감시(1초 간격 요청)와 배포(커밋·푸시)는 모두 Claude Code에게 프롬프트로 시킵니다. 우리는 그 결과를 "읽고 관찰"하는 것이 핵심입니다.

19.1 무중단 배포란 — 다운타임 0

먼저 문제 상황부터 봅시다. "배포"는 낡은 버전을 새 버전으로 바꾸는 일입니다. 그런데 이 교체를 어떻게 하느냐에 따라 서비스가 끊길 수도, 안 끊길 수도 있습니다.

용어

다운타임(Downtime) 서비스가 응답하지 못하는 시간입니다. 사용자가 접속했는데 오류 화면이 나오거나 무한 로딩이 걸리는 그 시간을 말합니다.

가장 순진한 배포를 생각해 봅시다. 구버전 컨테이너를 끄고 → 새 버전 컨테이너를 켠다. 이 사이의 몇십 초, 서비스는 완전히 죽어 있습니다. 사용자 입장에서는 "사이트가 안 들어가지는" 시간이죠. 하루 한 번 배포하면 하루 한 번 끊기는 서비스가 됩니다.

용어

무중단 배포(Zero-downtime Deployment) 배포가 진행되는 동안에도 다운타임이 0인 배포입니다. 사용자는 배포가 일어났다는 사실 자체를 모릅니다. "365일 24시간 서비스"의 필수 조건입니다.

[활용 사례] 무중단 배포가 실제로 어디에 쓰이나

무중단 배포는 특별한 대형 서비스만의 이야기가 아닙니다. 우리가 매일 쓰는 서비스 대부분이 이 방식으로 굴러갑니다.

무중단의 핵심 아이디어는 의외로 간단합니다. "끄고 나서 켜는" 게 아니라, "켜고 나서 끈다." 새것을 먼저 준비시켜 놓고, 새것이 정상임을 확인한 뒤에, 낡은 것을 내리는 순서로 바꾸는 것입니다. 이 아이디어를 구현하는 대표적인 두 방식이 있습니다.

19.2 롤링 업데이트 vs 블루/그린 — 두 가지 방식

방식 1. 롤링 업데이트(Rolling Update) — 한 대씩 갈아 끼우기

용어

롤링 업데이트 여러 대 중 한 대씩(또는 일부씩) 순서대로 새 버전으로 교체하는 방식입니다. 교체되는 동안 나머지가 사용자를 받칩니다. "롤링(rolling)"은 도미노가 한 칸씩 넘어가듯 순서대로 굴러간다는 뜻입니다.

우리처럼 태스크가 2개일 때의 진행을 그림으로 보면:

시작구1구2구버전 2대가 서비스 중1단계구1구2신1새 버전 1대를 추가로 띄운다2단계구1신1신1 정상 확인 → 구2를 내린다3단계구1신1신2새 버전 1대를 더 띄운다완료신1신2신2 정상 확인 → 구1을 내린다회색=구버전 파랑=새 버전 · 어느 순간에도 정상 2대 이상 유지

어느 순간에도 정상 동작하는 태스크가 최소 2대 유지됩니다. 다운타임이 생길 틈이 없습니다.

방식 2. 블루/그린 배포(Blue/Green) — 통째로 갈아타기

용어

블루/그린 배포 구버전 전체(블루)를 그대로 둔 채, 새 버전 전체(그린)를 한 벌 더 띄워 놓고, 준비가 끝나면 트래픽을 블루에서 그린으로 한 번에 전환하는 방식입니다. 문제가 생기면 다시 블루로 전환하면 되므로 롤백도 즉시입니다.

준비구1구2블루신1신2그린사용자 트래픽그린은 미리 띄워 놓고 검증전환구1구2블루신1신2그린사용자 트래픽준비 끝나면 한 번에 그린으로

[활용 사례] 두 방식은 실무에서 이렇게 나눠 씁니다

우리의 선택: ECS 서비스의 기본 배포 방식이 롤링 업데이트이고, 우리 규모에서는 그것으로 충분합니다. 이 장에서는 롤링을 실습하고, 블루/그린은 "더 큰 서비스에서 쓰는 상위 옵션"으로 기억해 두면 됩니다.

19.3 무중단의 진짜 주역 — ALB 헬스체크

그런데 롤링 그림에서 얼버무린 대목이 하나 있습니다. "신1이 정상 확인되면"이라는 부분입니다. 누가, 어떻게 확인할까요? 바로 2부에 만들고 18장에서 감시망 1겹이라 불렀던 ALB 헬스체크입니다.

배포 중에 벌어지는 일을 자세히 들여다보면:

  1. 새 태스크가 뜨면, ECS가 그 태스크를 ALB의 타깃 그룹에 등록합니다.
  2. ALB는 바로 트래픽을 보내지 않습니다. 먼저 헬스체크 요청을 몇 차례 보내서, 연속으로 정상 응답이 와야 "합격(healthy)" 판정을 내립니다.
  3. 합격한 태스크에만 사용자 트래픽을 보내기 시작합니다.
  4. 내려갈 구버전 태스크는 그 반대입니다. 먼저 분배 목록에서 빼서 새 요청이 가지 않게 하고, 처리 중이던 요청을 마칠 시간을 준 뒤 종료 합니다.
용어

드레이닝(Draining, 등록 취소 지연) 태스크를 내리기 전에 "하던 일을 마무리할 시간"을 주는 것입니다. 요청을 처리하던 도중에 뚝 끊기는 일을 막습니다. 식당이 문 닫을 때 새 손님은 안 받되, 이미 앉아 식사 중인 손님은 다 먹고 나갈 때까지 기다려 주는 것과 같습니다.

즉 무중단 배포는 롤링(순서)과 헬스체크(검증)의 합작입니다. "검증되지 않은 새것에는 트래픽을 주지 않고, 일하던 낡은 것은 마무리를 기다려 준다" — 이 두 원칙이 다운타임 0을 만듭니다.

여기에는 중요한 함의가 하나 있습니다. 만약 새 버전이 아예 뜨자마자 죽는 수준으로 고장 났다면, 헬스체크 불합격 → 트래픽이 가지 않음 → 구버전이 계속 서비스, 즉 불량 배포가 자동으로 차단됩니다. 하지만 "떠 있고 헬스체크는 통과하는데 특정 기능만 고장"인 경우는 걸러내지 못합니다. (20장의 버그가 정확히 이 유형입니다. 그래서 롤백이 필요합니다.)

19.4 우리 서비스의 무중단 설정 확인

좋은 소식: ECS 서비스는 기본이 롤링 배포라서, 우리는 이미 무중단의 틀 위에 있습니다. 확인하고 의미를 이해합시다.

[콘솔] 배포 설정 확인

  1. 웹브라우저에서 AWS 콘솔(https://console.aws.amazon.com)에 로그인하고, 오른쪽 위 리전이 서울(ap-northeast-2)로 되어 있는지 확인합니다.
  2. 위쪽 검색창에 ECS를 입력해 Elastic Container Service로 들어갑니다.
  3. 왼쪽 메뉴에서 클러스터(Clusters)vote-cluster를 클릭합니다.
  4. 서비스(Services) 탭에서 vote-service를 클릭합니다.
  5. 오른쪽 위 업데이트(Update) 버튼을 눌러 설정 화면으로 들어갑니다.
  6. 배포 옵션(Deployment options) 을 펼치면 두 숫자가 보입니다.
    • 최소 실행 태스크 비율(Minimum running tasks / Minimum healthy percent): 기본 100
    • 최대 실행 태스크 비율(Maximum running tasks / Maximum percent): 기본 200
주의

여기서는 값을 바꾸지 말고, 확인만 하고 나옵니다. 기본값 (100 / 200)이 무중단에 가장 안전합니다. 화면을 확인했으면 저장하지 말고 브라우저 뒤로 가기나 취소로 빠져나오세요. 실수로 숫자를 바꿔 저장하면 실험 결과가 달라질 수 있습니다.

이 두 숫자가 롤링의 규칙입니다. 우리(원하는 태스크 2개) 기준으로 읽으면:

그래서 배포는 "2개 → (새것 추가) 3~4개 → (검증 후 구버전 제거) → 2개"로 진행되고, 정상 태스크가 2개 미만이 되는 순간은 없습니다. 19.2의 그림이 이 두 숫자로 구현되는 것입니다.

참고

숫자를 바꾸면 배포 성격이 바뀝니다 최소 50% / 최대 100%로 두면 "구버전 1개를 먼저 내리고 그 자리에 새것" 방식이 됩니다. 추가 태스크 비용이 없는 대신 배포 중 수용량이 절반이 됩니다. 기본값(100/200)이 무중단에 가장 안전하므로 그대로 둡니다.

19.5 실험 준비 — HTTP 상태 코드부터 손에 익히기

이론은 끝났습니다. 이제 배포 중에 서비스가 정말 안 끊기는지 증명할 차례입니다. 그 전에, 실험의 눈이 될 도구 하나를 먼저 익혀 둡시다. 바로 HTTP 상태 코드입니다.

용어

HTTP 상태 코드(HTTP Status Code) 웹 요청의 결과를 나타내는 세 자리 숫자입니다. 서버가 요청을 잘 처리했으면 200(성공)을, 서버가 응답을 못 하는 상태면 502·503 같은 숫자를 돌려줍니다. 우리 실험에서는 200이 성공, 그 외 숫자가 실패입니다. 감시 화면에 200이 끊김 없이 이어지는지만 보면 됩니다.

[맛보기] 상태 코드를 눈으로 보기

감시 실험에 들어가기 전에, 상태 코드가 어떻게 생겼는지 우리 프로젝트와 분리해서 한 번 만져 봅시다. VS Code 위쪽 메뉴에서 터미널(Terminal) → 새 터미널(New Terminal)을 열고, 아래를 그대로 붙여넣어 Enter를 누르세요. (아무것도 바꾸지 않는, 그냥 남의 웹사이트에 요청 한 번 보내 상태 코드만 찍어 보는 안전한 명령입니다.)

curl -o /dev/null -s -w "%{http_code}\n" https://example.com   # 응답 본문은 버리고, HTTP 상태 코드만 한 줄로 출력

200 이라는 한 줄이 나오면 성공입니다. 방금 여러분은 "요청을 보내고 그 결과 코드를 읽는" 감시의 최소 단위를 직접 해 본 겁니다. 다음 절의 감시 실험은 이 한 줄을 1초마다 반복하면서, 우리 서비스 주소를 향해 쏘는 것일 뿐입니다.

19.6 실험 — 배포 중에도 정말 안 끊기는가

실험 설계는 이렇습니다. 배포가 진행되는 내내 서비스에 쉬지 않고 요청을 보내면서, 단 한 번이라도 실패가 나오는지 관찰한다.

0단계. 내 ALB 주소 확인

감시를 걸려면 우리 서비스 주소가 필요합니다. 14장에서 확인한 그 주소입니다. 잊었다면 19.4에서 들어간 것과 같은 경로로 콘솔에서 찾습니다: ECS 콘솔 → vote-clustervote-service → 아래쪽 네트워킹(Networking) 또는 연결된 로드밸런서에서 DNS 이름을 확인합니다. vote-alb-로 시작해 .ap-northeast-2.elb.amazonaws.com으로 끝나는 주소입니다. 이 주소를 복사해 둡니다. 아래 프롬프트의 예시 주소 자리에 여러분 주소를 넣습니다.

1단계. 감시 시작

VS Code에서 터미널을 하나 더 엽니다. (터미널 → 새 터미널.) 이 터미널에서 claude를 실행해 Claude Code를 켜고, 아래 프롬프트로 1초 간격 감시를 맡깁니다. 예시 주소를 여러분의 ALB 주소로 바꿔서 넣으세요.

프롬프트
우리 ALB 주소(http://vote-alb-xxxx.ap-northeast-2.elb.amazonaws.com)에
1초 간격으로 계속 요청을 보내면서, 시각·HTTP 상태 코드를 한 줄씩
출력하는 감시를 시작해 줘. 실패(200이 아닌 응답)가 나오면 눈에 띄게
표시해 줘. 내가 멈추라고 할 때까지 계속 실행해 줘.

몇 초만 지켜보면 ... 200, ... 200, ... 200 처럼 한 줄씩 계속 쌓입니다. 지금은 배포를 하지 않았으니 당연히 전부 200입니다. 이 감시 터미널은 끄지 말고 그대로 둡니다.

2단계. 배포 일으키기

감시가 도는 상태에서, 아무 변경이나 만들어 배포를 시작합니다. 15·17장 파이프라인 덕분에 main에 푸시만 하면 자동으로 배포됩니다. 감시가 도는 터미널은 그대로 두고, 앞서 쓰던 다른 터미널(또는 새 터미널)에서 Claude Code에게 아래를 시킵니다.

프롬프트
화면에 보이는 문구를 하나 살짝 바꿔서(예: 페이지 제목에 v1.1 표시)
커밋하고 main에 푸시해 줘.
주의

감시 터미널과 배포 터미널은 서로 다른 터미널이어야 합니다. 감시가 도는 터미널에 배포 프롬프트를 넣으면 감시가 멈춥니다. VS Code 터미널 영역에서 새 터미널을 열면 위쪽(또는 옆쪽)에 탭이 하나 더 생깁니다. 감시는 감시 탭, 배포는 배포 탭에서 각각 돌립니다.

3단계. 세 화면을 동시에 관찰

4단계. 판정

배포가 끝나면(Actions에 초록색 체크 표시) 감시 터미널로 가서, Claude Code 입력창에 아래처럼 멈추라고 지시하거나 Ctrl + C로 감시를 멈춥니다.

프롬프트
이제 감시를 멈춰 줘. 지금까지 나온 응답 중에 200이 아닌 게 한 번이라도
있었는지 알려 줘.

[확인]

이번 장이 제대로 끝났는지 눈으로 확인합니다.

이 세 가지가 확인되면, 여러분은 실무의 기본형인 무중단 롤링 배포를 눈으로 검증한 것입니다. 사용자가 투표하는 도중에 배포해도 아무도 모릅니다. 배포는 이제 한밤중에 몰래 하는 일이 아니라, 낮에 커피 마시며 하는 일상이 됩니다.

막히면

이 장에서 자주 나는 오류와 대처법입니다.

[체크리스트]

아래가 모두 됐는지 확인해 보세요. 하나라도 안 됐다면 해당 절로 돌아가 다시 진행합니다.

모두 체크됐다면, 여러분의 서비스는 이제 "배포해도 안 끊기는" 서비스입니다.