この記事について
Docker初心者が実務で困らないレベル(中級者の入り口)まで到達できる、完全学習ロードマップです。
📚 含まれる内容
- Dockerの基礎からDockerfile詳細解説まで
- ボリューム・ネットワークの実践的な使い方
- Docker Composeによる複数コンテナ管理
- トラブルシューティング集
- AWS ECS/ECRへの移行ガイド
🎯 対象読者
- Dockerを基礎から学びたい方
- Dockerfileの書き方を詳しく知りたい方
- 開発環境をDockerで構築したい方
- AWS ECS/ECRへの移行を考えている方
⏱️ 想定学習時間
全体で20〜30時間(実践含む)
目次
- Dockerの基礎知識
- Dockerの基本操作
- Dockerfileを書く
- ボリュームとデータ管理
- ネットワーク
- Docker Compose
- 実践的な開発環境構築
- デバッグとトラブルシューティング
- デプロイの基礎
- 付録
第1章:Dockerの基礎知識
1.1 Dockerとは何か
Dockerは、アプリケーションとその実行環境を「コンテナ」という単位でパッケージ化し、どこでも同じように動かせる技術です。
Dockerが解決する問題:
| 問題 | Docker以前 | Dockerを使うと |
|---|---|---|
| 環境の違い | 「自分のPCでは動くのに...」 | どの環境でも同じように動く |
| セットアップの手間 | 毎回手動でインストール | コンテナ起動だけでOK |
| 環境の汚染 | システムに直接インストール | コンテナ内に隔離される |
| チーム開発 | 環境構築手順書が必要 | Dockerfileを共有するだけ |
1.2 コンテナと仮想マシンの違い
【仮想マシン】
┌─────────────────┐
│ アプリA │
│ ゲストOS │
├─────────────────┤
│ アプリB │
│ ゲストOS │
├─────────────────┤
│ ハイパーバイザー │
├─────────────────┤
│ ホストOS │
└─────────────────┘
【Docker コンテナ】
┌──────┬──────┐
│アプリA│アプリB│
├──────┴──────┤
│ Docker Engine │
├──────────────┤
│ ホストOS │
└──────────────┘
| 項目 | 仮想マシン | Dockerコンテナ |
|---|---|---|
| 起動速度 | 数分 | 数秒 |
| リソース消費 | 大きい(OS丸ごと) | 小さい(必要な部分のみ) |
| 隔離レベル | 完全に独立 | プロセスレベルで隔離 |
| 用途 | 異なるOS環境の実行 | アプリの軽量な実行環境 |
1.3 イメージとコンテナの関係
イメージ = 設計図(クラス)
- アプリケーションと実行環境をパッケージ化したもの
- 読み取り専用
- 再利用可能
コンテナ = 実体(インスタンス)
- イメージから作成される実行中のプロセス
- 書き込み可能
- 使い捨て可能
イメージ(ubuntu:20.04)
↓ docker run
コンテナA(実行中)
コンテナB(実行中)
コンテナC(停止中)
重要な概念:
1つのイメージから複数のコンテナを作成できる
1.4 Dockerのインストール(Windows)
インストール手順
1. システム要件の確認
| 項目 | 要件 |
|---|---|
| OS | Windows 10/11 Pro, Enterprise, Education(64bit) |
| CPU | 仮想化対応(BIOSで有効化) |
| メモリ | 4GB以上推奨 |
| WSL2 | 有効化が必要 |
2. WSL2のインストール
PowerShellを管理者権限で開き、以下を実行:
# WSLを有効化
wsl --install
# 再起動後、WSL2をデフォルトに設定
wsl --set-default-version 2
3. Docker Desktopのインストール
- Docker公式サイトからインストーラーをダウンロード
- インストーラーを実行し、指示に従う
- 「Use WSL 2 instead of Hyper-V」にチェック
- インストール完了後、再起動
4. インストール確認
コマンドプロンプトまたはPowerShellで確認:
docker --version
# 出力例: Docker version 24.0.0, build abc1234
docker run hello-world
# 正常に動作すれば成功メッセージが表示される
トラブルシューティング
| エラー | 原因 | 解決方法 |
|---|---|---|
| WSL2が起動しない | 仮想化が無効 | BIOSで仮想化を有効化 |
| Docker Desktopが起動しない | Hyper-Vの競合 | WSL2モードで再インストール |
| コマンドが認識されない | パスが通っていない | Docker Desktopを再起動 |
Windows使用時の重要な注意点
1. パスの表記
Dockerコマンドでホストのパスを指定する際、環境によって表記が異なります:
| 環境 | カレントディレクトリ | 絶対パス |
|---|---|---|
| PowerShell |
${PWD} または $(pwd)
|
C:\Users\username\project |
| コマンドプロンプト | %cd% |
C:\Users\username\project |
| Git Bash | $(pwd) |
/c/Users/username/project |
推奨: PowerShellまたはGit Bashの使用
2. 改行コードの問題
Windowsでは改行コードがCRLFですが、Linux/DockerではLFが必要です。
# Gitの自動変換を無効化(推奨)
git config --global core.autocrlf false
# シェルスクリプト(.shファイル)は必ずLF形式で保存
エディタ設定:
- VS Code: 右下のステータスバーで
CRLF→LFに変更 - Vim:
:set fileformat=unix
3. ファイル共有設定
バインドマウント(-vオプション)を使用する際は、Docker Desktopで共有設定が必要です:
- Docker Desktop → Settings → Resources → File Sharing
- マウントするドライブ(C:, D:など)にチェック
- Apply & Restart
4. パフォーマンス
- WSL2を使用すると、Hyper-Vより高速
- ソースコードはWSL2のファイルシステムに配置すると最速
- Windowsファイルシステム(
/mnt/c/)からのマウントは遅い
5. シェルスクリプト実行時の注意
# Windowsで作成したスクリプトは実行権限がない場合がある
chmod +x script.sh
# または、Docker内で実行
docker run --rm -v ${PWD}:/work alpine sh /work/script.sh
第2章:Dockerの基本操作
2.1 最初の一歩:Hello Worldコンテナを動かす
docker run hello-world
このコマンドで何が起こっているか:
- ローカルに
hello-worldイメージがあるか確認 - なければDocker Hubから自動ダウンロード
- イメージからコンテナを作成
- コンテナを起動してメッセージを表示
- コンテナが自動的に停止
2.2 基本コマンド一覧と使い方
イメージ関連コマンド
| コマンド | 説明 | 例 |
|---|---|---|
docker pull |
イメージをダウンロード | docker pull ubuntu:20.04 |
docker images |
ローカルのイメージ一覧 | docker images |
docker rmi |
イメージを削除 | docker rmi ubuntu:20.04 |
docker build |
Dockerfileからイメージ作成 | docker build -t myapp . |
コンテナ関連コマンド
| コマンド | 説明 | 例 |
|---|---|---|
docker run |
コンテナを作成して起動 | docker run -d -p 8080:80 nginx |
docker ps |
実行中のコンテナ一覧 | docker ps |
docker ps -a |
すべてのコンテナ一覧 | docker ps -a |
docker stop |
コンテナを停止 | docker stop <コンテナID> |
docker start |
停止中のコンテナを起動 | docker start <コンテナID> |
docker restart |
コンテナを再起動 | docker restart <コンテナID> |
docker rm |
コンテナを削除 | docker rm <コンテナID> |
docker exec |
実行中のコンテナでコマンド実行 | docker exec -it <コンテナID> bash |
docker logs |
コンテナのログを表示 | docker logs <コンテナID> |
docker runの主要オプション
| オプション | 説明 | 例 |
|---|---|---|
-d |
バックグラウンドで実行(デタッチモード) | docker run -d nginx |
-it |
対話モードで実行 | docker run -it ubuntu bash |
-p |
ポートマッピング(ホスト:コンテナ) | docker run -p 8080:80 nginx |
--name |
コンテナに名前をつける | docker run --name web nginx |
-v |
ボリュームマウント | docker run -v /host:/container nginx |
-e |
環境変数を設定 | docker run -e API_KEY=xxx myapp |
--rm |
停止時に自動削除 | docker run --rm ubuntu echo hello |
2.3 コンテナのライフサイクル
┌──────────┐
│ イメージ │
└─────┬────┘
│ docker run
↓
┌──────────┐ docker stop ┌──────────┐
│ 実行中 │ ───────────────→│ 停止中 │
└─────┬────┘ └─────┬────┘
↑ │
└─────── docker start ─────────┘
│
│ docker rm
↓
(削除)
状態の確認:
# 実行中のコンテナのみ表示
docker ps
# すべてのコンテナを表示(停止中も含む)
docker ps -a
# 出力例:
# CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
# abc123def456 nginx "nginx" 2 hours ago Up 2 hours web
# 789ghi012jkl ubuntu "bash" 3 hours ago Exited (0) 3 hours ago my-ubuntu
2.4 コンテナの中に入る・ログを見る
実行中のコンテナに入る
新規コンテナを起動して中に入る:
# Ubuntuコンテナを起動してbashを実行
docker run -it ubuntu:20.04 bash
# コンテナ内で操作可能
root@abc123:/# ls
root@abc123:/# pwd
root@abc123:/# exit # 抜ける(コンテナも停止)
既に起動中のコンテナに入る:
# まずコンテナIDまたは名前を確認
docker ps
# execコマンドで接続
docker exec -it <コンテナIDまたは名前> bash
# 例:
docker exec -it web bash
# コンテナ内で操作可能
root@def456:/# ls
root@def456:/# exit # 抜ける(コンテナは起動したまま)
runとexecの違い:
| コマンド | 用途 | exitした後 |
|---|---|---|
docker run -it |
新しいコンテナを起動して入る | コンテナが停止する |
docker exec -it |
既存の起動中コンテナに入る | コンテナは起動したまま |
停止したコンテナを再起動して入る
# 停止中のコンテナを確認
docker ps -a
# コンテナを再起動
docker start <コンテナID>
# 再起動したコンテナに入る
docker exec -it <コンテナID> bash
ログを確認する
# 最新のログを表示
docker logs <コンテナID>
# リアルタイムでログを追跡(tail -f のように)
docker logs -f <コンテナID>
# 最後の100行のみ表示
docker logs --tail 100 <コンテナID>
# タイムスタンプ付きで表示
docker logs -t <コンテナID>
例:Nginxのアクセスログを確認
# Nginxコンテナを起動
docker run -d --name web -p 8080:80 nginx
# ブラウザで http://localhost:8080 にアクセス
# ログを確認
docker logs web
# 出力例:
# 172.17.0.1 - - [15/Nov/2025:10:30:45 +0000] "GET / HTTP/1.1" 200 615
コンテナ内でのデバッグ作業
# コンテナに入る
docker exec -it web bash
# パッケージをインストール(一時的に)
apt-get update
apt-get install -y curl vim
# ファイルを確認
cat /etc/nginx/nginx.conf
# プロセスを確認
ps aux
# ネットワークを確認
curl localhost
# 抜ける
exit
注意: コンテナ内での変更は、コンテナを削除すると失われます。永続的な変更はDockerfileに記述します。
第3章:Dockerfileを書く
3.1 Dockerfileとは
Dockerfileは、Dockerイメージを作成するための設計書です。テキストファイルに命令を記述することで、自動的にイメージをビルドできます。
Dockerfileの役割:
- 環境構築手順を自動化
- イメージの作成プロセスを記録
- チームメンバーと環境を共有
基本的な流れ:
Dockerfile(設計書)
↓ docker build
イメージ(完成品)
↓ docker run
コンテナ(実行環境)
3.2 Dockerfileの基本構造
最小限のDockerfile例:
# ベースイメージを指定
FROM ubuntu:20.04
# コマンドを実行
RUN apt-get update && apt-get install -y nginx
# コンテナ起動時のコマンド
CMD ["nginx", "-g", "daemon off;"]
ビルドと実行:
# イメージをビルド
docker build -t my-nginx .
# コンテナを起動
docker run -d -p 8080:80 my-nginx
3.3 Dockerfile命令の詳細解説
このセクションがDockerfile理解の核心です。各命令を詳しく見ていきます。
FROM - ベースイメージの指定
役割: Dockerイメージの土台となるベースイメージを指定します。すべてのDockerfileはFROMから始まります。
基本構文:
FROM <イメージ名>:<タグ>
使用例と解説:
# 公式のNode.js 18イメージを使用
FROM node:18
# 特定のバージョンを明示
FROM python:3.11-slim
# アルパインLinux版(軽量)
FROM node:18-alpine
# 複数のFROMを使う(マルチステージビルド)
FROM node:18 AS builder
# ... ビルド処理 ...
FROM node:18-slim AS production
タグの選び方:
| タグ | 説明 | 使用場面 |
|---|---|---|
node:18 |
最新の18系 | 開発環境 |
node:18.16.0 |
特定バージョン | 本番環境(再現性重視) |
node:18-alpine |
Alpine Linux版(軽量) | 本番環境(サイズ重視) |
node:18-slim |
最小限のパッケージ | 本番環境(バランス型) |
重要なポイント:
-
latestタグは避ける(バージョンが変わると動作が変わる可能性) - 本番環境では具体的なバージョンを指定
- Alpine版はサイズは小さいが、一部ライブラリで問題が出ることがある
RUN - コマンドの実行
役割: イメージビルド時にコマンドを実行します。パッケージのインストールやファイル操作に使用します。
基本構文:
# シェル形式(シェルを介して実行)
RUN <コマンド>
# exec形式(シェルなしで実行)
RUN ["実行ファイル", "引数1", "引数2"]
使用例と解説:
# 単一のコマンド
RUN apt-get update
# 複数のコマンドを&&で繋ぐ(推奨)
RUN apt-get update && apt-get install -y \
curl \
vim \
git
# パイプを使う
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
# exec形式
RUN ["apt-get", "update"]
ベストプラクティス:
❌ 悪い例(レイヤーが増える):
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN apt-get install -y git
✅ 良い例(1つのレイヤーにまとめる):
RUN apt-get update && apt-get install -y \
curl \
vim \
git \
&& rm -rf /var/lib/apt/lists/*
各OSでのパッケージインストール:
| OS | コマンド例 |
|---|---|
| Ubuntu/Debian | RUN apt-get update && apt-get install -y <パッケージ> |
| Alpine | RUN apk add --no-cache <パッケージ> |
| CentOS/RHEL | RUN yum install -y <パッケージ> |
キャッシュの削除:
# Ubuntuの場合
RUN apt-get update && apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
# Alpineの場合(--no-cacheオプションでキャッシュを作らない)
RUN apk add --no-cache curl
WORKDIR - 作業ディレクトリの設定
役割: 以降の命令で使用する作業ディレクトリを設定します。cdコマンドに相当します。
基本構文:
WORKDIR <ディレクトリパス>
使用例と解説:
# 作業ディレクトリを設定(存在しなければ自動作成)
WORKDIR /app
# この後のRUN, COPY, CMDなどは /app ディレクトリで実行される
COPY . .
RUN npm install
# 相対パスも使える(現在のWORKDIRからの相対パス)
WORKDIR src
# 現在は /app/src にいる
# 絶対パスで上書き
WORKDIR /opt/myapp
WORKDIRを使わない場合の問題:
❌ 悪い例:
RUN cd /app && npm install
RUN cd /app && npm run build
# 各RUNで毎回cdが必要
✅ 良い例:
WORKDIR /app
RUN npm install
RUN npm run build
# 一度WORKDIRを設定すれば、以降はそのディレクトリで実行
重要なポイント:
- ディレクトリが存在しない場合は自動的に作成される
- 相対パスは前のWORKDIRからの相対
- コンテナ起動時のデフォルトディレクトリにもなる
COPY と ADD - ファイルのコピー
役割: ホストのファイルをコンテナにコピーします。
基本構文:
COPY <ソース> <宛先>
ADD <ソース> <宛先>
COPYの使用例:
# 単一ファイルをコピー
COPY package.json /app/
# 複数ファイルをコピー
COPY package.json package-lock.json /app/
# ディレクトリごとコピー
COPY ./src /app/src
# カレントディレクトリの全内容をコピー
COPY . /app
# ワイルドカード使用
COPY *.json /app/
COPYとADDの違い:
| 機能 | COPY | ADD |
|---|---|---|
| ファイルコピー | ○ | ○ |
| URL指定 | × | ○ |
| tar自動解凍 | × | ○ |
| 推奨度 | 推奨 | 特殊な場合のみ |
ADDの使用例(特殊なケース):
# tarファイルを自動解凍してコピー
ADD archive.tar.gz /app/
# URLからダウンロード(非推奨:RUN + curlを推奨)
ADD https://example.com/file.txt /app/
ベストプラクティス:
✅ 基本的にCOPYを使う:
# シンプルで明示的
COPY package.json /app/
✅ ADDは自動解凍が必要な時だけ:
# tarファイルを展開したい場合
ADD app.tar.gz /app/
❌ ADDでURLダウンロードは避ける:
# 悪い例
ADD https://example.com/file.txt /app/
# 良い例(キャッシュの削除も含む)
RUN curl -o /app/file.txt https://example.com/file.txt
COPYの注意点:
# .dockerignoreファイルで除外対象を指定
# .dockerignore の内容例:
# node_modules
# .git
# *.log
# カレントディレクトリをコピー(除外対象以外)
COPY . /app
ENV - 環境変数の設定
役割: コンテナ内で使用する環境変数を設定します。ビルド時と実行時の両方で有効です。
基本構文:
ENV <キー>=<値>
ENV <キー1>=<値1> <キー2>=<値2>
使用例と解説:
# 単一の環境変数
ENV NODE_ENV=production
# 複数の環境変数
ENV APP_HOME=/app \
APP_PORT=3000 \
LOG_LEVEL=info
# 環境変数を別の命令で使用
ENV APP_DIR=/opt/myapp
WORKDIR $APP_DIR
COPY . $APP_DIR
よく使われる環境変数:
| 変数名 | 用途 | 例 |
|---|---|---|
NODE_ENV |
Node.jsの実行モード |
production, development
|
PYTHONUNBUFFERED |
Pythonの出力バッファリング |
1 (無効化) |
TZ |
タイムゾーン | Asia/Tokyo |
LANG |
ロケール | ja_JP.UTF-8 |
PATH |
実行パス | $PATH:/app/bin |
実用例:
FROM node:18
# 環境変数の設定
ENV NODE_ENV=production \
APP_PORT=3000 \
TZ=Asia/Tokyo
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# 環境変数を使用
EXPOSE $APP_PORT
CMD ["node", "server.js"]
ENVとARGの違い:
| 項目 | ENV | ARG |
|---|---|---|
| 使用時期 | ビルド時 + 実行時 | ビルド時のみ |
| docker run での上書き |
-e で可能 |
不可 |
| 用途 | アプリの設定 | ビルド時の設定 |
ARG - ビルド時の変数
役割: イメージのビルド時のみ使用できる変数を定義します。実行時には存在しません。
基本構文:
ARG <変数名>
ARG <変数名>=<デフォルト値>
使用例と解説:
# デフォルト値なし
ARG VERSION
# デフォルト値あり
ARG NODE_VERSION=18
ARG BUILD_ENV=development
# ARGを使ってベースイメージを指定
FROM node:${NODE_VERSION}
# ARGをビルド中に使用
RUN echo "Building version: ${VERSION}"
RUN echo "Build environment: ${BUILD_ENV}"
ビルド時に値を渡す:
# --build-argで値を指定
docker build --build-arg VERSION=1.0.0 --build-arg BUILD_ENV=production -t myapp .
ENVとARGの組み合わせ:
# ビルド時の変数
ARG NODE_VERSION=18
ARG BUILD_DATE
# ベースイメージに使用
FROM node:${NODE_VERSION}
# ビルド情報をENVに変換(実行時にも参照可能)
ENV BUILD_DATE=${BUILD_DATE}
RUN echo "Built on: ${BUILD_DATE}"
ARGのスコープ:
# FROMの前のARG(特殊なケース)
ARG BASE_IMAGE=node:18
FROM ${BASE_IMAGE}
# FROMの後のARGは再宣言が必要
ARG BASE_IMAGE
RUN echo "Base image: ${BASE_IMAGE}"
セキュリティ上の注意:
❌ 機密情報をARGに渡さない:
# 悪い例:ビルド履歴に残る
ARG DATABASE_PASSWORD=secret123
ENV DB_PASS=${DATABASE_PASSWORD}
✅ 機密情報は実行時に渡す:
# 実行時に環境変数で渡す
docker run -e DATABASE_PASSWORD=secret123 myapp
EXPOSE - ポートの宣言
役割: コンテナがリッスンするポートを文書化します。実際のポートマッピングは行いません。
基本構文:
EXPOSE <ポート番号>[/プロトコル]
使用例と解説:
# HTTPポートを公開
EXPOSE 80
# 複数のポートを公開
EXPOSE 80 443
# プロトコルを明示
EXPOSE 8080/tcp
EXPOSE 53/udp
# 環境変数を使用
ENV APP_PORT=3000
EXPOSE $APP_PORT
重要な誤解:
❌ EXPOSEだけではポートにアクセスできない:
FROM node:18
WORKDIR /app
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
# これだけではホストからアクセスできない
✅ docker runで-pオプションが必要:
# ホストの8080番をコンテナの3000番にマッピング
docker run -p 8080:3000 myapp
# ブラウザで http://localhost:8080 にアクセス可能
EXPOSEの目的:
| 目的 | 説明 |
|---|---|
| ドキュメント化 | どのポートを使うか明示 |
| docker-composeでの自動マッピング |
ports:で指定しやすくなる |
| -Pオプションでの自動公開 |
docker run -Pで自動マッピング |
-Pオプションの使用例:
# EXPOSEしたポートを自動的にランダムなホストポートにマッピング
docker run -P myapp
# どのポートにマッピングされたか確認
docker ps
# 0.0.0.0:32768->3000/tcp のように表示される
CMD と ENTRYPOINT - 起動コマンドの指定
これらは、コンテナ起動時に実行するコマンドを指定する命令です。違いが分かりにくいので、詳しく解説します。
基本的な違い:
| 項目 | CMD | ENTRYPOINT |
|---|---|---|
| 役割 | デフォルトコマンド | 必ず実行されるコマンド |
| docker run での上書き | 簡単に上書きされる | 上書きされない(引数として追加) |
| 用途 | デフォルト動作を提供 | コンテナを実行ファイルのように扱う |
CMD - デフォルトコマンド
役割: コンテナ起動時のデフォルトコマンドを指定します。docker runでコマンドを指定すると上書きされます。
3つの記法:
# 1. exec形式(推奨)
CMD ["実行ファイル", "引数1", "引数2"]
# 2. shell形式
CMD コマンド 引数1 引数2
# 3. ENTRYPOINTのパラメータとして(後述)
CMD ["引数1", "引数2"]
使用例:
# exec形式(推奨)
CMD ["node", "server.js"]
CMD ["python", "app.py"]
CMD ["nginx", "-g", "daemon off;"]
# shell形式(/bin/sh -c で実行される)
CMD node server.js
CMD python app.py
exec形式とshell形式の違い:
| 項目 | exec形式 | shell形式 |
|---|---|---|
| 実行方法 | 直接実行 |
/bin/sh -c経由 |
| PID | 1 | シェルが1、コマンドは別 |
| シグナル処理 | 正しく処理される | 処理されない場合がある |
| 環境変数展開 | されない | される |
実用例:
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
# デフォルトでサーバーを起動
CMD ["node", "server.js"]
docker runでの上書き:
# CMDで指定したコマンドを実行
docker run myapp
# → node server.js が実行される
# CMDを上書き
docker run myapp node debug.js
# → node debug.js が実行される
docker run myapp bash
# → bash が実行される(対話モードでコンテナに入る)
ENTRYPOINT - 必ず実行されるコマンド
役割: コンテナを実行ファイルのように扱いたい場合に使用します。docker runで指定した引数がパラメータとして追加されます。
基本構文:
# exec形式(推奨)
ENTRYPOINT ["実行ファイル", "固定引数"]
# shell形式
ENTRYPOINT コマンド
使用例:
# curlコマンドを実行するコンテナ
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
ENTRYPOINT ["curl"]
実行例:
# docker runの引数がcurlの引数になる
docker run mycurl https://example.com
# → curl https://example.com が実行される
docker run mycurl -I https://example.com
# → curl -I https://example.com が実行される
ENTRYPOINTの上書き:
# --entrypointオプションで上書き可能
docker run --entrypoint bash mycurl
CMD と ENTRYPOINT の組み合わせ
最も柔軟な使い方は、両方を組み合わせることです。
パターン1:ENTRYPOINTで固定部分、CMDでデフォルト引数
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
# 固定部分
ENTRYPOINT ["curl"]
# デフォルト引数
CMD ["https://example.com"]
実行パターン:
# デフォルト引数を使用
docker run mycurl
# → curl https://example.com
# 引数を上書き
docker run mycurl https://google.com
# → curl https://google.com
# オプション付き
docker run mycurl -I https://google.com
# → curl -I https://google.com
パターン2:アプリケーションでの実用例
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# 必ず node を実行
ENTRYPOINT ["node"]
# デフォルトは server.js
CMD ["server.js"]
実行パターン:
# デフォルトでserver.jsを実行
docker run myapp
# → node server.js
# 別のファイルを実行
docker run myapp debug.js
# → node debug.js
# オプション付き
docker run myapp --inspect server.js
# → node --inspect server.js
パターン3:スクリプトをエントリーポイントにする
FROM python:3.11
WORKDIR /app
# エントリーポイントスクリプトをコピー
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
COPY . .
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["python", "app.py"]
docker-entrypoint.sh の例:
#!/bin/bash
set -e
# 初期化処理
echo "Starting application..."
python manage.py migrate
# CMDで指定されたコマンドを実行
exec "$@"
選択ガイド:
| ケース | 推奨 | 理由 |
|---|---|---|
| Webアプリ(柔軟性必要) | CMD のみ | 開発時に別コマンドを実行しやすい |
| ツールコンテナ | ENTRYPOINT のみ | 実行ファイルのように扱える |
| 初期化が必要なアプリ | 両方 | 柔軟性と確実な初期化を両立 |
VOLUME - ボリュームのマウントポイント
役割: データの永続化やホストとの共有が必要なディレクトリを宣言します。
基本構文:
VOLUME ["<パス>"]
VOLUME <パス>
使用例:
# データベースのデータディレクトリ
VOLUME ["/var/lib/mysql"]
# 複数のボリューム
VOLUME ["/data", "/logs"]
# 環境変数を使用
ENV DATA_DIR=/app/data
VOLUME $DATA_DIR
実用例:MySQLコンテナ
FROM mysql:8.0
# データを永続化するディレクトリを宣言
VOLUME /var/lib/mysql
# 環境変数
ENV MYSQL_ROOT_PASSWORD=rootpass
ENV MYSQL_DATABASE=myapp
EXPOSE 3306
VOLUMEの効果:
# コンテナを起動(自動的に匿名ボリュームが作成される)
docker run -d --name db mysql:8.0
# ボリュームを確認
docker volume ls
# DRIVER VOLUME NAME
# local abc123def456...
# 名前付きボリュームで起動(推奨)
docker run -d --name db -v mydb-data:/var/lib/mysql mysql:8.0
# コンテナを削除してもデータは残る
docker rm -f db
docker volume ls
# mydb-data はまだ存在する
重要なポイント:
- Dockerfileの
VOLUMEは宣言のみ(ドキュメント化) - 実際のマウントは
docker run -vで行う -
VOLUMEで宣言されたディレクトリは、ビルド後の変更が保持されない
USER - 実行ユーザーの変更
役割: 以降の命令とコンテナ実行時のユーザーを指定します。セキュリティ向上のために使用します。
基本構文:
USER <ユーザー名またはUID>[:グループ名またはGID]
使用例と解説:
# rootユーザーでビルド作業
FROM node:18
WORKDIR /app
# パッケージインストール(rootが必要)
RUN apt-get update && apt-get install -y curl
# アプリケーションのセットアップ
COPY package*.json ./
RUN npm install
# 一般ユーザーを作成
RUN useradd -m -u 1001 appuser
# ファイルの所有者を変更
RUN chown -R appuser:appuser /app
# ユーザーを切り替え
USER appuser
# 以降は appuser として実行
COPY --chown=appuser:appuser . .
CMD ["node", "server.js"]
なぜUSERを使うのか:
| rootで実行 | 一般ユーザーで実行 |
|---|---|
| コンテナ脱出時にホストのrootに | 権限が制限されている |
| セキュリティリスク大 | セキュリティリスク小 |
| 本番環境では非推奨 | 本番環境で推奨 |
Node.jsの公式イメージを使う場合:
FROM node:18
# node イメージには既に node ユーザーが存在
WORKDIR /app
# 所有者を指定してコピー
COPY --chown=node:node package*.json ./
USER node
RUN npm install
COPY --chown=node:node . .
CMD ["node", "server.js"]
LABEL - メタデータの追加
役割: イメージにメタデータを追加します。バージョン管理や情報の記録に使用します。
基本構文:
LABEL <キー>=<値>
使用例:
FROM node:18
# メタデータを追加
LABEL maintainer="yourname@example.com"
LABEL version="1.0.0"
LABEL description="My awesome application"
LABEL org.opencontainers.image.source="https://github.com/user/repo"
# 複数のラベルを一度に
LABEL version="1.0.0" \
description="My app" \
maintainer="yourname@example.com"
WORKDIR /app
COPY . .
CMD ["node", "server.js"]
ラベルの確認:
# イメージのラベルを表示
docker inspect myapp | grep -A 10 Labels
# または
docker image inspect --format='{{json .Config.Labels}}' myapp
3.4 レイヤーとキャッシュの仕組み
Dockerイメージはレイヤー構造になっています。これを理解することで、ビルドを高速化できます。
レイヤーの仕組み
【Dockerイメージのレイヤー構造】
┌─────────────────────┐
│ CMD ["node", "app"] │ ← レイヤー6(最上位)
├─────────────────────┤
│ COPY . /app │ ← レイヤー5
├─────────────────────┤
│ RUN npm install │ ← レイヤー4
├─────────────────────┤
│ COPY package.json │ ← レイヤー3
├─────────────────────┤
│ WORKDIR /app │ ← レイヤー2
├─────────────────────┤
│ FROM node:18 │ ← レイヤー1(ベース)
└─────────────────────┘
レイヤーの特徴:
- 各命令(FROM、RUN、COPY等)が1つのレイヤーを作成
- レイヤーは読み取り専用
- 変更があったレイヤー以降が再ビルドされる
キャッシュの活用
キャッシュが効く条件:
- 命令の内容が同じ
- COPYするファイルの内容が同じ(チェックサムで判定)
- 前のレイヤーのキャッシュが有効
❌ 悪い例(キャッシュが効きにくい):
FROM node:18
WORKDIR /app
# すべてのファイルをコピー
COPY . .
# package.jsonが変わらなくてもapp.jsが変わると再実行される
RUN npm install
CMD ["node", "server.js"]
✅ 良い例(キャッシュを最大限活用):
FROM node:18
WORKDIR /app
# 依存関係の定義ファイルのみコピー
COPY package*.json ./
# npm installはpackage.jsonが変わった時だけ再実行
RUN npm install
# アプリケーションコードをコピー
COPY . .
CMD ["node", "server.js"]
効果の比較:
| ケース | 悪い例 | 良い例 |
|---|---|---|
| app.jsのみ変更 | npm install再実行(遅い) | キャッシュ使用(速い) |
| package.json変更 | npm install再実行 | npm install再実行 |
ビルドキャッシュの確認
# キャッシュを使ってビルド
docker build -t myapp .
# 出力例:
# Step 3/6 : COPY package*.json ./
# ---> Using cache
# ---> abc123def456
# Step 4/6 : RUN npm install
# ---> Using cache ← キャッシュが使われている
# ---> 789ghi012jkl
キャッシュを無効にしてビルド:
# 強制的に全体を再ビルド
docker build --no-cache -t myapp .
レイヤーサイズの確認
# イメージのレイヤー情報を表示
docker history myapp
# 出力例:
# IMAGE CREATED CREATED BY SIZE
# abc123 2 hours ago CMD ["node" "server.js"] 0B
# def456 2 hours ago COPY . /app 5.2MB
# ghi789 2 hours ago RUN npm install 85MB
# jkl012 2 hours ago COPY package*.json ./ 1.2kB
3.5 Dockerfileのベストプラクティス
1. ベースイメージは最小限に
# ❌ 大きいイメージ
FROM node:18
# サイズ: ~900MB
# ✅ 小さいイメージ
FROM node:18-alpine
# サイズ: ~170MB
# ✅ さらに小さく(マルチステージビルド)
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]
# 最終イメージサイズ: ~200MB
2. レイヤーを最小限に
# ❌ レイヤーが多い
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN apt-get install -y git
# ✅ 1つのレイヤーにまとめる
RUN apt-get update && apt-get install -y \
curl \
vim \
git \
&& rm -rf /var/lib/apt/lists/*
3. 変更頻度の低いものを先に
# ✅ 推奨順序
FROM node:18-alpine
WORKDIR /app
# 1. ベース環境(ほぼ変更なし)
RUN apk add --no-cache python3 make g++
# 2. 依存関係(時々変更)
COPY package*.json ./
RUN npm ci --only=production
# 3. アプリコード(頻繁に変更)
COPY . .
CMD ["node", "server.js"]
4. .dockerignoreを活用
.dockerignore の例:
# バージョン管理
.git
.gitignore
# 依存関係
node_modules
npm-debug.log
# ビルド成果物
dist
build
*.log
# 開発用ファイル
.env
.env.local
*.md
Dockerfile
docker-compose.yml
# OS生成ファイル
.DS_Store
Thumbs.db
5. マルチステージビルドを使う
# ステージ1: ビルド
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# ステージ2: 本番環境
FROM node:18-alpine
WORKDIR /app
# 必要なファイルだけコピー
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
メリット:
- 開発ツール(TypeScript、webpack等)が最終イメージに含まれない
- イメージサイズが小さくなる
- セキュリティが向上
6. セキュリティのベストプラクティス
FROM node:18-alpine
# rootユーザーを避ける
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# 依存関係のインストール
COPY --chown=nodejs:nodejs package*.json ./
RUN npm ci --only=production
# アプリケーションコード
COPY --chown=nodejs:nodejs . .
# 一般ユーザーに切り替え
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]
3.6 実践例:Node.js Webアプリケーション
完全な例を見ていきます。
ディレクトリ構成:
myapp/
├── Dockerfile
├── .dockerignore
├── package.json
├── package-lock.json
└── src/
└── server.js
Dockerfile(本番環境用):
# ベースイメージ
FROM node:18-alpine AS base
WORKDIR /app
# 依存関係のインストール用
FROM base AS dependencies
COPY package*.json ./
RUN npm ci --only=production
# ビルド用
FROM base AS build
COPY package*.json ./
RUN npm install
COPY src ./src
RUN npm run build
# 本番環境用
FROM base AS production
# 一般ユーザーの設定
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 依存関係のコピー
COPY --from=dependencies --chown=nodejs:nodejs /app/node_modules ./node_modules
# ビルド成果物のコピー
COPY --from=build --chown=nodejs:nodejs /app/dist ./dist
# メタデータ
LABEL version="1.0.0" \
description="My Node.js application" \
maintainer="yourname@example.com"
# 環境変数
ENV NODE_ENV=production \
PORT=3000
USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
CMD ["node", "dist/server.js"]
.dockerignore:
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
dist
build
*.md
Dockerfile
docker-compose.yml
.DS_Store
coverage
.vscode
.idea
ビルドと実行:
# イメージをビルド
docker build -t myapp:1.0.0 .
# コンテナを起動
docker run -d \
--name myapp \
-p 3000:3000 \
-e DATABASE_URL=postgresql://user:pass@db:5432/mydb \
myapp:1.0.0
# ログを確認
docker logs -f myapp
# ヘルスチェック
curl http://localhost:3000/health
3.7 実践例:Python Webアプリケーション
Dockerfile(Flask/FastAPI用):
FROM python:3.11-slim AS base
# 環境変数
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
# 依存関係のインストール
FROM base AS dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# 本番環境
FROM base AS production
# 一般ユーザーの作成
RUN useradd -m -u 1001 appuser
# 依存関係のコピー
COPY --from=dependencies /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=dependencies /usr/local/bin /usr/local/bin
# アプリケーションのコピー
COPY --chown=appuser:appuser . .
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
requirements.txt:
fastapi==0.104.1
uvicorn[standard]==0.24.0
pydantic==2.5.0
ビルドと実行:
docker build -t myapp-python:1.0.0 .
docker run -d -p 8000:8000 --name myapp-python myapp-python:1.0.0
第4章:ボリュームとデータ管理
4.1 なぜボリュームが必要か
コンテナは基本的に「使い捨て」の存在です。コンテナを削除すると、その中のデータも消えます。
問題点:
# MySQLコンテナを起動
docker run -d --name db mysql:8.0
# データベースを使用
# ... データを保存 ...
# コンテナを削除
docker rm -f db
# 再度起動すると、データが消えている!
docker run -d --name db mysql:8.0
ボリュームが解決すること:
| 課題 | ボリュームによる解決 |
|---|---|
| データの消失 | コンテナ削除後もデータが残る |
| ホストとの共有 | ソースコードをリアルタイムで反映 |
| コンテナ間共有 | 複数コンテナで同じデータを使用 |
| バックアップ | データを簡単にバックアップ可能 |
4.2 ボリュームの種類
Dockerには3種類のデータマウント方法があります。
1. 名前付きボリューム(Named Volume)
特徴:
- Dockerが管理する永続ストレージ
- 最も推奨される方法
- バックアップや移行が簡単
作成と使用:
# ボリュームを作成
docker volume create mydata
# ボリュームの一覧
docker volume ls
# ボリュームの詳細
docker volume inspect mydata
# コンテナで使用
docker run -d \
--name db \
-v mydata:/var/lib/mysql \
mysql:8.0
# コンテナを削除してもボリュームは残る
docker rm -f db
docker volume ls # mydata はまだ存在
# 新しいコンテナで同じボリュームを使用
docker run -d \
--name db2 \
-v mydata:/var/lib/mysql \
mysql:8.0
# 以前のデータがそのまま使える
ボリュームの削除:
# 特定のボリュームを削除
docker volume rm mydata
# 使用されていないボリュームをすべて削除
docker volume prune
2. バインドマウント(Bind Mount)
特徴:
- ホストの特定のディレクトリをマウント
- 開発時にソースコードをリアルタイムで反映
- ホストのファイルシステムに直接アクセス
使用方法:
# Windowsの場合(絶対パス)
docker run -d \
--name web \
-p 8080:80 \
-v C:\Users\yourname\myapp:/usr/share/nginx/html \
nginx
# または相対パス(カレントディレクトリから)
docker run -d \
--name web \
-p 8080:80 \
-v ${PWD}/html:/usr/share/nginx/html \
nginx
開発時の典型的な使用例:
# Node.jsアプリの開発
docker run -it --rm \
--name myapp \
-p 3000:3000 \
-v ${PWD}:/app \
-w /app \
node:18 \
npm run dev
# ホストでファイルを編集すると、コンテナ内にも即座に反映される
3. tmpfs マウント
特徴:
- ホストのメモリ上にマウント
- 高速だが再起動で消える
- 機密データの一時保存に使用
使用方法:
docker run -d \
--name app \
--tmpfs /tmp:rw,size=100m \
myapp
使用場面:
- 一時ファイルの保存
- キャッシュデータ
- 機密情報(メモリ上のみに存在させたい)
4.3 ボリュームの比較
| 項目 | 名前付きボリューム | バインドマウント | tmpfs |
|---|---|---|---|
| 管理 | Dockerが管理 | ユーザーが管理 | メモリ上 |
| 場所 | Docker管理領域 | 任意のホストパス | メモリ |
| 永続性 | ○ | ○ | × |
| 速度 | 普通 | 普通 | 高速 |
| 本番環境 | 推奨 | 状況による | 限定的 |
| 開発環境 | ○ | 推奨 | △ |
4.4 実践:ソースコードをマウントして開発する
シナリオ: Node.jsアプリを開発中、コード変更を即座にコンテナに反映したい
ディレクトリ構成:
myapp/
├── package.json
├── src/
│ └── server.js
└── Dockerfile.dev
Dockerfile.dev(開発用):
FROM node:18-alpine
WORKDIR /app
# 依存関係のインストール
COPY package*.json ./
RUN npm install
# 開発用サーバー(nodemon)をインストール
RUN npm install -g nodemon
# ポート公開
EXPOSE 3000
# nodemonで起動(ファイル変更を監視)
CMD ["nodemon", "--watch", "src", "src/server.js"]
起動コマンド:
# イメージをビルド
docker build -f Dockerfile.dev -t myapp-dev .
# ソースコードをマウントして起動
docker run -it --rm \
--name myapp-dev \
-p 3000:3000 \
-v ${PWD}/src:/app/src \
myapp-dev
動作確認:
- ブラウザで
http://localhost:3000を開く -
src/server.jsを編集 - 保存すると自動的にサーバーが再起動される
- ブラウザをリロードすると変更が反映される
注意点(Windowsの場合):
# Docker Desktopの設定で、マウントするドライブを共有する必要がある
# Settings → Resources → File Sharing で対象ドライブにチェック
4.5 実践:データベースのデータを永続化する
シナリオ: MySQLのデータをコンテナ削除後も保持したい
方法1:名前付きボリュームを使う(推奨)
# ボリュームを作成
docker volume create mysql-data
# MySQLコンテナを起動
docker run -d \
--name mysql-db \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=myapp \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
# データベースを使用
docker exec -it mysql-db mysql -uroot -prootpass
mysql> CREATE TABLE users (id INT, name VARCHAR(100));
mysql> INSERT INTO users VALUES (1, 'Alice');
mysql> SELECT * FROM users;
mysql> exit
# コンテナを削除
docker rm -f mysql-db
# 新しいコンテナで同じボリュームを使用
docker run -d \
--name mysql-db-new \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=myapp \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
# データが保持されている
docker exec -it mysql-db-new mysql -uroot -prootpass myapp -e "SELECT * FROM users;"
# +------+-------+
# | id | name |
# +------+-------+
# | 1 | Alice |
# +------+-------+
方法2:バインドマウントを使う(開発時)
# ホストにデータ用ディレクトリを作成
mkdir -p C:\mysql-data
# MySQLコンテナを起動
docker run -d \
--name mysql-db \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=myapp \
-v C:\mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
4.6 ボリュームのバックアップとリストア
バックアップ:
# ボリュームの内容をtarファイルに出力
docker run --rm \
-v mysql-data:/data \
-v ${PWD}:/backup \
ubuntu \
tar czf /backup/mysql-backup.tar.gz -C /data .
# バックアップファイルが作成される
ls mysql-backup.tar.gz
リストア:
# 新しいボリュームを作成
docker volume create mysql-data-restored
# バックアップファイルから復元
docker run --rm \
-v mysql-data-restored:/data \
-v ${PWD}:/backup \
ubuntu \
tar xzf /backup/mysql-backup.tar.gz -C /data
# 復元したボリュームを使用
docker run -d \
--name mysql-restored \
-v mysql-data-restored:/var/lib/mysql \
mysql:8.0
4.7 ボリュームのベストプラクティス
| ケース | 推奨方法 | 理由 |
|---|---|---|
| 本番DBデータ | 名前付きボリューム | 管理が簡単、バックアップしやすい |
| 開発時のコード | バインドマウント | リアルタイム反映 |
| ログファイル | 名前付きボリューム | 永続化が必要 |
| 一時ファイル | tmpfs | 高速、不要なデータ |
| 機密情報 | tmpfs | メモリ上のみに保存 |
注意点:
# Dockerfile内でVOLUMEを宣言すると、以降の変更が反映されない
FROM nginx
VOLUME /usr/share/nginx/html
# この後COPYしてもボリュームには反映されない
COPY index.html /usr/share/nginx/html/ # 効果なし
# 正しい方法:
FROM nginx
COPY index.html /usr/share/nginx/html/
# VOLUMEは必要に応じてdocker runで指定
第5章:ネットワーク
5.1 Dockerネットワークの基本
Dockerのネットワークは、コンテナ間の通信やホストとの通信を制御します。
Dockerネットワークの目的:
- コンテナ同士を接続
- 外部からのアクセスを制御
- ネットワークの分離(セキュリティ)
デフォルトで存在するネットワーク:
docker network ls
| ネットワーク | 説明 | 用途 |
|---|---|---|
| bridge | デフォルトのネットワーク | 単一ホスト上のコンテナ通信 |
| host | ホストのネットワークを直接使用 | ネットワークの分離不要な場合 |
| none | ネットワークなし | ネットワーク分離が必要な場合 |
5.2 ポートマッピング(-p オプション)の仕組み
コンテナのポートをホストに公開する方法です。
基本構文:
docker run -p <ホストポート>:<コンテナポート> <イメージ>
使用例:
# ホストの8080番をコンテナの80番にマッピング
docker run -d -p 8080:80 nginx
# ブラウザで http://localhost:8080 にアクセス
# 複数のポートをマッピング
docker run -d \
-p 8080:80 \
-p 8443:443 \
nginx
# すべてのインターフェースで公開
docker run -d -p 0.0.0.0:8080:80 nginx
# 特定のIPアドレスで公開
docker run -d -p 127.0.0.1:8080:80 nginx
# ホストポートを自動割り当て
docker run -d -P nginx
# Dockerが空いているポートを自動的に割り当てる
docker ps # ポート番号を確認
ポートマッピングの仕組み:
【ブラウザ】
↓
http://localhost:8080
↓
【ホスト】ポート8080
↓ (マッピング)
【コンテナ】ポート80
↓
【Nginx】
ポート番号の確認:
docker ps
# 出力例:
# PORTS
# 0.0.0.0:8080->80/tcp
# ホストの8080番がコンテナの80番にマッピングされている
5.3 コンテナ間通信
同じネットワーク上のコンテナは、コンテナ名で通信できます。
例:WebアプリとDBの接続
# 1. カスタムネットワークを作成
docker network create myapp-network
# 2. DBコンテナを起動
docker run -d \
--name mysql-db \
--network myapp-network \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=myapp \
mysql:8.0
# 3. Webアプリコンテナを起動
docker run -d \
--name webapp \
--network myapp-network \
-p 8080:3000 \
-e DATABASE_HOST=mysql-db \
-e DATABASE_PORT=3306 \
myapp:latest
# Webアプリから"mysql-db"というホスト名でDBに接続できる
コンテナ名前解決の仕組み:
【webappコンテナ】
↓
接続先: mysql-db:3306
↓
【Docker DNSサーバー】
↓
mysql-db → 172.18.0.2
↓
【mysql-dbコンテナ】
接続確認:
# webappコンテナ内から確認
docker exec -it webapp sh
# mysql-dbに接続できるか確認
ping mysql-db
# MySQLに接続
mysql -h mysql-db -uroot -prootpass
5.4 ネットワークの種類
1. bridge(デフォルト)
特徴:
- コンテナごとに独立したネットワーク
- 同じbridge上のコンテナ同士で通信可能
- 外部への接続はNATを使用
デフォルトbridgeの問題点:
- コンテナ名で名前解決できない(IPアドレス直指定が必要)
- 自動的なサービスディスカバリなし
カスタムbridgeネットワークの作成(推奨):
# ネットワーク作成
docker network create my-bridge
# コンテナを接続
docker run -d --name web --network my-bridge nginx
docker run -d --name app --network my-bridge myapp
# コンテナ名で通信可能
docker exec app ping web
ネットワークの詳細確認:
docker network inspect my-bridge
2. host
特徴:
- ホストのネットワークを直接使用
- ポートマッピング不要
- ネットワークの分離なし
使用例:
# hostネットワークを使用
docker run -d --network host nginx
# ホストの80番ポートで直接リッスン
# http://localhost にアクセス可能
使用場面:
- パフォーマンスが重要な場合
- 多数のポートを公開する場合
- ネットワークオーバーヘッドを避けたい場合
注意点:
- Windowsでは完全にはサポートされていない
- セキュリティ面でのリスクあり
3. none
特徴:
- ネットワーク接続なし
- 完全に分離された環境
使用例:
docker run -d --network none myapp
使用場面:
- セキュリティが重要な処理
- ネットワークアクセスが不要な処理
5.5 ネットワークの管理コマンド
# ネットワーク一覧
docker network ls
# ネットワーク作成
docker network create <ネットワーク名>
# ネットワーク詳細
docker network inspect <ネットワーク名>
# コンテナをネットワークに接続
docker network connect <ネットワーク名> <コンテナ名>
# コンテナをネットワークから切断
docker network disconnect <ネットワーク名> <コンテナ名>
# ネットワーク削除
docker network rm <ネットワーク名>
# 使用されていないネットワークを削除
docker network prune
5.6 実践例:3層アーキテクチャ
構成: フロントエンド + バックエンド + データベース
# ネットワーク作成
docker network create frontend
docker network create backend
# データベース(backendネットワークのみ)
docker run -d \
--name postgres \
--network backend \
-e POSTGRES_PASSWORD=dbpass \
postgres:15
# バックエンドAPI(両方のネットワークに接続)
docker run -d \
--name api \
--network backend \
-e DATABASE_URL=postgresql://postgres:dbpass@postgres:5432/mydb \
myapi:latest
docker network connect frontend api
# フロントエンド(frontendネットワークのみ)
docker run -d \
--name web \
--network frontend \
-p 80:80 \
-e API_URL=http://api:3000 \
myweb:latest
ネットワーク分離の効果:
【web】─── frontend ───【api】─── backend ───【postgres】
○可能 ○可能 ×不可
- webはapiに接続できる
- apiはpostgresに接続できる
- webはpostgresに直接接続できない(セキュリティ)
第6章:Docker Compose
6.1 Docker Composeとは
Docker Composeは、複数のコンテナを定義・管理するツールです。YAMLファイルで設定を記述します。
Docker Composeが解決すること:
| 課題 | Docker Composeによる解決 |
|---|---|
| 複雑なコマンド | YAMLファイルで設定を管理 |
| コンテナ間の依存関係 | 自動的に起動順序を制御 |
| ネットワーク設定 | 自動的にネットワークを作成 |
| 環境の再現 | ファイル共有で簡単に再現 |
比較:
❌ docker runの場合:
# ネットワーク作成
docker network create myapp-network
# DBコンテナ起動
docker run -d \
--name db \
--network myapp-network \
-e POSTGRES_PASSWORD=dbpass \
-v pgdata:/var/lib/postgresql/data \
postgres:15
# Webコンテナ起動
docker run -d \
--name web \
--network myapp-network \
-p 8080:3000 \
-e DATABASE_URL=postgresql://postgres:dbpass@db:5432/mydb \
myapp:latest
✅ Docker Composeの場合:
# docker-compose.yml
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: dbpass
volumes:
- pgdata:/var/lib/postgresql/data
web:
image: myapp:latest
ports:
- "8080:3000"
environment:
DATABASE_URL: postgresql://postgres:dbpass@db:5432/mydb
depends_on:
- db
volumes:
pgdata:
# 起動は1コマンド
docker-compose up -d
6.2 docker-compose.ymlの書き方
基本構造
version: '3.8' # Composeファイルのバージョン
services: # コンテナの定義
service1:
# サービス1の設定
service2:
# サービス2の設定
volumes: # 名前付きボリュームの定義
volume1:
networks: # カスタムネットワークの定義
network1:
最小限の例
version: '3.8'
services:
web:
image: nginx
ports:
- "8080:80"
6.3 各設定項目の詳細解説
version(バージョン)
Composeファイルのフォーマットバージョンを指定します。
version: '3.8' # 推奨
バージョンによる違い:
| バージョン | リリース | 主な機能 |
|---|---|---|
| 3.0 | 2016 | Swarmモード対応 |
| 3.8 | 2020 | 最新機能をサポート |
注意: 基本的に 3.8 を使用すればOKです。
services(サービス)
コンテナの定義です。各サービスが1つのコンテナに対応します。
基本例:
services:
web:
image: nginx:latest
container_name: my-nginx
db:
image: postgres:15
container_name: my-postgres
image(イメージ)
使用するDockerイメージを指定します。
services:
web:
# 公式イメージ
image: nginx
app:
# タグ指定
image: node:18-alpine
custom:
# プライベートレジストリ
image: myregistry.com/myapp:1.0.0
build(ビルド)
Dockerfileからイメージをビルドする場合に使用します。
基本形:
services:
web:
build: . # カレントディレクトリのDockerfileを使用
詳細指定:
services:
web:
build:
context: . # ビルドコンテキスト
dockerfile: Dockerfile.dev # 使用するDockerfile
args: # ビルド引数
NODE_VERSION: 18
BUILD_ENV: development
target: development # マルチステージビルドのターゲット
imageと併用:
services:
web:
build: .
image: myapp:latest # ビルドしたイメージに名前をつける
ports(ポートマッピング)
ホストとコンテナのポートをマッピングします。
services:
web:
ports:
# 基本形
- "8080:80"
# 複数のポート
- "8080:80"
- "8443:443"
# IPアドレス指定
- "127.0.0.1:8080:80"
# UDPプロトコル
- "53:53/udp"
# 範囲指定
- "3000-3005:3000-3005"
短縮形と長形式:
services:
web:
# 短縮形
ports:
- "8080:80"
# 長形式(より詳細な制御)
ports:
- target: 80 # コンテナポート
published: 8080 # ホストポート
protocol: tcp
mode: host
volumes(ボリューム)
データの永続化やホストとの共有を設定します。
services:
db:
volumes:
# 名前付きボリューム
- pgdata:/var/lib/postgresql/data
# バインドマウント(相対パス)
- ./data:/data
# バインドマウント(絶対パス)
- /host/path:/container/path
# 読み取り専用
- ./config:/etc/config:ro
# 一時ボリューム
- /tmp
# 名前付きボリュームの定義
volumes:
pgdata:
長形式:
services:
web:
volumes:
- type: volume
source: mydata # ボリューム名
target: /data # コンテナ内パス
- type: bind
source: ./app # ホストパス
target: /app # コンテナ内パス
read_only: true
environment(環境変数)
コンテナ内の環境変数を設定します。
services:
web:
environment:
# キー=値の形式
NODE_ENV: production
API_KEY: your-api-key
DEBUG: "true"
PORT: 3000
配列形式:
services:
web:
environment:
- NODE_ENV=production
- API_KEY=your-api-key
.envファイルから読み込む:
services:
web:
env_file:
- .env
- .env.local
.env ファイルの例:
NODE_ENV=production
DATABASE_URL=postgresql://user:pass@db:5432/mydb
API_KEY=secret-key
注意: .envファイルは.gitignoreに追加してバージョン管理から除外しましょう。
depends_on(依存関係)
サービスの起動順序を制御します。
services:
web:
image: myapp:latest
depends_on:
- db
- redis
db:
image: postgres:15
redis:
image: redis:alpine
起動順序:
- db と redis が起動
- その後、web が起動
注意: depends_onは起動順序のみを制御します。DBが準備完了になるまで待つわけではありません。
準備完了を待つ方法:
services:
web:
image: myapp:latest
depends_on:
db:
condition: service_healthy
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
command(コマンド)
コンテナ起動時のコマンドを上書きします。
services:
web:
image: node:18
command: npm run dev
db:
image: postgres:15
command: postgres -c max_connections=200
配列形式:
services:
web:
image: node:18
command: ["npm", "run", "dev"]
restart(再起動ポリシー)
コンテナが停止した時の動作を指定します。
services:
web:
image: nginx
restart: always
オプション:
| 値 | 説明 |
|---|---|
no |
再起動しない(デフォルト) |
always |
常に再起動 |
on-failure |
エラー終了時のみ再起動 |
unless-stopped |
手動停止以外は再起動 |
推奨:
- 開発環境:
noまたはon-failure - 本番環境:
alwaysまたはunless-stopped
networks(ネットワーク)
サービスが接続するネットワークを指定します。
services:
web:
image: nginx
networks:
- frontend
api:
image: myapi:latest
networks:
- frontend
- backend
db:
image: postgres:15
networks:
- backend
networks:
frontend:
backend:
詳細設定:
networks:
frontend:
driver: bridge
backend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
container_name(コンテナ名)
コンテナに名前をつけます。
services:
web:
image: nginx
container_name: my-nginx-server
注意: docker-compose up --scaleで複数起動する場合は、container_nameを指定しないでください。
healthcheck(ヘルスチェック)
コンテナの健全性をチェックします。
services:
web:
image: myapp:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s # チェック間隔
timeout: 10s # タイムアウト
retries: 3 # 失敗時のリトライ回数
start_period: 40s # 起動猶予期間
テストコマンドの例:
# HTTP チェック
test: ["CMD", "curl", "-f", "http://localhost/health"]
# TCP チェック
test: ["CMD-SHELL", "nc -z localhost 3000"]
# PostgreSQL
test: ["CMD-SHELL", "pg_isready -U postgres"]
# MySQL
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
labels(ラベル)
メタデータをコンテナに付与します。
services:
web:
image: nginx
labels:
com.example.description: "My web server"
com.example.version: "1.0.0"
com.example.team: "backend-team"
6.4 実践:WebアプリとDBを連携させる
完全な実例を見ていきます。
ディレクトリ構成:
myproject/
├── docker-compose.yml
├── .env
├── backend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/
│ └── server.js
└── db/
└── init.sql
docker-compose.yml:
version: '3.8'
services:
# データベース
db:
image: postgres:15-alpine
container_name: myapp-db
restart: unless-stopped
environment:
POSTGRES_USER: ${DB_USER:-postgres}
POSTGRES_PASSWORD: ${DB_PASSWORD:-dbpass}
POSTGRES_DB: ${DB_NAME:-myapp}
volumes:
# データの永続化
- postgres-data:/var/lib/postgresql/data
# 初期化スクリプト
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# バックエンドAPI
api:
build:
context: ./backend
dockerfile: Dockerfile
args:
NODE_VERSION: 18
container_name: myapp-api
restart: unless-stopped
ports:
- "${API_PORT:-3000}:3000"
environment:
NODE_ENV: ${NODE_ENV:-development}
DATABASE_URL: postgresql://${DB_USER:-postgres}:${DB_PASSWORD:-dbpass}@db:5432/${DB_NAME:-myapp}
PORT: 3000
volumes:
# 開発時はソースコードをマウント
- ./backend/src:/app/src
depends_on:
db:
condition: service_healthy
networks:
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Nginx(リバースプロキシ)
nginx:
image: nginx:alpine
container_name: myapp-nginx
restart: unless-stopped
ports:
- "${NGINX_PORT:-80}:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api
networks:
- backend
networks:
backend:
driver: bridge
volumes:
postgres-data:
driver: local
.env ファイル:
# データベース設定
DB_USER=myuser
DB_PASSWORD=mypassword
DB_NAME=myapp_db
# アプリケーション設定
NODE_ENV=development
API_PORT=3000
NGINX_PORT=8080
backend/Dockerfile:
FROM node:18-alpine
WORKDIR /app
# 依存関係のインストール
COPY package*.json ./
RUN npm ci
# 開発用ツール
RUN npm install -g nodemon
# アプリケーションコード
COPY . .
EXPOSE 3000
# 開発モード
CMD ["nodemon", "--watch", "src", "src/server.js"]
db/init.sql:
-- 初期化スクリプト
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- サンプルデータ
INSERT INTO users (username, email) VALUES
('alice', 'alice@example.com'),
('bob', 'bob@example.com')
ON CONFLICT DO NOTHING;
nginx/nginx.conf:
events {
worker_connections 1024;
}
http {
upstream api {
server api:3000;
}
server {
listen 80;
location / {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
6.5 Docker Composeの基本コマンド
起動・停止
# バックグラウンドで起動
docker-compose up -d
# フォアグラウンドで起動(ログを表示)
docker-compose up
# 特定のサービスのみ起動
docker-compose up -d db
# ビルドしてから起動
docker-compose up -d --build
# 停止
docker-compose stop
# 停止して削除
docker-compose down
# ボリュームも削除
docker-compose down -v
ログ確認
# すべてのサービスのログ
docker-compose logs
# リアルタイムでログを表示
docker-compose logs -f
# 特定のサービスのログ
docker-compose logs -f api
# 最新100行のみ
docker-compose logs --tail=100
サービス管理
# サービス一覧
docker-compose ps
# 詳細情報
docker-compose ps -a
# 実行中のプロセス
docker-compose top
# サービスの再起動
docker-compose restart api
# サービスの停止
docker-compose stop api
# サービスの開始
docker-compose start api
コンテナ内でコマンド実行
# コンテナ内でコマンド実行
docker-compose exec api npm install lodash
# コンテナ内に入る
docker-compose exec api sh
# 新しいコンテナでコマンド実行
docker-compose run api npm test
# 実行後に削除
docker-compose run --rm api npm test
ビルド
# イメージをビルド
docker-compose build
# キャッシュを使わずビルド
docker-compose build --no-cache
# 特定のサービスのみビルド
docker-compose build api
スケーリング
# サービスを複数起動
docker-compose up -d --scale api=3
# 確認
docker-compose ps
注意: container_nameを指定している場合はスケーリングできません。
設定の検証
# 設定ファイルの検証
docker-compose config
# 設定の表示(環境変数を展開)
docker-compose config --services
# 使用するボリュームを表示
docker-compose config --volumes
6.6 環境別の設定管理
開発環境と本番環境で設定を切り替える方法です。
ディレクトリ構成:
myproject/
├── docker-compose.yml # 共通設定
├── docker-compose.dev.yml # 開発環境用
├── docker-compose.prod.yml # 本番環境用
├── .env.dev # 開発環境変数
└── .env.prod # 本番環境変数
docker-compose.yml(ベース):
version: '3.8'
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
api:
build: ./backend
environment:
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
docker-compose.dev.yml(開発環境の上書き):
version: '3.8'
services:
db:
ports:
- "5432:5432" # 開発時はDBを公開
volumes:
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
api:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./backend/src:/app/src # ホットリロード
environment:
NODE_ENV: development
DEBUG: "true"
command: npm run dev
docker-compose.prod.yml(本番環境の上書き):
version: '3.8'
services:
db:
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
api:
build:
context: ./backend
dockerfile: Dockerfile.prod
restart: always
environment:
NODE_ENV: production
command: npm start
nginx:
image: nginx:alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
volumes:
postgres-data:
使用方法:
# 開発環境で起動
docker-compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.dev up -d
# 本番環境で起動
docker-compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d
# エイリアスを作成すると便利
alias dc-dev='docker-compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.dev'
alias dc-prod='docker-compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod'
# 使用例
dc-dev up -d
dc-prod up -d
第7章:実践的な開発環境構築
7.1 典型的な構成パターン
パターン1:フロントエンド + バックエンド + DB
構成: React + Node.js (Express) + PostgreSQL
ディレクトリ構成:
fullstack-app/
├── docker-compose.yml
├── .env
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/
├── backend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/
└── db/
└── init.sql
docker-compose.yml:
version: '3.8'
services:
# PostgreSQL データベース
db:
image: postgres:15-alpine
container_name: fullstack-db
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
# Node.js バックエンドAPI
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: fullstack-api
ports:
- "3001:3001"
environment:
NODE_ENV: development
PORT: 3001
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
CORS_ORIGIN: http://localhost:3000
volumes:
- ./backend/src:/app/src
- /app/node_modules # ホスト側のnode_modulesをマウントしない
depends_on:
db:
condition: service_healthy
networks:
- backend
- frontend
# React フロントエンド
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: fullstack-web
ports:
- "3000:3000"
environment:
REACT_APP_API_URL: http://localhost:3001
volumes:
- ./frontend/src:/app/src
- /app/node_modules
depends_on:
- backend
networks:
- frontend
stdin_open: true
tty: true
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
postgres-data:
backend/Dockerfile:
FROM node:18-alpine
WORKDIR /app
# 依存関係のインストール
COPY package*.json ./
RUN npm install
# nodemonをインストール(開発用)
RUN npm install -g nodemon
# アプリケーションコード
COPY . .
EXPOSE 3001
CMD ["nodemon", "src/server.js"]
frontend/Dockerfile:
FROM node:18-alpine
WORKDIR /app
# 依存関係のインストール
COPY package*.json ./
RUN npm install
# アプリケーションコード
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
起動:
# すべてのサービスを起動
docker-compose up -d
# ログを確認
docker-compose logs -f
# 確認
# フロントエンド: http://localhost:3000
# バックエンドAPI: http://localhost:3001
パターン2:マイクロサービス構成
構成: API Gateway + 複数のマイクロサービス + Redis + DB
version: '3.8'
services:
# Redis(キャッシュ・セッション管理)
redis:
image: redis:alpine
container_name: microservices-redis
networks:
- backend
# PostgreSQL
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: adminpass
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- backend
# MongoDB
mongodb:
image: mongo:6
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: adminpass
volumes:
- mongo-data:/data/db
networks:
- backend
# ユーザーサービス
user-service:
build: ./services/user-service
environment:
DATABASE_URL: postgresql://admin:adminpass@postgres:5432/users
REDIS_URL: redis://redis:6379
depends_on:
- postgres
- redis
networks:
- backend
# 商品サービス
product-service:
build: ./services/product-service
environment:
MONGO_URL: mongodb://admin:adminpass@mongodb:27017/products
REDIS_URL: redis://redis:6379
depends_on:
- mongodb
- redis
networks:
- backend
# 注文サービス
order-service:
build: ./services/order-service
environment:
DATABASE_URL: postgresql://admin:adminpass@postgres:5432/orders
USER_SERVICE_URL: http://user-service:3000
PRODUCT_SERVICE_URL: http://product-service:3000
depends_on:
- postgres
- user-service
- product-service
networks:
- backend
# API Gateway
api-gateway:
build: ./api-gateway
ports:
- "8080:8080"
environment:
USER_SERVICE_URL: http://user-service:3000
PRODUCT_SERVICE_URL: http://product-service:3000
ORDER_SERVICE_URL: http://order-service:3000
depends_on:
- user-service
- product-service
- order-service
networks:
- backend
networks:
backend:
volumes:
postgres-data:
mongo-data:
7.2 環境変数の管理(.envファイル)
.env(開発環境):
# データベース
DB_USER=devuser
DB_PASSWORD=devpass
DB_NAME=myapp_dev
# アプリケーション
NODE_ENV=development
API_PORT=3001
WEB_PORT=3000
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
# JWT
JWT_SECRET=dev-secret-key
JWT_EXPIRY=1h
# 外部API
STRIPE_API_KEY=sk_test_xxxxx
SENDGRID_API_KEY=SG.xxxxx
.env.example(テンプレート):
# データベース
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_NAME=your_db_name
# アプリケーション
NODE_ENV=development
API_PORT=3001
WEB_PORT=3000
# 外部API(本番環境の値に置き換える)
STRIPE_API_KEY=your_stripe_key
SENDGRID_API_KEY=your_sendgrid_key
セキュリティのベストプラクティス:
-
.envファイルを.gitignoreに追加
.env
.env.local
.env.*.local
-
.env.exampleをバージョン管理
# 他の開発者が使用する際
cp .env.example .env
# .envファイルを編集して実際の値を設定
- 機密情報は別管理
# docker-compose.yml
services:
api:
environment:
# 公開情報は.envから
NODE_ENV: ${NODE_ENV}
PORT: ${PORT}
# 機密情報はsecretsで管理(Docker Swarm/Kubernetes)
secrets:
- db_password
- api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
7.3 開発環境と本番環境の切り替え
マルチステージビルドを活用:
Dockerfile:
# ベースステージ
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
# 開発環境
FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
# ビルドステージ
FROM base AS builder
RUN npm ci --only=production
COPY . .
RUN npm run build
# 本番環境
FROM node:18-alpine AS production
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER node
CMD ["node", "dist/server.js"]
docker-compose.dev.yml:
version: '3.8'
services:
api:
build:
context: ./backend
target: development
volumes:
- ./backend/src:/app/src
environment:
NODE_ENV: development
DEBUG: "api:*"
docker-compose.prod.yml:
version: '3.8'
services:
api:
build:
context: ./backend
target: production
restart: always
environment:
NODE_ENV: production
7.4 ホットリロード(コード変更の自動反映)設定
Node.js(nodemon)
package.json:
{
"scripts": {
"dev": "nodemon --watch src src/server.js",
"start": "node src/server.js"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
docker-compose.yml:
services:
api:
build: ./backend
volumes:
- ./backend/src:/app/src
- /app/node_modules # node_modulesは除外
command: npm run dev
nodemon.json(詳細設定):
{
"watch": ["src"],
"ext": "js,json",
"ignore": ["src/**/*.test.js"],
"delay": 1000
}
React(Create React App)
Reactは標準でホットリロードが有効です。
docker-compose.yml:
services:
frontend:
build: ./frontend
volumes:
- ./frontend/src:/app/src
- ./frontend/public:/app/public
- /app/node_modules
environment:
CHOKIDAR_USEPOLLING: "true" # Windowsの場合必要
stdin_open: true
tty: true
注意(Windows): ファイル変更の検知にCHOKIDAR_USEPOLLING=trueが必要な場合があります。
Python(Flask)
docker-compose.yml:
services:
api:
build: ./backend
volumes:
- ./backend/app:/app
environment:
FLASK_ENV: development
FLASK_DEBUG: 1
command: flask run --host=0.0.0.0 --reload
または、watchdogを使用:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
RUN pip install watchdog
COPY . .
CMD ["python", "-m", "flask", "run", "--host=0.0.0.0", "--reload"]
第8章:デバッグとトラブルシューティング
8.1 よくあるエラーと対処法
エラー1:ポートが既に使用されている
エラーメッセージ:
Error starting userland proxy: listen tcp 0.0.0.0:3000: bind: address already in use
原因: ホストの3000番ポートが既に使用されている
対処法:
# Windows: ポートを使用しているプロセスを確認
netstat -ano | findstr :3000
# プロセスを終了
taskkill /PID <プロセスID> /F
# または、docker-compose.ymlでポート番号を変更
ports:
- "3001:3000" # ホスト側を3001に変更
エラー2:ボリュームのパーミッションエラー
エラーメッセージ:
Permission denied: '/var/lib/postgresql/data'
原因: コンテナ内のユーザーとボリュームの権限が合わない
対処法:
# Dockerfileでユーザーを明示
FROM postgres:15-alpine
# または
RUN chown -R postgres:postgres /var/lib/postgresql/data
# docker-compose.ymlでユーザーを指定
services:
db:
image: postgres:15-alpine
user: "1000:1000" # ホストのUIDに合わせる
エラー3:イメージのビルドに失敗
エラーメッセージ:
failed to solve with frontend dockerfile.v0: failed to build LLB
原因: Dockerfileの構文エラーまたはビルドコンテキストの問題
対処法:
# キャッシュを使わずにビルド
docker-compose build --no-cache
# ビルドログを詳細表示
docker-compose build --progress=plain api
# .dockerignoreを確認
# 必要なファイルが除外されていないか確認
エラー4:コンテナが immediately exits
エラーメッセージ:
Exited (0) 1 second ago
原因: CMDで指定したプロセスが即座に終了している
対処法:
# ログを確認
docker-compose logs api
# コンテナを対話モードで起動
docker-compose run api sh
# または、Dockerfileを修正
CMD ["tail", "-f", "/dev/null"] # デバッグ用に無限待機
エラー5:ネットワーク接続エラー
エラーメッセージ:
Error: connect ECONNREFUSED 172.18.0.2:5432
原因: サービス名の解決失敗またはサービスの未起動
対処法:
# サービスの状態を確認
docker-compose ps
# ネットワークを確認
docker network ls
docker network inspect <ネットワーク名>
# depends_onを追加
services:
api:
depends_on:
db:
condition: service_healthy
エラー6:環境変数が読み込まれない
原因: .envファイルのパスが正しくない
対処法:
# .envファイルの場所を確認
ls -la .env
# 明示的に指定
docker-compose --env-file .env up
# docker-compose.ymlで確認
docker-compose config
8.2 ログの確認方法
基本的なログ確認
# すべてのサービスのログ
docker-compose logs
# 特定のサービス
docker-compose logs api
# リアルタイムで追跡
docker-compose logs -f api
# 最新100行
docker-compose logs --tail=100 api
# タイムスタンプ付き
docker-compose logs -t api
# 複数サービス
docker-compose logs api db
ログのフィルタリング
# Windowsの場合
docker-compose logs api | findstr "ERROR"
# 特定の時間以降
docker-compose logs --since 2023-11-15T10:00:00
# 特定の時間まで
docker-compose logs --until 2023-11-15T11:00:00
ログドライバーの設定
docker-compose.yml:
services:
api:
image: myapi:latest
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
ログドライバーの種類:
| ドライバー | 説明 |
|---|---|
| json-file | デフォルト、JSON形式でファイルに保存 |
| syslog | Syslogサーバーに送信 |
| journald | systemd journalに送信 |
| none | ログを保存しない |
8.3 コンテナの状態確認
実行状態の確認
# コンテナ一覧
docker-compose ps
# 詳細情報
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# リソース使用状況
docker stats
# 特定のコンテナのリソース
docker stats <コンテナ名>
ヘルスチェックの確認
# コンテナの詳細情報
docker inspect <コンテナ名>
# ヘルスチェック結果のみ
docker inspect --format='{{json .State.Health}}' <コンテナ名>
プロセスの確認
# コンテナ内のプロセス
docker-compose top
# 特定のサービス
docker-compose top api
8.4 イメージ・コンテナの削除とクリーンアップ
基本的な削除
# 停止中のコンテナを削除
docker-compose down
# ボリュームも削除
docker-compose down -v
# イメージも削除
docker-compose down --rmi all
# すべて削除(ボリューム、イメージ、ネットワーク)
docker-compose down -v --rmi all --remove-orphans
個別削除
# 停止中のコンテナをすべて削除
docker container prune
# 未使用のイメージを削除
docker image prune
# 未使用のボリュームを削除
docker volume prune
# 未使用のネットワークを削除
docker network prune
# すべての未使用リソースを削除
docker system prune
# イメージも含めて削除
docker system prune -a
# ボリュームも含めて削除
docker system prune -a --volumes
ディスク使用量の確認
# Docker全体の使用量
docker system df
# 詳細表示
docker system df -v
特定のパターンで削除
# 名前パターンで削除(Windows PowerShell)
docker ps -a --filter "name=myapp" -q | ForEach-Object { docker rm $_ }
# 終了コードでフィルタ
docker ps -a --filter "exited=1" -q | ForEach-Object { docker rm $_ }
# 特定のイメージから作成されたコンテナを削除
docker ps -a --filter "ancestor=node:18" -q | ForEach-Object { docker rm $_ }
8.5 デバッグツールとテクニック
コンテナ内でのデバッグ
# コンテナに入る
docker-compose exec api sh
# 一時的にツールをインストール
apk add curl vim htop
# ネットワーク確認
ping db
nslookup db
curl http://db:5432
# ファイルシステム確認
ls -la /app
cat /app/config.json
# プロセス確認
ps aux
top
# 環境変数確認
env
echo $DATABASE_URL
ログレベルの調整
services:
api:
environment:
LOG_LEVEL: debug
DEBUG: "*" # Node.jsのdebugパッケージ
Visual Studio Codeでのデバッグ
launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Docker: Attach to Node",
"remoteRoot": "/app",
"localRoot": "${workspaceFolder}/backend",
"port": 9229,
"restart": true
}
]
}
docker-compose.yml:
services:
api:
build: ./backend
command: node --inspect=0.0.0.0:9229 src/server.js
ports:
- "3000:3000"
- "9229:9229" # デバッグポート
第9章:デプロイの基礎
9.1 イメージのビルドとタグ付け
バージョン管理のベストプラクティス
# セマンティックバージョニング
docker build -t myapp:1.0.0 .
docker build -t myapp:1.0 .
docker build -t myapp:1 .
docker build -t myapp:latest .
# Git コミットハッシュを使用(PowerShell/Git Bash)
GIT_HASH=$(git rev-parse --short HEAD)
docker build -t myapp:${GIT_HASH} .
# Windowsコマンドプロンプトの場合
# for /f %i in ('git rev-parse --short HEAD') do set GIT_HASH=%i
# docker build -t myapp:%GIT_HASH% .
# ビルド日付を含める
BUILD_DATE=$(date +%Y%m%d)
docker build -t myapp:${BUILD_DATE} .
マルチアーキテクチャビルド
# buildxを使用(ARM、x86対応)
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:1.0.0 .
9.2 Docker Hubへのプッシュ
Docker Hubアカウントの準備
# ログイン
docker login
# ユーザー名を確認
docker info | findstr Username
イメージのプッシュ
# イメージにタグ付け(ユーザー名/イメージ名:タグ)
docker tag myapp:1.0.0 yourusername/myapp:1.0.0
docker tag myapp:1.0.0 yourusername/myapp:latest
# プッシュ
docker push yourusername/myapp:1.0.0
docker push yourusername/myapp:latest
プライベートレジストリの使用
# docker-compose.yml
services:
api:
image: registry.example.com/myapp:1.0.0
# プライベートレジストリにログイン
docker login registry.example.com
# プッシュ
docker push registry.example.com/myapp:1.0.0
9.3 本番環境での起動
本番用docker-compose.yml
version: '3.8'
services:
db:
image: postgres:15-alpine
restart: always
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- backend
secrets:
- db_password
api:
image: yourusername/myapp:1.0.0
restart: always
environment:
NODE_ENV: production
DATABASE_URL_FILE: /run/secrets/db_url
depends_on:
- db
networks:
- backend
- frontend
secrets:
- db_url
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
nginx:
image: nginx:alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- api
networks:
- frontend
networks:
frontend:
backend:
volumes:
postgres-data:
secrets:
db_password:
file: ./secrets/db_password.txt
db_url:
file: ./secrets/db_url.txt
起動スクリプト
deploy.sh:
#!/bin/bash
# 環境変数の読み込み
export $(cat .env.prod | xargs)
# 最新イメージを取得
docker-compose -f docker-compose.prod.yml pull
# コンテナを再起動(ダウンタイムなし)
docker-compose -f docker-compose.prod.yml up -d --no-deps --build api
# 古いイメージを削除
docker image prune -f
# ヘルスチェック
sleep 10
curl -f http://localhost/health || exit 1
echo "デプロイ完了"
9.4 セキュリティの基本的な考え方
1. 最小権限の原則
# rootユーザーを避ける
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
COPY --chown=nodejs:nodejs . .
USER nodejs
CMD ["node", "server.js"]
2. 機密情報の管理
# secretsを使用
services:
api:
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
または環境変数(本番環境):
# サーバーの環境変数として設定
export DATABASE_PASSWORD="secure-password"
# docker-composeで参照
services:
api:
environment:
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
3. イメージの脆弱性スキャン
# Docker Desktopの機能を使用
docker scan myapp:latest
# または、Trivyを使用
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image myapp:latest
4. ネットワークの分離
networks:
# 外部公開
frontend:
# 内部専用
backend:
internal: true
5. 読み取り専用ファイルシステム
services:
api:
read_only: true
tmpfs:
- /tmp
- /var/run
6. リソース制限
services:
api:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
付録
A. よく使うコマンドチートシート
Docker基本コマンド
| コマンド | 説明 |
|---|---|
docker --version |
Dockerのバージョン確認 |
docker info |
Docker環境の詳細情報 |
docker login |
Docker Hubにログイン |
イメージ操作
| コマンド | 説明 |
|---|---|
docker images |
イメージ一覧 |
docker pull <イメージ> |
イメージのダウンロード |
docker build -t <名前> . |
イメージのビルド |
docker rmi <イメージ> |
イメージの削除 |
docker image prune |
未使用イメージの削除 |
コンテナ操作
| コマンド | 説明 |
|---|---|
docker ps |
実行中のコンテナ一覧 |
docker ps -a |
すべてのコンテナ一覧 |
docker run -d <イメージ> |
コンテナをバックグラウンド起動 |
docker run -it <イメージ> |
コンテナを対話モードで起動 |
docker stop <コンテナ> |
コンテナの停止 |
docker start <コンテナ> |
コンテナの開始 |
docker restart <コンテナ> |
コンテナの再起動 |
docker rm <コンテナ> |
コンテナの削除 |
docker exec -it <コンテナ> bash |
実行中のコンテナに入る |
docker logs -f <コンテナ> |
ログをリアルタイム表示 |
Docker Composeコマンド
| コマンド | 説明 |
|---|---|
docker-compose up -d |
サービスをバックグラウンド起動 |
docker-compose down |
サービスを停止・削除 |
docker-compose ps |
サービスの状態確認 |
docker-compose logs -f |
ログをリアルタイム表示 |
docker-compose exec <サービス> sh |
サービスのコンテナに入る |
docker-compose build |
イメージをビルド |
docker-compose restart <サービス> |
サービスの再起動 |
クリーンアップコマンド
| コマンド | 説明 |
|---|---|
docker system prune |
未使用リソースの削除 |
docker container prune |
停止中のコンテナ削除 |
docker image prune |
未使用イメージの削除 |
docker volume prune |
未使用ボリュームの削除 |
docker network prune |
未使用ネットワークの削除 |
B. docker-compose.yml サンプル集
1. WordPress環境
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpresspass
volumes:
- db-data:/var/lib/mysql
wordpress:
image: wordpress:latest
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpresspass
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-data:/var/www/html
depends_on:
- db
volumes:
db-data:
wp-data:
2. MERN Stack(MongoDB + Express + React + Node.js)
version: '3.8'
services:
mongodb:
image: mongo:6
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: adminpass
volumes:
- mongo-data:/data/db
networks:
- mern
backend:
build: ./backend
ports:
- "5000:5000"
environment:
MONGO_URI: mongodb://admin:adminpass@mongodb:27017/myapp?authSource=admin
PORT: 5000
depends_on:
- mongodb
networks:
- mern
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
REACT_APP_API_URL: http://localhost:5000
depends_on:
- backend
networks:
- mern
networks:
mern:
volumes:
mongo-data:
3. 開発環境(Python + PostgreSQL + Redis)
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpass
POSTGRES_DB: devdb
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
networks:
- backend
redis:
image: redis:alpine
ports:
- "6379:6379"
networks:
- backend
api:
build:
context: ./api
dockerfile: Dockerfile.dev
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://devuser:devpass@postgres:5432/devdb
REDIS_URL: redis://redis:6379
FLASK_ENV: development
volumes:
- ./api:/app
depends_on:
- postgres
- redis
networks:
- backend
command: flask run --host=0.0.0.0 --reload
networks:
backend:
volumes:
postgres-data:
C. さらに学ぶためのリソース
公式ドキュメント
学習サイト
- Docker公式チュートリアル
- Play with Docker(ブラウザでDocker実習)
ベストプラクティス
コミュニティ
おわりに
本資料では、Dockerの基礎から実践的な使い方まで解説しました。
学習のステップ:
- ✅ Dockerの基本概念を理解する
- ✅ 基本コマンドを使いこなす
- ✅ Dockerfileを書いて理解する
- ✅ Docker Composeで複数コンテナを管理する
- ✅ 開発環境を構築する
- ✅ トラブルシューティングができる
- ✅ デプロイの基礎を理解する
AWS コンテナサービスへの次のステップ
本資料で学んだDockerの知識は、AWSのコンテナサービスでそのまま活用できます。
Amazon ECR(Elastic Container Registry)
概要:
- Dockerイメージを保存するプライベートレジストリ
- Docker Hubと同じ役割
- AWS環境との統合が簡単
本資料との対応:
- 第9章「Docker Hubへのプッシュ」で学んだ内容がそのまま使える
-
docker pushコマンドの送信先がECRになるだけ
基本的な使い方:
# ECRにログイン
aws ecr get-login-password --region ap-northeast-1 | \
docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
# イメージにタグ付け
docker tag myapp:1.0.0 <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:1.0.0
# ECRにプッシュ
docker push <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:1.0.0
Amazon ECS(Elastic Container Service)
概要:
- Dockerコンテナを本番環境で動かすサービス
- サーバー管理不要のFargateモードが便利
- オートスケーリング、ロードバランサー統合
本資料との対応:
| 本資料で学んだこと | ECSでの対応 |
|---|---|
| Dockerfile | そのまま使用 |
| docker-compose.yml | ECSタスク定義に変換 |
| ポートマッピング | タスク定義のportMappings |
| 環境変数 | タスク定義のenvironment |
| ボリューム | EFSマウント、タスクストレージ |
| ネットワーク | VPC、セキュリティグループ |
ECSタスク定義の例:
{
"family": "myapp",
"containerDefinitions": [
{
"name": "web",
"image": "<ECRのイメージURL>",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NODE_ENV",
"value": "production"
}
]
}
]
}
学習の進め方
ステップ1:ECRの基本
- ✅ 本資料でDockerfileとイメージビルドを習得
- AWS CLIをインストール
- ECRリポジトリを作成
- ローカルでビルドしたイメージをECRにプッシュ
ステップ2:ECS Fargateでデプロイ
- ✅ 本資料でDocker Composeを習得
- ECSクラスタを作成(Fargateモード)
- タスク定義を作成(docker-compose.ymlを参考に)
- サービスを起動してアプリを公開
ステップ3:実践的な構成
- ALB(ロードバランサー)との統合
- RDS(データベース)との接続
- Auto Scalingの設定
- CI/CDパイプラインの構築
推奨リソース
公式ドキュメント:
ハンズオン:
ツール:
- ecs-cli - Docker ComposeからECSへの変換ツール
- AWS Copilot - ECS/Fargateの簡易デプロイツール
Docker → ECS移行のポイント
1. docker-compose.yml → ECSタスク定義の変換
本資料の第6章で学んだDocker Composeの知識がそのまま活かせます:
| docker-compose.yml | ECSタスク定義 |
|---|---|
services: |
containerDefinitions: |
image: |
image: |
ports: |
portMappings: |
environment: |
environment: |
volumes: |
mountPoints: + volumes:
|
depends_on: |
コンテナの起動順序 |
2. 環境変数の管理
{
"environment": [
{"name": "NODE_ENV", "value": "production"}
],
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:..."
}
]
}
3. ログの管理
{
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
最後に
継続的に実践しながら学習を進めることが、Docker習得の近道です。
実践のステップ:
- 本資料の例を実際に動かす
- 自分のプロジェクトでDockerを使ってみる
- チームでDocker環境を共有する
- AWS ECS/ECRで本番デプロイに挑戦
実際のプロジェクトでDockerを使いながら、この資料を参照してください。
対象者: Docker初心者〜中級者
目標レベル: 実務で困らないDocker力
次のステップ: AWS ECS/ECRでのコンテナ運用