•9 min read

ローカル開発のためのDockerCompose:完全なセットアップガイド

ローカル開発のためのDockerCompose:完全なセットアップガイド

最新のウェブアプリケーションが単一のサービスで構成されることは稀です。典型的な2026年のスタックには、Node.jsまたはPythonのAPI、PostgreSQLデータベース、Redisキャッシュ、CeleryまたはBullMQワーカー、そしておそらくMinIOのようなローカルのS3互換ストレージが含まれます。これらの依存関係を手動で管理する — 特定のPostgresバージョンをインストールし、ポートを設定し、ターミナルタブをやりくりする — ことは、環境のずれ(environment drift)や、おなじみの「私のマシンでは動くのに」という問題を引き起こす原因となります。

Docker Composeは、ローカルインフラ全体を単一の宣言型YAMLファイルとして表現することで、この問題を解決します。2026年には、Docker Compose v2がDocker DesktopおよびDocker Engineに同梱され、BuildKitとネイティブに統合され、GPUプロファイル、ヘルスチェック、シークレットをサポートします。これにより、ローカルワークフローにおいて本番環境に対応できる能力を備えています。

Audio Briefing
0:00 / 0:00
Interactive Dev Tool
100% Client-Side & Private

Docker Compose to Kubernetes Converter

Generate Deployments, Services, ConfigMaps & Ingress

Convert multi-service Docker Compose v2/v3 YAML into production-grade Kubernetes manifests with custom replicas, health probes, resource limits, and PVC storage.

K8s DeploymentsConfigMapsPVC StorageIngress YAML

なぜdocker compose upが本番環境をミラーリングすべきなのか

目標は単なる利便性だけではありません。本番環境をミラーリングするComposeファイルは、ステージングに到達する前に環境固有のバグを捕捉します。強制すべき具体的なパリティポイントは以下の通りです。

  • 同じPostgreSQLバージョン、同じエンコーディング(UTF8)、同じロケール(en_US.UTF-8)
  • 同じRedisバージョンとエビクションポリシー(セッションキャッシュにはvolatile-lru)
  • 同じ環境変数名(.envから取得し、ハードコードしない)
  • 同じネットワークトポロジー(サービスはlocalhostではなくサービス名で互いを検出する)
  • 同じユーザー権限(APIコンテナは非rootで実行され、K8sのrunAsNonRootをミラーリングする)
Advertisement

ステップ1: docker-compose.ymlファイル

以下は、Node.js APIとPostgreSQL、Redis、バックグラウンドワーカーのための、本番環境をミラーリングしたComposeファイルです。

# docker-compose.yml (Docker Compose v2 — no "version:" key needed)
name: myapp

services:
  api:
    build:
      context: ./api
      target: development
      cache_from:
        - type=local,src=/tmp/.buildx-cache
    ports:
      - "3000:3000"
    volumes:
      - ./api:/usr/src/app
      - node_modules:/usr/src/app/node_modules  # Prevent host node_modules leak
    env_file:
      - .env
    environment:
      NODE_ENV: development
      DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@db:5432/myappdb
      REDIS_URL: redis://redis:6379/0
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    command: npm run dev
    user: "1001:1001"   # Match your host UID to avoid root-owned volume files

  worker:
    build:
      context: ./api
      target: development
    volumes:
      - ./api:/usr/src/app
      - node_modules:/usr/src/app/node_modules
    env_file:
      - .env
    environment:
      NODE_ENV: development
      DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@db:5432/myappdb
      REDIS_URL: redis://redis:6379/0
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    command: npm run worker
    user: "1001:1001"

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: myappdb
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=en_US.UTF-8"
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/10-init.sql:ro
      - ./db/seed.sql:/docker-entrypoint-initdb.d/20-seed.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d myappdb"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 10s

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    ports:
      - "6379:6379"
    command: >
      redis-server
      --maxmemory 256mb
      --maxmemory-policy volatile-lru
      --save ""
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  minio:
    image: minio/minio:latest
    ports:
      - "9000:9000"
      - "9001:9001"   # MinIO Console UI
    environment:
      MINIO_ROOT_USER: ${MINIO_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
    command: server /data --console-address ":9001"
    volumes:
      - minio_data:/data
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 10s
      timeout: 5s
      retries: 3

volumes:
  pgdata:
  node_modules:
  minio_data:

condition: service_healthyを使ったdepends_onが重要な理由

素朴なdepends_on: [db]は、コンテナが起動するのを待つだけで、PostgreSQLが接続を受け入れるのを待ちません。APIの最初のデータベースクエリは「接続拒否」となり、起動時にクラッシュします。適切なhealthcheckを持つcondition: service_healthyを使用することで、APIが起動する前にPostgresが実際に準備できていることを保証します。

node_modulesボリュームのトリック

node_modules:/usr/src/app/node_modulesという名前付きボリュームは、ホストのnode_modules(macOSまたはWindows用にコンパイルされたもの)がコンテナのLinux用にコンパイルされたバイナリを上書きするのを防ぎます。これがないと、bcryptやsharpのようなネイティブアドオンがコンテナ内で失敗します。

ステップ2: BuildKitキャッシュマウントを使用したマルチステージDockerfile

BuildKitキャッシュマウントは、npmキャッシュをビルド間で永続化することで、リビルドを劇的に高速化します。

# api/Dockerfile
FROM node:20-alpine AS base
WORKDIR /usr/src/app
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser

# Development stage — includes devDependencies, live reload
FROM base AS development
COPY package*.json ./
# BuildKit cache mount: npm cache persists across `docker compose build` runs
RUN --mount=type=cache,target=/root/.npm \
    npm install
COPY . .
# CMD is overridden by docker-compose.yml

# Build stage — compiles TypeScript, runs tests
FROM base AS build
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci
COPY . .
RUN npm run build && npm run test:ci

# Production runtime — minimal image, no devDependencies
FROM node:20-alpine AS production
WORKDIR /usr/src/app
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci --omit=dev
COPY --from=build /usr/src/app/dist ./dist
USER appuser
EXPOSE 3000
CMD ["node", "dist/main.js"]

--mount=type=cacheディレクティブはBuildKitの機能です。最初のビルドで依存関係がダウンロードされ、その後のビルドではキャッシュレイヤーから読み取られるため、ほとんどの反復的な変更でビルド時間が数分から数秒に短縮されます。

ステップ3: 環境シークレット管理

認証情報をバージョン管理にコミットしてはいけません。コミットされた.env.exampleとともに.envを使用してください。

# .env.example — committed to git
POSTGRES_PASSWORD=change_me_in_env
MINIO_USER=minioadmin
MINIO_PASSWORD=change_me_in_env
REPORTER_SECRET=change_me_in_env
# .env — gitignored, copied from .env.example
POSTGRES_PASSWORD=dev_local_secret_32chars
MINIO_USER=minioadmin
MINIO_PASSWORD=dev_local_minio_secret
REPORTER_SECRET=dev_reporter_secret

ローカルテストでの本番環境のようなシークレットには、Docker Compose Secretsがより安全な代替手段を提供します。これは、環境変数としてではなく、ファイルとしてシークレットをマウントします。

secrets:
  db_password:
    file: ./secrets/db_password.txt

services:
  api:
    secrets:
      - db_password
    environment:
      DATABASE_PASSWORD_FILE: /run/secrets/db_password
Advertisement

ステップ4: 日常のワークフローコマンド

# Start the entire stack (foreground — see all logs)
docker compose up

# Start detached, tail only API logs
docker compose up -d && docker compose logs -f api

# Rebuild only the api image after Dockerfile changes
docker compose up --build api

# Run a one-off command inside the running api container
docker compose exec api npm run migrate:latest

# Run a new ephemeral container (doesn't affect the running api)
docker compose run --rm api npm run db:seed

# Stop containers, preserve volumes
docker compose down

# Nuclear reset: stop, remove containers, networks, AND volumes
docker compose down -v

# View resource usage
docker compose stats

ネットワークとサービスディスカバリ

Docker Composeは、name:の値がプレフィックスとして付加されたmyapp_defaultというプライベートネットワークを作成します。すべてのサービスはhttp://<service-name>:<internal-port>で到達可能です。

アクセス元ターゲットURL
apiコンテナPostgreSQLpostgres://app:…@db:5432/myappdb
apiコンテナRedisredis://redis:6379/0
apiコンテナMinIO APIhttp://minio:9000
あなたのブラウザAPIhttp://localhost:3000
あなたのブラウザMinIOコンソールhttp://localhost:9001

ポートマッピング(ports:)は、コンテナのポートをホストマシンに公開します。マッピングがない場合、サービスは他のコンテナからは到達可能ですが、ブラウザからは到達できません。これは内部サービスを隔離するのに役立ちます。

オプションサービスのためのプロファイル

Jaegerトレースコレクターのようなオプションサービスは、必要なときにのみ起動するようにプロファイルを使用します。

services:
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686"
      - "4317:4317"
    profiles: ["observability"]
# Start with optional observability stack
docker compose --profile observability up

# Start without (default)
docker compose up

結論

適切に作成されたdocker-compose.ymlは、環境のずれをなくし、オンボーディング時間を数時間から数分に短縮し、CIパイプラインにすべての開発者のマシンと同じ決定論的な環境を提供します。投資はセットアップに1時間ですが、その見返りはチーム全体で数ヶ月分のデバッグ時間の節約になります。

こちらもおすすめです

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement