C
DevOps/컨테이너/Lesson 02

Docker — 컨테이너·이미지·Dockerfile

45분·theory

Docker — 컨테이너·이미지·Dockerfile

🎯 이 lesson 을 읽고 나면

이 lesson 을 다 읽고 나면 아래 3가지를 자신 있게 할 수 있습니다.

  • ✅ Docker 이미지·컨테이너·볼륨·네트워크 4요소
  • ✅ Dockerfile 멀티스테이지 빌드 + alpine
  • ✅ docker-compose 로 다중 컨테이너 (DB+Redis+앱)

학습 목표를 체크리스트로 두고 다 답할 수 있게 되면 lesson 을 닫으세요.

Docker vs VM + 핵심 개념

한 줄: VM = 집 전체 빌리기, 컨테이너 = 방 하나 빌리기. 같은 OS 커널 공유, 자원 적음.

항목VM컨테이너 (Docker)
격리강함 (전체 OS)중간 (프로세스·네임스페이스)
시작분 단위초 단위
크기GBMB
자원무거움가벼움
호스트다른 OS 가능같은 OS 커널 (Linux on Linux 등)

핵심 개념:

용어의미
이미지컨테이너 청사진. 불변 (read-only)
컨테이너이미지의 실행 인스턴스
레지스트리이미지 저장소 (Docker Hub·ECR·GCR)
레이어이미지 = 레이어 누적. 캐시 효율
볼륨영속 데이터 (컨테이너 삭제해도 유지)
네트워크bridge·host·overlay (Swarm)

Dockerfile + 멀티 스테이지

Dockerfile — 이미지 빌드 명세:

dockerfile
# 1단계: 빌드
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 2단계: 실행 (작은 이미지)
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["npm", "start"]

핵심 명령어:

명령용도
FROM베이스 이미지
WORKDIR작업 디렉토리
COPY / ADD파일 복사 (ADD 는 압축 해제·URL 도)
RUN빌드 시 명령 실행
CMD컨테이너 시작 명령
ENTRYPOINT실행 파일 (인자 받는 형식)
EXPOSE노출 포트 (문서용)
ENV환경 변수
ARG빌드 시 변수
HEALTHCHECK헬스 체크 명령

최적화 팁:

  • 변경 잦은 파일 마지막에 COPY (캐시 ↑)
  • .dockerignorenode_modules·.git 제외
  • 멀티 스테이지 — 빌드 도구 제외해 작은 이미지
  • Alpine 베이스 (5MB) 우선, 호환성 이슈 시 distroless
💻 📌 Docker 명령어
# === 이미지 ===
docker build -t myapp:1.0 .          # 빌드
docker images                        # 목록
docker rmi myapp:1.0                 # 삭제
docker tag myapp:1.0 user/myapp:1.0  # 태그
docker push user/myapp:1.0           # 레지스트리 push
docker pull nginx:latest             # pull

# === 컨테이너 ===
docker run -d -p 3000:3000 --name web myapp:1.0
docker ps                            # 실행 중
docker ps -a                         # 모든 (중지 포함)
docker logs -f web                   # 로그 실시간
docker exec -it web bash             # 컨테이너 진입
docker stop web                      # 중지
docker rm web                        # 삭제
docker rm -f $(docker ps -aq)        # 모든 컨테이너 강제 삭제

# === 볼륨 ===
docker volume create mydata
docker run -v mydata:/data myapp
docker run -v $(pwd):/app myapp      # bind mount (로컬 개발)

# === 네트워크 ===
docker network create mynet
docker run --network mynet --name db postgres
docker run --network mynet --name web myapp  # web → db 호스트명으로 접근

# === Docker Compose (다중 컨테이너) ===
# docker-compose.yml 작성 후:
docker-compose up -d                 # 전체 시작
docker-compose logs -f               # 모든 로그
docker-compose down                  # 정리

# === 진단 ===
docker stats                         # 자원 사용 실시간
docker system df                     # 디스크 사용
docker system prune -a               # 안 쓰는 모든 정리

Docker Compose — 로컬 개발 환경 한 번에 띄우기

단일 컨테이너의 한계

bash
# 매번 따로 실행
docker run -d postgres
docker run -d redis
docker run -d --link postgres --link redis myapp

명령어 5개 + 네트워크 연결 수동 — 복잡하고 실수 다발.

docker-compose.yml — 한 파일로 끝

yaml
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: mydb
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://app:secret@postgres:5432/mydb
      REDIS_URL: redis://redis:6379
    depends_on:
      - postgres
      - redis

volumes:
  postgres-data:
bash
docker compose up -d      # 모두 시작
docker compose down       # 모두 중지·삭제
docker compose logs app   # 특정 서비스 로그
docker compose ps         # 상태 확인

3개 컨테이너 + 네트워크 + 볼륨명령어 한 줄로 뜹니다.

자동 네트워크

Compose 는 모든 서비스를 하나의 네트워크에 묶음. 서비스 이름이 호스트명 이 됩니다:

code
app 컨테이너 안에서:
  postgres:5432   ← 이름으로 접근
  redis:6379

localhost 가 아닌 서비스 이름. 옛 --link 방식보다 깔끔.

환경변수 — .env 자동 로딩

yaml
# docker-compose.yml
services:
  app:
    environment:
      DB_PASSWORD: ${DB_PASSWORD}   # .env 에서 자동
bash
# .env (같은 폴더)
DB_PASSWORD=secret123

compose up 하면 자동으로 .env 읽어서 치환. .gitignore 에 .env 추가 잊지 마세요.

volumes — 데이터 영속화

yaml
services:
  postgres:
    volumes:
      - postgres-data:/var/lib/postgresql/data    # 명명된 볼륨
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # 호스트 파일

volumes:
  postgres-data:    # Docker 관리 영역

컨테이너 삭제해도 데이터 유지. down 으로는 안 지워지고 down -v 로 명시 시에만 삭제.

healthcheck + depends_on (현대 방식)

yaml
services:
  postgres:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      retries: 5

  app:
    depends_on:
      postgres:
        condition: service_healthy   # postgres 가 *준비* 됐을 때만 시작

depends_on 만으로는 "시작 순서" 만 보장 — DB 가 받을 준비 됐다는 보장은 안 됨. healthcheck 결합이 표준.

프로덕션에는 Compose 단독으로 안 씁니다

  • Compose 는 로컬·개발 환경 표준
  • 프로덕션은 Kubernetes, ECS, Cloud Run
  • Compose 파일을 그대로 K8s 매니페스트로 변환 하는 도구 (Kompose) 도 있음

🤖 AI 에게 이렇게 요청해보세요

  • "PostgreSQL + Redis + Node.js 앱이 함께 뜨는 docker-compose.yml 만들어줘"
  • "이 compose 파일에 healthcheck 와 depends_on 조건부 시작 추가해줘"
  • "환경변수를 .env 파일로 분리해줘"
먼저 읽으면 좋은 개념: DevOps
Docker — 컨테이너·이미지·Dockerfile - DevOps