Docker 컨테이너화
Dockerfile 최적화, multi-stage build, Docker Compose로 Python 앱을 컨테이너화합니다.
기본 Dockerfile
# ── 기본 예제 (최적화 전) ─────────────────────────────────
FROM python:3.12-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"]
최적화 Dockerfile (Multi-stage Build)
# ── Stage 1: 의존성 빌드 ──────────────────────────────────
FROM python:3.12-slim AS builder
WORKDIR /app
# 빌드 도구 설치 (최종 이미지에 포함 안 됨)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 가상 환경 생성
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 의존성 먼저 복사 (레이어 캐시 최대화)
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# ── Stage 2: 런타임 이미지 ───────────────────────────────
FROM python:3.12-slim AS runtime
# 보안: root가 아닌 사용자로 실행
RUN groupadd --gid 1001 appgroup && \
useradd --uid 1001 --gid appgroup --shell /bin/bash --create-home appuser
WORKDIR /app
# 런타임 의존성만 설치
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
curl \
&& rm -rf /var/lib/apt/lists/*
# 빌드 단계에서 가상 환경 복사
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 소스 코드 복사
COPY --chown=appuser:appgroup . .
# 비root 사용자로 전환
USER appuser
EXPOSE 8000
# 헬스체크
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
.dockerignore
# .dockerignore
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.egg-info/
dist/
build/
# 개발 환경
.env
.env.*
!.env.example
.venv/
venv/
# 버전 관리
.git/
.gitignore
# 테스트 / 문서
tests/
docs/
*.md
!README.md
# IDE
.vscode/
.idea/
*.swp
# OS
.DS_Store
Thumbs.db
# 빌드 결과물
*.log
logs/
Docker Compose — 로컬 개발
# docker-compose.yml
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile
target: runtime # multi-stage의 특정 스테이지 지정
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/appdb
- REDIS_URL=redis://redis:6379/0
- DEBUG=true
env_file:
- .env.local # 로컬 전용 오버라이드
volumes:
- .:/app # 개발 중 코드 마운트
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
ports:
- "5432:5432" # 개발 시 로컬 접속용
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
command: redis-server --appendonly yes
ports:
- "6379:6379"
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- app
volumes:
postgres_data:
redis_data:
# docker-compose.override.yml — 개발 전용 오버라이드
version: "3.9"
services:
app:
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
environment:
- LOG_LEVEL=DEBUG
자주 쓰는 Docker 명령
# 빌드
docker build -t myapp:latest .
docker build --target builder -t myapp:dev .
# 실행
docker run -d -p 8000:8000 --name myapp \
--env-file .env \
myapp:latest
# Docker Compose
docker compose up -d # 백그라운드 시작
docker compose up --build # 빌드 후 시작
docker compose down -v # 볼륨까지 삭제
docker compose logs -f app # 실시간 로그
docker compose exec app bash # 컨테이너 셸 접속
docker compose ps # 서비스 상태
# 이미지 최적화 확인
docker image inspect myapp:latest --format '{{.Size}}'
docker history myapp:latest
이미지 크기 비교
python:3.12 ~1.0 GB (기본)
python:3.12-slim ~130 MB (경량)
python:3.12-alpine ~55 MB (최소, glibc 없음 — 호환성 주의)
multi-stage + slim ~150 MB (빌드 도구 제외)
정리
| 기법 | 효과 |
|---|---|
-slim 베이스 이미지 | 이미지 크기 80% 감소 |
| Multi-stage build | 빌드 도구 최종 이미지 제외 |
.dockerignore | 불필요한 컨텍스트 제외 |
| 의존성 레이어 분리 | 코드 변경 시 캐시 재사용 |
| 비root 사용자 | 보안 강화 |
HEALTHCHECK | 컨테이너 상태 모니터링 |