Docker는 애플리케이션을 컨테이너(Container) 단위로 패키징해 어느 환경에서나 동일하게 실행할 수 있는 오픈소스 플랫폼이다. 2013년 Solomon Hykes가 개발했으며, "내 컴퓨터에서는 되는데..." 문제를 해결한다.
컨테이너 vs 가상머신(VM)
가상머신 (VM): 컨테이너 (Docker):
┌─────────┬─────────┐ ┌─────────┬─────────┐
│ App A │ App B │ │ App A │ App B │
├─────────┼─────────┤ ├─────────┼─────────┤
│ Guest OS│ Guest OS│ │ │ │
├─────────┴─────────┤ │ Docker Engine │
│ Hypervisor │ ├────────────────────┤
├───────────────────┤ │ Host OS │
│ Host OS │ ├────────────────────┤
├───────────────────┤ │ Hardware │
│ Hardware │ └────────────────────┘
└───────────────────┘
크기: GB, 부팅: 수 분 크기: MB, 시작: 수 초
Docker 아키텍처
Docker CLI ←→ Docker Daemon (dockerd) ←→ containerd ←→ runc
↑ ↑
REST API / Unix Socket OCI 런타임 (컨테이너 실제 실행)
- •Docker CLI: 사용자 인터페이스
- •dockerd: 고수준 관리 (이미지, 네트워크, 볼륨)
- •containerd: 컨테이너 생명주기 관리
- •runc: OCI 명세에 따라 컨테이너 실제 구동
핵심 개념
| 개념 | 설명 |
|---|
| 이미지(Image) | 컨테이너 실행에 필요한 파일·설정의 읽기 전용 템플릿 |
| 컨테이너 | 이미지를 실행한 인스턴스 (프로세스) |
| Dockerfile | 이미지 빌드 명령어를 담은 텍스트 파일 |
| 레지스트리 | 이미지를 저장·배포하는 저장소 (Docker Hub) |
Dockerfile 예시
dockerfile
# Node.js 앱 컨테이너화
FROM node:22-alpine # 베이스 이미지
WORKDIR /app # 작업 디렉토리 설정
COPY package*.json ./ # 의존성 파일 복사
RUN npm install # 의존성 설치
COPY . . # 소스코드 복사
EXPOSE 3000 # 포트 노출
CMD ["node", "server.js"] # 실행 명령
주요 Docker 명령어
bash
# 이미지 빌드
docker build -t my-app:1.0 .
# 컨테이너 실행
docker run -d -p 8080:3000 --name my-container my-app:1.0
# ↑ ↑ ↑
# 백그라운드 포트매핑 컨테이너명
# 컨테이너 목록
docker ps
# 로그 확인
docker logs my-container
# 컨테이너 접속
docker exec -it my-container sh
# 중지 및 삭제
docker stop my-container
docker rm my-container
볼륨 (Volume)
컨테이너는 종료 시 데이터가 사라진다. 볼륨으로 데이터를 영속화한다.
bash
# 네임드 볼륨 (Docker가 관리, 권장)
docker run -v pgdata:/var/lib/postgresql/data postgres:16
# 바인드 마운트 (호스트 경로 직접 마운트, 개발 시 유용)
docker run -v /host/path:/container/path my-app
# tmpfs 마운트 (메모리에만 저장, 민감한 임시 데이터)
docker run --tmpfs /tmp my-app
| 볼륨 유형 | 저장 위치 | 적합 상황 |
|---|
| Named Volume | Docker 관리 영역 | 프로덕션 DB, 영속 데이터 |
| Bind Mount | 호스트 임의 경로 | 개발 환경 코드 핫리로드 |
| tmpfs | 메모리 | 민감한 임시 파일 |
네트워킹
bash
# 네트워크 유형
bridge # 기본, 동일 호스트의 컨테이너 간 통신
host # 호스트 네트워크 스택 공유 (리눅스만)
overlay # 여러 Docker 호스트 간 통신 (Swarm)
none # 네트워크 격리
# 커스텀 브리지 네트워크 (컨테이너 이름으로 DNS 해석 가능)
docker network create my-network
docker run --network my-network --name app my-app
docker run --network my-network --name db postgres
# app 컨테이너에서 'db' 호스트명으로 DB 접근 가능
여러 컨테이너를 함께 실행하는 도구.
yaml
# docker-compose.yml
services:
app:
build: .
ports:
- "8080:3000"
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
bash
docker compose up -d # 전체 서비스 시작
docker compose down # 전체 서비스 중지
docker compose logs -f # 전체 로그 스트리밍
멀티스테이지 빌드
빌드 환경과 실행 환경을 분리해 최종 이미지 크기를 대폭 줄이는 기법.
# 빌드 스테이지 (Go 컴파일러 포함, 크기 큼)
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# 런타임 스테이지 (바이너리만 복사, 크기 작음)
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]
효과: golang 컴파일러(~300MB)가 최종 이미지에 포함되지 않음 → alpine 기반 최종 이미지 ~5MB
이미지 최적화 모범 사례
| 기법 | 설명 | 효과 |
|---|
| 경량 베이스 | ubuntu(80MB) → alpine(5MB) | 이미지 크기 축소 |
| 멀티스테이지 | 빌드/런타임 분리 | 빌드 도구 제외 |
| .dockerignore | node_modules, *.log 제외 | 빌드 컨텍스트 축소 |
| 레이어 결합 | RUN a && b && c (한 레이어) | 레이어 수 감소 |
| 캐시 최적화 | 자주 변경되는 COPY는 하단 배치 | 재빌드 최소화 |
레이어 캐싱
FROM node:22-alpine ← 레이어 1 (캐시됨)
WORKDIR /app ← 레이어 2 (캐시됨)
COPY package*.json ./ ← 레이어 3 (package.json 변경 시에만 재빌드)
RUN npm install ← 레이어 4 (3번 변경 시에만 재실행)
COPY . . ← 레이어 5 (소스코드 변경마다 재빌드)
보안 Best Practices
dockerfile
# 1. 비루트 사용자로 실행 (컨테이너 탈출 공격 방지)
FROM node:22-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser # 이후 명령은 appuser 권한으로 실행
# 2. 최소 권한 베이스 이미지
FROM gcr.io/distroless/nodejs22 # 쉘·패키지 관리자 없는 최소 이미지
# 3. 헬스체크 내장
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
# 4. 민감 정보는 빌드 시 포함하지 않기
# ✗ ENV DB_PASSWORD=secret (이미지 레이어에 평문 저장됨)
# ✓ docker run --env-file .env 또는 Docker Secrets 사용
bash
# 이미지 취약점 스캔
docker scout cves my-app:1.0
docker scan my-app:1.0 # Snyk 기반
관련 개념
참고문헌
- •Docker 공식 문서: docs.docker.com
- •Turnbull, J. (2018). The Docker Book
- •Docker Best Practices 2026 — thinksys.com/devops/docker-best-practices