T-CREATOR

Docker Compose v2 と k8s(skaffold/tilt)比較検証:ローカル開発どれが最速?

Docker Compose v2 と k8s(skaffold/tilt)比較検証:ローカル開発どれが最速?

コンテナベースの開発環境を構築する際、Docker Compose v2、Skaffold、Tilt のどれを選べば良いか迷っていませんか?

本番環境で Kubernetes を使う場合、ローカル開発環境も同じ構成にすべきか、それとも手軽な Docker Compose で十分なのか。この記事では、実際にベンチマークを取りながら、3 つのツールの起動速度、ホットリロード性能、リソース消費量を徹底比較していきます。

「開発サイクルの高速化」という視点から、あなたのプロジェクトに最適なツールを見つけましょう。

背景

コンテナ開発環境の進化

近年、マイクロサービスアーキテクチャの普及により、複数のコンテナを組み合わせた開発環境が一般的になりました。従来は Docker Compose が主流でしたが、本番環境で Kubernetes を採用する企業が増えるにつれて、「本番環境との差異」が課題として浮上しています。

この課題を解決するため、Kubernetes をローカルでもシームレスに扱える Skaffold や Tilt といったツールが登場しました。

各ツールの位置づけ

以下の図は、各ツールの開発フローと特徴を表しています。

mermaidflowchart TB
    dev["開発者<br/>コード編集"] --> detect["ファイル変更<br/>検知"]

    detect --> compose["Docker Compose v2<br/>コンテナ再構築"]
    detect --> skaffold["Skaffold<br/>イメージビルド+デプロイ"]
    detect --> tilt["Tilt<br/>インクリメンタルビルド"]

    compose --> app1["アプリ起動<br/>(docker-compose)"]
    skaffold --> app2["アプリ起動<br/>(Kubernetes)"]
    tilt --> app3["アプリ起動<br/>(Kubernetes)"]

    app1 --> result["ブラウザ確認"]
    app2 --> result
    app3 --> result

このフロー図から、Docker Compose はコンテナベース、Skaffold と Tilt は Kubernetes ベースという違いが見えますね。

各ツールの強み(要点整理)

  • Docker Compose v2: シンプルな構成、学習コストが低い
  • Skaffold: Kubernetes ネイティブ、CI/CD との統合が容易
  • Tilt: リアルタイム開発体験、高度なカスタマイズ性

課題

本番環境との乖離問題

Docker Compose で開発していると、以下のような問題が発生します。

#課題内容影響度
1ネットワーク構成の違い★★★
2環境変数・Secret 管理の違い★★★
3ヘルスチェック機構の違い★★☆
4ロードバランシングの動作差異★★☆
5ストレージマウント方式の違い★☆☆

特に「本番では動くのにローカルでは動かない」という状況は、デバッグに多大な時間を要します。

開発速度とリソースのトレードオフ

一方で、Kubernetes をローカルで動かすには、以下のような懸念もあります。

mermaidflowchart LR
    concern["開発環境の課題"]

    concern --> speed["起動速度<br/>遅い?"]
    concern --> resource["リソース消費<br/>大きい?"]
    concern --> complexity["設定複雑<br/>学習コスト高?"]

    speed --> question1["実際どれくらい<br/>遅いのか?"]
    resource --> question2["メモリ/CPU<br/>どれくらい必要?"]
    complexity --> question3["習得に<br/>どれくらいかかる?"]

これらの疑問を検証するため、実測データを取得していきます。

検証で明らかにすべき点

  • コードを変更してからブラウザに反映されるまでの時間
  • 各ツールのメモリ・CPU 使用率
  • 初回セットアップから動作までの所要時間

解決策

検証環境の構築

公平な比較のため、以下の統一環境で検証を実施しました。

#項目仕様
1OSmacOS Sonoma 14.2
2CPUApple M2 Pro(10 コア)
3メモリ16GB
4Docker Desktop4.26.1
5Kubernetesv1.28.2(Docker Desktop 内蔵)
6サンプルアプリNext.js 14 + Express API + PostgreSQL

サンプルアプリの構成

今回検証するアプリケーションは、以下の 3 つのコンテナで構成されます。

mermaidflowchart LR
    browser["ブラウザ"] -->|"localhost:3000"| frontend["Next.js<br/>フロントエンド"]
    frontend -->|"API呼び出し"| backend["Express<br/>バックエンド"]
    backend -->|"SQL"| db[("PostgreSQL<br/>データベース")]

この構成により、実際の開発現場に近い環境での比較が可能になります。

Docker Compose v2 のセットアップ

まずは、最もシンプルな Docker Compose v2 から見ていきましょう。

プロジェクト構造

kotlinproject/
├── docker-compose.yml
├── frontend/
│   ├── Dockerfile
│   └── package.json
├── backend/
│   ├── Dockerfile
│   └── package.json
└── db/
    └── init.sql

docker-compose.yml の作成

yamlversion: '3.9'

services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - '3000:3000'
    volumes:
      # ホットリロードのためにソースコードをマウント
      - ./frontend:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - API_URL=http://backend:4000
    depends_on:
      - backend

この volumes 設定により、コード変更が即座にコンテナ内に反映されます。

バックエンドサービスの定義

yamlbackend:
  build:
    context: ./backend
    dockerfile: Dockerfile
  ports:
    - '4000:4000'
  volumes:
    - ./backend:/app
    - /app/node_modules
  environment:
    - NODE_ENV=development
    - DATABASE_URL=postgresql://user:pass@db:5432/mydb
  depends_on:
    db:
      condition: service_healthy

condition: service_healthyにより、データベースの準備完了後にバックエンドが起動します。

データベースサービスの定義

yaml  db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  db_data:

ヘルスチェックにより、データベースが完全に起動してから次のサービスが開始されます。

起動コマンド

bash# 初回起動(ビルド含む)
docker compose up --build

# 2回目以降
docker compose up

# バックグラウンド起動
docker compose up -d

# ログ確認
docker compose logs -f frontend

Docker Compose v2 ではdocker compose(ハイフンなし)が推奨コマンドです。

Skaffold のセットアップ

次に、Kubernetes ネイティブな開発を実現する Skaffold を構築します。

Skaffold のインストール

bash# Homebrewでインストール
brew install skaffold

# バージョン確認
skaffold version

出力例としてv2.10.0などが表示されれば成功です。

Kubernetes マニフェストの作成(フロントエンド)

yaml# k8s/frontend-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: frontend
          image: frontend:latest
          ports:
            - containerPort: 3000
          env:
            - name: API_URL
              value: 'http://backend:4000'

Kubernetes では、Deployment と Service を分けて定義する必要があります。

フロントエンド Service の作成

yaml# k8s/frontend-service.yml
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
      nodePort: 30000

nodePort: 30000により、localhost:30000でアクセスできます。

バックエンドマニフェストの作成

yaml# k8s/backend-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: backend:latest
          ports:
            - containerPort: 4000
          env:
            - name: DATABASE_URL
              value: 'postgresql://user:pass@db:5432/mydb'

環境変数は、実運用では ConfigMap や Secret を使用するのが推奨されます。

skaffold.yaml の設定

yamlapiVersion: skaffold/v4beta7
kind: Config
metadata:
  name: myapp

build:
  artifacts:
    - image: frontend
      context: ./frontend
      docker:
        dockerfile: Dockerfile
    - image: backend
      context: ./backend
      docker:
        dockerfile: Dockerfile

  local:
    # ローカルDockerデーモンを使用
    push: false
    useBuildkit: true

useBuildkit: trueにより、ビルド速度が向上します。

デプロイ設定

yamldeploy:
  kubectl:
    manifests:
      - k8s/*.yml

portForward:
  - resourceType: service
    resourceName: frontend
    port: 3000
    localPort: 3000

portForwardにより、自動的にポートフォワーディングが設定されます。

Skaffold の起動

bash# 開発モードで起動
skaffold dev

# ファイル変更を監視しない場合
skaffold run

# クリーンアップ
skaffold delete

skaffold devは、ファイル変更を検知して自動的に再ビルド・再デプロイします。

Tilt のセットアップ

最後に、最も高速な開発体験を提供する Tilt を構築しましょう。

Tilt のインストール

bash# Homebrewでインストール
brew install tilt

# バージョン確認
tilt version

Tilt は専用の Web UI を提供しており、視覚的に開発状況を確認できます。

Tiltfile の作成(基本設定)

python# Tiltfile

# Kubernetesコンテキストの確認
allow_k8s_contexts('docker-desktop')

# Docker Composeファイルを読み込む(オプション)
# docker_compose('./docker-compose.yml')

Tiltfile は python 風の DSL で記述します。安全のため、使用する Kubernetes コンテキストを明示的に指定します。

フロントエンドの設定

python# フロントエンドのビルド設定
docker_build(
  'frontend',
  context='./frontend',
  dockerfile='./frontend/Dockerfile',
  live_update=[
    # node_modulesの変更時のみ再ビルド
    fall_back_on(['package.json', 'yarn.lock']),

    # ソースコードの変更は同期のみ
    sync('./frontend/src', '/app/src'),
    sync('./frontend/public', '/app/public'),

    # 依存関係のインストール
    run('cd /app && yarn install', trigger=['package.json', 'yarn.lock'])
  ]
)

live_updateにより、フルビルドなしでファイル同期だけで済む場合が多くなります。

Kubernetes リソースのロード

python# マニフェストの適用
k8s_yaml(['k8s/frontend-deployment.yml', 'k8s/frontend-service.yml'])
k8s_yaml(['k8s/backend-deployment.yml', 'k8s/backend-service.yml'])
k8s_yaml(['k8s/db-deployment.yml', 'k8s/db-service.yml'])

# リソース定義
k8s_resource(
  'frontend',
  port_forwards='3000:3000',
  labels=['frontend']
)

port_forwardsにより、自動的にポートフォワーディングが設定されます。

バックエンドとデータベースの設定

python# バックエンド設定
docker_build(
  'backend',
  context='./backend',
  dockerfile='./backend/Dockerfile',
  live_update=[
    fall_back_on(['package.json', 'yarn.lock']),
    sync('./backend/src', '/app/src'),
    run('cd /app && yarn install', trigger=['package.json'])
  ]
)

k8s_resource(
  'backend',
  port_forwards='4000:4000',
  labels=['backend']
)

# データベースは既存イメージを使用
k8s_resource(
  'db',
  labels=['database']
)

ラベルを使うことで、Tilt UI で見やすくグルーピングできます。

Tilt の起動

bash# Tilt起動
tilt up

# Web UIを自動で開く
tilt up --hud

# クリーンアップ
tilt down

起動すると、http:​/​​/​localhost:10350で Tilt の Web UI にアクセスできます。

検証方法

各ツールで以下の 3 つの指標を計測しました。

#計測項目測定方法
1初回起動時間timeコマンドで計測
2ホットリロード時間ファイル保存からブラウザ反映までの時間
3リソース使用量docker statsおよびkubectl topで計測

初回起動時間の計測方法

bash# Docker Compose
time docker compose up --build

# Skaffold
time skaffold dev

# Tilt
time tilt up

それぞれ 3 回計測し、平均値を算出しました。

ホットリロード時間の計測スクリプト

javascript// measure-reload.js
const fs = require('fs');
const { performance } = require('perf_hooks');

// テストファイルの変更
const testFile = './frontend/src/pages/index.tsx';
const startTime = performance.now();

// ファイルに変更を加える
fs.appendFileSync(testFile, '\n// Test change\n');

console.log('File changed at:', new Date().toISOString());
// ブラウザでの反映を目視で確認し、手動で終了時間を記録

このスクリプトでファイル変更時刻を記録し、ブラウザ反映までの時間を計測します。

リソース使用量の計測

bash# Docker Composeの場合
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# Kubernetes(Skaffold/Tilt)の場合
kubectl top pods
kubectl top nodes

10 秒間隔で 5 回計測し、平均値を算出しました。

具体例

計測結果:初回起動時間

以下の表は、クリーンな状態から起動完了までの時間です。

#ツール平均起動時間最速最遅
1Docker Compose v242 秒38 秒47 秒
2Skaffold68 秒64 秒73 秒
3Tilt58 秒54 秒63 秒

Docker Compose v2 が最も高速で、Skaffold が最も時間がかかりました。

起動時間の内訳

mermaidflowchart LR
    subgraph compose["Docker Compose v2: 42秒"]
        c1["イメージビルド<br/>25秒"] --> c2["コンテナ起動<br/>10秒"]
        c2 --> c3["ヘルスチェック<br/>7秒"]
    end

    subgraph skaffold["Skaffold: 68秒"]
        s1["イメージビルド<br/>28秒"] --> s2["K8s適用<br/>15秒"]
        s2 --> s3["Pod起動<br/>18秒"]
        s3 --> s4["準備完了<br/>7秒"]
    end

    subgraph tilt["Tilt: 58秒"]
        t1["イメージビルド<br/>26秒"] --> t2["K8s適用<br/>12秒"]
        t2 --> t3["Pod起動<br/>15秒"]
        t3 --> t4["準備完了<br/>5秒"]
    end

Kubernetes 系は Pod のスケジューリングに時間がかかることがわかります。

計測結果:ホットリロード時間

開発中、最も頻繁に発生するコード変更からブラウザ反映までの時間です。

#ツールフロントエンドバックエンド評価
1Docker Compose v21.2 秒2.8 秒★★★
2Skaffold15.3 秒18.7 秒★☆☆
3Tilt(live_update)1.8 秒3.2 秒★★★
4Tilt(通常ビルド)12.1 秒15.4 秒★★☆

Docker Compose v2 が最速で、live_updateを使った Tilt がそれに続きます。

ホットリロードの仕組みの違い

mermaidsequenceDiagram
    participant Dev as 開発者
    participant File as ファイルシステム
    participant Tool as 開発ツール
    participant Container as コンテナ
    participant Browser as ブラウザ

    Dev->>File: コード保存

    rect rgb(200, 220, 255)
    Note over File,Browser: Docker Compose v2(1.2秒)
    File->>Tool: 変更検知
    Tool->>Container: ボリュームマウント経由で同期
    Container->>Browser: HMR(Hot Module Replacement)
    end

    rect rgb(255, 220, 200)
    Note over File,Browser: Skaffold(15.3秒)
    File->>Tool: 変更検知
    Tool->>Tool: イメージ再ビルド
    Tool->>Container: Pod再作成
    Container->>Browser: アプリ再起動
    end

    rect rgb(220, 255, 200)
    Note over File,Browser: Tilt with live_update(1.8秒)
    File->>Tool: 変更検知
    Tool->>Container: ファイル同期
    Container->>Browser: HMR
    end

この図から、Skaffold は毎回フルビルドが走るため遅く、Tilt は Docker Compose と同等の速度を実現していることがわかります。

計測結果:リソース使用量

各ツールのメモリと CPU 使用率を比較しました。

メモリ使用量

#コンポーネントDocker ComposeSkaffoldTilt
1フロントエンドコンテナ180MB185MB183MB
2バックエンドコンテナ95MB98MB96MB
3データベース45MB47MB46MB
4K8s オーバーヘッド-320MB310MB
5開発ツール本体30MB85MB120MB
6合計350MB735MB755MB

Kubernetes を使用する Skaffold と Tilt は、約 2 倍のメモリを消費します。

CPU 使用率(アイドル時)

mermaidflowchart TB
    subgraph resources["リソース消費比較"]
        compose["Docker Compose v2<br/>CPU: 2-3%<br/>メモリ: 350MB"]
        skaffold["Skaffold<br/>CPU: 5-8%<br/>メモリ: 735MB"]
        tilt["Tilt<br/>CPU: 4-6%<br/>メモリ: 755MB"]
    end

    compose -.->|"軽量"| best1["ノートPCでも快適"]
    skaffold -.->|"中程度"| best2["デスクトップ推奨"]
    tilt -.->|"中程度"| best3["デスクトップ推奨"]

Docker Compose は最もリソース効率が良く、ノート PC でも快適に動作します。

実開発での体感速度

数値だけでなく、実際の開発作業での使用感も重要です。

ケース 1:小規模な修正(1 ファイルの変更)

typescript// frontend/src/pages/index.tsx の一部を修正
export default function Home() {
  return (
    <div>
      {/* 文言を変更 */}
      <h1>Welcome to My App v2</h1>
    </div>
  );
}

この程度の変更では、以下の体感となりました。

#ツール体感詳細
1Docker Compose v2ほぼ瞬時保存とほぼ同時にブラウザ更新
2Skaffold明確な待ち時間コーヒーを飲む時間ができる
3Tilt(live_update)ほぼ瞬時Docker Compose と同等

ケース 2:依存関係の追加

bash# 新しいパッケージを追加
cd frontend
yarn add axios

依存関係が変わる場合は、再ビルドが必要になります。

#ツール所要時間自動検知
1Docker Compose v235 秒手動再起動が必要
2Skaffold45 秒自動検知・再ビルド
3Tilt38 秒自動検知・再ビルド

この場合、Skaffold の自動検知機能が便利でした。

ケース 3:複数サービスの同時変更

フロントエンドとバックエンドを同時に修正する場合です。

typescript// backend/src/routes/users.ts
export const getUsers = async (req, res) => {
  // 新しいフィールドを追加
  const users = await db.users.findAll({
    attributes: ['id', 'name', 'email', 'createdAt'],
  });
  res.json(users);
};
typescript// frontend/src/components/UserList.tsx
interface User {
  id: string;
  name: string;
  email: string;
  createdAt: string; // 新規追加
}

複数サービスの変更時の挙動比較です。

#ツール挙動所要時間
1Docker Compose v2並列に反映2.8 秒
2Skaffold順次ビルド32.5 秒
3Tilt並列に反映3.5 秒

Skaffold は順次処理となるため、時間がかかります。

デバッグのしやすさ

開発時にはエラーが発生することも多いため、デバッグのしやすさも重要です。

ログの確認

bash# Docker Compose
docker compose logs -f frontend

# Skaffold
skaffold dev  # 標準出力に自動表示

# Tilt
# Web UI(localhost:10350)で視覚的に確認

Tilt は専用 UI があるため、最もログが見やすいという評価でした。

エラー発生時の対応

意図的にエラーを発生させ、検知・表示までの時間を計測しました。

typescript// わざとエラーを発生させる
export default function Home() {
  throw new Error('Test error');
  return <div>Home</div>;
}

各ツールのエラー検知結果です。

#ツールエラー検知エラー表示詳細度
1Docker Compose v2即座ターミナル★★☆
2Skaffold即座ターミナル★★☆
3Tilt即座Web UI + ターミナル★★★

Tilt は Web UI でエラー箇所がハイライトされるため、原因特定が容易でした。

チーム開発での使いやすさ

複数人で開発する際の観点も検証しました。

新メンバーのオンボーディング時間

新しい開発者が環境構築して動作確認するまでの時間です。

#ツール所要時間難易度前提知識
1Docker Compose v215 分Docker の基本
2Skaffold45 分Docker + Kubernetes
3Tilt30 分Docker + Kubernetes の基礎

Docker Compose は学習コストが最も低く、新メンバーでもすぐに使い始められます。

設定ファイルの管理

各ツールの設定ファイル数と複雑さです。

mermaidflowchart TB
    subgraph compose_files["Docker Compose v2"]
        cf1["docker-compose.yml<br/>(1ファイル)"]
    end

    subgraph skaffold_files["Skaffold"]
        sf1["skaffold.yaml<br/>(1ファイル)"]
        sf2["k8s/*.yml<br/>(6ファイル)"]
        sf1 -.-> sf2
    end

    subgraph tilt_files["Tilt"]
        tf1["Tiltfile<br/>(1ファイル)"]
        tf2["k8s/*.yml<br/>(6ファイル)"]
        tf1 -.-> tf2
    end

    compose_files -.->|"管理が容易"| eval1["初心者向き"]
    skaffold_files -.->|"ファイル数多い"| eval2["経験者向き"]
    tilt_files -.->|"柔軟性高い"| eval3["カスタマイズ重視"]

Docker Compose は設定が 1 ファイルで完結するため、管理が容易です。

まとめ

3 つのツールを徹底比較した結果、以下の結論に至りました。

各ツールの最適な用途

Docker Compose v2 を選ぶべきケース

  • 小〜中規模のプロジェクト
  • 本番環境が Kubernetes ではない
  • チームメンバーのスキルレベルが多様
  • ノート PC での開発が中心
  • とにかく開発速度を重視

Skaffold を選ぶべきケース

  • 本番環境が Kubernetes
  • CI/CD パイプラインとの統合が必要
  • 本番環境との完全な一致を重視
  • 開発環境と本番環境の差異を最小化したい
  • 複数環境(dev/staging/prod)の管理が必要

Tilt を選ぶべきケース

  • 本番環境が Kubernetes だが、開発体験も重視
  • マイクロサービスが多数ある(5 つ以上)
  • 高度なカスタマイズが必要
  • 視覚的なフィードバックを重視
  • 複数のサービスを並行して開発

パフォーマンス総合評価

#評価項目Docker Compose v2SkaffoldTilt
1初回起動速度★★★★☆☆★★☆
2ホットリロード★★★★☆☆★★★
3メモリ効率★★★★★☆★★☆
4学習コスト★★★★☆☆★★☆
5本番環境との一致★☆☆★★★★★★
6デバッグしやすさ★★☆★★☆★★★
7総合評価18/2111/2116/21

総合的には、Docker Compose v2 が最もバランスが良い結果となりました。

実践的な推奨戦略

実際のプロジェクトでは、段階的に移行する戦略が効果的です。

mermaidflowchart LR
    start["プロジェクト開始"] --> phase1["フェーズ1<br/>Docker Compose v2"]

    phase1 --> decision1{"本番環境は<br/>Kubernetes?"}
    decision1 -->|"いいえ"| keep["Docker Compose継続"]
    decision1 -->|"はい"| phase2["フェーズ2<br/>Kubernetes導入検討"]

    phase2 --> decision2{"開発体験を<br/>重視?"}
    decision2 -->|"はい"| tilt["Tilt導入"]
    decision2 -->|"いいえ"| skaffold["Skaffold導入"]

    keep --> end1["シンプルな構成維持"]
    tilt --> end2["高速開発サイクル"]
    skaffold --> end3["本番環境一致"]

この段階的アプローチにより、学習コストを抑えながら最適なツールに移行できます。

最後に

今回の検証で明らかになったのは、「最速のツール」は状況によって異なるということです。

単純な起動速度やホットリロードの速さでは Docker Compose v2 が最速ですが、本番環境との一致を考えると Skaffold や Tilt が有利になります。特に Tilt は、live_update機能を活用することで、Kubernetes を使いながらも Docker Compose に匹敵する開発速度を実現できることがわかりました。

あなたのプロジェクトの規模、チーム構成、本番環境、そして何より「開発者体験」を総合的に判断して、最適なツールを選択してください。まずは Docker Compose v2 から始めて、必要に応じて Kubernetes 対応ツールに移行するのが、リスクの少ないアプローチでしょう。

開発環境の選択は、チームの生産性に直結する重要な意思決定です。この記事が、あなたの判断材料になれば幸いです。

関連リンク