Docker 컨테이너화 배포 - 100-hours-a-week/20-real-wiki GitHub Wiki

📌 목차

1. 도입 배경

1.1. 기존 서비스 구조 개요

현재 서비스는 AWS와 GCP에 걸쳐 다음과 같이 분산된 구성으로 운영되고 있다.

  • AWS EC2 인스턴스 내에 주요 애플리케이션이 설치되어 실행
    • Next.js (프론트엔드): SSR 및 정적 페이지 렌더링
    • Spring Boot (백엔드 API 서버): 주요 비즈니스 로직 및 DB 연동
    • MySQL (RDB): 사용자 및 서비스 데이터 저장소
  • GCP GCE FastAPI 서버: AI 모델 응답 처리
  • 정적 리소스는 S3 Bucket에 저장되고 CloudFront를 통해 배포
  • CI/CD GitHub Actions → S3 → AWS CodeDeploy로 연결

이 구조는 기능적으로는 분산된 서비스를 잘 운영하고 있으나, 배포 및 운영의 효율성과 신뢰성 측면에서 개선 여지가 크다.

1.2. 기존 운영 및 배포 방식의 한계

항목 현황 문제점
환경 구성 각 EC2 인스턴스에 직접 의존성 설치 ( npm, openjdk 등) 환경 차이에 따른 실행 오류, 재현 어려움
CI/CD 파이프라인 GitHub Actions → zip 업로드 → CodeDeploy zip 내 실행 스크립트 의존 → 운영 자동화 수준이 낮음
서비스 구성 요소 간 통합 Next.js, Spring Boot, MySQL이 단일 인스턴스에서 병렬 운영 리소스 충돌, 배포/모니터링/복구가 어려움
FastAPI 서버 연동 GCP 별도 구성, 수동 테스트 필요 통합 테스트 어렵고, 배포 시점 동기화 어려움
문제 대응 배포 실패 시 수동 롤백 (scp, cp, mv 등) 시간 소요 크고, 안정성 낮음

1.3. Docker 도입 필요성 요약

이러한 문제들을 근본적으로 해결하기 위해, 서비스의 핵심 구성 요소를 Docker 기반으로 컨테이너화할 필요가 있다.

  • 환경 구성 자동화: Dockerfile로 OS, 라이브러리, 런타임까지 명확히 정의
  • CI/CD 연동 최적화: GitHub Actions에서 Docker 이미지 빌드 → ECR 푸시 → 배포까지 완전 자동화
  • 배포 안정성 확보: 버전 명시된 이미지 태그 기반으로 재현 가능, 롤백 신속
  • 운영 효율화: 각 구성 요소 컨테이너 격리 → 서비스간 충돌 방지, 자원 사용 최적화
  • 클라우드 네이티브 전환 기반: 이후 ECS, EKS 등 오케스트레이션 환경으로 확장 가능

1.4. 도입 시 고려 포인트

  • 어떤 구성요소를 컨테이너화할 것인가?
    • 프론트(Next.js) / 백엔드(Spring Boot) / DB(MySQL) / AI 서버(FastAPI) 별로 나눠 설계
  • 기존 CodeDeploy와의 연결성 유지 혹은 전환 여부
    • ECS or Fargate 활용 여부 검토
  • 컨테이너 이미지 저장소는 ECR 또는 GitHub Container Registry 중 선택
  • 기존 모니터링/로그 시스템(CloudWatch, S3)과 통합 유지 필요

2. Docker 도입의 필요성

2.1. 환경 불일치 문제 해결

🚨 현재 문제

  • 개발자는 로컬(Mac, Windows)에서 Node.js, Java, Python 등 개별적으로 설치해 작업
  • 운영 환경은 Amazon Linux 기반 → 라이브러리 버전 차이, OS 디렉토리 구조 차이 발생
  • 내 로컬에서는 되는데 서버에서는 안 돼요” 문제 반복

✅ Docker 도입 시 개선

  • Dockerfile을 통해 실행 환경을 명시적으로 정의 (FROM, RUN, COPY, CMD)
  • 개발, 테스트, 운영 전 구간에서 동일한 컨테이너 이미지로 실행
  • 환경 의존성 제거 → 문제 발생 지점이 OS가 아닌 애플리케이션 레벨로 국한

예시: Next.js의 SSR 시 node 버전 차이로 인한 ReferenceError: fetch is not defined 오류 → 컨테이너로 버전 고정하여 해소

2.2. 배포 자동화 수준 향상

🚨 현재 문제

  • GitHub Actions에서 zip으로 묶은 코드 → S3에 업로드 → CodeDeploy가 EC2에서 압축 해제
  • .sh 스크립트와 appspec.yml에 로직을 직접 작성 → 변경에 매우 민감, 유지보수 어려움
  • 롤백 시 수동 스크립트 실행 → 다운타임 위험 높음

✅ Docker 도입 시 개선

  • GitHub Actions에서 Docker 이미지 자동 빌드 및 태깅
  • AWS ECR에 이미지 푸시 → ECS 혹은 EC2에서 docker run만으로 배포
  • 버전 태그만 변경해도 과거 상태로 즉시 복구 가능 (v1.2.0v1.1.9)

정량 근거:

기존 배포 시간: 평균 15분 (압축, 업로드, 배포 대기 포함) -> Docker 도입 후: 평균 10분 (이미지 Pull + 실행)

2.3. 구성 요소별 의존성 및 자원 분리

🚨 현재 문제

  • Next.js, Spring Boot, MySQL이 동일 EC2 인스턴스 내에 설치되어 공존
  • 포트 충돌, 자원(메모리/CPU) 경쟁, 로그 혼합 저장 등 관리 복잡성 증가

✅ Docker 도입 시 개선

  • 각 서비스별 Docker 컨테이너로 실행 → 독립된 네트워크/자원/로그 경로 사용
  • 장애 격리 및 문제 진단 용이 (e.g., docker logs, docker stats로 개별 추적 가능)
  • 필요 시 개별 컨테이너만 재배포하거나 재시작 가능

예: Spring Boot만 재시작할 때 기존엔 전체 서버를 재부팅해야 했으나, 컨테이너 기반에서는 해당 컨테이너만 restart 하면 됨

2.4. FastAPI 서버와의 통합 및 배포 전략 동일

🚨 현재 문제

  • GCP에서 운영되는 FastAPI 서버는 AWS 서비스들과 별도 배포 관리
  • 수동 배포 → 관리 일관성 부족

✅ Docker 도입 시 개선

  • GCP 역시 Docker 지원이 기본 → 동일한 Dockerfile 구조로 FastAPI 이미지 빌드 가능
  • CI/CD 파이프라인을 동일한 GitHub Actions 워크플로우로 확장 가능
  • 단일 이미지 버전으로 여러 클라우드 간 일관된 배포 전략 확보

2.5. 협업 생산성 향상

🚨 현재 문제

  • 개발자마다 Node.js, Java, Python 등 설치 및 설정이 상이
  • 프로젝트 셋업 문서가 길고, 세팅 중 오류가 자주 발생

✅ Docker 도입 시 개선

  • docker-compose.yml 하나로 전체 개발 환경 구동 가능
  • “로컬에서 실행 안 되는 문제” 거의 제거 → 개발에 집중 가능

2.6 향후 확장을 위한 기반 확보

🛠 실무 시나리오

  • 마이크로서비스 아키텍처로의 확장
  • ECS, Fargate, Kubernetes 기반 오토스케일링 고려
  • Canary, Blue/Green Deployment, A/B 테스트 등 고급 배포 전략 도입

✅ Docker 없이는 불가능하거나 매우 복잡

  • 모든 오케스트레이션 플랫폼은 컨테이너 기반이 전제
  • Docker 도입이 되어 있어야 ECS Task, Kubernetes Pod 등으로 이전 가능

2.7. 요약 표

문제 영역 기존 문제 Docker 도입 시
실행 환경 불일치 OS/버전 문제 이미지 기반 환경 통일
배포 자동화 zip + 스크립트 의존 이미지 기반 자동화
운영 효율성 단일 서버 운영 컨테이너별 분리 운영
장애 대응 수동 롤백 이미지 태깅 기반 자동 롤백
확장성 EC2 한계 ECS/K8s 확장 준비 완료
협업 생산성 개발 환경 세팅 소요 1줄로 동일 환경 실행

🧭 3. 도커 아키텍처 설계

3.1. 전체 아키텍처 다이어그램

ktb final-(new)Docker 아키텍처 drawio (6)

3.1.1 구성 AWS 리소스

구성 요소 설명
VPC PROD, DEV 환경을 논리적으로만 분리 운영
Public Subnet Bastion Host, Grafana를 배치해 외부 접속 및 모니터링 지원
Application Tier Auto Scaling이 적용된 EC2 인스턴스에 Spring Boot 및 Next.js 컨테이너 실행
DB Tier 사설 서브넷에 배치된 MySQL 컨테이너 구동 EC2 인스턴스. EBS를 활용해 DB 저장
CodeDeploy + ECR CI/CD 파이프라인과 연동되어, 이미지 배포 자동화 수행
Lambda + CloudWatch 알람 발생 시 SNS와 Discord 연동 알림 수행
WAF + CloudFront + Route 53 사용자 요청을 보호하고 정적 자산을 배포, DNS 도메인 연결 관리

3.1.2 구성 GCP 리소스

구성 요소 설명
VPC (10.178.0.0/20) GCP의 FastAPI 서버가 위치한 퍼블릭 네트워크
FastAPI 서버 AI 서버
Cloud Storage Faiss로 활용

3.2. 배포 전략

3.2.1 아키텍처 및 개요

ktb final-(new)Docker 아키텍처 drawio (4)

본 아키텍처는 Blue/Green 배포 전략을 채택하여 다운타임 없는 안전한 배포를 목표로 한다. AWS CodeDeploy, Auto Scaling Group, ALB(Application Load Balancer)를 활용하여 두 개의 대상 그룹(Blue/Green) 간 전환을 자동화하였다.

3.2.2 Blue/Greeen 선택 이유

항목 설명
무중단 배포 실시간으로 대응해야하는 AI 챗봇 서비스상 모델의 변경이 필요해도 서비스 운영 기간에는 배포가 힘든 경우가 많았음. Blue/Green 배포를 사용하면 배포 중에도 기존 트래픽은 Blue 그룹이 유지, 오류 발생 시 롤백 가능.
장애 대응 유연성 챗봇 응답 지연이나 오류 발생은 사용자 불만으로 직결됨. Health check 실패 시 자동 롤백 또는 수동 전환 가능하므로 안정성 확보.
CI/CD 자동화 사용자 피드백이나 챗봇 응답 품질 개선을 빠르게 반영할 수 있도록 GitHub Actions → ECR → CodeDeploy까지 자동화 연동
실시간 모니터링 Codedeploy의 Blue/Green에는 cloudwatch alarm 연동이 가능하므로 Discord 알림으로 배포 진행 상황을 실시간 확인 가능
ASG 확장성 배포 대상이 Auto Scaling Group으로 구성되어 챗봇 사용자 수 증가에 따라 서비스 탄력성 확보

3.2.3 구성 리소스

구성 요소 설명
CodeDeploy ECR에 푸시된 새 Docker 이미지의 배포를 트리거하고, 대상 그룹 교체를 수행
Auto Scaling Group 서비스 인스턴스를 탄력적으로 운영하며, 각 배포 시 Blue 또는 Green 그룹에 신규 인스턴스를 생성
ALB 라우팅 트래픽을 Blue 또는 Green 대상 그룹 중 하나로 전달
CloudWatch + SNS + Lambda 배포 성공/실패 이벤트를 감지하여 Discord로 실시간 알림 전달
ECR 컨테이너 이미지를 저장하고 CodeDeploy와 연결
Discord 운영/개발자가 실시간으로 배포 상태를 확인할 수 있도록 알림 채널 제공

3.2.4 Blue/Greeen 배포 프로세스

[1] GitHub Actions에서 이미지 빌드 후 ECR에 푸시
 ↓
[2] CodeDeploy가 CD 트리거 수신 → 신규 ASG 인스턴스 기동 (Green Target Group)
 ↓
[3] Health Check 성공 시 → ALB 대상 그룹을 Blue → Green 으로 전환
 ↓
[4] 기존 Blue 그룹은 대기 상태로 유지 또는 종료
 ↓
[5] CloudWatch Alarm으로 성공/실패 감지 후 → Lambda → SNS → Discord 알림 발송

3.3. 이미지 빌드 및 관리 전략

3.3.1 Dockerfile 관리

항목 정책
위치 각 리포지토리 루트 (frontend/, backend/, ai/)
목적별 구분 dev, prod 환경 변수 기반 분기
최적화 멀티 스테이지 빌드 활용, 빌드 사이즈 최소화
공통 .dockerignore 설정으로 빌드 캐시 최적화

3.3.2 CI/CD자동화

단계 구성 설명
1 GitHub Actions PR merge 시 파이프라인 트리거
2 Docker 이미지 빌드 docker build -t $IMAGE_NAME .
3 이미지 서명 Cosign Keyless + GitHub OIDC
4 이미지 푸시 docker push $IMAGE_NAME to ECR
5 이미지 검증 cosign verify --certificate
6 EC2 배포 CodeDeploy + docker pull && run
7 로그 수집 CloudWatch Logs로 수집, 서비스별 로그 그룹 분리
8 배포 알림 Discord Webhook을 통한 성공/실패 알림

3.3.3 태깅 정책

브랜치 환경 태그 형식 용도 정책
dev 개발 dev-{v.X.Y.Z}-{SHA} 통합 테스트용 이미지 PR merge 시 자동 태깅
main 운영 prod-{v.X.Y.Z}-{SHA} 운영 반영 추적용 PR merge 시 자동 태깅
release 운영 vX.Y.Z 롤백 기준 및 기록 GitHub Release에서 수동 생성
  • latest 태그로 이미지 이중 생성 후 ECR에서 관리

3.3.4 저장소

  • AWS ECR에 서비스 단위 저장소 및 태그 분리하여 관리
  • ex: ecr.repo/fe-app, ecr.repo/be-app, ecr.repo/ai-app

3.3.5 서명

  • 운영용 이미지에 대해 cosign 서명 자동화 (main 브랜치 배포 시 서명 후 push)
  • 서명 키는 OIDC 연동 + GitHub Actions 내부 Keyless Signing으로 관리

📋 4. Docker 기술 구성 명세서


4.1. Next.js

항목
Base Image node:18-alpine
컨테이너 수 2개 (Dev/Prod)
포트 3000
환경 변수 NODE_ENV, PORT
빌드 명령어 npm run build, npm start

📦 Dockerfile

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

4.2. Spring Boot

항목 | 값 -- | -- Base Image | gradle:8.5-jdk21-alpine → temurin:21-jdk-alpine 컨테이너 수 | 2개 (Dev/Prod) 포트 | 8080 환경 변수 | SPRING_PROFILES_ACTIVE, DB_HOST 등 빌드 명령어 | gradle clean build --no-daemon

📦 Dockerfile

# 🔹 1단계: 빌드 스테이지
FROM gradle:8.5-jdk17-alpine AS build

WORKDIR /app

# 종속성 캐싱을 위해 먼저 복사
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle ./gradle
RUN gradle build --no-daemon || return 0

# 전체 소스 복사 후 빌드
COPY . .
RUN gradle clean build --no-daemon

# 🔹 2단계: 런타임 이미지
FROM eclipse-temurin:17-jdk-alpine

WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

4.3. FastAPI

항목
Base Image python:3.13-slim
컨테이너 수 1개 (Dev/Prod 겸용)
포트 8000
환경 변수 MODEL_PATH, ENV 등
실행 명령어 uvicorn main:app

📦 Dockerfile

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

5.4. 로컬 테스트 용도 Docker-Compose

Next.js, Spring Boot, FastAPI. MySQL을 포함하며, 각 서비스는 동일한 브릿지 네트워크에서 통신 가능하다.

# docker-compose.yml
version: "3.8"

services:
  nextjs:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - PORT=3000
    depends_on:
      - spring
      - fastapi

  spring:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=local
      - DB_HOST=db
      - DB_PORT=3306
      - DB_USERNAME=root
      - DB_PASSWORD=secret
    depends_on:
      - db

  fastapi:
    build:
      context: ./ai
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      - ENV=local
      - MODEL_PATH=/app/model

  db:
    image: mysql:8.0
    container_name: mysql
    restart: always
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=appdb
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

📁 프로젝트 디렉토리 구조 예시

project-root/
├── docker-compose.yml
├── frontend/
│   └── Dockerfile (Next.js SSR)
├── backend/
│   └── Dockerfile (Spring Boot)
├── ai/
│   └── Dockerfile (FastAPI)

✅ 실행 환경 설정 (MAC 기준)

  1. Docker Desktop 설치
  2. Docker Compose 설치
sudo rm /usr/local/bin/docker-compose
sudo ln -s /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose /usr/local/bin/docker-compose
docker-compose --version

✅ Docker-Compose 명령어

# 실행
docker-compose up -d 
# 중지
docker-compose down
# 컨테이너 상태 확인
docker-compose ps
# 로그 확인
docker-compose logs

💰 5. 비용 계산 보고서

5.1. 개요

  • 본 문서는 하루 최대 접속자 150명을 기준으로 구성된 Docker 기반 마이크로서비스 아키텍처의 AWS 비용 산정 결과다.
  • 모든 인프라는 EC2 인스턴스를 기반으로 컨테이너를 직접 배포하는 구조이며, DB와 Redis도 EC2에 직접 설치하여 운영 비용을 절감하였다.

5.2. 리소스별 비용 및 구성

리소스 유형 상세 구성 월간 사용량 가정 단가 (USD) 월 예상 비용 (USD) 비고
EC2 t3.medium x3 2대 x 24시간, 1대 x 6시간 $0.0416/시간 $61.92 2대: 720시간, 1대: 180시간
t2.micro x3 2대 x 24시간, 1대 x 6시간 $0.0116/시간 $17.28 2대: 720시간, 1대: 180시간
t2.small x1 1대 x 24시간 $0.023/시간 $16.56 720시간
ALB Application Load Balancer x1 24시간 x 30일 $0.0225/시간 $16.20 720시간
NAT Gateway NAT Gateway x1 24시간 x 30일 $0.045/시간 $32.40 720시간
S3 버킷 x2 50GB 저장, 월 100,000 요청 $0.023/GB $1.15 저장: $1.15, 요청: 무시
CloudFront 배포 x1 50GB 전송 $0.085/GB $4.25
WAF Web ACL x1 1백만 요청 $5.00/백만 요청 $5.00
Parameter Store 파라미터 x10 표준 파라미터 무료 $0.00
SNS 주제 x1 월 100,000 요청 $0.50/백만 요청 $0.05
Lambda 함수 x1 월 50회 호출 무료 $0.00 무료 범위 내
CodeDeploy 배포 그룹 x1 월 10회 배포 무료 $0.00 EC2 대상 무료
ECR 저장소 x1 1GB 저장 $0.10/GB $0.10
CloudWatch 로그, 알람, 대시보드 3GB, 10개, 2개 무료 범위 내 $0.00

5.3. 총 예상 비용

  • $198.66 / 월
  • 해당 비용은 On-Demand 기준, 하루 24시간 또는 일부 리소스는 6시간 가동을 기준으로 AWS 요금 계산기에서 산출하였음
  • 실제 사용 시간, 요청 수, 트래픽 등에 따라 다소 차이가 발생할 수 있음

5.4. 참고 사항

  • CloudWatch 지표 수는 프리 티어 내에서 활용하고, Prometheus 기반 수집 및 Grafana 시각화로 외부 전송 비용 절약
  • EC2 기반 DB, Redis는 자동 백업 및 점검 스크립트를 통해 운영 효율성 확보
  • 모든 인프라 구성은 비용 최적화를 고려하여 EC2 인스턴스 기반으로 구성하였으며, 불필요한 매니지드 서비스 사용을 최소화함
⚠️ **GitHub.com Fallback** ⚠️