「ハンバーガーセットを一つください」の意味
前回、私たちは Dockerfile を使って自分だけの「Docker イメージ(Image)」を作った。これは、パンの上にパティを乗せ、レタスを重ねる順番が書かれた「絶対に変わらないレシピ」のようなものだ。
今度はそのレシピを使って、実際の商売、つまりサービス、を始める番だ。ところが Docker を使い始めると、ややこしい用語が次々に出てくる。Container、Service、Stack…どれも似て見えるが、いったい何が違うのだろう?
この概念をつかめないままだと、あとでデプロイスクリプトを書くときに「コンテナは起動したのに、なんでサービスが上がらないんだ?」と迷子になる。今日はこの系譜を整理するために、世界共通語である「ハンバーガーショップ」の比喩を持ってきた。

1. コンテナ(Container): 注文を受けて作られたハンバーガー
- 定義: イメージを実行してメモリに載せた「実体」だ。
- 特徴: お客さんにレシピの紙(イメージ)を渡すわけにはいかない。レシピどおりに調理された、できたての「ハンバーガー(コンテナ)」を渡さなければならない。
- コマンド:
docker run my-burger
もし私がサーバーを一台立ち上げたなら、それは「コンテナを一つ実行した」ということだ。
2. サービス(Service): 同じメニューの在庫管理
昼時になり、お客さんが一気に押し寄せてきた。ハンバーガーを一つずつ焼く速度では、とても追いつかない。そこで厨房の人手を増やして、「チーズバーガー」をあらかじめ 3 個ずつ作っておくことにした。こうなると私は、もはや「チーズバーガー 1 個」ではなく、「チーズバーガーのライン全体」、つまり Service を管理しなければならない。
- 定義: 同じイメージから作られた「コンテナの集まり」。
- 特徴: 「このメニューは常に在庫を 3 個キープせよ(Replicas=3)」といった「ポリシー」を与えられる。ハンバーガーを一つ落としても(エラー)、勝手に新しいものを焼いてくれる。
- 目的: 「スケールアウト(Scale-out)」。トラフィックの分散と安定性。
3. スタック(Stack): セットメニュー、つまりフルパッケージ
ハンバーガーだけでは喉が詰まる。ポテト、DB、も必要だし、コーラ、Redis、も必要だ。これらが全部そろって初めて、「完璧な一食」、つまりアプリケーションになる。
- 定義: 異なるサービス(Web、DB、キャッシュ)をひとまとめにした「最上位のグループ」。
- 特徴: ハンバーガーとコーラは同じトレーに載っているほうが食べやすい(ネットワーク接続)。スタックとしてまとめると、これらが互いに通信できる専用の空間(Network)が生まれる。
問題点: 注文しているだけで息切れしそうだ
概念としては分かったが、実際に運用しようとすると頭が痛くなる。私たちが作ろうとしている Web サービス、つまり Stack、には普通、次のような構成要素が必要になる。
- Spring Boot (Burger): メインメニュー 2 個(冗長化)
- PostgreSQL (Fries): サイドメニュー 1 個
- Redis (Coke): ドリンク 1 杯
これを生の Docker コマンド、docker run、で起動するのは、カウンターでこんな注文をするようなものだ。
「すみません、『チーズバーガー(Spring)』をお願いします。ただしパティはミディアムウェルダンで、ピクルス抜き、ソースは多めで。あ、あと『ポテト(DB)』もお願いしますが、塩はなしで、ケチャップを 3 個つけてください。最後に『コーラ(Redis)』はゼロコーラで、氷は半分だけにしてください。そしてこの 3 つを『一つのトレー(Network)』に乗せてください」
# 実際にはこういうコマンドを毎回打たなければならない
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...
デプロイのたびに、この長い注文を一字一句間違えずに言えるだろうか? しかも順番を間違えて、ハンバーガーよりポテトのほうが遅く出てきたらどうする? お客さん、つまりユーザーは怒るはずだ。
救世主: Docker Compose – 「いつものください!」
そこで開発者たちは考えた。 「この複雑な注文内容を、あらかじめ『メニュー表、文書』に書いておいて、『1 番セットをください』とだけ言えるようにできないだろうか?」
そうして生まれたのが「Docker Compose」だ。docker-compose.yml というファイルに、自分たちだけの「カスタムセットメニュー」を定義しておけば、Docker が厨房に注文を通し、順番どおりにトレーへきれいに載せてくれる。

[Action] 自分だけのセットメニューを作る
言葉だけで終わらせず、実際にローカルにある Spring サーバーと DB を一発で立ち上げてみよう。やることは、プロジェクトのルートフォルダに docker-compose.yml を作り、下の内容を貼り付けるだけだ。
version: '3.8'
services:
# 1. ポテト(DB)
my-database:
image: postgres:14
environment:
POSTGRES_PASSWORD: mysecretpassword
ports:
- "5432:5432"
# 2. チーズバーガー(Web)
my-web-server:
image: my-spring-app:latest
ports:
- "8080:8080"
depends_on:
- my-database # "ポテトが先に揚がってからバーガーを出します" (順序保証)
environment:
DB_URL: jdbc:postgresql://my-database:5432/mydb
そしてターミナルで魔法の呪文を唱える。
# "1番セット(docker-compose.yml)一つください!"
$ docker-compose up -d
結果: Docker はこのメニュー表を読み、まず DB を起動し、DB の準備ができたら Spring サーバーを起動する。これこそが、IaC、Infrastructure as Code、つまり「インフラをコードで管理する」という考え方の始まりだ。これからは複雑なサーバー構成でも、「メニュー表ファイル」一つを共有するだけで済む。
おわりに: Works on My Machine? Works on ANY Machine!
このシリーズの始まりだった「自分の PC では動くんですが…」という言い訳は、もう通用しない。私たちは Linux、OS、を理解し、Docker、Container、で環境を隔離し、Docker Compose で複雑なサービスを一枚のファイルに定義できるようになった。
つまり、自分で書いた docker-compose.yml さえあれば、自分の PC でも、チーム長の PC でも、AWS サーバーでも、どこでもまったく同じハンバーガーセットを 1 分で用意できる。
私たちはもう「ただのコーダー」ではない。自分のコードだけでなく、そのコードが動く世界、環境、まで設計できる「エンジニア」になった。これで [Works on My Machine] シリーズを終える。
次のシリーズからは、この堅牢なインフラの上で、面倒なデプロイ作業を機械に任せる [The Pipeline: CI/CD 自動化] の世界へ進んでいこう。


