본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 20. · 5 Views
컨테이너 앱 CI/CD 구축 완벽 가이드
AWS ECS와 CodePipeline을 활용하여 컨테이너 기반 애플리케이션의 자동 배포 파이프라인을 구축하는 방법을 알아봅니다. 블루/그린 배포 전략과 모니터링 설정까지 단계별로 설명합니다.
목차
1. 프로젝트 아키텍처 설계
어느 날 김개발 씨는 팀장님께 새로운 프로젝트를 배정받았습니다. "우리 서비스를 컨테이너로 전환하고, 배포 자동화를 구축해보세요." 처음 듣는 용어들에 김개발 씨는 당황했지만, 선배 박시니어 씨가 차근차근 설명해주기 시작했습니다.
컨테이너 CI/CD 아키텍처는 소스 코드 변경부터 운영 배포까지의 전체 흐름을 자동화하는 설계도입니다. 마치 공장의 생산 라인처럼, 각 단계가 유기적으로 연결되어 있습니다.
이를 통해 개발자는 코드만 작성하면 나머지는 자동으로 처리됩니다.
다음 코드를 살펴봅시다.
# 프로젝트 구조 예시
project/
├── src/ # 애플리케이션 소스코드
│ └── app.py
├── Dockerfile # 컨테이너 이미지 정의
├── buildspec.yml # CodeBuild 빌드 설정
├── appspec.yml # 배포 설정
└── taskdef.json # ECS 태스크 정의
# app.py - 간단한 Flask 애플리케이션
from flask import Flask
app = Flask(__name__)
@app.route('/health')
def health_check():
# 헬스체크 엔드포인트
return {'status': 'healthy'}, 200
김개발 씨는 회의실 화이트보드 앞에 섰습니다. 박시니어 씨가 그림을 그리며 설명을 시작했습니다.
"자, 먼저 전체 그림을 이해해야 해요." CI/CD 파이프라인이란 무엇일까요? 쉽게 비유하자면, 자동차 공장의 조립 라인과 같습니다.
부품들이 컨베이어 벨트를 타고 이동하면서 조립되고, 검사되고, 최종적으로 완성된 자동차가 출고됩니다. 마찬가지로 우리의 코드도 커밋되는 순간부터 빌드, 테스트, 배포까지 자동으로 흘러갑니다.
전통적인 배포 방식의 문제점을 먼저 살펴봅시다. 예전에는 개발자가 직접 서버에 접속해서 코드를 복사하고, 의존성을 설치하고, 서비스를 재시작했습니다.
실수하기 쉬웠고, 환경마다 다른 설정으로 골치가 아팠습니다. 더 큰 문제는 배포할 때마다 서비스가 중단되는 것이었습니다.
컨테이너 기반 아키텍처가 이런 문제를 해결합니다. Docker 컨테이너는 애플리케이션과 모든 의존성을 하나의 패키지로 묶어줍니다.
개발 환경에서 작동하면 운영 환경에서도 똑같이 작동합니다. "내 컴퓨터에서는 잘 되는데요"라는 변명은 이제 통하지 않습니다.
전체 아키텍처를 단계별로 살펴보겠습니다. 첫 번째 단계는 소스 코드 저장소입니다.
개발자가 GitHub에 코드를 푸시하면 모든 과정이 시작됩니다. 두 번째는 빌드 단계로, CodeBuild가 Dockerfile을 읽어 컨테이너 이미지를 만듭니다.
세 번째는 저장소로, ECR에 이미지를 저장합니다. 마지막은 배포로, ECS가 새 컨테이너를 실행합니다.
실제 프로젝트에서는 어떻게 적용할까요? 쇼핑몰 API 서버를 예로 들어봅시다.
개발자가 상품 조회 기능을 수정하고 커밋합니다. 그러면 자동으로 빌드가 시작되고, 테스트가 실행되고, 문제가 없으면 운영 서버에 배포됩니다.
전체 과정이 10분도 걸리지 않습니다. 아키텍처 설계 시 고려해야 할 핵심 포인트가 있습니다.
가용성을 위해 최소 2개 이상의 가용 영역을 사용해야 합니다. 확장성을 위해 Auto Scaling을 설정합니다.
보안을 위해 프라이빗 서브넷에 컨테이너를 배치하고, ALB를 통해서만 접근하도록 합니다. 모니터링과 로깅도 처음부터 고려해야 합니다.
CloudWatch Logs로 컨테이너 로그를 수집하고, X-Ray로 분산 추적을 구성하고, Metrics로 성능을 모니터링합니다. 문제가 발생하면 즉시 알림을 받을 수 있도록 설정합니다.
비용 최적화도 중요한 고려사항입니다. 개발 환경은 Fargate Spot을 사용하여 비용을 70%까지 절감할 수 있습니다.
운영 환경은 안정성을 위해 일반 Fargate를 사용합니다. 트래픽 패턴을 분석하여 적절한 스케일링 정책을 설정합니다.
박시니어 씨가 화이트보드를 가리키며 말했습니다. "이제 전체 그림이 보이죠?
하나씩 구축해보면 어렵지 않아요." 김개발 씨는 노트에 열심히 메모했습니다. 복잡해 보였지만, 각 조각이 어떻게 맞물리는지 이해하기 시작했습니다.
실전 팁
💡 - 처음부터 완벽한 아키텍처를 만들려 하지 말고, 단계별로 확장하세요
- 개발/스테이징/운영 환경을 분리하여 안전하게 테스트하세요
- 아키텍처 다이어그램을 문서화하여 팀과 공유하세요
2. ECR과 ECS 인프라 구성
아키텍처 설계를 마친 김개발 씨는 이제 실제 인프라를 구축할 차례입니다. 박시니어 씨가 AWS 콘솔을 열며 말했습니다.
"먼저 컨테이너 이미지를 저장할 곳과 실행할 환경을 만들어야 해요."
ECR(Elastic Container Registry)은 Docker 이미지를 안전하게 저장하는 프라이빗 저장소이고, ECS(Elastic Container Service)는 컨테이너를 실행하고 관리하는 오케스트레이션 서비스입니다. 마치 도서관(ECR)에 책을 보관하고, 열람실(ECS)에서 독자들이 책을 읽는 것과 같습니다.
다음 코드를 살펴봅시다.
# ECR 리포지토리 생성 (AWS CLI)
aws ecr create-repository \
--repository-name my-app \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES256
# ECS 클러스터 생성
aws ecs create-cluster \
--cluster-name production-cluster \
--capacity-providers FARGATE FARGATE_SPOT \
--default-capacity-provider-strategy \
capacityProvider=FARGATE,weight=1,base=1
# 태스크 정의 등록
aws ecs register-task-definition \
--cli-input-json file://taskdef.json
김개발 씨는 AWS 콘솔에 처음 접속했을 때를 떠올렸습니다. 수많은 서비스 목록에 압도당했던 기억이 납니다.
하지만 지금은 어디서 무엇을 해야 할지 명확했습니다. ECR부터 시작해봅시다.
Docker Hub를 사용해본 적이 있다면 ECR은 그것의 프라이빗 버전이라고 생각하면 됩니다. 우리 회사의 소스코드가 담긴 이미지를 공개 저장소에 올릴 수는 없으니까요.
ECR은 AWS 계정 내에서만 접근 가능한 안전한 저장소를 제공합니다. ECR 리포지토리를 생성할 때 중요한 옵션들이 있습니다.
이미지 스캔 기능을 활성화하면 푸시할 때마다 자동으로 보안 취약점을 검사합니다. CVE 데이터베이스와 비교하여 알려진 취약점이 있는지 확인하고 보고서를 생성합니다.
암호화 설정으로 저장된 이미지를 보호할 수 있습니다. 이미지 수명 주기 정책도 설정해야 합니다.
오래된 이미지가 계속 쌓이면 스토리지 비용이 증가합니다. 예를 들어 "최근 10개 이미지만 유지"하거나 "30일 이상 된 이미지는 삭제"하는 규칙을 만들 수 있습니다.
운영 환경에서 사용 중인 이미지는 태그로 보호합니다. 이제 ECS 클러스터를 살펴보겠습니다.
클러스터는 컨테이너를 실행할 논리적인 그룹입니다. 마치 데이터센터 안의 한 구역을 할당받는 것과 같습니다.
Fargate를 사용하면 서버 관리 걱정 없이 컨테이너만 신경 쓰면 됩니다. Fargate vs Fargate Spot의 차이를 알아야 합니다.
일반 Fargate는 안정적이지만 비용이 높습니다. Fargate Spot은 여유 용량을 활용하므로 최대 70% 저렴하지만, 가끔 중단될 수 있습니다.
실무에서는 운영 트래픽은 일반 Fargate로, 배치 작업은 Spot으로 처리합니다. 태스크 정의는 컨테이너 실행의 청사진입니다.
어떤 이미지를 사용할지, CPU와 메모리는 얼마나 할당할지, 환경 변수는 무엇인지, 로그는 어디로 보낼지 모두 정의합니다. JSON 형식으로 작성하며, 버전 관리됩니다.
네트워킹 설정이 중요합니다. 컨테이너를 프라이빗 서브넷에 배치하고, NAT Gateway를 통해 인터넷에 접근하도록 합니다.
외부 사용자는 Application Load Balancer를 통해서만 접근할 수 있습니다. 보안 그룹으로 필요한 포트만 열어둡니다.
서비스 Auto Scaling을 설정해봅시다. CPU 사용률이 70%를 넘으면 컨테이너를 추가하고, 30% 아래로 떨어지면 줄이는 정책을 만듭니다.
예측 스케일링을 사용하면 트래픽 패턴을 학습하여 미리 확장할 수도 있습니다. 박시니어 씨가 실제 운영 중인 클러스터를 보여주었습니다.
"보세요, 지금 10개 태스크가 실행 중이고, 모두 정상이에요." 김개발 씨는 감탄했습니다. 화면에 표시된 지표들이 실시간으로 업데이트되고 있었습니다.
CPU, 메모리, 네트워크 사용량이 한눈에 보였습니다. 로그 관리도 초기부터 제대로 설정해야 합니다.
CloudWatch Logs로 모든 컨테이너 로그를 중앙에서 수집합니다. 로그 그룹을 애플리케이션별로 나누고, 보관 기간을 설정합니다.
중요한 로그는 S3로 아카이브하여 장기 보관합니다.
실전 팁
💡 - ECR 리포지토리는 환경별(dev/staging/prod)로 분리하지 말고, 태그로 구분하세요
- Fargate Spot은 stateless한 작업에만 사용하고, 중단을 대비한 재시도 로직을 구현하세요
- 태스크 정의는 Git으로 버전 관리하여 롤백 가능하도록 하세요
3. CodePipeline 설정
인프라 구축을 마친 김개발 씨에게 박시니어 씨가 물었습니다. "이제 매번 수동으로 배포할 건가요, 아니면 자동화할 건가요?" 대답은 명확했습니다.
자동화야말로 DevOps의 핵심이니까요.
CodePipeline은 소스 코드 변경부터 운영 배포까지의 전체 과정을 자동화하는 워크플로우 엔진입니다. 마치 공항의 수하물 처리 시스템처럼, 각 단계를 거치며 자동으로 다음 단계로 넘어갑니다.
한 번 설정하면 개발자는 코드만 푸시하면 됩니다.
다음 코드를 살펴봅시다.
# buildspec.yml - CodeBuild 빌드 설정
version: 0.2
phases:
pre_build:
commands:
# ECR 로그인
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO
build:
commands:
# Docker 이미지 빌드
- echo Build started on `date`
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $ECR_REPO/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
# ECR에 푸시
- docker push $ECR_REPO/$IMAGE_REPO_NAME:$IMAGE_TAG
- printf '[{"name":"app","imageUri":"%s"}]' $ECR_REPO/$IMAGE_REPO_NAME:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
김개발 씨는 CodePipeline 콘솔을 처음 열었습니다. "파이프라인 생성" 버튼이 눈에 들어왔습니다.
클릭하자 단계별 설정 화면이 나타났습니다. 파이프라인은 크게 세 단계로 구성됩니다.
Source 단계는 소스 코드를 가져오는 단계입니다. GitHub, CodeCommit, S3 등 다양한 소스를 연결할 수 있습니다.
코드가 푸시되면 자동으로 파이프라인이 트리거됩니다. 웹훅을 통해 실시간으로 변경을 감지합니다.
GitHub을 연결하는 과정을 살펴봅시다. OAuth를 통해 GitHub 계정을 연동하고, 리포지토리와 브랜치를 선택합니다.
main 브랜치에 푸시되면 자동으로 배포되도록 설정합니다. Pull Request 브랜치는 별도 파이프라인으로 분리하여 스테이징 환경에 배포합니다.
Build 단계는 CodeBuild가 담당합니다. buildspec.yml 파일에 빌드 명령어를 정의합니다.
이 파일은 소스 코드 저장소 루트에 위치합니다. CodeBuild는 이 파일을 읽어 자동으로 빌드를 실행합니다.
buildspec.yml의 구조를 자세히 알아봅시다. phases 섹션은 빌드의 생명주기를 정의합니다.
pre_build에서는 준비 작업을, build에서는 실제 빌드를, post_build에서는 결과물 처리를 합니다. 각 단계는 순차적으로 실행되며, 하나라도 실패하면 전체가 중단됩니다.
환경 변수 관리가 중요합니다. 데이터베이스 비밀번호 같은 민감한 정보는 절대 하드코딩하면 안 됩니다.
Systems Manager Parameter Store나 Secrets Manager에 저장하고, buildspec.yml에서 참조합니다. CodeBuild가 실행 시점에 값을 가져옵니다.
빌드 환경을 선택할 수 있습니다. AWS가 제공하는 관리형 이미지를 사용하면 편리합니다.
Ubuntu, Amazon Linux 등 다양한 OS를 선택할 수 있습니다. 특별한 요구사항이 있다면 커스텀 Docker 이미지를 사용할 수도 있습니다.
Deploy 단계는 ECS로 배포합니다. imagedefinitions.json 파일에 새 이미지 정보가 담겨있습니다.
CodePipeline은 이 파일을 읽어 ECS 서비스를 업데이트합니다. 롤링 업데이트 방식으로 하나씩 교체되므로 무중단 배포가 가능합니다.
승인 단계를 추가할 수도 있습니다. 스테이징 환경 배포 후, 운영 배포 전에 수동 승인을 요구하도록 설정합니다.
SNS로 알림을 받고, 담당자가 콘솔에서 승인 버튼을 클릭해야 다음 단계로 진행됩니다. 중요한 배포에는 이중 확인 절차를 두는 것이 안전합니다.
실패 처리 전략도 고민해야 합니다. 빌드나 배포가 실패하면 자동으로 롤백할지, 알림만 보낼지 결정합니다.
실패 원인을 빠르게 파악하도록 CloudWatch Logs를 확인합니다. 재시도 로직을 구현하여 일시적인 네트워크 오류는 자동으로 복구합니다.
박시니어 씨가 실제 파이프라인 실행 화면을 보여주었습니다. "이게 우리 운영 파이프라인이에요.
하루에 20번 정도 실행되죠." 김개발 씨는 각 단계가 초록색으로 표시되는 것을 보며 뿌듯해했습니다. Source에서 3초, Build에서 5분, Deploy에서 3분, 총 8분 만에 배포가 완료되었습니다.
파이프라인 메트릭을 모니터링하세요. 평균 실행 시간, 성공률, 실패 원인을 추적합니다.
빌드 시간이 점점 길어진다면 캐싱을 적용하거나 병렬 빌드를 고려합니다. 대시보드를 만들어 팀 전체가 배포 현황을 공유합니다.
실전 팁
💡 - buildspec.yml 파일은 YAML 문법에 민감하므로, 린터를 사용하여 검증하세요
- 빌드 시간 단축을 위해 Docker 레이어 캐싱을 활용하세요
- 파이프라인 실행 히스토리를 정기적으로 검토하여 병목 지점을 찾으세요
4. 빌드와 배포 자동화
파이프라인이 구축되자 김개발 씨는 실제로 코드를 수정하고 푸시해보고 싶어졌습니다. 박시니어 씨가 말했습니다.
"자, 그럼 간단한 기능을 추가하고 푸시해보세요. 자동으로 배포되는 걸 지켜볼게요."
빌드 자동화는 소스 코드를 실행 가능한 아티팩트로 변환하는 과정이고, 배포 자동화는 그 아티팩트를 운영 환경에 적용하는 과정입니다. 이 두 과정이 연결되면 개발자는 코드만 작성하고, 나머지는 시스템이 알아서 처리합니다.
다음 코드를 살펴봅시다.
# Dockerfile - 멀티 스테이지 빌드 예시
FROM python:3.11-slim as builder
WORKDIR /app
# 의존성 설치 (캐싱 최적화)
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
# 빌더 스테이지에서 복사
COPY --from=builder /root/.local /root/.local
COPY src/ ./src/
# PATH에 pip 패키지 추가
ENV PATH=/root/.local/bin:$PATH
# 헬스체크 설정
HEALTHCHECK --interval=30s --timeout=3s \
CMD python -c "import requests; requests.get('http://localhost:8000/health')"
CMD ["python", "src/app.py"]
김개발 씨는 IDE를 열고 간단한 API 엔드포인트를 추가했습니다. 상품 목록을 반환하는 기능이었습니다.
코드를 작성하고 로컬에서 테스트한 후, git commit을 실행했습니다. 커밋 메시지를 신중하게 작성했습니다.
"feat: Add product list API endpoint"라고 입력했습니다. 팀에서는 Conventional Commits 규칙을 따르고 있었습니다.
feat, fix, docs 등의 접두사로 변경 유형을 명시합니다. 이렇게 하면 나중에 릴리스 노트를 자동 생성할 수 있습니다.
git push 명령어를 실행하자 마법이 시작되었습니다. GitHub에 코드가 올라가는 순간, 웹훅이 CodePipeline을 트리거했습니다.
파이프라인 콘솔을 열어보니 Source 단계가 진행 중이었습니다. 몇 초 후 Build 단계로 넘어갔습니다.
CodeBuild가 buildspec.yml을 읽고 실행합니다. 먼저 pre_build 단계에서 ECR에 로그인합니다.
AWS CLI를 사용하여 임시 토큰을 받아 Docker 로그인을 수행합니다. 그다음 Docker 이미지 빌드가 시작됩니다.
Dockerfile의 최적화가 빌드 시간에 큰 영향을 줍니다. 멀티 스테이지 빌드를 사용하면 최종 이미지 크기를 크게 줄일 수 있습니다.
첫 번째 스테이지에서 의존성을 설치하고, 두 번째 스테이지에서 필요한 파일만 복사합니다. 빌드 도구나 캐시 파일은 최종 이미지에 포함되지 않습니다.
레이어 캐싱을 활용하는 것이 핵심입니다. Dockerfile의 명령어 순서가 중요합니다.
자주 변경되지 않는 명령어를 먼저 배치합니다. requirements.txt를 먼저 복사하여 설치하면, 소스 코드가 변경되어도 의존성 레이어는 캐시에서 재사용됩니다.
이렇게 하면 빌드 시간이 5분에서 1분으로 단축됩니다. 이미지 태깅 전략도 중요합니다.
단순히 latest 태그만 사용하면 안 됩니다. Git 커밋 SHA를 태그로 사용하여 정확한 버전을 추적합니다.
예를 들어 my-app:abc123f 같은 형식입니다. 추가로 latest와 production 같은 별칭 태그도 함께 붙입니다.
빌드가 완료되면 이미지를 ECR로 푸시합니다. 네트워크 속도에 따라 1-2분 정도 걸립니다.
레이어가 이미 존재하면 건너뛰므로 증분 업로드가 빠릅니다. 푸시가 완료되면 imagedefinitions.json 파일을 생성합니다.
이제 Deploy 단계가 시작됩니다. CodePipeline은 ECS 서비스에 새 태스크 정의를 배포하도록 지시합니다.
ECS는 롤링 업데이트 전략을 사용합니다. 먼저 새 태스크를 시작하고, 정상 확인 후 기존 태스크를 종료합니다.
헬스체크가 배포의 안전장치 역할을 합니다. ALB가 새 컨테이너의 /health 엔드포인트를 주기적으로 호출합니다.
연속 2번 성공해야 healthy 상태가 됩니다. 만약 계속 실패하면 배포가 중단되고 롤백됩니다.
이렇게 잘못된 코드가 운영에 배포되는 것을 방지합니다. 김개발 씨는 배포 진행 상황을 지켜보았습니다.
ECS 콘솔에서 새 태스크가 PROVISIONING에서 PENDING, 그리고 RUNNING 상태로 변하는 것을 볼 수 있었습니다. Desired count 2, Running count 3으로 표시되었습니다.
이는 새 태스크가 시작되고 기존 태스크가 아직 종료되지 않았다는 의미입니다. 몇 분 후 배포가 완료되었습니다.
Running count가 2로 돌아왔습니다. 모두 새 버전이었습니다.
박시니어 씨가 브라우저를 열어 API를 호출했습니다. 새로 추가한 상품 목록 엔드포인트가 정상적으로 응답했습니다.
"축하해요! 첫 자동 배포에 성공했어요." 박시니어 씨가 엄지를 치켜세웠습니다.
김개발 씨는 뿌듯했습니다. 코드를 푸시한 지 8분 만에 운영 환경에 반영되었습니다.
예전에는 30분 이상 걸리던 수동 작업이 완전히 자동화되었습니다.
실전 팁
💡 - Dockerfile은 .dockerignore를 사용하여 불필요한 파일을 제외하세요
- 빌드 단계에서 자동화된 테스트를 실행하여 품질을 보장하세요
- 배포 전 스모크 테스트를 추가하여 기본 기능이 작동하는지 확인하세요
5. 블루그린 배포 적용
성공적인 배포를 경험한 김개발 씨에게 팀장님이 새로운 요구사항을 주었습니다. "좋아요.
그런데 배포 중에 문제가 생기면 즉시 롤백할 수 있어야 해요. 블루/그린 배포를 적용해보세요."
블루/그린 배포는 두 개의 동일한 환경을 유지하면서, 트래픽을 순식간에 전환하는 배포 전략입니다. 마치 무대 뒤에서 모든 준비를 마친 후 조명만 바꾸는 것과 같습니다.
문제가 발생하면 조명을 다시 원래대로 돌리면 됩니다.
다음 코드를 살펴봅시다.
# appspec.yml - CodeDeploy 블루/그린 배포 설정
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "arn:aws:ecs:region:account:task-definition/my-app"
LoadBalancerInfo:
ContainerName: "app"
ContainerPort: 8000
# 배포 설정
PlatformVersion: "LATEST"
Hooks:
- BeforeInstall: "LambdaFunctionToValidateBeforeInstall"
- AfterInstall: "LambdaFunctionToValidateAfterInstall"
- AfterAllowTestTraffic: "LambdaFunctionToValidateAfterTestTraffic"
- BeforeAllowTraffic: "LambdaFunctionToValidateBeforeAllowTraffic"
- AfterAllowTraffic: "LambdaFunctionToValidateAfterTraffic"
김개발 씨는 블루/그린 배포라는 용어를 처음 들었습니다. 박시니어 씨가 그림을 그려가며 설명했습니다.
블루/그린 배포의 핵심 개념을 이해해봅시다. 블루 환경은 현재 운영 중인 버전입니다.
사용자 트래픽을 처리하고 있습니다. 그린 환경은 새 버전을 배포할 대상입니다.
완전히 동일한 구성의 환경이지만, 아직 트래픽을 받지 않습니다. 전통적인 롤링 배포와의 차이를 살펴봅시다.
롤링 배포는 기존 인스턴스를 하나씩 교체합니다. 배포 중에는 구버전과 신버전이 섞여 실행됩니다.
문제가 발생하면 롤백하는 데 시간이 걸립니다. 반면 블루/그린은 완전히 새로운 환경에 배포하고, 한 번에 전환합니다.
ECS에서 블루/그린 배포를 구현하는 방법입니다. CodeDeploy를 사용합니다.
CodeDeploy는 ECS 서비스 앞에 위치한 ALB의 리스너 규칙을 조작합니다. 블루 타겟 그룹에서 그린 타겟 그룹으로 트래픽을 전환하는 방식입니다.
배포 프로세스를 단계별로 살펴보겠습니다. 첫 번째, 새 태스크 세트가 생성됩니다.
이것이 그린 환경입니다. 기존 태스크 세트(블루)는 그대로 실행 중입니다.
두 번째, ALB가 그린 환경을 테스트 리스너로 연결합니다. 내부 테스트를 수행할 수 있습니다.
테스트 단계가 중요합니다. 실제 사용자 트래픽을 보내기 전에 충분히 검증해야 합니다.
스모크 테스트를 자동으로 실행하거나, QA 팀이 수동으로 확인할 수 있습니다. 문제가 없으면 다음 단계로 진행합니다.
트래픽 전환은 순식간에 일어납니다. CodeDeploy가 ALB의 프로덕션 리스너를 그린 타겟 그룹으로 변경합니다.
DNS나 라우팅 변경이 필요 없습니다. 몇 초 안에 모든 사용자가 새 버전을 사용하게 됩니다.
전환 후에도 블루 환경은 유지됩니다. 설정한 시간 동안 블루 태스크 세트가 대기 상태로 남아있습니다.
문제가 발생하면 즉시 트래픽을 블루로 되돌릴 수 있습니다. 이것이 진정한 원클릭 롤백입니다.
canary 배포 전략도 적용할 수 있습니다. 한 번에 모든 트래픽을 전환하는 대신, 10%만 먼저 전환합니다.
10분간 모니터링하고, 문제가 없으면 나머지 90%를 전환합니다. 더 안전하지만 배포 시간은 조금 더 걸립니다.
CloudWatch 알람과 연동하는 것이 핵심입니다. 배포 중 오류율이 임계값을 초과하면 자동으로 롤백됩니다.
응답 시간이 급격히 증가해도 마찬가지입니다. 사람이 개입하지 않아도 시스템이 스스로 안전을 보장합니다.
김개발 씨는 실제로 블루/그린 배포를 실행해보았습니다. CodeDeploy 콘솔에서 배포 진행 상황을 지켜보았습니다.
"Replacement task set created" 상태를 거쳐, "Test traffic served" 단계로 넘어갔습니다. 그리고 "Production traffic shifted" 메시지가 표시되었습니다.
ECS 콘솔을 확인해보니 흥미로웠습니다. 두 개의 태스크 세트가 보였습니다.
PRIMARY 세트(그린)가 100% 트래픽을 받고, ACTIVE 세트(블루)는 0%였습니다. 블루는 여전히 실행 중이었지만, 사용자 요청을 받지 않았습니다.
박시니어 씨가 일부러 에러를 발생시켰습니다. 새 버전에 버그를 심어 배포했습니다.
오류율이 급증하자 CloudWatch 알람이 트리거되었습니다. CodeDeploy가 즉시 트래픽을 블루로 되돌렸습니다.
전체 과정이 30초도 걸리지 않았습니다. "이게 블루/그린의 힘이에요.
실패해도 안전하죠." 박시니어 씨가 설명했습니다.
실전 팁
💡 - 블루/그린 배포는 비용이 두 배 들지만, 안정성을 생각하면 가치가 있습니다
- 알람 임계값을 너무 낮게 설정하면 정상 배포도 롤백될 수 있으니 주의하세요
- 데이터베이스 스키마 변경은 블루/그린과 호환되도록 설계하세요(backward compatible)
6. 모니터링 설정
배포 자동화가 완성되자 팀장님이 마지막 과제를 주었습니다. "좋아요.
이제 시스템이 잘 돌아가는지 지켜볼 수 있어야 해요. 모니터링과 알람을 설정해보세요."
모니터링은 시스템의 상태를 실시간으로 관찰하고 기록하는 것입니다. 마치 환자의 활력징후를 계속 체크하는 것과 같습니다.
문제가 발생하기 전에 징후를 포착하고, 발생한 문제를 빠르게 진단할 수 있습니다.
다음 코드를 살펴봅시다.
# CloudWatch 알람 설정 (Terraform 예시)
resource "aws_cloudwatch_metric_alarm" "high_cpu" {
alarm_name = "ecs-high-cpu"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/ECS"
period = "300"
statistic = "Average"
threshold = "80"
alarm_description = "ECS CPU usage is above 80%"
dimensions = {
ClusterName = "production-cluster"
ServiceName = "my-app-service"
}
# SNS 토픽으로 알림
alarm_actions = [aws_sns_topic.alerts.arn]
}
김개발 씨는 모니터링이 왜 중요한지 몸소 경험했습니다. 얼마 전 새벽에 서비스가 다운되었는데, 아무도 몰랐던 적이 있었습니다.
아침에 출근해서야 문제를 발견했습니다. 효과적인 모니터링에는 세 가지 계층이 있습니다.
인프라 메트릭은 CPU, 메모리, 네트워크 같은 시스템 리소스입니다. 애플리케이션 메트릭은 요청 수, 응답 시간, 오류율 같은 서비스 지표입니다.
비즈니스 메트릭은 주문 수, 결제 성공률 같은 비즈니스 KPI입니다. CloudWatch는 AWS의 통합 모니터링 서비스입니다.
ECS는 자동으로 CPU와 메모리 메트릭을 CloudWatch로 전송합니다. 추가 설정 없이도 기본 지표를 확인할 수 있습니다.
하지만 애플리케이션 내부의 메트릭은 직접 전송해야 합니다. 커스텀 메트릭을 보내는 방법을 알아봅시다.
애플리케이션 코드에서 CloudWatch API를 호출하여 메트릭을 전송합니다. 예를 들어 주문 생성 API가 호출될 때마다 카운터를 증가시킵니다.
응답 시간도 측정하여 히스토그램으로 기록합니다. 로그는 메트릭만큼 중요합니다.
CloudWatch Logs로 모든 컨테이너 로그를 중앙에서 수집합니다. 애플리케이션은 stdout으로 로그를 출력하기만 하면 됩니다.
ECS가 자동으로 CloudWatch Logs로 스트리밍합니다. 로그 인사이트로 강력한 쿼리를 실행할 수 있습니다.
SQL과 비슷한 문법으로 로그를 검색하고 분석합니다. 예를 들어 "지난 1시간 동안 발생한 500 에러를 시간대별로 집계"하는 쿼리를 작성할 수 있습니다.
정규표현식으로 패턴을 추출하고, 시각화도 가능합니다. 알람 설정이 모니터링의 핵심입니다.
메트릭만 수집하고 보지 않으면 의미가 없습니다. 임계값을 초과하면 자동으로 알림을 보내도록 설정합니다.
SNS 토픽을 통해 이메일, SMS, Slack 등 다양한 채널로 알림을 받습니다. 알람 임계값을 설정하는 요령이 있습니다.
너무 낮게 설정하면 오탐(false positive)이 많아집니다. 너무 높게 설정하면 진짜 문제를 놓칩니다.
과거 데이터를 분석하여 정상 범위를 파악하고, 여기에 20-30% 버퍼를 두고 설정합니다. 복합 알람으로 노이즈를 줄일 수 있습니다.
CPU가 80%를 넘는 동시에 응답 시간도 증가해야 알림을 보내도록 설정합니다. 단일 메트릭만으로는 오탐이 많지만, 여러 조건을 결합하면 정확도가 높아집니다.
대시보드를 만들어 시각화합니다. CloudWatch 대시보드에 주요 메트릭을 배치합니다.
CPU, 메모리, 요청 수, 오류율, 응답 시간을 한 화면에서 볼 수 있습니다. 회의실 모니터에 띄워놓으면 팀 전체가 시스템 상태를 공유합니다.
X-Ray로 분산 추적을 구현합니다. 마이크로서비스 아키텍처에서는 하나의 요청이 여러 서비스를 거칩니다.
X-Ray는 전체 경로를 추적하여 어디서 시간이 오래 걸리는지 보여줍니다. 병목 지점을 쉽게 찾을 수 있습니다.
Container Insights로 더 깊이 들여다봅니다. 기본 메트릭 외에도 네트워크 트래픽, 디스크 I/O, 프로세스 레벨 메트릭을 수집합니다.
컨테이너별, 태스크별, 서비스별로 세분화된 정보를 제공합니다. 추가 비용이 들지만, 문제 해결 시간을 크게 단축시킵니다.
김개발 씨는 대시보드를 완성했습니다. 화면 가득 그래프들이 실시간으로 업데이트되었습니다.
CPU 사용률이 부드러운 곡선을 그리며 올라갔다 내려갔습니다. 요청 수는 낮 시간대에 높고, 새벽에는 낮았습니다.
패턴이 보이기 시작했습니다. 박시니어 씨가 테스트를 위해 부하를 걸었습니다.
순식간에 CPU가 90%까지 올라갔습니다. 1분 후 SNS 알림이 왔습니다.
"ECS CPU usage is above 80%" 김개발 씨의 휴대폰이 울렸습니다. 알람이 정상 작동했습니다.
"이제 안심하고 퇴근할 수 있어요. 문제가 생기면 알람이 알려주니까요." 박시니어 씨가 웃으며 말했습니다.
김개발 씨는 뿌듯했습니다. 몇 주 전만 해도 낯설었던 용어들이 이제는 익숙해졌습니다.
ECR, ECS, CodePipeline, 블루/그린 배포, CloudWatch. 모든 조각이 맞춰져 완전한 CI/CD 파이프라인이 완성되었습니다.
실전 팁
💡 - 알람 피로도를 줄이기 위해 중요도에 따라 알림 채널을 분리하세요(Critical은 전화, Warning은 이메일)
- 정기적으로 알람을 검토하고, 울리지 않는 알람은 삭제하세요
- 대시보드는 역할별로 만들어서 개발자용, 운영자용, 경영진용을 분리하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Spring Boot 상품 서비스 구축 완벽 가이드
실무 RESTful API 설계부터 테스트, 배포까지 Spring Boot로 상품 서비스를 만드는 전 과정을 다룹니다. JPA 엔티티 설계, OpenAPI 문서화, Docker Compose 배포 전략을 초급 개발자도 쉽게 따라할 수 있도록 스토리텔링으로 풀어냅니다.
Docker로 컨테이너화 완벽 가이드
Spring Boot 애플리케이션을 Docker로 컨테이너화하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 중심으로 설명합니다. Dockerfile 작성부터 멀티스테이지 빌드, 이미지 최적화, Spring Boot의 Buildpacks까지 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.