開発者のためのDocker:イメージ、コンテナ、Compose
Dockerとコンテナについて聞いたことはあるけれど、いざ使ってみるとイメージ、コンテナ、ボリューム、Composeといった用語に圧倒されてしまう。あるいはすでにDockerを使っているけれど、イメージが巨大でビルドが遅く、複数サービスの管理が面倒に感じているかもしれない。このガイドはそんな混乱を解消します。開発者のためのDockerの要点を、コンテナ化アプリケーションを効率的に構築・実行・管理するために本当に必要な知識に絞って解説します。
なぜDockerなのか? 解決する課題
Docker以前、アプリのデプロイは特定のOSバージョン、ライブラリ、設定を正確に揃えることを意味していました。「私のマシンでは動く」がクリシェになったのは、それが真実だったからです。Dockerは、アプリケーションとその依存関係をコンテナと呼ばれる単一のポータブルな単位にパッケージ化することでこれを解決します。コンテナは軽量で分離されており、Dockerがインストールされたどこでも一貫して動作します。
イメージとコンテナ:核心的な違い
イメージは、アプリケーションコード、ランタイム、ライブラリ、設定を含む読み取り専用のテンプレートです。コンテナはイメージの実行インスタンスです。イメージをクラス、コンテナをオブジェクトと考えてください。同じイメージから複数のコンテナを実行でき、それぞれが他から分離されています。
イメージのビルド方法
イメージはDockerfileという命令を含むテキストファイルからビルドされます。以下はNode.jsアプリの最小例です:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
各命令がレイヤーを作成します。レイヤーはキャッシュされるため、ソースコードだけを変更した場合、Dockerは依存関係のキャッシュされたレイヤーを再利用し、再ビルドが高速になります。
ビルドと実行
イメージをビルドし、コンテナを実行します:
docker build -t my-app .
docker run -p 3000:3000 my-app
-pフラグはホストのポート3000をコンテナのポート3000にマッピングします。
効率的なDockerイメージのベストプラクティス
大きなイメージはビルド、プッシュ、デプロイを遅くします。イメージをスリムに保つために以下のプラクティスに従ってください:
- 可能な限り公式のslimまたはalpineベースイメージを使用する。例えば、
node:18-alpineはnode:18よりはるかに小さくなります。 - マルチステージビルドを活用することで、ビルド時の依存関係とランタイムを分離します。これにより最終イメージが最小限になります。
- RUNコマンドをまとめることでレイヤーを減らし、同じレイヤー内でパッケージマネージャのキャッシュをクリーンアップします。
- .dockerignoreファイルを使用することで、不要なファイル(例:node_modules、.git)をビルドコンテキストから除外します。
以下はGoアプリケーションのマルチステージDockerfileです:
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/main .
# Final stage
FROM alpine:latest
COPY --from=builder /app/main /main
CMD ["/main"]
最終イメージにはコンパイル済みバイナリとAlpineのみが含まれ、Goツールチェーンは含まれません。
ボリュームによるデータ管理
コンテナは一時的です。削除されるとデータは失われます。ボリュームはコンテナのライフサイクル外でデータを永続化します。データベース、アップロード、再起動後も保持する必要がある状態に使用してください。
docker run -v my-data:/var/lib/postgresql/data postgres
これによりDockerが管理する名前付きボリュームmy-dataが作成されます。開発では、ソースコードをコンテナにバインドマウントしてライブリロードすることもできます:
docker run -v $(pwd):/app -p 3000:3000 my-app
Docker Compose:複数コンテナのオーケストレーション
実際のアプリケーションはしばしば複数のサービスを必要とします:Webサーバー、データベース、キャッシュ。それぞれをdocker runで起動するのは面倒でエラーが発生しやすいです。Docker Composeを使えば、単一のYAMLファイルでマルチコンテナアプリを定義して実行できます。
実践的なdocker-compose.yml
以下はPostgreSQLとRedisを備えたNode.jsアプリのComposeファイルです:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
cache:
image: redis:7-alpine
volumes:
postgres_data:
docker compose upを実行してすべてのサービスを起動します。Composeはデフォルトネットワークを作成するため、コンテナはサービス名(例:db、cache)で互いに到達できます。
よく使うComposeコマンド
docker compose up -d– デタッチドモード(バックグラウンド)で起動。docker compose logs -f web– サービスのログをフォロー。docker compose exec web sh– 実行中のコンテナでシェルを開く。docker compose down– コンテナ、ネットワーク、ボリュームを停止して削除(ボリュームが外部でない限り)。
Docker ComposeとKubernetesの使い分け
Docker Composeはローカル開発、テスト、小規模デプロイに最適です。Kubernetesは大規模な本番環境でクラスタ全体のコンテナをオーケストレーションするのに優れています。多くの開発者にとって、Composeは日常業務に十分であり、オートスケーリング、自己修復、高度なネットワーキングが必要になったときにKubernetesにステップアップできます。
| 機能 | Docker Compose | Kubernetes |
|---|---|---|
| 用途 | ローカル開発、小規模デプロイ | 本番クラスタ |
| 複雑さ | 低 | 高 |
| スケーリング | 手動 | 自動 |
| 学習曲線 | 緩やか | 急峻 |
FAQ
イメージとコンテナの違いは何ですか?
イメージはアプリと依存関係を含む読み取り専用テンプレートです。コンテナはイメージの実行インスタンスです。1つのイメージから多数のコンテナを実行できます。
Dockerイメージのサイズを減らすには?
より小さなベースイメージ(Alpineなど)を使用し、マルチステージビルドを活用し、RUNコマンドをまとめ、.dockerignoreファイルで不要なファイルを除外します。
Docker Composeを本番環境で使えますか?
はい、小規模なデプロイでは可能です。より大規模で動的な環境では、KubernetesやDocker Swarmが適しています。
Dockerワークフローを最適化する準備はできましたか?コンテナ化されたWebサーバーをデバッグ・監視するには、Nginx Log Analyzerをチェックしてください。