
はじめに
コンテナはクラウドネイティブの基盤です。マイクロサービス アーキテクチャ内のすべてのサービスは、開発、ステージング、運用の間の一貫性を確保するためにコンテナ イメージにパッケージ化されています。この記事では、コンテナー、Docker、および重要なベスト プラクティスについて詳しく説明します。
1. コンテナと仮想マシンの比較
1.1 仮想マシン
┌──────────────────────────────────────┐
│ Host Hardware │
├──────────────────────────────────────┤
│ Host OS (Linux) │
├──────────────────────────────────────┤
│ Hypervisor (KVM/ESXi) │
├────────────┬────────────┬────────────┤
│ Guest OS │ Guest OS │ Guest OS │
│ (Ubuntu) │ (CentOS) │ (Debian) │
├────────────┼────────────┼────────────┤
│ Libs/Bins │ Libs/Bins │ Libs/Bins │
├────────────┼────────────┼────────────┤
│ App A │ App B │ App C │
└────────────┴────────────┴────────────┘
- 各 VM は 別の OS (別のカーネル) を実行します
- リソース消費量: 500MB - OS のみ 2GB RAM
- 起動:30秒~数分
- 分離: 強力 (ハードウェアレベル)
1.2 コンテナ
┌──────────────────────────────────────┐
│ Host Hardware │
├──────────────────────────────────────┤
│ Host OS (Linux Kernel) │
├──────────────────────────────────────┤
│ Container Runtime (containerd)│
├────────────┬────────────┬────────────┤
│ Libs/Bins │ Libs/Bins │ Libs/Bins │
├────────────┼────────────┼────────────┤
│ App A │ App B │ App C │
└────────────┴────────────┴────────────┘
- すべてのコンテナはホスト OS の カーネルを共有
- ライト: コンテナ イメージの場合は 5MB ~ 200MB
- 起動: < 1 秒
- Isolation: Process-level (namespaces + cgroups)
1.3 比較
| 基準 | VM | コンテナ |
|---|---|---|
| Startup time | 30s - 5min | < 1s |
| Image size | GB | MB |
| RAM overhead | 500MB+ per VM | ~0 overhead |
| Isolation | Strong (hypervisor) | Process-level (namespace) |
| Density | 10-20 VMs per host | 100-1000 containers per host |
| Portability | Moderate | Excellent |
2. Docker Architecture
2.1 Core Components
┌─────────────────────────────────────────────────┐
│ Docker Client (CLI) │
│ docker build, docker run, docker push │
└───────────────────────┬─────────────────────────┘
│ REST API
┌───────────────────────▼─────────────────────────┐
│ Docker Daemon (dockerd) │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Images │ │ Containers │ │ Volumes │ │
│ └──────────┘ └──────────────┘ └────────────┘ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Networks │ │ containerd │ │
│ └──────────┘ └──────┬───────┘ │
└──────────────────────┬──────────────────────────┘
│
┌──────────────────────▼──────────────────────────┐
│ Container Runtime (runc) │
│ Linux Kernel: namespaces + cgroups │
└─────────────────────────────────────────────────┘
2.2 Linux Kernel Features
コンテナは、Linux カーネルの 2 つのコア機能に基づいて構築されています。
名前空間 — 分離:
pid— プロセスの分離 (コンテナは自身のプロセスのみを認識します)net— ネットワーク分離 (各コンテナには独自のネットワーク スタックがあります)mnt— 独自のファイルシステムをマウントするuts— プライベートホスト名ipc— プロセス間通信を個別に行うuser— ユーザー/グループ ID の個別のマッピング
Cgroups — リソース制限:
- CPU (millicores)
- Memory (bytes)
- Disk I/O
- Network bandwidth
3. Dockerfile Best Practices
3.1 Multi-stage Build
マルチステージ ビルドは、ビルド環境とランタイムを分離することで、コンパクトなイメージを作成するのに役立ちます。
# === Stage 1: Build ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build
# === Stage 2: Runtime ===
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
CMD ["node", "dist/main.js"]
結果: イメージ ビルド ~800MB → イメージ ランタイム ~150MB
3.2 Layer Caching
Docker は各レイヤーをキャッシュします。キャッシュを最適化するために Dockerfile を調整します。
# ✅ Tốt: Dependencies ít thay đổi → cache được layer này
COPY package*.json ./
RUN npm ci
# Source code thay đổi thường xuyên → chỉ invalidate từ đây
COPY . .
RUN npm run build
# ❌ Xấu: Mỗi lần thay đổi code → rebuild tất cả
COPY . .
RUN npm ci && npm run build
3.3 Security Best Practices
# 1. Dùng specific version, KHÔNG dùng :latest
FROM node:20.11-alpine3.19
# 2. Chạy với non-root user
RUN addgroup -S app && adduser -S app -G app
USER app
# 3. Không copy file nhạy cảm
# .dockerignore:
# .env
# .git
# node_modules
# *.secret
# 4. Scan image trước khi push
# trivy image myapp:1.0.0
3.4 Java / Go / Python Examples
Java (Spring Boot):
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY gradle/ gradle/
COPY gradlew build.gradle.kts settings.gradle.kts ./
RUN ./gradlew dependencies --no-daemon
COPY src/ src/
RUN ./gradlew bootJar --no-daemon
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S app && adduser -S app -G app
COPY --from=build /app/build/libs/*.jar /app/app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Go:
FROM golang:1.22-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server
FROM scratch
COPY --from=build /server /server
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
一緒に行く scratch base image → image size ~10-15MB.
4. Container Networking
4.1 Docker Network Types
Bridge (default) Host None Overlay (Swarm/K8s)
┌────────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐
│ Container A │ │Container │ │Container │ │ Host 1 Host 2 │
│ 172.17.0.2 │ │shares │ │no network│ │ ┌─────┐ ┌─────┐│
│ Container B │ │host's │ │isolated │ │ │ C-A │ │ C-B ││
│ 172.17.0.3 │ │network │ │ │ │ └──┬──┘ └──┬──┘│
│ │ │ │stack │ │ │ │ └────┬────┘ │
│ docker0 bridge │ │ │ │ │ │ VXLAN overlay │
└────────────────┘ └──────────┘ └──────────┘ └──────────────────┘
4.2 Docker Compose Networking
services:
order-service:
build: ./order-service
ports:
- "8080:8080"
networks:
- backend
depends_on:
postgres:
condition: service_healthy
payment-service:
build: ./payment-service
ports:
- "8081:8080"
networks:
- backend
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: orders
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 3s
retries: 5
networks:
backend:
driver: bridge
volumes:
pgdata:
secrets:
db_password:
file: ./secrets/db_password.txt
同じネットワーク内では、サービスは サービス名 で相互に呼び出します。
order-service → http://payment-service:8080/api/pay
5. Image Registry
5.1 レジストリを使用した CI/CD パイプライン
Developer → Git Push → CI Pipeline:
1. docker build -t registry.example.com/order-service:v1.2.3
2. trivy image registry.example.com/order-service:v1.2.3
3. docker push registry.example.com/order-service:v1.2.3
4. Update K8s manifest → ArgoCD sync
Registry Options:
├── Docker Hub (public)
├── Harbor (self-hosted, recommended)
├── AWS ECR
├── Google Artifact Registry
└── GitHub Container Registry (ghcr.io)
5.2 Image Tagging Strategy
# ✅ Semantic versioning
registry.example.com/order-service:1.2.3
registry.example.com/order-service:1.2.3-alpine
# ✅ Git commit hash (immutable)
registry.example.com/order-service:abc123f
# ❌ KHÔNG dùng :latest trong production
registry.example.com/order-service:latest
6. まとめ
| Concept | Key Point |
|---|---|
| コンテナと VM | コンテナーの軽量化、起動の高速化、高密度化 |
| 多段階ビルド | ビルドとランタイムを分離してイメージ サイズを削減する |
| レイヤーキャッシュ | キャッシュを最適化するために Dockerfile を調整する |
| 非 root ユーザー | 常に非 root ユーザーでコンテナを実行する |
| 画像スキャン | プッシュ前にスキャンし、危険な画像をブロック |
| ネットワーキング | 同じネットワーク内のコンテナは、サービス名 |
次の記事: Kubernetes アーキテクチャ — Kubernetes が何百ものコンテナをオーケストレーションし、自動的にスケールし、自己修復し、ライフサイクル全体を管理する方法。