Developer Tools

開発者のためのDocker入門:コンテナ、Compose、実践的なワークフロー

Dockerの概念、Dockerfileのベストプラクティス、Docker Composeを使ったマルチサービス開発環境の構築に関する実践的なガイドです。

9分で読めます

青い照明のサーバールーム

「自分のマシンでは動くのに」という言葉は、数えきれないほどのデプロイ失敗を生み出してきました。Dockerはアプリケーションとそのすべての依存関係をコンテナにパッケージ化することでこの問題を解決します。コンテナとは、ラップトップから本番環境まで、どこでも同じように動作する隔離された再現可能な環境です。

コンテナと仮想マシンの違い

仮想マシン コンテナ
分離レベル OS全体 プロセスレベル
起動時間 数分 数秒
サイズ GB単位 MB単位
オーバーヘッド 高い(ハイパーバイザー) ほぼゼロ
ユースケース OS完全分離 アプリのパッケージ化

コンテナはホストのカーネルを共有しつつ、ファイルシステム・プロセス・ネットワークを分離します。これがコンテナが非常に軽量である理由です。

Dockerの基本概念

イメージ — コンテナの読み取り専用の設計図。クラス定義のようなものです。

コンテナ — イメージの実行中のインスタンス。クラスから生成されたオブジェクトのようなものです。

レジストリ — イメージの保存・配布システム。Docker Hubがデフォルトの公開レジストリで、GitHub Container RegistryやAWS ECRもよく使われます。

ボリューム — コンテナの再起動後も保持される永続ストレージ。ボリュームなしでコンテナ内に書き込まれたデータは、コンテナ停止時に失われます。

良いDockerfileの書き方

# 特定のバージョンを使用 — 本番環境では"latest"は避ける
FROM node:20-alpine

# 作業ディレクトリを設定
WORKDIR /app

# 依存ファイルを先にコピー(レイヤーキャッシュを活用)
COPY package.json package-lock.json ./
RUN npm ci --only=production

# アプリケーションコードをコピー
COPY . .

# アプリをビルド
RUN npm run build

# セキュリティのため非rootユーザーで実行
USER node

# ポートを明示(公開はしない)
EXPOSE 3000

# 適切なシグナル処理のためexec形式を使用
CMD ["node", "server.js"]

レイヤーキャッシュ:高速ビルドの鍵

Dockerfileの各命令はレイヤーを作成します。Dockerはレイヤーをキャッシュし、あるレイヤーやその前のレイヤーに変更がなければ、キャッシュを再利用します。

アプリケーションコードより先に依存ファイルをコピーしましょう。 依存関係はコードに比べてはるかに変更頻度が低いです。すべてを一度にコピーすると、index.jsの1行の変更で依存関係インストールのキャッシュが無効化され、npm installが毎回フルで実行されてしまいます。

マルチステージビルド

マルチステージビルドを使って本番イメージをスリムに保ちましょう:

# ステージ1:ビルド
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ステージ2:本番(開発依存なし、ソースコードなし)
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]

最終イメージにはビルド成果物のみが含まれ、ソースコード・テストファイル・開発依存関係は含まれません。

ローカル開発のためのDocker Compose

Docker Composeはマルチコンテナアプリケーションを orchestrate します。1つのdocker-compose.ymlでローカルスタック全体を定義できます:

version: "3.9"

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - .:/app
      - /app/node_modules

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

Docker Compose Generatorを使えば、スタックのひな形を簡単に生成できます。使用するサービスを選択するだけで、すぐに使えるdocker-compose.ymlが数秒で手に入ります。

Composeの主要コマンド

# すべてのサービスをバックグラウンドで起動
docker compose up -d

# ログを表示(フォローモード)
docker compose logs -f app

# 実行中のコンテナ内でコマンドを実行
docker compose exec app sh

# コンテナを停止・削除(ボリュームは保持)
docker compose down

# コンテナとボリュームを停止・削除
docker compose down -v

# Dockerfile変更後にイメージを再ビルド
docker compose up -d --build

環境変数とシークレット

DockerfileやComposeファイルに認証情報をハードコードしないでください。.envファイルを使いましょう:

# .env(.gitignoreに追加!)
DATABASE_URL=postgresql://user:pass@db:5432/myapp
REDIS_URL=redis://redis:6379
JWT_SECRET=your-super-secret-key

Composeはプロジェクトディレクトリから.envを自動的に読み込みます。YAML内では${変数名}の形式で変数を参照します。

Env Generatorを使うと、適切なデフォルト値を持つ.envファイルや、チーム共有用の.env.exampleテンプレートをすばやく生成できます。

Dockerのネットワーキング

Composeはスタック用のデフォルトネットワークを作成します。サービスはサービス名をホスト名として互いに通信できます。アプリがlocalhostではなくdbをデータベースホストとして使用するのはこのためです。

app → "db:5432" に接続
app → "redis:6379" に接続

ports:で明示的にマッピングされたポートのみが、ホストマシンからアクセス可能になります。

ヘルスチェックと起動順序

depends_onはコンテナが起動するまで待つだけで、準備完了になるまでは待ちません。データベースコンテナは数秒で起動しますが、Postgresが接続を受け付けるまでにさらに数秒かかることがあります。healthcheckcondition: service_healthyを組み合わせて、サービスが実際に準備完了になるまで待つようにしましょう。

本番環境での考慮事項

  1. 特定のイメージタグを使用するnode:latestではなくnode:20.11.1-alpineのように指定する。
  2. イメージの脆弱性をスキャンするdocker scout cves myapp:latest
  3. リソース制限を設定する — 1つのコンテナが他のリソースを奪わないようにする。
  4. 可能な限り読み取り専用ファイルシステムを使用する — Composeでread_only: trueを設定。
  5. rootとして実行しないUSER nodeや同等の設定を追加する。
  6. Nginxをリバースプロキシとして設定するNginx Config Generatorを使って実績のある設定を取得できます。

クイックリファレンス

# イメージ
docker images                    # ローカルイメージの一覧
docker pull nginx:alpine         # イメージのダウンロード
docker rmi myimage               # イメージの削除

# コンテナ
docker ps                        # 実行中のコンテナ
docker ps -a                     # すべてのコンテナ
docker stop <id>                 # グレースフルに停止
docker rm <id>                   # 停止済みコンテナを削除
docker logs <id> -f              # ログをストリーミング

# クリーンアップ
docker system prune              # 未使用のすべてを削除
docker volume prune              # 未使用のボリュームを削除

Dockerは環境起因のバグを根本から解消し、新しい開発者のオンボーディングを驚くほど簡単にします。ここで基本をマスターすれば、Kubernetes・CI/CDパイプライン・クラウドデプロイメントへと進む確かな基礎が身につきます。