docker
Docker
概要
Dockerに関する知識とメモ
基本概念
コンテナとは
イメージとは
ボリューム(Volumes)とは
- Dockerコンテナのデータを永続化するための仕組み
- コンテナが削除されてもデータが保持される
- ホストとコンテナ間でファイルを共有できる
重要な概念:
- コンテナ自体はデータを永続化できない - コンテナ削除でデータも消失
- ボリュームでデータの永続化を実現 - コンテナとは独立してデータを保存
- 本番環境でのデータ保護 - サーバーデプロイ時もボリューム設定でデータを保持
ボリュームの種類
-
名前付きボリューム(Named Volumes)
- Dockerが管理する永続化ストレージ
- 複数のコンテナ間でデータを共有可能
-
バインドマウント(Bind Mounts)
- ホストの特定のディレクトリをコンテナにマウント
- ホストのファイルシステムに直接アクセス
-
tmpfs マウント
- メモリ上に一時的なファイルシステムを作成
- コンテナ停止時にデータは消失
ボリュームの基本的な使い方
# 名前付きボリュームの作成
docker volume create my-volume
# ボリューム一覧の確認
docker volume ls
# ボリュームの詳細情報
docker volume inspect my-volume
# ボリュームを使ってコンテナを起動
docker run -v my-volume:/app/data nginx
# バインドマウントの例
docker run -v /host/path:/container/path nginx
# 読み取り専用でマウント
docker run -v /host/path:/container/path:ro nginx
ボリュームの実際の保存場所
名前付きボリュームの保存場所:
- Linux:
/var/lib/docker/volumes/<volume_name>/_data - macOS (Docker Desktop): Docker VM内の
/var/lib/docker/volumes/<volume_name>/_data - Windows (Docker Desktop): Docker VM内の
/var/lib/docker/volumes/<volume_name>/_data
Docker VMとは?
- macOSやWindowsでは、DockerはLinuxカーネルが必要なため、仮想マシン(VM)上で動作する
- Docker Desktopが自動的にLinux VMを作成・管理している
- このVMは通常のFinderやエクスプローラーからは見えない隠れた仮想環境
ローカルファイルからの確認について:
- Linux: ホストマシンのファイルシステムに直接保存されるため、通常のファイルマネージャーでアクセス可能
- macOS/Windows: Docker VM内に保存されるため、通常のFinderやエクスプローラーからは直接アクセス不可
- ただし、Docker Desktopの設定でファイル共有を有効にしている場合、一部アクセス可能な場合もある
# ボリュームの詳細情報で保存場所を確認
docker volume inspect my-volume
# Mountpoint フィールドに実際のパスが表示される
# macOS/WindowsでDocker VM内にアクセスする場合
docker run --rm -it --privileged --pid=host alpine:latest nsenter -t 1 -m -u -n -i sh
# この後、/var/lib/docker/volumes/ を確認可能
# よりシンプルな方法:一時コンテナでボリュームをマウントしてアクセス
docker run --rm -it -v my-volume:/data alpine:latest sh
# /data ディレクトリ内でボリュームのデータを確認・編集可能
注意点:
- macOS/WindowsのDocker Desktopでは、ボリュームはDocker VM内に保存される
- ホストから直接アクセスするのは推奨されない
- ボリューム内のデータにアクセスしたい場合は、コンテナ経由でアクセスすることを推奨
本番環境でのボリューム活用
データ永続化の重要性:
# 開発環境
docker run -v $(pwd)/data:/app/data myapp
# 本番環境(Docker Compose例)
version: '3.8'
services:
web:
image: myapp
volumes:
- app-data:/app/data
- /opt/config:/app/config # ホストの設定ファイル
db:
image: postgres
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
app-data: # 名前付きボリューム(Dockerが管理)
postgres-data: # DBデータの永続化
クラウド環境での考慮点:
- AWS: EBS Volume, EFS
- GCP: Persistent Disk
- Azure: Azure Disk
- Kubernetes: PersistentVolume (PV) / PersistentVolumeClaim (PVC)
よく使うコマンド
基本操作
# コンテナ一覧
docker ps # 実行中のコンテナ
docker ps -a # すべてのコンテナ
# イメージ一覧
docker images
# コンテナ起動
docker run <image_name>
# コンテナ停止
docker stop <container_name_or_id>
# コンテナ削除
docker rm <container_name_or_id>
実行中のコンテナに入る
# 基本的な方法(bashで入る)
docker exec -it <container_name_or_id> /bin/bash
# shで入る(bashが使えない場合)
docker exec -it <container_name_or_id> /bin/sh
# 特定のユーザーで入る
docker exec -it --user root <container_name_or_id> /bin/bash
# 作業ディレクトリを指定して入る
docker exec -it --workdir /app <container_name_or_id> /bin/bash
# 一時的にコマンドを実行(コンテナに入らない)
docker exec <container_name_or_id> ls -la
docker exec <container_name_or_id> cat /etc/os-release
ボリューム関連コマンド
docker volume create <volume_name> # ボリューム作成
docker volume ls # ボリューム一覧
docker volume rm <volume_name> # ボリューム削除
docker volume prune # 未使用ボリューム削除
Dockerfile
マルチステージビルド
マルチステージビルドとは
1つのDockerfile内で複数のビルドステージを定義し、最終的なイメージを軽量化する手法。
典型的な構成(静的サイトジェネレーターの場合)
# ステージ1: ビルドステージ(Node.jsが必要)
FROM node:24-alpine AS builder
WORKDIR /app
# 依存関係のインストール
COPY package*.json ./
RUN npm ci
# ソースコードのコピー
COPY . .
# ビルド(静的ファイルを生成)
RUN npm run build
# ステージ2: 実行ステージ(Node.jsは不要)
FROM nginx:alpine
# ビルド済みの静的ファイルをコピー
COPY --from=builder /app/dist /usr/share/nginx/html
# Nginx設定
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
なぜ2番目のステージでNode.jsが不要なのか
重要なポイント:
-
ビルドは完了している
- 1番目のステージ(
builder)でnpm run buildを実行し、静的ファイル(HTML、CSS、JS)をdist/ディレクトリに生成済み - 2番目のステージでは、このビルド済みファイルをコピーするだけ
- 1番目のステージ(
-
静的ファイルのみを配信
- Astroなどの静的サイトジェネレーターは、ビルド時にすべてのページをHTMLに変換
- 実行時にはサーバーサイド処理が不要(Node.jsランタイム不要)
- Nginxは静的ファイルを配信するだけのWebサーバー
-
イメージサイズの最適化
- Node.jsを含めないことで、最終イメージが大幅に軽量化される
node:24-alpineは約200MB、nginx:alpineは約40MB- 約160MBの削減効果
-
セキュリティ面のメリット
- Node.jsを含めないことで、攻撃面が減る
- 必要最小限のコンポーネントのみを含める
マルチステージビルドの利点まとめ
- 軽量化: 最終イメージにビルドツールを含めない
- セキュリティ: 不要なコンポーネントを除外
- パフォーマンス: 軽量なイメージで起動が速い
- 明確な分離: ビルド環境と実行環境を分離
実際の使用例
# ビルド(マルチステージビルド)
docker build -t myapp:latest .
# 実行(Nginxのみで動作)
docker run -p 8080:80 myapp:latest
# イメージサイズの確認
docker images myapp:latest
# → Node.jsを含まない軽量なイメージ
Docker Compose
マルチサービス構成の基本
app + web の一般的な構成パターン
典型的な構成:
- web: リバースプロキシ(Nginx など)- 静的ファイル配信、SSL終端、リクエスト振り分け
- app: アプリケーションサーバー(Node.js、Python、PHP など)- ビジネスロジック処理
なぜ分けるのか?
- 責任分離: 静的ファイル配信とアプリロジックを分離
- パフォーマンス: Nginxが静的ファイルを高速配信、アプリは動的処理に専念
- スケーラビリティ: webとappを独立してスケール可能
- セキュリティ: 外部からの直接アクセスはwebのみ、appは内部ネットワークで保護
version: '3.8'
services:
# Webサーバー(フロントエンド)
web:
image: nginx:latest
restart: unless-stopped
ports:
- "80:80" # 外部からのアクセスはwebのみ
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- static-files:/usr/share/nginx/html
depends_on:
- app # appサービスが起動してからwebを起動
networks:
- frontend # 外部ネットワーク
- backend # 内部ネットワーク(appとの通信用)
# アプリケーションサーバー(バックエンド)
app:
image: node:16-alpine
restart: unless-stopped
# ポート公開なし(外部から直接アクセス不可)
expose:
- "3000" # 内部ネットワークでのみアクセス可能
environment:
- NODE_ENV=production
volumes:
- app-data:/app/data
- static-files:/app/public # 静的ファイルをwebと共有
networks:
- backend # 内部ネットワークのみ
- db-network # データベースとの通信用
# データベース
db:
image: postgres:13
restart: unless-stopped
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- db-network # アプリとの通信専用
secrets:
- db_password
# ネットワーク定義(サービス間通信の設定)
networks:
frontend: # 外部アクセス用
driver: bridge
backend: # web ↔ app 通信用
driver: bridge
internal: true # 外部からアクセス不可
db-network: # app ↔ db 通信用
driver: bridge
internal: true # 外部からアクセス不可
volumes:
app-data:
postgres-data:
static-files: # webとappで共有
secrets:
db_password:
external: true
サービス間通信の仕組み
1. ネットワークレベルでの通信制御
networks:
backend:
driver: bridge
internal: true # 外部からアクセス不可、内部通信のみ
2. サービス名での名前解決
- Docker Compose内ではサービス名がホスト名として機能
- 例:webサービスからappサービスへは
http://app:3000でアクセス
3. Nginxの設定例(web → app への通信)
# nginx.conf
server {
listen 80;
server_name localhost;
# 静的ファイル(CSS、JS、画像など)
location /static/ {
alias /usr/share/nginx/html/;
expires 30d;
}
# API リクエストをappサービスにプロキシ
location /api/ {
proxy_pass http://app:3000/; # サービス名'app'で通信
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# その他のリクエストもappにプロキシ
location / {
proxy_pass http://app:3000/; # サービス名'app'で通信
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
4. 通信フロー
外部クライアント → web:80/443 → app:3000 → db:5432
↑ ↑ ↑
外部公開 内部通信 内部通信
5. 依存関係とヘルスチェック
services:
web:
depends_on:
- app # app起動後にweb起動
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
app:
depends_on:
- db # db起動後にapp起動
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:3000/health"]
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
実際の運用での確認方法
サービス間通信のテスト:
# webコンテナからappへの通信確認
docker-compose exec web curl http://app:3000/health
# appコンテナからdbへの通信確認
docker-compose exec app nc -zv db 5432
# ネットワーク情報確認
docker network ls
docker network inspect <project_name>_backend
# サービス間のネットワーク接続確認
docker-compose exec web nslookup app
ログでの通信確認:
# 全サービスのログ
docker-compose logs -f
# 特定サービスのログ
docker-compose logs -f web
docker-compose logs -f app
# リアルタイムでの通信監視
docker-compose exec web tail -f /var/log/nginx/access.log
本番環境での重要な設定
命名規則
多くのオープンソースプロジェクトではケバブケース(kebab-case)が使用される
# 推奨:ケバブケース(kebab-case)
services:
web-app: # ハイフンで区切る
image: nginx
api-server: # 複数単語もハイフンで繋ぐ
image: node
worker-queue:
image: redis
volumes:
app-data: # ボリュームもケバブケース
log-files:
backup-storage:
networks:
frontend-net: # ネットワークもケバブケース
backend-net:
理由:
- 可読性: 単語の区切りが明確
- 一貫性: Kubernetesなど他のツールとの統一
- 標準的: 多くのOSSプロジェクトで採用
- URL安全: ハイフンはURL内で安全に使用可能
restart ポリシー
restart: unless-stopped は本番環境では必須の設定
restart ポリシーの種類:
- no: 再起動しない(デフォルト)
- always: 常に再起動(Docker起動時も含む)
- on-failure: 異常終了時のみ再起動
- unless-stopped: 手動で停止するまで再起動(推奨)
# 本番環境での推奨設定例
version: '3.8'
services:
web:
image: nginx:latest
restart: unless-stopped # 重要:自動復旧設定
ports:
- "80:80"
volumes:
- web-data:/usr/share/nginx/html
- /opt/config/nginx:/etc/nginx/conf.d:ro
depends_on:
- app
app:
image: myapp:latest
restart: unless-stopped # 重要:自動復旧設定
environment:
- NODE_ENV=production
volumes:
- app-data:/app/data
depends_on:
- db
db:
image: postgres:13
restart: unless-stopped # 重要:自動復旧設定
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres-data:/var/lib/postgresql/data
secrets:
- db_password
volumes:
web-data:
app-data:
postgres-data:
secrets:
db_password:
external: true
なぜ unless-stopped が推奨なのか?
メリット:
- サーバー再起動時: 自動でコンテナが起動
- コンテナクラッシュ時: 自動で復旧
- 手動制御可能:
docker stopで明示的に停止可能 - 意図しない起動回避: 手動停止したコンテナは起動時に自動起動しない
always との違い:
# unless-stopped の場合
docker stop my-container # 手動停止
# → サーバー再起動時も起動しない
# always の場合
docker stop my-container # 手動停止
# → サーバー再起動時に自動起動してしまう
本番環境でのその他重要設定
services:
app:
image: myapp:latest
restart: unless-stopped
# リソース制限(重要)
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 256M
# ヘルスチェック(重要)
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# ログ設定
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# セキュリティ設定
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
ヘルスチェックの詳細設定
ヘルスチェックとは:
- コンテナの健全性を定期的に監視する仕組み
- サービスが正常に動作しているかを自動確認
- 異常時の自動復旧やロードバランサーからの除外に活用
設定パラメータ:
test: 実行するヘルスチェックコマンドinterval: チェック間隔(デフォルト: 30s)timeout: タイムアウト時間(デフォルト: 30s)retries: 失敗の許容回数(デフォルト: 3)start_period: 初回チェックまでの猶予時間(デフォルト: 0s)
サービス別ヘルスチェック例:
services:
# Webアプリケーション
web:
image: nginx:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# MySQL データベース
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secretpassword
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-uroot", "-p$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 10
start_period: 60s
# PostgreSQL データベース
postgres:
image: postgres:13
environment:
POSTGRES_PASSWORD: secretpassword
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Redis
redis:
image: redis:6-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 3s
retries: 3
# Node.js API
api:
image: node:16-alpine
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
ヘルスチェック状態の確認:
# コンテナの健康状態確認
docker ps
# 詳細なヘルスチェック履歴
docker inspect <container_name> | grep -A 20 "Health"
# Docker Composeでの確認
docker-compose ps
ヘルスチェックのベストプラクティス:
- 軽量なチェック: 過度に重い処理は避ける
- 適切な間隔: あまり頻繁すぎるとリソース消費が大きい
- 依存関係を考慮: DBが起動してからAPIをチェック
- 適切なタイムアウト: ネットワーク遅延を考慮した設定
実際の運用例:
# 本番環境での起動
docker compose -f compose.yaml -f compose.production.yaml up -d
# サービス状態確認
docker-compose ps
# ログ確認
docker-compose logs -f --tail=100
# 特定サービスの再起動
docker-compose restart app
# 設定変更後の再デプロイ
docker-compose pull
docker-compose up -d
Docker Composeの停止・削除
# 基本的な停止(コンテナを停止、削除しない)
docker-compose stop
# 停止と削除(コンテナとネットワークを削除)
docker-compose down
# 特定のサービスのみ停止
docker-compose stop web
# ボリュームも含めて完全削除
docker-compose down -v
# イメージも削除(開発環境のクリーンアップ)
docker-compose down --rmi all
# 孤立したコンテナも削除
docker-compose down --remove-orphans
# 強制停止(緊急時)
docker-compose kill
# プロダクションファイル指定での停止
docker compose -f compose.yaml -f compose.production.yaml down
stopとdownの違い:
stop: コンテナを停止するが、コンテナとネットワークは残るdown: コンテナを停止し、コンテナとネットワークを削除する
本番環境での推奨手順:
# 1. まず状態確認
docker-compose ps
# 2. ログを確認(必要に応じて)
docker-compose logs
# 3. グレースフルシャットダウン
docker-compose down
# 4. 完全停止の確認
docker ps -a | grep <project_name>
Dangling(ダングリング)リソース
Danglingとは
「ぶら下がっている」という意味で、参照されなくなった孤立したリソースを指す。ディスク容量を無駄に消費するため、定期的にクリーンアップが必要。
Dangling Images(ダングリングイメージ)
- 同じタグで新しいイメージをビルドした時、古いイメージがタグを失う
docker buildで同じタグを何度も使用した場合に発生
# ダングリングイメージの確認
docker images -f dangling=true
# ダングリングイメージの削除
docker image prune
docker image prune -f # 確認なしで削除
Dangling Volumes(ダングリングボリューム)
- コンテナ削除後も残ったボリューム
- 明示的に削除しない限り残り続ける
# ダングリングボリュームの確認
docker volume ls -f dangling=true
# ダングリングボリュームの削除
docker volume prune
docker volume prune -f # 確認なしで削除
全体的なクリーンアップ
# すべての未使用リソースを確認
docker system df
# すべての未使用リソースを削除
docker system prune
# より積極的なクリーンアップ(未使用イメージも含む)
docker system prune -a
# 未使用ネットワークの削除
docker network prune