この記事の対象読者
- Linuxの基本コマンドは使えるが、Dockerを「なんとなく」使っている方
-
docker runは叩けるけど、コンテナと仮想マシンの違いを説明できない方 - WSL2上でDockerを使っているが、イメージ・レイヤー・ボリュームの仕組みを理解したい方
この記事で得られること
- コンテナの正体: 仮想マシンとの根本的な違いをカーネルレベルで理解できる
- Dockerの全体像: イメージ、コンテナ、ボリューム、ネットワークの関係が図解でわかる
- 実践力: Dockerfileの書き方、マルチステージビルド、GPUコンテナの構築まで身につく
この記事で扱わないこと
- Docker Composeによる複数コンテナの管理(次回記事で扱います)
- Docker Swarm / Kubernetesによるオーケストレーション
- Docker Desktop の GUI操作の詳細
1. Dockerとの出会い
「Docker使ってみて」と言われて、よくわからないままdocker runした。あの日から何も変わっていない。
これ、私のことです。PyTorchの環境構築で「Dockerイメージ使えば一発ですよ」と言われ、言われるがままdocker run。動いた。でも「なぜ動いたのか」「何が起きているのか」は理解していなかった。
ある日、RTX 5090を買ってCUDA環境をDockerで構築しようとしたら、コンテナ内からGPUが見えない。docker buildしたらイメージが30GBを超えてWSL2のディスクが溢れる。ボリュームマウントしたはずのデータが消える。全部「Dockerの仕組みを理解していなかった」のが原因でした。
Dockerを一言で説明するなら、「アプリケーションとその実行環境をまるごとパッケージ化して、どこでも同じように動かすためのコンテナプラットフォーム」です。
料理に例えるなら、仮想マシンが「キッチンごと引っ越す」なら、Dockerは「レシピと食材をジップロックに詰めて持っていく」。キッチン(OS)は共有するから軽い。でもジップロックの中身は完全に独立しているから、他の料理と混ざらない。
ここまでで、Dockerのざっくりしたイメージが掴めたでしょうか。次は、この記事で使う用語を整理しておきましょう。
2. 前提知識の確認
本題に入る前に、この記事で頻出する用語を確認します。
2.1 コンテナ とは
Linuxカーネルのnamespace(名前空間)とcgroups(コントロールグループ)を利用して、プロセスを隔離した実行環境のことです。仮想マシンと違い、独自のOSカーネルを持ちません。ホストOSのカーネルを共有するため、起動が速く、軽量です。
2.2 イメージ とは
コンテナの「設計図」です。アプリケーションのコード、ランタイム、ライブラリ、設定ファイルなどを含む読み取り専用のテンプレート。イメージから何個でもコンテナを作成できます。
2.3 レイヤー とは
Dockerイメージは複数の読み取り専用レイヤーの積み重ねで構成されています。Dockerfileの各命令(FROM, RUN, COPY等)が1つのレイヤーを作ります。共通のレイヤーは複数イメージ間で共有されるため、ディスクを節約できます。
2.4 レジストリ とは
Dockerイメージを保存・配布するサービスです。最も有名なのはDocker Hub(hub.docker.com)。他にGitHub Container Registry(ghcr.io)、Amazon ECR、Google Artifact Registryなどがあります。
これらの用語が押さえられたら、Dockerの背景を見ていきましょう。
3. Dockerが生まれた背景
3.1 「うちの環境では動くんだけど...」問題
Dockerが登場する前、ソフトウェアのデプロイには根深い問題がありました。
「It works on my machine(私のマシンでは動く)」
開発者のPCでは動くのに、本番サーバーに持っていくと動かない。Pythonのバージョンが違う、ライブラリのバージョンが違う、OS自体が違う。この問題を根本的に解決するために、2013年にSolomon Hykes率いるdotCloud社(後にDocker社に改名)がDockerをオープンソースとしてリリースしました。
3.2 仮想マシンではダメだったのか
「環境ごとパッケージ化」は仮想マシンでもできました。VMwareやVirtualBoxで仮想マシンのイメージを配布すれば、同じ環境を再現できます。しかし:
| 問題 | 仮想マシン | Docker |
|---|---|---|
| サイズ | 数GB〜数十GB(OS込み) | 数MB〜数百MB(カーネル不要) |
| 起動時間 | 数分 | 数秒以下 |
| リソース消費 | OS分のメモリ・CPUが必要 | ホストOSとカーネルを共有 |
| 密度 | 1台に数個〜十数個 | 1台に数百個 |
| イメージ共有 | 巨大ファイルのコピー | レイヤー共有で効率的 |
【仮想マシン】 【Docker コンテナ】
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│App A│ │App B│ │App C│ │App A│ │App B│ │App C│
├─────┤ ├─────┤ ├─────┤ ├─────┤ ├─────┤ ├─────┤
│Libs │ │Libs │ │Libs │ │Libs │ │Libs │ │Libs │
├─────┤ ├─────┤ ├─────┤ └──┬──┘ └──┬──┘ └──┬──┘
│Guest│ │Guest│ │Guest│ │ │ │
│ OS │ │ OS │ │ OS │ ┌──┴──────┴──────┴──┐
├─────┴─┴─────┴─┴─────┤ │ Docker Engine │
│ ハイパーバイザー │ ├────────────────────┤
├──────────────────────┤ │ ホスト OS │
│ ホスト OS │ │ (カーネル共有) │
├──────────────────────┤ ├────────────────────┤
│ ハードウェア │ │ ハードウェア │
└──────────────────────┘ └────────────────────┘
OS×3 = 重い OS×1 = 軽い
3.3 Dockerのエコシステム(2026年現在)
Dockerは単一のツールではなく、巨大なエコシステムに成長しています。
| コンポーネント | 役割 | 2026年現在のバージョン |
|---|---|---|
| Docker Engine | コンテナランタイム | v29.x |
| Docker Desktop | Win/Mac向け統合環境 | v4.42+ |
| Docker Compose | 複数コンテナの定義・管理 | v5.x(Go SDK搭載) |
| Docker Hub | 公式イメージレジストリ | - |
| Docker Buildx | マルチプラットフォームビルド | v0.22+ |
| Docker Scout | イメージのセキュリティ分析 | - |
| Docker Model Runner | LLM推論の実行基盤(新機能) | - |
2026年の注目: Docker Desktop 4.42以降でDocker Model Runnerが搭載されました。docker modelコマンドでLLMモデルの管理・推論が可能に。OpenAI互換APIやAnthropic互換APIを公開でき、さらにDocker MCP ToolkitでMCPサーバーの管理もできるようになっています。AI開発者にとってDockerの重要性はますます増しています。
背景がわかったところで、基本的な仕組みを見ていきましょう。
4. 基本概念と仕組み
4.1 コンテナの隔離メカニズム
Dockerコンテナの「隔離」は、Linuxカーネルの2つの機能で実現されています。
namespace(名前空間) — 「何が見えるか」を制御
| namespace | 隔離対象 | 効果 |
|---|---|---|
| PID | プロセスID | コンテナ内のプロセスはホストの他のプロセスが見えない |
| NET | ネットワーク | コンテナ固有のIPアドレス・ポート空間 |
| MNT | マウントポイント | コンテナ固有のファイルシステム |
| UTS | ホスト名 | コンテナ固有のホスト名 |
| IPC | プロセス間通信 | 共有メモリの分離 |
| USER | UID/GID | コンテナ内のroot ≠ ホストのroot |
cgroups(コントロールグループ) — 「どれだけ使えるか」を制御
| リソース | 制御内容 |
|---|---|
| CPU | 使用率の上限、優先度 |
| メモリ | 使用量の上限 |
| ディスクI/O | 読み書き速度の制限 |
| ネットワーク | 帯域幅の制限 |
4.2 イメージとレイヤーの仕組み
Dockerイメージの構造を理解するのが、Docker習得の最大の鍵です。
Dockerfile: 生成されるレイヤー:
┌─────────────────────┐ ┌─────────────────────┐
│ FROM python:3.12 │───────→│ Layer 1: Python基盤 │ ← 200MB(共有可能)
├─────────────────────┤ ├─────────────────────┤
│ COPY requirements.. │───────→│ Layer 2: requirements│ ← 1KB
├─────────────────────┤ ├─────────────────────┤
│ RUN pip install ... │───────→│ Layer 3: pip packages│ ← 500MB
├─────────────────────┤ ├─────────────────────┤
│ COPY . . │───────→│ Layer 4: アプリコード │ ← 10MB
├─────────────────────┤ ├─────────────────────┤
│ CMD ["python",...] │───────→│ Layer 5: 起動設定 │ ← メタデータのみ
└─────────────────────┘ └─────────────────────┘
イメージ = すべてのレイヤーの合計(読み取り専用)
コンテナ = イメージ + 書き込み可能レイヤー(薄い1層)
レイヤーキャッシュの法則: Dockerは各レイヤーをキャッシュします。Dockerfileの上から順に実行し、変更がないレイヤーはキャッシュを再利用します。だからCOPY requirements.txtをCOPY . .より先に書くと、コードを変えてもpipのレイヤーはキャッシュが効いてビルドが速くなる。
4.3 ボリュームとバインドマウント
コンテナ内のデータは、コンテナを削除すると消えます。データを永続化するには:
| 方式 | コマンド例 | データの場所 | 用途 |
|---|---|---|---|
| ボリューム | -v mydata:/app/data |
Docker管理領域 | DB、永続データ |
| バインドマウント | -v $(pwd):/app |
ホストの任意パス | 開発中のソースコード |
| tmpfs | --tmpfs /tmp |
メモリ上 | 一時ファイル、秘密情報 |
┌──────────────────────────────────────────────┐
│ ホスト OS │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Docker Volume │ │ ホストディレクトリ │ │
│ │ /var/lib/ │ │ /home/user/project │ │
│ │ docker/volumes│ │ │ │
│ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │
│ ┌──────┴───────────────────────┴───────────┐ │
│ │ Docker コンテナ │ │
│ │ /app/data ←── ボリューム │ │
│ │ /app ←── バインドマウント │ │
│ │ /tmp ←── tmpfs(メモリ上) │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
WSL2でのバインドマウント注意: /mnt/c/(Windows側)をマウントすると9Pプロトコル経由になり極端に遅くなります。WSL2のLinux FS(/home/...)をマウント元にしてください。
4.4 ネットワーク
Dockerは複数のネットワークドライバを提供しています。
| ドライバ | 用途 | 特徴 |
|---|---|---|
| bridge(デフォルト) | 単一ホスト内 | コンテナ間通信、NATでホストに公開 |
| host | パフォーマンス重視 | ホストのネットワークを直接使用 |
| none | 完全隔離 | ネットワークなし |
| overlay | マルチホスト | Docker Swarm / クラスタ環境向け |
基本概念が理解できたところで、実際にコードを書いて動かしてみましょう。
5. 実践:実際に使ってみよう
5.1 環境構築
この記事の実践パートは、以下の環境で動作確認しています。
| 項目 | バージョン |
|---|---|
| OS | Windows 11 Pro 24H2 + WSL2 |
| ディストロ | Ubuntu 24.04 LTS |
| Docker Engine | 29.x |
| Docker Desktop | 4.42+ |
| Docker Compose | v5.x |
| GPU | NVIDIA RTX 5090 |
5.2 環境別の設定ファイル(daemon.json)
Docker Engineの挙動はdaemon.jsonで制御します。
開発環境用(daemon.json.dev)
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"features": {
"buildkit": true
},
"debug": true,
"dns": ["8.8.8.8", "8.8.4.4"],
"default-address-pools": [
{
"base": "172.20.0.0/16",
"size": 24
}
]
}
本番環境用(daemon.json.prod)
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
},
"storage-driver": "overlay2",
"features": {
"buildkit": true
},
"debug": false,
"live-restore": true,
"userland-proxy": false,
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65536,
"Soft": 65536
}
},
"default-address-pools": [
{
"base": "172.30.0.0/16",
"size": 24
}
]
}
テスト/CI環境用(daemon.json.ci)
{
"log-driver": "json-file",
"log-opts": {
"max-size": "5m",
"max-file": "1"
},
"storage-driver": "overlay2",
"features": {
"buildkit": true
},
"debug": false,
"builder": {
"gc": {
"defaultKeepStorage": "5GB",
"enabled": true
}
}
}
5.3 Dockerfileの書き方(基本から最適化まで)
初心者が書きがちなDockerfile(BAD)
# ❌ BAD: よくある初心者のDockerfile
FROM python:3.12
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "main.py"]
問題点:
-
COPY . .が先にあるため、コードを1行変えるだけでpip installが再実行される - フルサイズのPythonイメージ(約1GB)を使用
- rootユーザーで実行(セキュリティリスク)
-
.dockerignoreがないとゴミファイルもコピーされる
最適化したDockerfile(GOOD)
# ✅ GOOD: 最適化されたDockerfile
# === ステージ1: ビルドステージ ===
FROM python:3.12-slim AS builder
WORKDIR /app
# 依存関係だけ先にインストール(レイヤーキャッシュ活用)
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# === ステージ2: 実行ステージ ===
FROM python:3.12-slim AS runtime
# セキュリティ: 非rootユーザーを作成
RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
# ビルドステージからpipパッケージだけコピー
COPY --from=builder /install /usr/local
# アプリコードをコピー
COPY --chown=appuser:appuser . .
# 非rootユーザーに切り替え
USER appuser
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1
EXPOSE 8080
CMD ["python", "main.py"]
# .dockerignore(必須)
.git
.gitignore
__pycache__
*.pyc
.env
.venv
node_modules
*.md
Dockerfile
docker-compose*.yml
.dockerignore
5.4 Docker操作の実践スクリプト
#!/usr/bin/env bash
# docker_guide.sh - Docker操作ガイドスクリプト
# 実行方法: bash docker_guide.sh
set -euo pipefail
echo "=========================================="
echo " Docker 実践ガイド"
echo "=========================================="
echo ""
echo "=== 1. Docker情報の確認 ==="
docker version --format ' Client: {{.Client.Version}} / Server: {{.Server.Version}}'
echo " ストレージドライバ: $(docker info --format '{{.Driver}}')"
echo " イメージ数: $(docker images -q | wc -l)"
echo " コンテナ数: $(docker ps -aq | wc -l)(実行中: $(docker ps -q | wc -l))"
echo ""
echo "=== 2. イメージ操作 ==="
cat << 'EOF'
# イメージの取得
$ docker pull python:3.12-slim
# イメージの一覧
$ docker images
# イメージの詳細(レイヤー構造を確認)
$ docker history python:3.12-slim
# イメージサイズの確認
$ docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}"
# 不要イメージの削除
$ docker image prune # ダングリングイメージのみ
$ docker image prune -a # 未使用イメージもすべて
EOF
echo ""
echo "=== 3. コンテナ操作 ==="
cat << 'EOF'
# コンテナの起動(バックグラウンド)
$ docker run -d --name myapp -p 8080:8080 myimage
# コンテナに入る
$ docker exec -it myapp bash
# ログの確認
$ docker logs myapp -f --tail 100
# リソース使用状況のリアルタイム表示
$ docker stats
# コンテナの停止・削除
$ docker stop myapp
$ docker rm myapp
# 全停止コンテナの一括削除
$ docker container prune
EOF
echo ""
echo "=== 4. ボリューム操作 ==="
cat << 'EOF'
# ボリュームの作成
$ docker volume create mydata
# ボリューム一覧
$ docker volume ls
# ボリューム付きでコンテナ起動
$ docker run -d -v mydata:/app/data myimage
# バインドマウント(開発時に使用)
$ docker run -d -v $(pwd):/app myimage
# ボリュームの中身を確認(一時コンテナで)
$ docker run --rm -v mydata:/data busybox ls -la /data
# 不要ボリュームの削除
$ docker volume prune
EOF
echo ""
echo "=== 5. ディスク使用量の全体確認 ==="
echo "$ docker system df"
docker system df 2>/dev/null || echo "(Docker未起動)"
echo ""
echo "# 一括クリーンアップ(注意: 停止コンテナ、未使用イメージ・ボリュームを削除)"
echo '$ docker system prune -a --volumes'
echo ""
echo "=== 6. よく使うワンライナー ==="
cat << 'EOF'
# 全コンテナ停止
$ docker stop $(docker ps -q)
# 全コンテナ削除
$ docker rm $(docker ps -aq)
# イメージのサイズ順ソート
$ docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -rh
# 特定コンテナのIPアドレス
$ docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container>
# コンテナ内の環境変数一覧
$ docker exec <container> env
EOF
echo ""
echo "=========================================="
echo "✅ ガイド完了!"
5.5 実行結果
マルチステージビルドの効果:
$ docker build -t myapp:optimized -f Dockerfile.good .
[+] Building 45.2s (12/12) FINISHED
=> [builder 1/3] FROM python:3.12-slim
=> [builder 2/3] COPY requirements.txt .
=> [builder 3/3] RUN pip install ... ← キャッシュされる
=> [runtime 1/4] FROM python:3.12-slim
=> [runtime 2/4] RUN groupadd ...
=> [runtime 3/4] COPY --from=builder ...
=> [runtime 4/4] COPY . .
=> exporting to image
# イメージサイズの比較
$ docker images --format "{{.Repository}}:{{.Tag}}\t{{.Size}}"
myapp:naive 1.2GB ← 最適化前
myapp:optimized 285MB ← 最適化後(76%削減)
5.6 よくあるエラーと対処法
| エラー | 原因 | 対処法 |
|---|---|---|
Cannot connect to the Docker daemon |
Docker Engineが起動していない | WSL2: Docker Desktopを起動。Linux: sudo systemctl start docker
|
port is already allocated |
同じポートを使うコンテナが既に存在 |
docker psで確認 → 停止するか別ポートを指定(-p 8081:8080) |
no space left on device |
Dockerのディスク使用量が限界 |
docker system prune -a --volumesでクリーンアップ。WSL2の場合は.wslconfigの仮想ディスクサイズも確認 |
COPY failed: file not found |
.dockerignoreでコピー対象が除外されている、またはビルドコンテキスト外のファイル |
.dockerignoreを確認。docker buildのコンテキスト(.)に対象ファイルがあるか確認 |
RuntimeError: No CUDA GPUs are available |
GPUがコンテナに渡されていない |
docker run --gpus allを指定。NVIDIA Container Toolkitがインストールされているか確認 |
OOMKilled |
コンテナのメモリ上限超過 |
docker run -m 4gでメモリ上限を引き上げる。アプリのメモリリークも疑う |
docker system prune -a --volumesは本番環境で絶対に実行しないでください。永続データを含むボリュームもすべて削除されます。開発環境でのクリーンアップ専用コマンドです。
5.7 環境診断スクリプト
#!/usr/bin/env python3
"""
Docker環境診断スクリプト
実行方法: python3 check_docker_env.py
"""
import subprocess
import json
import shutil
import sys
import os
def run_cmd(cmd: str) -> str:
"""コマンドを実行して結果を返す"""
try:
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True, timeout=15
)
return result.stdout.strip()
except (subprocess.TimeoutExpired, Exception):
return ""
def run_json(cmd: str) -> dict:
"""JSONを返すコマンドを実行"""
output = run_cmd(cmd)
try:
return json.loads(output)
except (json.JSONDecodeError, Exception):
return {}
def check_docker_environment():
"""Docker環境を包括的にチェック"""
issues = []
warnings = []
print("=" * 55)
print(" Docker 環境診断レポート")
print("=" * 55)
# --- 1. Docker基本情報 ---
print("\n🐳 Docker基本情報:")
if not shutil.which("docker"):
print(" ❌ Dockerがインストールされていません")
return
client_ver = run_cmd("docker version --format '{{.Client.Version}}' 2>/dev/null")
server_ver = run_cmd("docker version --format '{{.Server.Version}}' 2>/dev/null")
if client_ver:
print(f" Client: {client_ver}")
if server_ver:
print(f" Server: {server_ver}")
else:
issues.append("Docker Engineに接続できません。Docker Desktopが起動しているか確認してください。")
print(" ❌ Serverに接続不可")
return
# Compose
compose_ver = run_cmd("docker compose version --short 2>/dev/null")
print(f" Compose: {compose_ver or '未インストール'}")
# BuildKit
buildkit = run_cmd("docker info --format '{{.ClientInfo.Plugins}}' 2>/dev/null")
print(f" BuildKit: {'有効' if 'buildx' in str(buildkit) else '要確認'}")
# --- 2. リソース使用状況 ---
print("\n📊 リソース使用状況:")
df_output = run_cmd("docker system df --format '{{.Type}}\t{{.Size}}\t{{.Reclaimable}}' 2>/dev/null")
if df_output:
print(f" {'種別':<15} {'使用量':<12} {'回収可能'}")
for line in df_output.strip().split('\n'):
parts = line.split('\t')
if len(parts) >= 3:
print(f" {parts[0]:<15} {parts[1]:<12} {parts[2]}")
# --- 3. コンテナ状況 ---
print("\n📦 コンテナ状況:")
running = run_cmd("docker ps -q 2>/dev/null | wc -l").strip()
stopped = run_cmd("docker ps -aq --filter 'status=exited' 2>/dev/null | wc -l").strip()
print(f" 実行中: {running}")
print(f" 停止中: {stopped}")
if int(stopped) > 10:
warnings.append(f"停止中のコンテナが{stopped}個あります。`docker container prune`で削除を検討してください。")
# --- 4. イメージ状況 ---
print("\n🖼️ イメージ状況:")
total_images = run_cmd("docker images -q 2>/dev/null | wc -l").strip()
dangling = run_cmd("docker images -f 'dangling=true' -q 2>/dev/null | wc -l").strip()
print(f" 合計: {total_images}")
print(f" ダングリング(未タグ): {dangling}")
if int(dangling) > 5:
warnings.append(f"ダングリングイメージが{dangling}個あります。`docker image prune`で削除を推奨します。")
# 上位5イメージ
top_images = run_cmd(
"docker images --format '{{.Repository}}:{{.Tag}}\t{{.Size}}' 2>/dev/null | head -5"
)
if top_images:
print(" 上位5イメージ:")
for line in top_images.strip().split('\n'):
print(f" {line}")
# --- 5. GPU ---
print("\n🎮 GPU (NVIDIA Container Toolkit):")
nvidia_runtime = run_cmd(
"docker info --format '{{.Runtimes}}' 2>/dev/null"
)
if "nvidia" in str(nvidia_runtime).lower():
print(" NVIDIA Runtime: 有効 ✅")
gpu_test = run_cmd(
"docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu24.04 "
"nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null"
)
if gpu_test:
print(f" GPU (コンテナ内): {gpu_test} ✅")
else:
warnings.append("NVIDIA Runtimeは有効ですが、コンテナ内からGPUにアクセスできません。")
else:
print(" NVIDIA Runtime: 未検出")
print(" → GPU不要なら問題なし。GPU利用時はNVIDIA Container Toolkitをインストール")
# --- 6. ネットワーク ---
print("\n🌐 ネットワーク:")
networks = run_cmd(
"docker network ls --format '{{.Name}} ({{.Driver}})' 2>/dev/null"
)
if networks:
for line in networks.strip().split('\n'):
print(f" {line}")
# --- 7. WSL2固有の確認 ---
is_wsl = os.path.exists("/proc/sys/fs/binfmt_misc/WSLInterop")
if is_wsl:
print("\n🪟 WSL2固有チェック:")
docker_context = run_cmd("docker context show 2>/dev/null")
print(f" コンテキスト: {docker_context}")
# ディスク使用量
vhdx_size = run_cmd(
"ls -lh /mnt/wslg/distro/../../*.vhdx 2>/dev/null | awk '{print $5}' | head -1"
)
if vhdx_size:
print(f" VHDxサイズ: {vhdx_size}")
# --- 結果サマリー ---
print("\n" + "=" * 55)
if issues:
print("❌ 問題:")
for issue in issues:
print(f" 🔴 {issue}")
if warnings:
print("⚠️ 注意:")
for w in warnings:
print(f" 🟡 {w}")
if not issues and not warnings:
print("✅ Docker環境は正常です!")
print("=" * 55)
if __name__ == "__main__":
check_docker_environment()
実装方法がわかったので、次は具体的なユースケースを見ていきます。
6. ユースケース別ガイド
6.1 ユースケース1: AI/ML開発のGPUコンテナ
想定読者: CUDA環境をDockerで構築し、PyTorchをコンテナ内で動かしたい方
推奨構成: Docker + NVIDIA Container Toolkit + CUDA base image
サンプルコード:
# Dockerfile.gpu - AI/ML開発用GPUコンテナ
# ビルド: docker build -t ai-dev -f Dockerfile.gpu .
# 実行: docker run --gpus all -it -v $(pwd):/workspace ai-dev
# === ステージ1: 依存関係のインストール ===
FROM nvidia/cuda:12.4.1-devel-ubuntu24.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --break-system-packages \
--prefix=/install -r requirements.txt
# === ステージ2: 実行環境 ===
FROM nvidia/cuda:12.4.1-runtime-ubuntu24.04 AS runtime
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip \
&& rm -rf /var/lib/apt/lists/*
# 非rootユーザー
RUN groupadd -r mluser && useradd -r -g mluser -m mluser
WORKDIR /workspace
# ビルドステージからPythonパッケージをコピー
COPY --from=builder /install /usr/local
# CUDA環境変数
ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility
USER mluser
CMD ["bash"]
# requirements.txt
torch==2.6.0
torchvision==0.21.0
torchaudio==2.6.0
numpy>=1.26
pandas>=2.2
matplotlib>=3.9
jupyter>=1.0
#!/usr/bin/env bash
# run_gpu_container.sh - GPUコンテナの起動
# 実行方法: bash run_gpu_container.sh
set -euo pipefail
IMAGE_NAME="ai-dev"
CONTAINER_NAME="ai-workspace"
echo "=== GPUコンテナ起動 ==="
# ビルド
echo "[1/2] イメージをビルド中..."
docker build -t "$IMAGE_NAME" -f Dockerfile.gpu .
# 起動
echo "[2/2] コンテナを起動中..."
docker run --gpus all -it --rm \
--name "$CONTAINER_NAME" \
-v "$(pwd)":/workspace \
-p 8888:8888 \
--shm-size=8g \
"$IMAGE_NAME" \
python3 -c "
import torch
print('PyTorch:', torch.__version__)
print('CUDA:', torch.cuda.is_available())
if torch.cuda.is_available():
print('GPU:', torch.cuda.get_device_name(0))
print('VRAM:', f\"{torch.cuda.get_device_properties(0).total_mem / 1024**3:.1f} GB\")
"
--shm-size=8gが重要です。PyTorchのDataLoaderは/dev/shm(共有メモリ)を使います。デフォルトの64MBでは足りずにクラッシュすることがあります。
6.2 ユースケース2: マルチステージビルドでWebアプリを軽量化
想定読者: Dockerイメージのサイズが肥大化して困っている方
推奨構成: マルチステージビルド + distrolessベースイメージ
サンプルコード:
# Dockerfile.web - マルチステージビルドの実例
# Node.js + TypeScript アプリを最小サイズにパッケージ
# === ステージ1: 依存関係 ===
FROM node:22-slim AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# === ステージ2: ビルド ===
FROM node:22-slim AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# === ステージ3: 実行(最小構成) ===
FROM gcr.io/distroless/nodejs22-debian12 AS runtime
WORKDIR /app
# depsステージから本番依存のみコピー
COPY --from=deps /app/node_modules ./node_modules
# builderステージからビルド成果物のみコピー
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["dist/index.js"]
# ビルドしてサイズを比較
$ docker build -t myapp:multi -f Dockerfile.web .
$ docker images myapp
REPOSITORY TAG SIZE
myapp naive 1.1GB ← node:22 フルイメージ
myapp slim 350MB ← node:22-slim
myapp multi 120MB ← distroless + マルチステージ(89%削減)
6.3 ユースケース3: Dockerでのセキュリティベストプラクティス
想定読者: 本番環境にDockerコンテナをデプロイする予定がある方
推奨構成: 非rootユーザー + 読み取り専用FS + セキュリティスキャン
サンプルコード:
#!/usr/bin/env bash
# docker_security_check.sh - Dockerセキュリティチェック
# 実行方法: bash docker_security_check.sh <image_name>
set -euo pipefail
IMAGE="${1:?使用法: $0 <image_name>}"
echo "=========================================="
echo " Docker セキュリティチェック: $IMAGE"
echo "=========================================="
# --- 1. rootユーザーチェック ---
echo ""
echo "[1/5] ユーザー設定チェック..."
USER=$(docker inspect --format='{{.Config.User}}' "$IMAGE" 2>/dev/null)
if [ -z "$USER" ] || [ "$USER" = "root" ] || [ "$USER" = "0" ]; then
echo " ⚠️ rootユーザーで実行されます"
echo " → DockerfileにUSER命令を追加してください"
else
echo " ✅ 非rootユーザー: $USER"
fi
# --- 2. HEALTHCHECK ---
echo ""
echo "[2/5] HEALTHCHECKチェック..."
HEALTH=$(docker inspect --format='{{.Config.Healthcheck}}' "$IMAGE" 2>/dev/null)
if [ -z "$HEALTH" ] || [ "$HEALTH" = "<nil>" ]; then
echo " ⚠️ HEALTHCHECKが未設定"
echo " → DockerfileにHEALTHCHECK命令を追加してください"
else
echo " ✅ HEALTHCHECK設定あり"
fi
# --- 3. 公開ポート ---
echo ""
echo "[3/5] 公開ポートチェック..."
PORTS=$(docker inspect --format='{{range $k, $v := .Config.ExposedPorts}}{{$k}} {{end}}' "$IMAGE" 2>/dev/null)
if [ -n "$PORTS" ]; then
echo " 公開ポート: $PORTS"
else
echo " 公開ポートなし"
fi
# --- 4. イメージサイズ ---
echo ""
echo "[4/5] イメージサイズチェック..."
SIZE=$(docker images --format '{{.Size}}' "$IMAGE" 2>/dev/null | head -1)
echo " サイズ: $SIZE"
if [[ "$SIZE" == *GB* ]]; then
echo " ⚠️ 1GB超。マルチステージビルドでの最適化を検討してください"
fi
# --- 5. Docker Scout(脆弱性スキャン) ---
echo ""
echo "[5/5] 脆弱性スキャン..."
if docker scout version &>/dev/null 2>&1; then
echo " Docker Scout でスキャン中..."
docker scout quickview "$IMAGE" 2>/dev/null || echo " (スキャン結果省略)"
else
echo " ℹ️ Docker Scout未インストール"
echo " → docker scout quickview $IMAGE で脆弱性を確認できます"
fi
echo ""
echo "=========================================="
echo "📋 セキュリティ推奨事項:"
echo " 1. 非rootユーザーで実行(USER命令)"
echo " 2. 読み取り専用FS(docker run --read-only)"
echo " 3. 不要なcapabilityの削除(--cap-drop ALL --cap-add ...)"
echo " 4. HEALTHCHECKの設定"
echo " 5. 定期的なイメージ更新とスキャン"
echo "=========================================="
ユースケースを把握できたところで、この先の学習パスを確認しましょう。
7. 学習ロードマップ
この記事を読んだ後、次のステップとして以下をおすすめします。
初級者向け(まずはここから)
-
既存のイメージでコンテナを起動する:
docker run -it ubuntu:24.04 bashでコンテナ内のUbuntuを触る - 自分のDockerfileを書く: 簡単なPythonアプリをコンテナ化する
- 公式ドキュメント: Docker Get Started を一通り進める
中級者向け(実践に進む)
- マルチステージビルドを使いこなす: イメージサイズを最適化する
- Docker Composeで複数コンテナを管理: Webアプリ + DB + キャッシュの構成を作る
- CI/CDにDockerを組み込む: GitHub Actions + Dockerでテスト・デプロイを自動化
上級者向け(さらに深く)
- コンテナセキュリティ: Docker Scout、Trivy、Falcoによる脆弱性管理
- カスタムネットワーク: overlayネットワーク、サービスメッシュの理解
- containerd / runc の理解: Docker Engineの内部構造を深掘りする
8. まとめ
この記事では、Dockerについて以下を解説しました:
- コンテナの正体: namespace + cgroups によるLinuxカーネルレベルの隔離であり、仮想マシンとは根本的に異なること
- イメージの仕組み: レイヤー構造とキャッシュの法則、マルチステージビルドによる最適化
- データの永続化: ボリューム / バインドマウント / tmpfs の使い分け
- 実践: GPUコンテナ、マルチステージビルド、セキュリティチェック
私の所感
Dockerを「本当に理解した」と感じたのは、RTX 5090のBlackwellアーキテクチャ(sm_120)でCUDA Gapに直面し、WSL2 → Docker → コンテナ内CUDAの3段階でGPUパススルーを成功させた時です。Windows側のNVIDIAドライバ → WSL2のGPU-PV → Docker の--gpus allフラグ → コンテナ内のlibcuda.so。この一連の流れを理解していないと、どこで問題が起きているのか切り分けすらできませんでした。
2026年現在、DockerはDocker Engine v29、Compose v5(Go SDK搭載)、さらにDocker Model RunnerというLLM推論基盤まで搭載し始めています。Docker MCP ToolkitによるMCPサーバー管理も加わり、AIインフラとしてのDockerの重要性は増す一方です。
「docker runできる」から「Dockerを理解している」へ。レイヤー、ネットワーク、ボリューム、セキュリティ。これらを正しく理解すれば、Dockerは「おまじない」から「信頼できるインフラ」に変わります。
参考文献
- Docker Official Documentation — 公式ドキュメント
- Dockerfile Best Practices — Dockerfile公式ベストプラクティス
- NVIDIA Container Toolkit — GPU対応公式ガイド
- Docker Desktop Release Notes — 最新リリース情報
- Docker Compose v5 — Compose最新版
この記事は「わかったつもりになってない?」シリーズの一部です。
| No. | タイトル | 状態 |
|---|---|---|
| 1 | Linuxってなんだ? | ✅ 公開済み |
| 2 | Ubuntuってなんだ? | ✅ 公開済み |
| 3 | WSLってなんだ? | ✅ 公開済み |
| 4 | Dockerってなんだ?(この記事) | ✅ 公開済み |
| 5 | Docker Composeってなんだ? | 🔜 次回 |
| 6 | Kubernetesってなんだ? | 📝 準備中 |
📌 X(Twitter)でも技術情報を発信しています: @geneLab_999