개발 환경에서 잘 동작하던 컨테이너가 프로덕션에서 예상치 못한 문제를 일으키는 경우는 흔하다. 이미지 태깅 전략부터 무중단 배포까지, 실무에서 검증된 배포 흐름을 정리한다. Registry, Rolling Update, Blue-Green 세 가지 축을 중심으로 살펴본다.
소스 코드가 병합되는 순간부터 배포까지 자동화하는 것이 핵심이다. 아래는 GitHub Actions 기준의 일반적인 흐름이다.
[Push to main]
|
v
[CI: build & test]
|
v
[Docker build]
|
v
[Registry push] --> ECR / GCR / Docker Hub
|
v
[CD: deploy trigger]
|
v
[Kubernetes / ECS rollout]
이미지 태그는 latest 대신 Git SHA 또는 시맨틱 버전을 사용한다. latest는 어느 시점의 이미지인지 추적이 불가능해 롤백 시 위험하다.
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: Login to ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $REGISTRY/my-app:$IMAGE_TAG .
docker push $REGISTRY/my-app:$IMAGE_TAG
ECR(Elastic Container Registry): AWS에서 제공하는 완전 관리형 컨테이너 레지스트리. GCR(Google Container Registry)은 Google Cloud의 동일한 역할을 한다.
| 레지스트리 | 제공사 | 특징 |
|---|---|---|
| ECR | AWS | IAM 연동, 동일 리전 무료 전송 |
| GCR / Artifact Registry | GCP | GKE와 네이티브 연동 |
| Docker Hub | Docker | 퍼블릭 이미지 배포에 적합 |
| GHCR | GitHub | GitHub Actions와 토큰 공유 간편 |
프라이빗 인프라가 AWS 위에 있다면 ECR이 네트워크 비용과 인증 측면에서 가장 유리하다.
Rolling Update는 기존 인스턴스를 하나씩 새 버전으로 교체한다. 추가 인프라 비용이 없고 Kubernetes의 기본 전략이다.
[v1][v1][v1][v1]
|
[v2][v1][v1][v1] <- 순차 교체
|
[v2][v2][v2][v2]
Blue-Green 배포는 동일한 환경을 두 벌 유지하고 트래픽을 한 번에 전환한다. 롤백이 Load Balancer 전환만으로 즉시 가능하다는 장점이 있지만, 인프라 비용이 두 배가 된다.
Load Balancer
/ \
[Blue: v1] [Green: v2] <- 신규 배포
(트래픽 100%) (대기 중)
|
전환 후
\ /
[Blue: v1] [Green: v2]
(대기 중) (트래픽 100%)
Rolling Update: 무중단이지만 전환 중 두 버전이 동시에 서비스된다. API 하위 호환성이 보장되어야 한다.
# Multi-stage build로 이미지 크기 최소화
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "dist/main.js"]
USER node로 루트 권한 실행을 막고, Multi-stage build로 빌드 도구가 최종 이미지에 포함되지 않도록 한다. 결과 이미지는 200MB 이하를 목표로 한다.
DONE: C:\Users\finger\Desktop\projects\newgirok.github.io\content\posts\2027-01-26-docker-production.mdx