1. Docker란?
Docker는 애플리케이션을 컨테이너라는 표준화된 유닛으로 패키징하여 어떤 환경에서도 동일하게 실행할 수 있게 돕는 플랫폼입니다. VM과 달리 호스트 OS 커널을 공유하므로 훨씬 가볍습니다.
| 항목 | 가상 머신 (VM) | Docker 컨테이너 |
|---|---|---|
| 부팅 시간 | 수 분 | 수 초 미만 |
| 이미지 크기 | 수 GB | 수 MB ~ 수백 MB |
| OS 커널 | 독립 Guest OS | Host OS 공유 |
| 격리 수준 | 완전한 하드웨어 격리 | 프로세스 격리 (namespace) |
2. 설치
# macOS / Windows: Docker Desktop 설치
# https://www.docker.com/get-started
# Linux (Ubuntu)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER # sudo 없이 사용
newgrp docker
docker --version # Docker version 27.x.x
docker compose version # Docker Compose v2.x.xbash
3. 핵심 개념
| 용어 | 설명 |
|---|---|
| Image | 컨테이너 실행에 필요한 파일 시스템 스냅샷. 읽기 전용 레이어 스택 |
| Container | 이미지의 실행 인스턴스. 읽기/쓰기 레이어 추가 |
| Dockerfile | 이미지 빌드 지시문 파일 |
| Registry | 이미지 저장·배포 서버 (Docker Hub, GHCR, ECR 등) |
| Volume | 컨테이너 외부의 영구 데이터 저장소 |
| Network | 컨테이너 간 통신을 위한 가상 네트워크 |
4. 기본 명령어
# ── 이미지 ───────────────────────────────────────
docker pull nginx:alpine # 이미지 다운로드
docker images # 로컬 이미지 목록
docker rmi nginx:alpine # 이미지 삭제
docker image prune # 미사용 이미지 정리
# ── 컨테이너 실행 ─────────────────────────────────
docker run -d \
--name web \
-p 8080:80 \
-e ENV=production \
-v $(pwd)/html:/usr/share/nginx/html \
nginx:alpine
# 주요 옵션
# -d 백그라운드 실행
# --name 컨테이너 이름 지정
# -p 호스트:컨테이너 포트 매핑
# -e KEY=VAL 환경 변수
# -v 호스트:컨테이너 볼륨 마운트
# --rm 종료 시 자동 삭제
# ── 컨테이너 관리 ─────────────────────────────────
docker ps # 실행 중 컨테이너
docker ps -a # 전체 컨테이너
docker logs -f web # 로그 스트리밍
docker exec -it web sh # 컨테이너 내부 접속
docker stats # CPU·메모리 실시간 모니터링
docker stop web && docker rm web # 중지 후 삭제
docker container prune # 중지된 컨테이너 정리bash
5. Dockerfile
# Node.js 애플리케이션 Dockerfile
FROM node:20-alpine
# 비루트 사용자 생성 (보안)
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
# 레이어 캐싱 최적화: 의존성 먼저 복사
COPY package*.json ./
RUN npm ci --only=production
# 소스 복사
COPY --chown=app:app . .
# 비루트 사용자로 전환
USER app
EXPOSE 3000
# ENTRYPOINT: 고정 실행 명령어 (CMD와 조합)
ENTRYPOINT ["node"]
CMD ["server.js"]dockerfile
| 명령어 | 설명 |
|---|---|
FROM | 베이스 이미지 지정 |
RUN | 빌드 시 셸 명령 실행 (새 레이어 생성) |
COPY | 호스트 파일을 이미지로 복사 |
ENV | 환경 변수 설정 |
ARG | 빌드 시점 변수 (--build-arg로 전달) |
EXPOSE | 컨테이너가 사용할 포트 문서화 |
ENTRYPOINT | 컨테이너 시작 명령어 (고정) |
CMD | 기본 인자 (ENTRYPOINT 없으면 실행 명령) |
HEALTHCHECK | 헬스 체크 명령 설정 |
6. 멀티스테이지 빌드
빌드 도구를 포함한 무거운 빌드 환경과 실제 실행 환경을 분리해 최종 이미지 크기를 최소화합니다.
# Go 애플리케이션 예시 — 빌드 결과물만 최종 이미지에 포함
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o /app ./cmd/server
# ── 최종 이미지: 바이너리만 포함 ──────────────────
FROM scratch # 빈 이미지 (가장 작음)
COPY --from=builder /app /app
COPY --from=builder /etc/ssl/certs /etc/ssl/certs
EXPOSE 8080
ENTRYPOINT ["/app"]
# 결과: Go 빌드 이미지 ~800MB → 최종 이미지 ~10MBdockerfile
# Next.js 예시 — 빌드 → 의존성 → 실행 3단계
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -S nextjs && adduser -S nextjs -G nextjs
COPY --from=builder --chown=nextjs:nextjs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nextjs /app/public ./public
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]dockerfile
7. 볼륨과 마운트
# ── Named Volume (권장) ────────────────────────────
docker volume create pgdata
docker run -d \
--name postgres \
-v pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret \
postgres:16-alpine
docker volume ls # 볼륨 목록
docker volume inspect pgdata
docker volume rm pgdata
# ── Bind Mount (개발 환경) ─────────────────────────
docker run -d \
-v $(pwd):/app \ # 호스트 경로를 컨테이너에 직접 마운트
-w /app \
node:20-alpine \
npm run dev
# ── tmpfs Mount (메모리 임시 저장) ─────────────────
docker run --tmpfs /tmp:rw,size=100m nginxbash
8. 네트워크
# Docker 네트워크 드라이버
# bridge: 기본값, 같은 호스트의 컨테이너 간 통신
# host: 호스트 네트워크 직접 사용 (Linux만)
# none: 네트워크 비활성화
# overlay: Swarm/Kubernetes 멀티 호스트 통신
# 사용자 정의 bridge 네트워크 (컨테이너명으로 DNS 통신 가능)
docker network create mynet
docker run -d --name api --network mynet my-api
docker run -d --name db --network mynet postgres:16-alpine
# api 컨테이너에서 db 이름으로 접근 가능:
# postgresql://db:5432/mydb
docker network ls
docker network inspect mynet
docker network rm mynetbash
9. Docker Compose
# docker-compose.yml — 실무 수준 예시
services:
api:
build:
context: .
dockerfile: Dockerfile
args:
NODE_ENV: production
ports: ["3000:3000"]
environment:
DATABASE_URL: postgresql://user:${DB_PASS}@db:5432/mydb
REDIS_URL: redis://cache:6379
depends_on:
db: { condition: service_healthy }
cache: { condition: service_started }
restart: unless-stopped
volumes:
- ./uploads:/app/uploads
networks: [backend]
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD", "pg_isready", "-U", "user"]
interval: 5s
timeout: 3s
retries: 5
networks: [backend]
cache:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes: [redisdata:/data]
networks: [backend]
volumes:
pgdata:
redisdata:
networks:
backend:
driver: bridgeyaml
# Compose 주요 명령어
docker compose up -d # 전체 스택 백그라운드 실행
docker compose up -d api # 특정 서비스만 실행
docker compose logs -f api # 특정 서비스 로그
docker compose exec db psql -U user mydb # 서비스 내부 명령
docker compose down # 중지 + 컨테이너 삭제
docker compose down -v # 볼륨까지 삭제bash
10. 이미지 레지스트리
# ── Docker Hub ────────────────────────────────────
docker login
docker tag myapp:latest username/myapp:1.0.0
docker push username/myapp:1.0.0
# ── GitHub Container Registry (GHCR) ──────────────
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
docker tag myapp ghcr.io/org/myapp:latest
docker push ghcr.io/org/myapp:latest
# ── 프라이빗 레지스트리 (로컬) ─────────────────────
docker run -d -p 5000:5000 --name registry registry:2
docker tag myapp localhost:5000/myapp
docker push localhost:5000/myappbash
11. 보안 & 베스트 프랙티스
# .dockerignore — 불필요한 파일 제외 (빌드 컨텍스트 최소화)
node_modules
.git
.env
*.log
coverage
.next
disttext
이미지 최적화 체크리스트
✅ Alpine / Distroless 베이스 이미지 사용 — 공격 표면 최소화
✅ 멀티스테이지 빌드로 빌드 도구 제거
✅
✅
✅ 레이어 캐싱:
✅
✅
✅ Alpine / Distroless 베이스 이미지 사용 — 공격 표면 최소화
✅ 멀티스테이지 빌드로 빌드 도구 제거
✅
USER nonroot로 비루트 실행✅
.dockerignore로 빌드 컨텍스트 최소화✅ 레이어 캐싱:
COPY package.json → RUN npm ci → COPY . . 순서 유지✅
HEALTHCHECK 설정으로 오케스트레이터 헬스체크 활성화✅
docker scout 또는 trivy로 취약점 스캔
12. 다음 단계
Docker 이후 로드맵
• Kubernetes — 컨테이너 오케스트레이션 (자동 스케일링, 롤링 업데이트, 자가 치유)
• Docker Swarm — 경량 클러스터 오케스트레이션 (소규모 팀)
• CI/CD 통합 — GitHub Actions에서 자동 빌드 · 푸시 · 배포
• Buildkit / BuildX — 병렬 빌드, 멀티 아키텍처 이미지 (ARM + AMD64)
• Kubernetes — 컨테이너 오케스트레이션 (자동 스케일링, 롤링 업데이트, 자가 치유)
• Docker Swarm — 경량 클러스터 오케스트레이션 (소규모 팀)
• CI/CD 통합 — GitHub Actions에서 자동 빌드 · 푸시 · 배포
• Buildkit / BuildX — 병렬 빌드, 멀티 아키텍처 이미지 (ARM + AMD64)