- Published on
Docker完全攻略2025:コンテナ基礎からマルチステージビルド、Compose、本番デプロイまで
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに
- 1. コンテナとは何か — VMとの<ruby>違<rp>(</rp><rt>ちが</rt><rp>)</rp></ruby>い
- 2. Dockerアーキテクチャ
- 3. Dockerfileマスタークラス
- <ruby>主要<rp>(</rp><rt>しゅよう</rt><rp>)</rp></ruby>な<ruby>命令<rp>(</rp><rt>めいれい</rt><rp>)</rp></ruby>の<ruby>解説<rp>(</rp><rt>かいせつ</rt><rp>)</rp></ruby>
- COPYとADDの<ruby>違<rp>(</rp><rt>ちが</rt><rp>)</rp></ruby>い
- CMDとENTRYPOINTの<ruby>違<rp>(</rp><rt>ちが</rt><rp>)</rp></ruby>い
- ARGとENV
- Dockerfile<ruby>最適化<rp>(</rp><rt>さいてきか</rt><rp>)</rp></ruby>ベストプラクティス
- 4. マルチステージビルド — イメージを90%<ruby>削減<rp>(</rp><rt>さくげん</rt><rp>)</rp></ruby>
- Node.jsの<ruby>例<rp>(</rp><rt>れい</rt><rp>)</rp></ruby>:1.2GBから120MBへ
- Goの<ruby>例<rp>(</rp><rt>れい</rt><rp>)</rp></ruby>:800MBから15MBへ
- Java/Spring Bootの<ruby>例<rp>(</rp><rt>れい</rt><rp>)</rp></ruby>:400MBから80MBへ
- Pythonの<ruby>例<rp>(</rp><rt>れい</rt><rp>)</rp></ruby>
- ビルド<ruby>結果<rp>(</rp><rt>けっか</rt><rp>)</rp></ruby>の<ruby>比較<rp>(</rp><rt>ひかく</rt><rp>)</rp></ruby><ruby>要約<rp>(</rp><rt>ようやく</rt><rp>)</rp></ruby>
- 5. Docker Compose<ruby>実践<rp>(</rp><rt>じっせん</rt><rp>)</rp></ruby>
- docker-compose.ymlの<ruby>基本<rp>(</rp><rt>きほん</rt><rp>)</rp></ruby><ruby>構造<rp>(</rp><rt>こうぞう</rt><rp>)</rp></ruby>
- <ruby>環境<rp>(</rp><rt>かんきょう</rt><rp>)</rp></ruby><ruby>変数<rp>(</rp><rt>へんすう</rt><rp>)</rp></ruby>とSecrets<ruby>管理<rp>(</rp><rt>かんり</rt><rp>)</rp></ruby>
- Composeの<ruby>主要<rp>(</rp><rt>しゅよう</rt><rp>)</rp></ruby>コマンド
- <ruby>開発<rp>(</rp><rt>かいはつ</rt><rp>)</rp></ruby><ruby>環境<rp>(</rp><rt>かんきょう</rt><rp>)</rp></ruby>と<ruby>本番<rp>(</rp><rt>ほんばん</rt><rp>)</rp></ruby><ruby>環境<rp>(</rp><rt>かんきょう</rt><rp>)</rp></ruby>の<ruby>分離<rp>(</rp><rt>ぶんり</rt><rp>)</rp></ruby>
- 6. ネットワーキング<ruby>深掘<rp>(</rp><rt>ふかぼ</rt><rp>)</rp></ruby>り
- 7. セキュリティベストプラクティス
- 1) Non-rootユーザーでの<ruby>実行<rp>(</rp><rt>じっこう</rt><rp>)</rp></ruby>
- 2) <ruby>読<rp>(</rp><rt>よ</rt><rp>)</rp></ruby>み<ruby>取<rp>(</rp><rt>と</rt><rp>)</rp></ruby>り<ruby>専用<rp>(</rp><rt>せんよう</rt><rp>)</rp></ruby>ファイルシステム
- 3) イメージセキュリティスキャン
- 4) Secrets<ruby>管理<rp>(</rp><rt>かんり</rt><rp>)</rp></ruby>(<ruby>絶対<rp>(</rp><rt>ぜったい</rt><rp>)</rp></ruby>にDockerfileに<ruby>入<rp>(</rp><rt>い</rt><rp>)</rp></ruby>れないこと)
- 5) ルートレスDocker
- 6) Content Trustとイメージ<ruby>署名<rp>(</rp><rt>しょめい</rt><rp>)</rp></ruby>
- 7) セキュリティチェックリスト
- 8. CI/CDパイプライン
- 9. <ruby>本番<rp>(</rp><rt>ほんばん</rt><rp>)</rp></ruby><ruby>運用<rp>(</rp><rt>うんよう</rt><rp>)</rp></ruby>
- 10. Docker<ruby>代替<rp>(</rp><rt>だいたい</rt><rp>)</rp></ruby>ツール:Podman、containerd、nerdctl
- 11. Docker + AIワークロード
- クイズ
- <ruby>参考<rp>(</rp><rt>さんこう</rt><rp>)</rp></ruby><ruby>資料<rp>(</rp><rt>しりょう</rt><rp>)</rp></ruby>
はじめに
コンテナ技術は、現代のソフトウェア開発の基盤となっています。「私のローカルでは動くのに?」という伝説的な言い訳をなくしたのがDockerです。2013年の登場以来、Dockerは開発、テスト、デプロイのパラダイムを完全に変革し、2025年現在はKubernetes、サーバーレス、AIワークロードまでコンテナが基本単位として定着しています。
このガイドでは、コンテナの根本原理からDockerfile最適化、マルチステージビルド、Docker Compose実践、セキュリティ、CI/CDパイプライン、本番運用、Docker代替ツール、AIワークロードまで体系的に解説します。
1. コンテナとは何か — VMとの違い
コンテナの定義
コンテナは、アプリケーションとその依存関係を一つのパッケージにまとめ、隔離された環境で実行する技術です。仮想マシン(VM)とは異なり、OSカーネルを共有するため、はるかに軽量で高速です。
Linuxの3つの核心技術
コンテナはLinuxカーネルの3つの機能を組み合わせたものです。
1) Namespaces — 隔離
プロセスが参照できるシステムリソースの範囲を制限します。
| Namespace | 隔離対象 |
|---|---|
| PID | プロセスID |
| NET | ネットワークインターフェース、ルーティング |
| MNT | ファイルシステムマウントポイント |
| UTS | ホスト名、ドメイン名 |
| IPC | プロセス間通信 |
| USER | ユーザーおよびグループID |
| CGROUP | cgroupルートディレクトリ |
2) Cgroups — リソース制限
CPU、メモリ、ディスクI/O、ネットワーク帯域幅などのリソース使用量を制限します。
# cgroupでメモリを256MBに制限する例
sudo cgcreate -g memory:mycontainer
echo 268435456 | sudo tee /sys/fs/cgroup/memory/mycontainer/memory.limit_in_bytes
3) Union Filesystem — レイヤー構造
複数
のファイルシステムレイヤーを一つに
統合
して
表示
する
技術
です。DockerはOverlayFSを
使用
します。
-
読
み取
り専用
(Read-Only)レイヤー:ベースイメージ、パッケージインストールなど -
書
き込
み可能
(Read-Write)レイヤー:コンテナ実行
時
の変更
事項
コンテナ vs VM 比較
┌─────────────────────────────────────────────────────────────┐
│ コンテナアーキテクチャ │
├──────────┬──────────┬──────────┬──────────┐ │
│ App A │ App B │ App C │ App D │ │
├──────────┼──────────┼──────────┼──────────┤ │
│ Bins/ │ Bins/ │ Bins/ │ Bins/ │ │
│ Libs │ Libs │ Libs │ Libs │ │
├──────────┴──────────┴──────────┴──────────┤ │
│ Container Runtime │ │
├────────────────────────────────────────────┤ │
│ Host OS Kernel │ │
├────────────────────────────────────────────┤ │
│ Infrastructure │ │
└────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ VMアーキテクチャ │
├──────────┬──────────┬──────────┬──────────┐ │
│ App A │ App B │ App C │ App D │ │
├──────────┼──────────┼──────────┼──────────┤ │
│ Bins/ │ Bins/ │ Bins/ │ Bins/ │ │
│ Libs │ Libs │ Libs │ Libs │ │
├──────────┼──────────┼──────────┼──────────┤ │
│ Guest OS │ Guest OS │ Guest OS │ Guest OS │ │
├──────────┴──────────┴──────────┴──────────┤ │
│ Hypervisor │ │
├────────────────────────────────────────────┤ │
│ Host OS │ │
├────────────────────────────────────────────┤ │
│ Infrastructure │ │
└────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
| 項目 | コンテナ | VM |
|---|---|---|
| 起動時間 | ミリ秒 | 数分 |
| イメージサイズ | MB単位 | GB単位 |
| パフォーマンスオーバーヘッド | ほぼなし | 10〜20% |
| 隔離レベル | プロセスレベル | ハードウェアレベル |
| OS対応 | ホストカーネル共有 | 独立OS可能 |
OCI標準
OCI(Open Container Initiative)は、コンテナイメージとランタイムのオープン標準を定義しています。
- Runtime Specification:コンテナの実行方法の標準化
- Image Specification:コンテナイメージフォーマットの標準化
- Distribution Specification:イメージ配布方法の標準化
Docker、Podman、containerdはすべてOCI標準に準拠しているため、イメージの互換性があります。
2. Dockerアーキテクチャ
クライアント・サーバーモデル
Dockerはクライアント・サーバーアーキテクチャを採用しています。
┌─────────────┐ REST API ┌─────────────────────┐
│ Docker │ ──────────────> │ Docker Daemon │
│ CLI │ │ (dockerd) │
│ │ │ │
│ docker run │ │ ┌─────────────────┐ │
│ docker build│ │ │ containerd │ │
│ docker pull │ │ │ │ │
└─────────────┘ │ │ ┌───────────┐ │ │
│ │ │ runc │ │ │
│ │ └───────────┘ │ │
│ └─────────────────┘ │
└─────────────────────┘
- Docker CLI:ユーザーがコマンドを入力するクライアント
- Docker Daemon (dockerd):コンテナのライフサイクルを管理するサーバープロセス
- containerd:OCI準拠のコンテナランタイムマネージャー
- runc:実際にコンテナを作成・実行する低レベルランタイム
4つの核心概念
1) イメージ(Image)
読
み
取
り
専用
のテンプレートで、コンテナを
作
るための
設計図
です。レイヤー
構造
により
効率的
に
保存
・
転送
されます。
# イメージ管理コマンド
docker images # ローカルイメージ一覧
docker pull nginx:1.25-alpine # イメージダウンロード
docker image inspect nginx # イメージ詳細情報
docker image prune # 未使用イメージの整理
2) コンテナ(Container)
イメージの実行インスタンスです。イメージの上に書き込み可能なレイヤーが追加されます。
# コンテナの実行と管理
docker run -d --name my-nginx -p 8080:80 nginx:1.25-alpine
docker ps # 実行中のコンテナ一覧
docker logs my-nginx # ログ確認
docker exec -it my-nginx /bin/sh # コンテナ内部にアクセス
docker stop my-nginx # 停止
docker rm my-nginx # 削除
3) ボリューム(Volume)
コンテナのデータを永続的に保存するメカニズムです。
# ボリュームの作成と使用
docker volume create my-data
docker run -d -v my-data:/app/data my-app
docker run -d -v /host/path:/container/path my-app # バインドマウント
4) ネットワーク(Network)
コンテナ間の通信を管理します。
# ネットワークの作成と管理
docker network create my-network
docker run -d --network my-network --name api my-api
docker run -d --network my-network --name db postgres
# apiコンテナからdb:5432でアクセス可能
3. Dockerfileマスタークラス
主要な命令の解説
# ベースイメージの選択
FROM node:20-alpine
# メタデータの追加
LABEL maintainer="dev@example.com"
LABEL version="1.0"
# ビルド時変数(ビルド時のみ使用可能)
ARG NODE_ENV=production
# 環境変数(ランタイムでも使用可能)
ENV NODE_ENV=$NODE_ENV
ENV PORT=3000
# 作業ディレクトリの設定
WORKDIR /app
# ファイルコピー(COPYを推奨)
COPY package*.json ./
# コマンド実行
RUN npm ci --only=production
# ソースコードのコピー
COPY . .
# ポートのドキュメント化(実際にはポートを開かない)
EXPOSE 3000
# コンテナのヘルスチェック
HEALTHCHECK \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# 実行ユーザーの変更(セキュリティ)
USER node
# コンテナ起動時に実行するコマンド
ENTRYPOINT ["node"]
CMD ["server.js"]
COPYとADDの違い
| 機能 | COPY | ADD |
|---|---|---|
| ローカルファイルコピー | O | O |
| URLダウンロード | X | O |
| tar自動展開 | X | O |
| 推奨度 | 推奨 | 特殊な場合のみ |
結論:ほとんどの場合COPYを使用してください。ADDはtar自動展開が必要な場合のみ使います。
CMDとENTRYPOINTの違い
# CMDのみ使用 - docker run時に完全に上書き可能
CMD ["python", "app.py"]
# docker run my-image python test.py (CMDがtest.pyに置換)
# ENTRYPOINTのみ使用 - 常に実行される
ENTRYPOINT ["python"]
# docker run my-image app.py (python app.pyとして実行)
# ENTRYPOINT + CMD の組み合わせ(最も柔軟)
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run my-image -> python app.py
# docker run my-image test.py -> python test.py
ARGとENV
# ARG: ビルド時にのみ存在(ランタイムには存在しない)
ARG BUILD_VERSION=1.0
RUN echo "Building version: $BUILD_VERSION"
# ENV: ビルド時 + ランタイムの両方で存在
ENV APP_VERSION=1.0
# コンテナ実行時にもAPP_VERSIONが使用可能
Dockerfile最適化ベストプラクティス
1) レイヤーの順序がキャッシュを決定する
# 悪い例: ソース変更時にnpm installが再実行
COPY . .
RUN npm install
# 良い例: package.json変更時にのみnpm installが再実行
COPY package*.json ./
RUN npm ci
COPY . .
2) RUNコマンドの統合
# 悪い例: 3つのレイヤーを生成
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# 良い例: 1つのレイヤーを生成 + キャッシュクリア
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
3) .dockerignoreの活用
# .dockerignore
node_modules
.git
.env
*.md
Dockerfile
docker-compose*.yml
.github
coverage
dist
4) 小さなベースイメージの選択
| イメージ | サイズ | 用途 |
|---|---|---|
| ubuntu:22.04 | 約77MB | フルOS必要時 |
| node:20-slim | 約200MB | ほとんどのNode.jsアプリ |
| node:20-alpine | 約130MB | 軽量Node.jsアプリ |
| alpine:3.19 | 約7MB | 最小ベース |
| distroless | 約2MB | 最小限のランタイムのみ |
| scratch | 0MB | 静的バイナリ専用 |
4. マルチステージビルド — イメージを90%削減
マルチステージビルドは、ビルドツールと最終実行環境を分離し、イメージサイズを劇的に削減する技法です。
Node.jsの例:1.2GBから120MBへ
# Stage 1: ビルド環境
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: プロダクション環境
FROM node:20-alpine AS production
WORKDIR /app
# プロダクション依存関係のみインストール
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# ビルド成果物のみコピー
COPY /app/dist ./dist
# セキュリティ: non-rootユーザー
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
結果:node:20フルイメージ(約1.1GB)の代わりにAlpineベース(約130MB)+ ビルドツール除外で最終120MB
Goの例:800MBから15MBへ
# Stage 1: ビルド
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .
# Stage 2: 最小イメージ
FROM scratch
COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
結果:Goビルド環境(約800MB)から静的バイナリのみ抽出して15MB以下
Java/Spring Bootの例:400MBから80MBへ
# Stage 1: ビルド
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY gradlew build.gradle settings.gradle ./
COPY gradle ./gradle
RUN ./gradlew dependencies --no-daemon
COPY src ./src
RUN ./gradlew bootJar --no-daemon
# Stage 2: ランタイム
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY /app/build/libs/*.jar app.jar
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Pythonの例
# Stage 1: 依存関係のビルド
FROM python:3.12-slim AS builder
WORKDIR /app
RUN pip install --no-cache-dir poetry
COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt -o requirements.txt --without-hashes
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Stage 2: ランタイム
FROM python:3.12-slim
WORKDIR /app
COPY /install /usr/local
COPY . .
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app.main:app"]
ビルド結果の比較要約
| 言語 | シングルステージ | マルチステージ | 削減率 |
|---|---|---|---|
| Node.js | 1.2GB | 120MB | 90% |
| Go | 800MB | 15MB | 98% |
| Java | 400MB | 80MB | 80% |
| Python | 900MB | 150MB | 83% |
5. Docker Compose実践
docker-compose.ymlの基本構造
Docker Composeは、複数のコンテナを1つのYAMLファイルで定義・管理します。
# docker-compose.yml
version: '3.9'
services:
api:
build:
context: ./api
dockerfile: Dockerfile
ports:
- '3000:3000'
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ['CMD', 'wget', '--spider', 'http://localhost:3000/health']
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
networks:
- app-network
db:
image: postgres:16-alpine
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
ports:
- '5432:5432'
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
command: redis-server --appendonly yes --maxmemory 256mb
volumes:
- redis-data:/data
ports:
- '6379:6379'
networks:
- app-network
nginx:
image: nginx:1.25-alpine
ports:
- '80:80'
- '443:443'
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- api
networks:
- app-network
volumes:
postgres-data:
driver: local
redis-data:
driver: local
networks:
app-network:
driver: bridge
環境変数とSecrets管理
# .envファイルの使用
services:
api:
env_file:
- .env
- .env.production
# Docker Secrets(Swarmモード)
services:
api:
secrets:
- db_password
- api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
external: true
Composeの主要コマンド
# サービスの起動(バックグラウンド)
docker compose up -d
# サービスログの確認
docker compose logs -f api
# サービスのスケーリング
docker compose up -d --scale api=3
# サービスの再ビルド + 再起動
docker compose up -d --build
# サービスの停止とリソースのクリーンアップ
docker compose down
# ボリュームも含めてすべて削除
docker compose down -v
# 特定サービスのみ再起動
docker compose restart api
# 実行中のサービスのステータス確認
docker compose ps
開発環境と本番環境の分離
# docker-compose.yml(共通)
services:
api:
build: ./api
# docker-compose.override.yml(開発環境 - 自動ロード)
services:
api:
volumes:
- ./api:/app # ホットリロード用マウント
environment:
- DEBUG=true
command: npm run dev
# docker-compose.prod.yml(本番)
services:
api:
environment:
- NODE_ENV=production
deploy:
replicas: 3
resources:
limits:
memory: 512M
cpus: "0.5"
# 開発環境(overrideファイルが自動ロード)
docker compose up
# 本番環境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
6. ネットワーキング深掘り
Dockerネットワークドライバー
| ドライバー | 説明 | ユースケース |
|---|---|---|
| bridge | デフォルトドライバー、同一ホスト内のコンテナ通信 | シングルホスト開発環境 |
| host | ホストネットワークスタックを直接使用 | パフォーマンスが重要な場合 |
| overlay | 複数Dockerホスト間の通信 | Swarm、マルチホスト |
| macvlan | コンテナに実際のMACアドレスを割り当て | レガシーネットワーク統合 |
| none | ネットワーク無効化 | セキュリティ隔離 |
コンテナDNS解決
同
じDockerネットワーク
上
のコンテナは、サービス
名
で
互
いを
発見
できます。
# カスタムネットワークの作成
docker network create my-app
# 同じネットワークでコンテナを実行
docker run -d --name api --network my-app my-api-image
docker run -d --name db --network my-app postgres
# apiコンテナ内部からdbコンテナにアクセス
# ホスト名 "db" が自動的に解決される
curl http://db:5432
ポートマッピング戦略
# 特定のポートマッピング
docker run -p 8080:80 nginx
# 特定のインターフェースにのみバインド
docker run -p 127.0.0.1:8080:80 nginx
# ランダムなホストポートの割り当て
docker run -p 80 nginx
# 複数のポートマッピング
docker run -p 80:80 -p 443:443 nginx
サービスディスカバリパターン
# Docker Composeでの自動サービスディスカバリ
services:
api-gateway:
image: nginx
depends_on:
- user-service
- order-service
user-service:
image: my-user-service
# api-gatewayからhttp://user-service:3000でアクセス可能
order-service:
image: my-order-service
# api-gatewayからhttp://order-service:3000でアクセス可能
7. セキュリティベストプラクティス
1) Non-rootユーザーでの実行
# Node.js - 組み込みのnodeユーザーを活用
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --only=production
USER node
CMD ["node", "server.js"]
# カスタムユーザーの作成
FROM python:3.12-slim
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
COPY . .
USER appuser
CMD ["python", "app.py"]
2) 読み取り専用ファイルシステム
docker run --read-only --tmpfs /tmp --tmpfs /var/run my-app
# Docker Composeで
services:
api:
read_only: true
tmpfs:
- /tmp
- /var/run
3) イメージセキュリティスキャン
# Trivyで脆弱性をスキャン
trivy image my-app:latest
# 深刻度でフィルタリング
trivy image --severity HIGH,CRITICAL my-app:latest
# Docker Scout(Docker Desktop内蔵)
docker scout cves my-app:latest
docker scout recommendations my-app:latest
# Snyk
snyk container test my-app:latest
4) Secrets管理(絶対にDockerfileに入れないこと)
# 絶対にやってはいけません
ENV API_KEY=sk-1234567890
COPY .env /app/.env
# 正しい方法: ビルド時にシークレットをマウント(BuildKit)
RUN npm ci
# ビルド時にシークレットを渡す
DOCKER_BUILDKIT=1 docker build --secret id=npmrc,src=.npmrc -t my-app .
5) ルートレスDocker
# ルートレスモードでDockerをインストール
dockerd-rootless-setuptool.sh install
# 確認
docker info | grep -i rootless
# Security Options: rootless
6) Content Trustとイメージ署名
# Docker Content Trustを有効化
export DOCKER_CONTENT_TRUST=1
# 署名されたイメージのみPull可能
docker pull my-registry/my-app:latest # 署名がなければ失敗
# Cosignでイメージに署名(Sigstore)
cosign sign my-registry/my-app:latest
cosign verify my-registry/my-app:latest
7) セキュリティチェックリスト
[セキュリティチェックリスト]
- Non-rootユーザーで実行(USERディレクティブ)
- 最小ベースイメージの使用(Alpine、distroless、scratch)
- イメージ脆弱性の定期スキャン(Trivy、Snyk、Scout)
- Dockerfileにシークレット/認証情報を絶対に含めない
- 読み取り専用ファイルシステムの使用
- ネットワーク露出の最小化
- リソース制限の設定(メモリ、CPU)
- Docker Content Trustの有効化
- ベースイメージの定期的な更新
- 不要なパッケージ/ツールの削除
8. CI/CDパイプライン
GitHub Actions + Dockerビルド/プッシュ
# .github/workflows/docker-build.yml
name: Docker Build and Push
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: my-org/my-app
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: my-username
password: my-token
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/my-org/my-app
tags: |
type=ref,event=branch
type=sha,prefix=
type=semver,pattern=v{{version}}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: steps.meta.outputs.tags
labels: steps.meta.outputs.labels
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
マルチプラットフォームビルド(amd64 + arm64)
# Buildxビルダーの作成
docker buildx create --name multiarch --use
# マルチプラットフォームでビルドおよびプッシュ
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag my-registry/my-app:latest \
--push .
CIでのレイヤーキャッシュ
# GitHub Actions Cache
- name: Build with cache
uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
# レジストリベースのキャッシュ
- name: Build with registry cache
uses: docker/build-push-action@v5
with:
cache-from: type=registry,ref=ghcr.io/my-org/my-app:buildcache
cache-to: type=registry,ref=ghcr.io/my-org/my-app:buildcache,mode=max
主要コンテナレジストリの比較
| レジストリ | 無料ティア | 特徴 |
|---|---|---|
| Docker Hub | プライベートリポ1つ | 最大のパブリックレジストリ |
| GitHub Container Registry(GHCR) | パブリック無制限 | GitHub Actions統合 |
| Amazon ECR | 500MBフリーティア | AWSサービス統合 |
| Google Artifact Registry | 500MBフリーティア | GCPサービス統合 |
| Azure Container Registry | なし | Azureサービス統合 |
9. 本番運用
ロギング戦略
# コンテナのログドライバー設定
docker run -d \
--log-driver=json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
my-app
# syslogドライバー
docker run -d \
--log-driver=syslog \
--log-opt syslog-address=tcp://logserver:514 \
my-app
# Docker Composeでのロギング設定
services:
api:
logging:
driver: json-file
options:
max-size: '10m'
max-file: '3'
ロギングのベストプラクティス:アプリケーションは常にstdout/stderrにログを出力してください。ファイルに直接書き込まないでください。Dockerがログドライバーを通じて収集します。
モニタリング:Prometheus + cAdvisor
# docker-compose.monitoring.yml
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- '9090:9090'
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- '8080:8080'
grafana:
image: grafana/grafana:latest
volumes:
- grafana-data:/var/lib/grafana
ports:
- '3000:3000'
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
grafana-data:
リソース制限
# メモリ制限: 512MB(超過時OOM Kill)
docker run -m 512m my-app
# CPU制限: 1.5コア
docker run --cpus="1.5" my-app
# 組み合わせ
docker run -m 512m --cpus="1.5" --memory-swap=1g my-app
# Docker Composeで
services:
api:
deploy:
resources:
limits:
memory: 512M
cpus: '1.5'
reservations:
memory: 256M
cpus: '0.5'
グレースフルシャットダウン(SIGTERM処理)
// Node.jsの例
process.on('SIGTERM', async () => {
console.log('SIGTERM received. Graceful shutdown...')
// 新しいリクエストを拒否
server.close(async () => {
// DB接続を閉じる
await db.close()
// Redis接続を閉じる
await redis.quit()
console.log('All connections closed. Exiting.')
process.exit(0)
})
// 30秒後に強制終了
setTimeout(() => {
console.error('Forced shutdown after timeout')
process.exit(1)
}, 30000)
})
# Dockerfileでの正しいENTRYPOINT形式
# exec形式を使用(SIGTERMがアプリに直接配信される)
ENTRYPOINT ["node", "server.js"]
# shell形式は使用禁止(SIGTERMがshにのみ配信される)
# ENTRYPOINT node server.js # これはやめてください
ヘルスチェックパターン
# Dockerfileでのヘルスチェック
HEALTHCHECK \
CMD curl -f http://localhost:3000/health || exit 1
# Docker Composeでのヘルスチェック
services:
api:
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 10s
timeout: 5s
retries: 5
10. Docker代替ツール:Podman、containerd、nerdctl
Podman — ルートレス、デーモンレス
Podmanは、Red Hatが開発したDocker互換のコンテナエンジンです。
# Dockerとほぼ同じCLI
podman run -d -p 8080:80 nginx
podman ps
podman images
# Docker互換のためのエイリアス
alias docker=podman
| 特徴 | Docker | Podman |
|---|---|---|
| アーキテクチャ | デーモン基盤(dockerd) | デーモンなし(フォークモデル) |
| root必要 | 基本的に必要 | デフォルトでルートレス |
| Compose対応 | docker compose | podman-compose |
| システム統合 | 独自 | systemd統合 |
| Pod対応 | X | O(Kubernetes Pod互換) |
containerd — Kubernetesデフォルトランタイム
containerdは、Dockerから分離されたコンテナランタイムで、Kubernetesのデフォルトランタイムです。
# ctr - containerdネイティブCLI(低レベル)
ctr images pull docker.io/library/nginx:latest
ctr run docker.io/library/nginx:latest my-nginx
nerdctl — containerd用Docker互換CLI
# Dockerと同じUX
nerdctl run -d -p 8080:80 nginx
nerdctl compose up -d
nerdctl build -t my-app .
# 追加機能: イメージ暗号化
nerdctl image encrypt --recipient jwe:public-key.pem my-app encrypted-app
ツール選択ガイド
| シナリオ | 推奨ツール |
|---|---|
| ローカル開発 | Docker Desktopまたは Podman |
| CI/CDパイプライン | Docker(BuildKit) |
| Kubernetesランタイム | containerd |
| セキュリティ重視環境 | Podman(ルートレス) |
| Docker互換 + containerd | nerdctl |
11. Docker + AIワークロード
NVIDIA Container Toolkit
AI/MLワークロードでGPUをコンテナに渡すには、NVIDIA Container Toolkitが必要です。
# NVIDIA Container Toolkitのインストール
distribution=ubuntu22.04
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
# パッケージのインストール
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
# Dockerランタイムの設定
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
GPUパススルー
# すべてのGPUを使用
docker run --gpus all nvidia/cuda:12.3-base nvidia-smi
# 特定のGPUのみ使用
docker run --gpus '"device=0,1"' my-ml-app
# Docker Composeで
services:
ml-training:
image: my-ml-app
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 2
capabilities: [gpu]
MLモデルサービングコンテナ
# PyTorchモデルサービングDockerfile
FROM pytorch/pytorch:2.2.0-cuda12.1-cudnn8-runtime
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY model/ ./model/
COPY serve.py .
EXPOSE 8000
HEALTHCHECK \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["python", "serve.py"]
# TensorFlow Serving
services:
tf-serving:
image: tensorflow/serving:latest-gpu
ports:
- '8501:8501'
volumes:
- ./models:/models
environment:
- MODEL_NAME=my_model
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
triton-server:
image: nvcr.io/nvidia/tritonserver:24.01-py3
ports:
- '8000:8000'
- '8001:8001'
- '8002:8002'
volumes:
- ./model-repository:/models
command: tritonserver --model-repository=/models
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
主要MLフレームワークコンテナ
| フレームワーク | 公式イメージ | GPU対応 |
|---|---|---|
| PyTorch | pytorch/pytorch | O |
| TensorFlow | tensorflow/tensorflow | O |
| NVIDIA Triton | nvcr.io/nvidia/tritonserver | O |
| Hugging Face TGI | ghcr.io/huggingface/text-generation-inference | O |
| vLLM | vllm/vllm-openai | O |
クイズ
DockerとコンテナについてのDockerとコンテナについての理解を確認しましょう。
Q1:コンテナがVMよりも軽量である根本的な理由は?
正解:コンテナはホストOSのカーネルを共有するからです。VMはそれぞれ独立したゲストOSを持ち、数GBのオーバーヘッドがありますが、コンテナはLinuxのnamespacesとcgroupsを使ってプロセスレベルで隔離するため、MB単位のサイズとミリ秒単位の起動時間を実現します。
Q2:DockerfileにおけるCOPYとADDの違い、そしてCOPYが推奨される理由を説明してください。
正解:COPYはローカルファイル/ディレクトリをイメージにコピーする単純な機能のみを提供します。ADDは追加でURLダウンロードとtar自動展開機能があります。COPYが推奨される理由は、(1)動作が明示的で予測可能、(2)ADDのURLダウンロードがキャッシュ無効化を引き起こす可能性がある、(3)意図しないtar展開を防ぐためです。tar自動展開が必要な場合のみADDを使ってください。
Q3:マルチステージビルドでGoイメージを800MBから15MBに削減できる理由は?
正解:Goは静的バイナリ(static binary)をコンパイルできるからです。CGO_ENABLED=0でビルドすると、外部Cライブラリ依存のない独立実行ファイルが生成され、これをscratch(空のイメージ)上にコピーすると、バイナリサイズそのものが最終イメージサイズになります。ビルドツール(コンパイラ、パッケージマネージャーなど)がすべて除去されるため、サイズが劇的に削減されます。
Q4:Dockerfileのレイヤー順序がビルドキャッシュに重要な理由を例を使って説明してください。
正解:Dockerは各レイヤーをキャッシュし、あるレイヤーが変更されると、それ以降のすべてのレイヤーを再ビルドします。例えば、Node.jsアプリでCOPY . .の後にRUN npm installを行うと、ソースコードが1文字変わるだけでもnpm installが再実行されます。代わりに、package.jsonだけを先にCOPYし、npm installを実行してからソースをコピーすると、package.jsonが変更されない限り依存関係インストールレイヤーがキャッシュから再利用され、ビルド時間が大幅に短縮されます。
Q5:Dockerのセキュリティでnon-rootユーザーで実行すべき理由と方法は?
正解:コンテナがrootで実行されていると、コンテナエスケープ(container escape)の脆弱性が発生した場合、攻撃者がホストシステムのroot権限を取得できる可能性があります。DockerfileのUSERディレクティブを使ってnon-rootユーザーを指定する必要があります。Node.jsイメージには組み込みのnodeユーザーがあり、他のイメージではRUN groupadd/useraddでユーザーを作成し、USERディレクティブで切り替えます。ファイルの所有権はCOPY --chownオプションで設定します。
参考資料
- Docker公式ドキュメント — 最も信頼できるリファレンス
- Dockerfileベストプラクティス — 公式最適化ガイド
- Docker Composeスペック — Composeファイルリファレンス
- OCI仕様 — コンテナ標準仕様
- NVIDIA Container Toolkit — GPUコンテナガイド
- Trivyセキュリティスキャナー — コンテナ脆弱性スキャン
- Podman公式ドキュメント — Docker代替コンテナエンジン
- containerd公式ドキュメント — 業界標準コンテナランタイム
- Docker BuildKitガイド — 高度なビルド機能
- Sigstore/Cosign — コンテナイメージ署名
- cAdvisor — コンテナリソースモニタリング
- Distrolessイメージ — 最小コンテナイメージ
- Dockerセキュリティチートシート — OWASPセキュリティチェックリスト
- マルチプラットフォームDockerビルド — クロスプラットフォームビルドガイド
- Docker + AI/MLガイド — Docker GenAIスタック
- Hugging Face TGI — LLMモデルサービング
- vLLMプロジェクト — 高性能LLM推論エンジン