컨테이너란?
컨테이너는 애플리케이션과 그 실행에 필요한 모든 것 (코드, 라이브러리, 설정)을 하나의 패키지로 묶은 것입니다.
VM vs 컨테이너
가상 머신 (VM)
⚠️ OS 전체를 포함하여 무겁고 느림
컨테이너
✅ OS 커널 공유로 가볍고 빠름
Docker 핵심 개념
이미지 (Image)
컨테이너의 "설계도". Dockerfile로 정의하고, 한 번 빌드하면 변경 불가 (불변).
컨테이너 (Container)
이미지를 실행한 "인스턴스". 여러 개 실행 가능, 삭제하면 데이터도 사라짐.
레지스트리 (Registry)
이미지를 저장하고 공유하는 "저장소". Docker Hub, GCR, GHCR 등.
왜 컨테이너를 사용하나요?
- 환경 일관성: 개발/테스트/프로덕션 환경이 동일
- 빠른 배포: 이미지만 다운받아 바로 실행
- 격리: 다른 앱과 충돌 없이 독립 실행
- 확장성: 같은 이미지로 여러 컨테이너 실행
Dockerfile 기본
Dockerfile은 이미지를 만드는 "레시피"입니다. 각 명령어가 한 줄씩 레이어를 쌓아 최종 이미지를 만듭니다.
기본 명령어
| 명령어 | 설명 | 예시 |
|---|---|---|
| FROM | 베이스 이미지 지정 | FROM python:3.11-slim |
| WORKDIR | 작업 디렉토리 설정 | WORKDIR /app |
| COPY | 파일 복사 | COPY . . |
| RUN | 빌드 시 명령 실행 | RUN pip install -r requirements.txt |
| EXPOSE | 포트 문서화 | EXPOSE 8000 |
| CMD | 컨테이너 실행 명령 | CMD ["uvicorn", "main:app"] |
Python (FastAPI) Dockerfile
# Python 3.11 슬림 이미지 사용 (크기 최소화)
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"]Node.js (Hono) Dockerfile
# Node.js 20 LTS 알파인 이미지 (경량)
FROM node:20-alpine
# 작업 디렉토리 설정
WORKDIR /app
# 패키지 파일 먼저 복사 (캐싱 최적화)
COPY package*.json ./
# 의존성 설치 (프로덕션 전용)
RUN npm ci --only=production
# 소스 코드 복사
COPY . .
# 포트 노출
EXPOSE 3000
# 컨테이너 실행 명령
CMD ["node", "src/index.js"]멀티 스테이지 빌드 (최적화)
빌드용 이미지와 실행용 이미지를 분리하여 최종 이미지 크기를 줄입니다.
# Stage 1: 빌드
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: 프로덕션
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]이미지 빌드 & 실행
# 이미지 빌드
docker build -t my-api:latest .
# 컨테이너 실행
docker run -p 8000:8000 my-api:latest
# 백그라운드 실행
docker run -d -p 8000:8000 --name my-api my-api:latest
# 로그 확인
docker logs my-api
# 컨테이너 중지 & 삭제
docker stop my-api && docker rm my-apiDocker Compose
여러 컨테이너를 한 번에 정의하고 실행합니다. 프론트엔드, 백엔드, DB를 하나의 파일로 관리할 수 있습니다.
왜 Compose가 필요한가?
❌ docker run 반복
docker run -d postgres...
docker run -d redis...
docker run -d my-api...
docker run -d my-frontend...
✅ Compose 한 줄
docker compose up -d
모든 서비스 한 번에 시작!
docker-compose.yml 예시 (Python 스택)
version: '3.8'
services:
# PostgreSQL 데이터베이스
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
# FastAPI 백엔드
api:
build: ./backend
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://myuser:mypassword@db:5432/mydb
depends_on:
- db
# React 프론트엔드
web:
build: ./frontend
ports:
- "3000:3000"
depends_on:
- api
volumes:
postgres_data:docker-compose.yml 예시 (Node.js 스택)
version: '3.8'
services:
# PostgreSQL 데이터베이스
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
# Hono 백엔드
api:
build: ./backend
ports:
- "3001:3001"
environment:
DATABASE_URL: postgresql://myuser:mypassword@db:5432/mydb
depends_on:
- db
# Vite 프론트엔드
web:
build: ./frontend
ports:
- "5173:5173"
depends_on:
- api
volumes:
postgres_data:주요 명령어
| 명령어 | 설명 |
|---|---|
| docker compose up -d | 모든 서비스 백그라운드 시작 |
| docker compose down | 모든 서비스 중지 & 삭제 |
| docker compose logs -f api | api 서비스 로그 실시간 확인 |
| docker compose exec api sh | api 컨테이너에 쉘 접속 |
| docker compose build | 이미지 다시 빌드 |
이미지 레지스트리
빌드한 이미지를 클라우드에 저장하면 어디서든 다운받아 실행할 수 있습니다.
주요 레지스트리 비교
Docker Hub
가장 널리 사용되는 공개 레지스트리
- ✅ 공개 이미지 무제한
- ✅ 간편한 사용법
- ⚠️ 비공개 1개만 무료
GCR / Artifact Registry
Google Cloud의 레지스트리
- ✅ Cloud Run과 최적 연동
- ✅ 비공개 기본
- ⚠️ 스토리지 비용 발생
GitHub Container Registry
GitHub와 통합된 레지스트리
- ✅ GitHub Actions 연동
- ✅ 공개 무료
- ⚠️ 비공개 500MB 무료
GCR에 이미지 푸시
# 1. gcloud 인증
gcloud auth configure-docker asia-northeast3-docker.pkg.dev
# 2. 이미지 태깅
docker tag my-api:latest \
asia-northeast3-docker.pkg.dev/PROJECT_ID/my-repo/my-api:latest
# 3. 이미지 푸시
docker push asia-northeast3-docker.pkg.dev/PROJECT_ID/my-repo/my-api:latest
# 4. Cloud Run에서 사용
gcloud run deploy my-api \
--image=asia-northeast3-docker.pkg.dev/PROJECT_ID/my-repo/my-api:latest \
--region=asia-northeast3GitHub Container Registry 푸시
# 1. GitHub Personal Access Token으로 로그인
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# 2. 이미지 태깅
docker tag my-api:latest ghcr.io/USERNAME/my-api:latest
# 3. 이미지 푸시
docker push ghcr.io/USERNAME/my-api:latest실전 팁 & 베스트 프랙티스
.dockerignore 설정
빌드 컨텍스트에서 불필요한 파일을 제외하여 빌드 속도를 높입니다.
# 버전 관리
.git
.gitignore
# 의존성 (이미지에서 새로 설치)
node_modules
__pycache__
.venv
venv
# 빌드 산출물
dist
build
*.pyc
# 개발용 파일
.env.local
.env.development
*.log
# IDE
.vscode
.idea
# Docker 관련
Dockerfile
docker-compose*.yml
.dockerignore레이어 캐싱 최적화
❌ 비효율적
COPY . .
RUN pip install -r requirements.txt
소스 변경 시 의존성도 매번 재설치
✅ 효율적
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
의존성 파일 미변경 시 캐시 사용
보안 주의사항
- root 유저 피하기: USER 명령으로 non-root 사용자 설정
- 시크릿 주의: Dockerfile에 비밀번호 하드코딩 금지
- 최신 베이스 이미지: 보안 패치가 적용된 최신 버전 사용
- 최소 권한: 필요한 패키지만 설치
디버깅 팁
# 실행 중인 컨테이너 확인
docker ps
# 컨테이너 로그 확인
docker logs -f my-container
# 컨테이너 내부 접속
docker exec -it my-container sh
# 이미지 히스토리 확인 (레이어별 크기)
docker history my-api:latest
# 사용하지 않는 리소스 정리
docker system prune -a다음 단계
Docker로 컨테이너화를 완료했다면, 이제 CI/CD를 설정하여 코드 푸시만으로 자동 배포되도록 구성해보세요.