Docker・インフラ構築

Docker Composeの本番運用とKubernetes移行:個人開発における現実的な境界線

個人開発のサービスは、思っているより早く壁にぶつかる。VPS 1台に Docker Compose で全部詰め込んだ構成。アクティブユーザーが増えてきたあたりから、リクエストがスパイクした瞬間に nginx コンテナが 502 を返し始める——という相談は、後を絶たない。…

Docker Composeの本番運用とKubernetes移行:個人開発における現実的な境界線

個人開発が本番運用で直面する「静かな壁」

しかも厄介なのは、「落ちた」と感じる前に「じわじわ遅くなっている」状態が続くこと。コンテナ自体は無事で再起動も不要。でもレスポンスタイムだけが、日に日に悪化していく。この「静かな劣化」は、単一ノードの限界そのものだ。コンテナオーケストレーションを意識すべき本当のきっかけは、致命的な障害ではなく、こうした微妙なパフォーマンス低下の中に潜んでいる。

よくある症状をもう少し具体的に並べておく。日中のピーク帯にコンテナの CPU 使用率が 80% を超える状態が続く、夜間のバッチ処理が終わらないまま朝を迎えてバックアップが滞る、ヘルスチェックは成功しているのに DB 接続が詰まってアプリ全体が応答不能になる——いずれも「落ちてはいないが、品質が落ちている」状態だ。アラートを設定していないと気付きにくく、設定していても閾値の調整を誤れば見逃す。

コンテナは基本「使い捨て」。だからデータを持たせる設計を、最初から組み込まないと本番運用は脆くなる。

Docker Composeによる単一サーバー運用の現実

Docker Compose の立ち位置は明確で、ローカル開発環境、小規模な 1 台構成の本番環境、そして CI 環境のオーケストレーションに適している。docker-compose.yml 一つで Web、API、DB、Redis までを定義し、docker compose up -d で一気に立ち上がる——この体験の簡潔さは、競合ツールにはない武器だ。

ただし、その簡潔さの裏には、構造的な制約がある。Docker Compose は自動スケーリング機能を持たない。複数台構成の冗長化や、ダウンタイムを最小化するローリングアップデートも標準では備えていない。要するに「1台のホストの中でコンテナ群を協調させる」ことにフォーカスしたツールであり、「複数ホストを跨いでコンテナ群を協調させる」ことは、そもそも設計思想の外にある。

この制約は、必ずしも欠点ではない。月間数千 PV 規模の個人サービスであれば、ロードバランサもサーキットブレーカもオートスケールも、必要ない場面のほうがむしろ多い。Docker Compose を選ぶか、Kubernetes を選ぶかは、技術的な優劣ではなく、「今、自分たちが抱えている運用の痛み」がどこにあるかで決まる。

実際に Docker Compose で本番運用を回すときに効いてくるのは、リソース制限と再起動ポリシーの設計だ。deploy.resources.limits で CPU とメモリの上限を明示しておくと、コンテナが暴走したときにホスト全体を巻き込んで落ちることを防げる。restart: unless-stopped を付けておけば、VPS 側の再起動後に自動で立ち上がる。さらに healthcheck を入れて nginx や app の readiness を監視し、Traefik などのリバースプロキシ側で unhealthy なコンテナを自動で切り離す構成にしておくと、ダウンタイムを最小化できる。「1台構成だからといって何も考えなくていい」ということにはならないのが、運用の現場感覚だ。

1台構成が機能する条件

  • アクセスピーク時の RPS が、シングルインスタンスの処理能力を大きく超えない
  • DB と Web を同一ホスト内で管理できるデータ規模(数百 MB〜数 GB 程度)
  • デプロイ時に短時間のサービス停止を許容できる
  • 障害発生時に手動で docker compose restart を打つ運用が回せる
  • ログ・メトリクスがホスト側の仕組みで完結する

逆に言えば、このうちのどれか一つでも明確に「No」になった瞬間が、Docker Compose の限界点になる。

Kubernetesが解決する領域 — 自動スケーリング・自己復旧・サービスディスカバリ

Kubernetes(k8s)は、複数ホストに分散した大量のコンテナを自動で管理するためのオーケストレーションツールだ。Docker Compose が苦手とする領域——自動スケーリング、障害発生時の自動再起動、サービスディスカバリ、宣言的なローリングアップデート——これらを標準で備えている。

特に価値が大きいのが「自己復旧」の部分だ。コンテナがクラッシュした場合、k8s は ReplicaSet に基づいて新しい Pod を自動で起動する。ノード自体が落ちた場合も、残りのノードにワークロードを自動で再配置してくれる。これは「夜中にアラートで叩き起こされる」運用からの解放を意味する。

自動スケーリングについても少し補足しておく。HPA(Horizontal Pod Autoscaler)は CPU やメモリの使用率を指標に Pod 数を自動伸縮させる仕組みで、カスタムメトリクスを組み込めばリクエスト数やキューの長さでもトリガーが引ける。さらに Cluster Autoscaler を併用すれば、Pod の増減に応じてノード自体も自動で追加・削除される。突発スパイクに対して「数分以内にスケールアウト」が現実になるのは、このスタックが揃ってからだ。

ただし、この恩恵には明確な対価がある。学習コストと運用コストだ。Pod、Service、Deployment、Ingress、ConfigMap、Secret、PersistentVolumeClaim——最初は YAML ファイルを書くだけでも概念マップが必要になる。さらに Helm や Kustomize、cert-manager、Prometheus、Grafana と周辺ツールの生態系も広がっていく。コンテナを「動かす」こと自体は難しくないが、コンテナ群を「安定運用」することのハードルは、Docker Compose と比較して明確に跳ね上がる。

比較で見る両者の特性

観点Docker ComposeKubernetes
自動スケーリング非対応(手動でコンテナ数を増やす必要あり)HPA / VPA / Cluster Autoscaler で対応
ノード障害時の自動復旧非対応(手動対応)別ノードへ自動再配置
ローリングアップデート手動での段階実行が必要Deployment で宣言的に対応
学習コスト低い(docker-compose.yml を読めば理解できる)高い(複数リソースの概念理解が必須)
サービスディスカバリ同一ネットワーク内ならコンテナ名で解決可能Service / DNS で抽象化
ステートフルワークロード1ホスト内では扱えるが、分散前提の設計は要対応StatefulSet / Operator で対応
向いている規模1台構成・小規模本番複数ノード・数百 Pod 規模

この表を見て分かるのは、「Kubernetes は Docker Compose の上位互換ではない」ということだ。両者はそもそも解決する問題が異なり、選択は「規模」と「運用体制」に依存する。Kubernetes を選ぶ動機は「Docker Compose の上位版が欲しいから」ではなく、「複数ホストにまたがる自律運用が必要になったから」だ。

Kubernetesは銀の弾丸ではない。「個人開発の規模」に対しては、明確にオーバースペックな領域がある。

個人開発の天秤 — 運用コストと学習コストの現実

ここまでの整理を踏まえると、個人開発における Kubernetes の必要性は「機能の有無」ではなく「コスト対効果」で判断すべきだという結論に収斂する。

運用コストの話からしよう。Kubernetes クラスタを本番運用するには、コントロールプレーンの管理が必要になる。EKS・GKE・AKS のようなマネージドサービスを使えばコントロールプレーンの構築・運用はマネージド側に委ねられるが、それでも YAML の設計、IAM ロール、ネットワークポリシー、Secret の管理、ログ基盤の構築、アップグレード戦略といったレイヤーは自分で背負うことになる。個人開発で 1 人で全部回すには、正直、覚悟がいるコストだ。

学習コストも軽視できない。Kubernetes を本番運用レベルで使いこなすには、Docker Compose の経験値とは比較にならない量のドキュメントを読み、ハンズオンを回し、失敗する必要がある。kind や minikube でクラスタを触る段階と、EKS や GKE で本番ワークロードを安定運用する段階の間には、かなり深い谷がある。「とりあえず k8s を触っておくか」程度のノリで始めると、大抵の場合は「個人サービスより先に、運用の方が複雑化してしまう」という本末転倒が起きる。

ここで、もう一段分けておきたい。「個人開発のステージ」を 3 つに分けると、選択の指針がクリアになる。

  • 趣味ステージ:自分の勉強・実験が主目的。利用者は自分ひとり。コンテナを触る経験値自体が目的で、この段階で Kubernetes に手を出すのは目的と手段の逆転になる。
  • マイクロビジネスステージ:小さく収益が出ているが、ユーザーは数百〜数千。Docker Compose + 適切な VPS(または Lightsail などのシンプルサービス)で十分。スケールよりも安定性のほうが価値を持つ領域。
  • グロースステージ:DAU が伸びてきて、SLO や複数リージョン、監査対応など「チーム運用」の匂いがしてくる領域。ここで初めて Kubernetes や PaaS(Fly.io、Cloud Run 等)が選択肢に乗る。

つまり、Kubernetes を本気で検討するべきなのは、ステージ 3 に入ったときで、しかも「複数コンポーネントを跨る継続的デプロイ」「ピーク時の自動スケール」「厳格な分離要件」のいずれかがクリティカルな要件になったとき、というのが自分の感覚値だ。

判断フレームワーク

「Kubernetesに移行すべきか」を判断する観点を、自分なりに整理してみる。

1. 可用性の要求水準 — 99.9% を目指すのか、99% で許容できるのか

2. ピークトラフィックの変動幅 — 定常的に余裕があるのか、突発スパイクがあるのか

3. チームの人数 — 自分 1 人なのか、協力者がいるのか

4. デプロイ頻度 — 1週間に1回なのか、1日に何度も出すのか

5. 障害時の許容時間 — 30分で復帰すればいいのか、5分で自動復旧が必要か

6. コンプライアンス要件 — ログの保管や分離要件など、規制側の要請はあるか

1〜3が軽めのサービスであれば、Docker Compose での運用を最適化したほうが、たいていの場合コスパがいい。逆に 4・5・6 がクリティカルな要件として立ち上がってきたとき、初めて Kubernetes が選択肢に入る。重要なのは、「将来の拡張性のために予防的に k8s を入れる」という判断は、たいていの場合、自分にとってのリソースを溶かす結果になるということだ。今痛いところだけを癒して、将来痛みが出たらそのときにまた判断する。この「痛み駆動」の考え方のほうが、個人開発では健全だと考えている。

移行するなら現実的な一手 — Komposeと永続化データの設計

「将来的に k8s 移行を見据えたい」というニーズは、個人開発でも珍しくなくなった。そのときに使えるツールが Kompose だ。既存の docker-compose.yml を読み込んで、Kubernetes のマニフェストファイル(Deployment、Service、PersistentVolumeClaim など)に変換してくれる。

ただし、Kompose は「魔法の変換ツール」ではない。あくまで出発点であり、変換後のマニフェストをそのまま本番で使えるケースは少ない。特に以下の点は、手動での再設計が必要になる。

  • ネットワーク構成 — Docker Compose のネットワークモデルと k8s の Service / Ingress は概念が異なる。Compose では同一ネットワーク内のコンテナ名で解決できたものが、k8s では Service と DNS 経由の解決になる
  • シークレット管理.env ファイルの代わりに Secret リソースへの移行が必要。環境変数の扱い方も整理が必要
  • ボリューム設計 — 単純な bind mount を PersistentVolumeClaim に書き換えるだけでなく、StorageClass の選定や StatefulSet 化も視野に入る
  • ヘルスチェック — Compose の healthcheck は liveness / readiness probe に分かれる。意味的には同じだが設計の粒度は上がる
  • ビルド/リリース — ローカルの docker build から、コンテナレジストリ経由の Pull ベースデプロイに変わる

実際には、Kompose で書き出したマニフェストをベースにしながら、上記の観点を手で整えていく作業が、本番運用に耐える k8s マニフェストへの最短距離になる。Helmfile や Kustomize などの中間レイヤーを挟むかどうかは、サービスの複雑度と相談だが、最初の 1〜2 サービスでは素の Manifest でも十分回せる。

そして、移行の有無に関わらず絶対に避けて通れないのが、永続化データの設計だ。Docker コンテナは基本「使い捨て」構造で、コンテナが再作成されると内部のデータは消える。これは設計思想であり、欠陥ではない。したがって、データベースやアップロードファイルなどを保持するには、Docker Compose の段階で Volume を正しく設定し、定期バックアップの仕組みを最初から組み込んでおく必要がある。

具体的には、Named Volume を活用し、ホスト側のバックアップスクリプトから pg_dumpmysqldump を定期実行する設計が定石になる。出力先はローカル NAS でも S3 互換ストレージでも良いが、大切なのは「世代管理」と「リストア手順のドキュメント化」だ。「取ってある」と「戻せる」の間には距離がある。毎月 1 回でもいいので、ステージング環境でリストア演習を回しておくと、本番で初めて慌てることがなくなる。

コンテナがいつ落ちても、リストアできる状態を作る——この「いつでも壊して再起動できる」前提が、個人開発の運用負荷を劇的に下げる。

macOS環境のI/O劣化とローカル開発の意地

少し話は変わるが、本番運用と並んで重要なのがローカル開発環境の快適さだ。macOS 上で Docker Compose を使っていると、ホスト-コンテナ間のファイル同期で I/O が劇遅くなる現象に悩まされる人が多い。

これは bind mount の構造的問題で、macOS 上のファイル変更が Linux VM 内のコンテナに反映されるまでに、ファイルシステムレイヤーを複数跨ぐ必要がある。Webpack や Vite のホットリロードが詰まる、テストの実行が遅延する、ファイル監視のイベントが抜け落ちる——こうした痛みは、本番運用とは別軸で開発体験を蝕む。特に Node.js と macOS の組み合わせはこの問題が出やすい。

対策は大きく分けて 4 つある。

1. delegated / cached / consistent オプションの使い分け — bind mount の :delegated:cached:consistent 指定で、コンテナ側から見たファイルの整合性チェックを簡略化できる。delegated はコンテナ→ホスト方向の反映を優先し、cached はその逆。用途ごとに使い分ける

2. Named Volume への切り替え — ソースコード本体ではなく node_modules や vendor ディレクトリ、ビルドキャッシュなど、ホストとの頻繁な同期が不要な箇所は Volume に逃がす。BuildKit の --mount=type=cache も併用するとビルド自体が速くなる

3. Docker Desktop の VirtioFS 活用 — 比較的新しい Docker Desktop では VirtioFS がデフォルトになっており、旧来の gRPC-FUSE より大幅に I/O が改善されている。Mac が Apple Silicon か Intel かによって効果も変わる

4. 代替ツールの検討 — Mutagen や docker-sync、OrbStack のような代替を使う手もある。チーム規模やプロジェクトの規模感で採用判断は分かれる

これらを地道に詰めることで、ローカル開発のフィードバックループが短くなり、結果的にデプロイ頻度が上がる。デプロイ頻度が上がれば、本番環境の改善サイクルも早くなる——つまり、ローカル環境の最適化は、本番運用の改善速度に直結する投資だ。「ローカル開発が重い」と感じたときこそ、本番と地続きの改善対象として向き合う価値がある。

境界線を引くための仮説

ここまでの議論を一度、整理する。Docker Compose は「1台構成の本番環境で十分に機能する」というのが現時点での妥当な結論で、Kubernetes は「複数ノードでの安定運用が必要な段階で初めて意味を持つ」というのが、コンテナオーケストレーションの実態に合っている。

ただし、この境界線は個人開発の文脈では曖昧だ。月間 PV や RPS の絶対値で「ここまで来たら k8s」と切れる閾値は、サービスの特性やアーキテクチャによって大きく変わる。アクセスが読みやすい API 中心のサービスと、画像や動画のリッチメディアを配信するサービスでは、同じ DAU でもインフラ要件はまるで違うからだ。

付け加えておきたいのは、「Kubernetes か Docker Compose か」の二項対立自体が、後から振り返ると狭い問いだった、というケースが多いことだ。Fly.io、Cloud Run、Azure App Service、ECS Fargate といった「コンテナ前提の PaaS」は、その中間に位置する選択肢として現実的に検討に値する。自前でコントロールプレーンを持たないぶん運用負担は軽く、オートスケールやブルーグリーンデプロイの一部機能を得ることができる。個人開発の立場から見れば、この「中間の選択肢」をテーブルに載せるかどうかで、総所有コストは大きく変わる。

次に試したいこと

  • 自分のサービスのレスポンスタイム推移を可視化する — Datadog でも Promtail + Grafana でも良いので、95パーセンタイルの遅延を 1 ヶ月単位で追う。p99 まで追えると、ボトルネックの所在が見えやすくなる
  • スパイク時のコンテナリソース使用率を計測する — CPU だけでなく I/O wait も見る。Single-node の限界は CPU より先に I/O で出ることが多い。iostat や CloudWatch のメトリクスから disk レイテンシを追うのが個人的にはおすすめ
  • バックアップからのリストア手順を実際に走らせる — 「取ってある」ではなく「戻せる」状態を作る。これが個人開発の最大の保険になる
  • コスト表を作る — VPS 月額・マネージド DB・ストレージ・CDN の月額を 1 つの表にまとめる。改善余地が金額で見えるようになると、最適化の優先順位がブレなくなる

この 4 つを 30 日間回してみれば、今いる場所が「Docker Compose で戦える領域」なのか「そろそろ Kubernetes や PaaS を見たほうがいい領域」なのか、感覚でなくなるはずだ。データは、仮説を裏切ってくれる。だからこそ、まず測るところから始めたい。感覚ではなく数字で語れる状態を、最初に作っておく。それが、後々の正しい選択を後押ししてくれる。

よくある質問

Docker Composeで本番運用する際の限界はどこですか?
自動スケーリング機能がないことや、複数ホストを跨いだ冗長化が設計思想に含まれていない点が限界です。アクセススパイク時の対応や、ダウンタイムを最小化するローリングアップデートが標準では困難になります。
Kubernetesを導入すべきタイミングはいつですか?
複数ホストにまたがる自律運用が必要になった時や、自動スケーリング、厳格な分離要件、継続的デプロイがクリティカルな要件となった段階で検討すべきです。
Docker Composeで本番運用する際に最低限やっておくべきことは?
リソース制限の設定、再起動ポリシーの指定、ヘルスチェックの導入が重要です。また、コンテナ内のデータが消えないよう、ボリュームを正しく設定し、定期的なバックアップとリストア手順の確立が必須となります。
macOSでDocker Composeを使うと動作が遅いのはなぜですか?
ホストとコンテナ間のファイル同期(bind mount)において、ファイルシステムレイヤーを複数跨ぐ必要があるためです。VirtioFSの活用や、Named Volumeへの切り替え、キャッシュオプションの調整で改善が可能です。
Kubernetes以外に個人開発で検討できる選択肢はありますか?
Fly.io、Cloud Run、Azure App Service、ECS Fargateといったコンテナ前提のPaaSが有効です。これらは自前でコントロールプレーンを管理する負担を抑えつつ、オートスケールなどの機能を利用できます。

参考情報