自分の PC では動いたアプリが、別のサーバーでは動かない。 Docker は、アプリに必要なファイルをイメージにまとめ、同じものを配って起動しやすくします。
では、そのサーバー自体が止まったらどうするのでしょうか。 別のサーバーで起動し直す、台数を増やす、順番に更新する。 この章では、Docker でできることから始めて、複数のマシンでアプリを動かし続ける Kubernetes の役割へ進みます。
GOAL この章のゴール
- イメージを作り、配り、起動する流れを説明できる
- 複数のマシンでアプリを動かすときに必要な仕事を挙げられる
- Kubernetes がコンテナを動かす単位である Pod を説明できる
Build、Share、Run
まず、アプリを別のマシンへ届ける流れを見ます。
- Build: アプリに必要なファイルを、イメージにまとめます。
- Share: イメージを保管・配布する場所へ置き、使うマシンにダウンロードします。
- Run: そのイメージからコンテナを起動します。
この配布先をレジストリと呼び、置く操作を push、取得する操作を pull と呼びます。
この流れの要は、イメージが変更されないことと、内容のハッシュで識別されることです。
Docker のドキュメントは「イメージは不変 (immutable) で、一度作られたら変更できない」と書いています。
Kubernetes のドキュメントは、イメージの参照の仕方を 2 つに分けています。
タグ (:v1.2.0) は「同じ系列のイメージのバージョンを区別する名前で、別のイメージを指すように付け替えられる」のに対し、ダイジェスト (@sha256:…) は「イメージの内容のハッシュで、変わらない」ものです。
ghcr.io/example/web@sha256:3f1a… という参照は、世界中どこで pull しても同じバイト列を指します。
CI で作ったイメージをステージングと本番で同じダイジェストに固定すれば、イメージ内のライブラリやアプリを揃えられます。
外から渡す設定やファイル、ホストの違いは別に管理します。
この「1 回作って、どこでも同じものを動かす」ためのイメージを ゴールデンイメージ と呼びます。
Kubernetes のドキュメントはコンテナの利点として「開発、テスト、本番で環境が一貫する。ラップトップでもクラウドでも同じように動く」を挙げていて、これはゴールデンイメージの効果そのものです。
VM の世界でも AMI を焼いて配る運用はありましたが、イメージが数 GB で、焼くのに分単位かかりました。
コンテナイメージは層の差分なので、COPY した数 MB だけが新しい層になります。
AWS の語彙でいえば、イメージは AMI、レジストリは ECR、Dockerfile は Packer のテンプレートに相当します。
違いは粒度と速さで、考え方は同じです。
ゴールデンイメージが消す差異、消さない差異
イメージが固定するのはルートファイルシステムの中身です。 OS のライブラリ、言語ランタイム、アプリのコード、設定ファイルの初期値。 ここまでは、どのホストでも同じです。
イメージの外に残るものもあります。 環境変数、接続先の DB のアドレス、秘密情報、永続データ、そして カーネル です。 前の章の通り、コンテナはホストのカーネルを共有するので、ホストのカーネルが古ければ新しいシステムコールは使えません。 「開発機 (macOS の Linux VM) では動いたのに本番 (古い Amazon Linux) で動かない」はここで起きます。 イメージが同じでも、カーネルが違うからです。
1 台の Docker に無いもの
ここまでの話は 1 台のホストで完結しています。
docker run が話しかける相手は、そのホストで動いている Docker デーモンだけです。
Docker Compose も、1 つの Docker デーモンに対して複数のコンテナをまとめて宣言する道具で、対象は 1 台です。
本番で動かすときに何が要るかは、Kubernetes のドキュメントの「Kubernetes が必要な理由と、できること」の一覧が、そのまま答えになっています。 複数ホストにまたがる管理で、Kubernetes が引き受ける仕事を挙げます。 単一ホストでの再起動や名前解決など、Docker にも備わる機能はあります。
- 自己修復。「落ちたコンテナを再起動し、置き換え、ヘルスチェックに応答しないコンテナを止め、準備ができるまでクライアントに見せない」。
- 自動化されたロールアウトとロールバック。「望む状態を書くと、Kubernetes は実際の状態を、制御された速さで望む状態に変えていく」。v1 の横で v2 を起動し、問題なければ v1 を止める手順が、これにあたります。
- 水平スケール。「コマンド 1 つ、UI、または CPU 使用率に応じて自動で、アプリの数を増減する」。
- 自動のビンパッキング。「各コンテナに必要な CPU とメモリを伝えると、資源を最大限使えるようにコンテナをノードに詰める」。つまり、どのホストに空きがあるかを見て、そこで起動する判断です。
- サービスディスカバリとロードバランシング。「コンテナを DNS 名か IP で公開し、負荷が高ければ通信を分散する」。
- 秘密情報と設定の管理。「イメージを作り直さずに、秘密情報と設定を配り、更新する」。
このうち 1 から 4 は、ホストが 1 台なら手作業やスクリプトでもどうにかなります。 ホストが 2 台以上になった瞬間に、「どのホストで」「落ちたらどこで」を決める主体が要るようになります。
複数ホストが問題を変える
6 つのうち、「どのホストで」が絡むものは質が違います。 host-2 が深夜に落ちたとき、その上の api コンテナを host-3 で起動し直し、web からの接続先を書き換え、db のデータが host-2 のディスクに残っている問題を扱う、というのを人がやるのは無理です。 「気付く」「決める」「実行する」「宛先を直す」を全部プログラムにする必要があります。
Kubernetes では、この「気付く」もプログラムの仕事です。 Kubernetes のドキュメントによると、各ホストは自分の状態を定期的に報告していて、報告が途絶えたホストは一定時間後に「状態不明」と記録され、既定ではさらに 5 分後に、そのホスト上のコンテナの立ち退きが始まります。 入門チュートリアルの言い方では「インスタンスを載せているノードが落ちたり消されたりすると、Deployment controller がクラスタ内の別のノードにインスタンスを作り直す」ことになります (Deployment という言葉は 5 章で導入します)。
このように複数のホストを見張り、配置と復旧を行うプログラムを、一般に オーケストレーターオーケストレーター複数のホストを見張り、コンテナの配置、死活監視、復旧、宛先の更新を行うプログラム。Kubernetes、ECS、Nomad など。用語集で見る と呼びます。 オーケストレーターが 1 つにまとめて管理するホストの集まりが、前の章で出てきた「クラスタ」です。 利用者は「web を 2 つ、api を 2 つ、db を 1 つ」と宣言し、どのホストで動かすかは書きません。 オーケストレーターが空きを見て配置し、死活を見て、落ちたら別のホストで起動し直し、宛先を更新します。 Kubernetes はこのオーケストレーターの 1 つで、2026 年現在は事実上の標準です。 ECS も同じ役割のもので、AWS に閉じている代わりに覚えることが少ない、という位置づけです。
ここで扱う単位が「コンテナ」から「宣言」に変わる点に注意してください。
docker run は「起動しろ」という命令でしたが、オーケストレーターに渡すのは「こうあってほしい」という状態です。
この「こうあってほしい」を書いておき、現実との差を埋め続けるプログラムが Kubernetes の中心にあります (4 章と 5 章で見ます)。
Kubernetes が動かす単位: Pod
オーケストレーターに「web を 2 つ」と頼むとき、数える単位が要ります。 docker compose では、その単位は service で、1 つの service は 1 種類のコンテナです。 Kubernetes が数える単位は PodポッドKubernetes が配置と起動の単位として扱う、1 つ以上のコンテナの集まり。同じ Pod のコンテナは同じノードで動き、IP とネットワーク namespace を共有する。用語集で見る です。 Kubernetes のドキュメントは、Pod を「Kubernetes で作成、管理できる最小のデプロイ単位」であり、「共有のストレージとネットワーク資源を持つ、1 つ以上のコンテナの集まりと、それをどう動かすかの指定」だと定義しています。
Pod が必要になる場面は、docker compose でも思い当たります。
nginx のログを S3 に転送する小さなコンテナを、nginx の隣に置きたいとします。
転送側から nginx にアクセスするときに localhost で届き、ログのディレクトリを共有でき、2 つが必ず同じホストで動いてほしい。
この「必ず一緒に動かしたい組」を 1 つの単位にしたものが Pod です。
ドキュメントの言葉では、Pod の中身は「常に同じ場所に置かれ、同時にスケジュールされ、共有された文脈の中で動く」もので、「Pod の各コンテナはネットワーク namespace を共有し、IP アドレスとポートも共有する」ので、「Pod の中でだけ、コンテナ同士は localhost で通信できる」とあります。
ストレージも Pod 単位で共有できます。
ドキュメントは Pod を「アプリケーション専用の論理的なホスト」にたとえています。
1 章の Linux の言葉で言えば、Pod の共有される文脈とは「namespace と cgroups の集まり」で、コンテナを隔離しているのと同じものです。
多くの Pod には、コンテナが 1 つしか入っていません。 ドキュメントによると「1 Pod に 1 コンテナ」が最もよくある使い方で、その場合の Pod は、コンテナを Kubernetes の単位に包むための入れ物です。 また、Pod は「比較的短命で使い捨ての存在」として設計されていて、「個々の Pod を直接作ることは、1 つだけの Pod であっても滅多に無い」と書かれています。 Pod を作るのは、5 章で導入する Deployment のような上位のリソースの仕事です。
ハンズオン: イメージのダイジェストを見る
自分専用クラスタで動いている Pod (クラスタの部品自身も Pod として動いています) が、どのイメージをどのダイジェストで動かしているかを見ます。
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.containerStatuses[0].imageID}{"\n"}{end}' | head出力例
cilium-xxxxx quay.io/cilium/cilium@sha256:1e4c…
coredns-xxxxx registry.k8s.io/coredns/coredns@sha256:8f1b…imageID にはダイジェストが入ります。
Kubernetes はタグで pull しても、実際に動かしたイメージをダイジェストで記録します。
同じタグでも中身が差し替わることがあるので、障害調査ではこちらを見ます。
ふりかえり
Qゴールデンイメージが固定しないものはどれ?
イメージが固定するのはルートファイルシステムの中身です。カーネルはホストのもので、イメージには含まれません。
Q指す先が後から変わらないのはどちら?
タグは付け替えられます。ダイジェストは内容から計算されるので、同じダイジェストは同じバイト列を指します。
QKubernetes のドキュメントが「Kubernetes が提供するもの」に挙げていないのはどれ?
ログ、監視、アラートの方法は「Kubernetes が指定しないもの」の側に書かれています。自己修復と水平スケールは提供する側の一覧にあります。
参考
- Kubernetes ドキュメント「Overview」(Why you need Kubernetes and what it can do / What Kubernetes is not / Going back in time): https://kubernetes.io/docs/concepts/overview/
- Kubernetes ドキュメント「Images」(タグとダイジェスト): https://kubernetes.io/docs/concepts/containers/images/
- Kubernetes ドキュメント「Pods」: https://kubernetes.io/docs/concepts/workloads/pods/
- Kubernetes ドキュメント「Nodes」(ハートビートと node controller): https://kubernetes.io/docs/concepts/architecture/nodes/
- Kubernetes チュートリアル「Using kubectl to Create a Deployment」: https://kubernetes.io/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/
- Kubernetes ブログ「Updated: Dockershim Removal FAQ」(docker build のイメージはそのまま動く): https://kubernetes.io/blog/2022/02/17/dockershim-faq/
- Docker docs「What is an image?」: https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/
- Docker blog「Announcing Docker Machine, Swarm, and Compose」(2014-12-04): https://web.archive.org/web/20141210000000/http://blog.docker.com/2014/12/announcing-docker-machine-swarm-and-compose-for-orchestrating-distributed-apps/
- Docker blog「Docker 1.12: Now with Built-in Orchestration!」(2016-06-20): https://www.docker.com/blog/docker-1-12-built-in-orchestration/
- AWS blog「EC2 Container Service (ECS) preview」(2014-11-13): https://aws.amazon.com/blogs/aws/cloud-container-management/
- docker.com (2015 年のキャッチコピー): https://web.archive.org/web/20150801000000/https://www.docker.com/