K8s 基礎 #1 Kubernetes とは — なぜコンテナオーケストレーターが必要か

Docker 基礎シリーズ 6 編を終えると、コンテナ 1 台のビルド・実行・データ保存・レジストリへの公開までが手につきます。次に自然に出てくる問いはこれです — コンテナ 1 台は立てられる。では 100 台を、しかも死んだら自動で立て直し、トラフィックに合わせて増やしたり減らしたりしながらどう運用するのか? その問いに対するデファクト標準の答えが Kubernetes (K8s) です。このシリーズはその K8s を最初から追っていきます。

このシリーズは K8s 基礎 7 編です。

  • #1 Kubernetes とは — なぜコンテナオーケストレーターが必要か ← この記事
  • #2 ローカル環境 — minikube / kind / Docker Desktop k8s
  • #3 kubectl と最初の Pod
  • #4 Deployment / ReplicaSet
  • #5 Service — ClusterIP / NodePort / LoadBalancer
  • #6 ConfigMap / Secret
  • #7 Namespace とラベル

この記事では単一コンテナツールの限界と K8s が解決する問題を先に押さえ、続いて desired state / reconcile loop と control plane / worker node の全体像を整理します。実習は #2 から始めます。

ヒント
本シリーズの実習記事は YAML マニフェストを手で書いていきます。インデント 1 つ、引用符 1 つがずれただけで kubectl apply が意図と異なるエラーを返し、原因をクラスタ側から逆に辿ることになります。マニフェストを適用する前に utilrepo の YAML 検証ツール に貼り付けておくと、構文エラーを行・列番号で示してくれます。utilrepo はブラウザで動作する軽量な Web ユーティリティ集で、秘密情報が外部に出ず --- で連結された複数文書マニフェストやタブ・スペース混在のような頻出する罠もまとめて拾ってくれます。

docker run だけでは足りなくなる瞬間 #

Docker 基礎シリーズの最終話 #6 で、コンテナ 1 台を運用向けに立てるコマンドを整理しました。こんな形です。

コンテナ 1 台 — Docker 基礎の到達点
docker run -d --name myapp \
  --restart unless-stopped \
  -p 127.0.0.1:8000:8000 \
  -v myapp-data:/app/data \
  ghcr.io/curtis/myapp:1.0.0

ホスト 1 台にコンテナ 1 つ、あるいは Compose で同じホストに web + db + cache をまとめる程度であれば、これで十分です。しかしトラフィックが増え、無停止デプロイが要求され、サーバが 1 台落ちてもサービスは生きていなければならない段階になると、同じツールでは解けない問題が出てきます。

1) ノードが 1 台落ちるとサービスも一緒に落ちる。 docker run で立てたコンテナはそのホストに紐付いています。ホストが再起動したりディスクが壊れたりすると、コンテナを別マシンに移す標準的な方法がありません。人が新しいマシンに SSH で入って同じコマンドを叩き直す必要があります。

2) コンテナが死んだときの自動復旧が弱い。 --restart unless-stopped は同じホスト内でしか効きません。ホスト自体が生きていれば Docker デーモンがコンテナを再起動しますが、ホストが落ちればそこまでです。さらに「プロセスは生きているのに応答しない」といった部分障害は捕まえられません。

3) インスタンス数をトラフィックに合わせて調整する作業が全部手動。 普段 2 台で十分な API サーバをキャンペーン期間中だけ 10 台に増やすには、誰かが新しいマシンを用意して、同じイメージを引いて同じコマンドを 10 回実行し、前段のロードバランサ設定まで手作業で書き換える必要があります。終わったらまた減らします。手作業の量がかなり多くなります。

4) 無停止デプロイ(ローリングアップデート)を自前で組む必要があります。 v1.0.0 から v1.1.0 に切り替えるとき、10 台のうち 1 台ずつ新しいバージョンに置き換えながらヘルスチェックを通ったコンテナにだけトラフィックを流す流れを、手書きで組むのは厄介です。失敗時の自動ロールバックまで入れるとさらにそうです。

5) 設定・秘密値・サービスディスカバリに標準がありません。 DB の接続情報を環境変数で渡したいのですが、環境(stage/prod)ごとに異なる値を安全に注入する標準の方法が、Docker の基本ツールにはありません。コンテナ A がコンテナ B をどんな名前で見つけるか(サービスディスカバリ)も、自分で決める必要があります。

これら 5 つが、ホスト 1 台・コンテナ 1 単位のツールの限界です。そしてこの限界を解くのが コンテナオーケストレーター の役割です。

コンテナオーケストレーターが解く問題 #

上の 5 項目を 1 行で抽象化するとこうなります。

複数のマシンを 1 つの塊(クラスタ)としてまとめ、「どんな形で動くべきか」だけを宣言すれば、システムがその形を維持してくれる。

この考え方を呼ぶ名前が 宣言的 desired statereconcile loop(整合ループ) です。

  • 宣言的 (declarative) — 「どう作るか(how)」ではなく 「何であるべきか(what)」 だけを書きます。例: 「myapp コンテナが常に 3 つ立っている状態であるべき」。
  • reconcile loop — システムが絶え間なく 現在の状態 (actual state)望ましい状態 (desired state) を比較し、差があればその差を縮める方向に動きます。コンテナ 1 つが死んで 2 つになったら、システムが自分で 1 つ追加して 3 つに戻します。
reconcile loop の図
   ┌──────────────────┐         ┌──────────────────┐
   │  desired state   │         │   actual state   │
   │  "myapp 3 replicas"│ ◀───▶  │ 現在立っている 2 つ │
   └──────────────────┘ compare └──────────────────┘
                  │                       │
                  └────────┬──────────────┘
                    ┌─────────────┐
                    │  controller │  → 差 → 「もう 1 つ立てろ」
                    └─────────────┘

このモデルが K8s の核心の発想です。人は「こうあるべきだ」とだけ宣言し、その宣言を維持し続ける作業はシステムが行います。ノードが 1 つ落ちても、新バージョンを段階的にロールアウトしても、トラフィックに応じて 5 つを 8 つに増やしても — すべて同じモデルの中で解決されます。

Kubernetes の出自 #

このモデルは K8s が初めて作ったものではありません。Google が社内で 10 年以上運用していた Borg というクラスタ管理システムが起源です。Google の検索・Gmail・マップなどのサービスは Borg の上で動いており、その運用経験から次世代システムの Omega が生まれました。さらにその発想をオープンソースで作り直したのが Kubernetes です。

おおまかな流れは次の通りです。

  • 2014 年 6 月 — Google が Kubernetes の初期版をオープンソースで公開
  • 2015 年 7 月 — Kubernetes 1.0 リリース。同時期に CNCF (Cloud Native Computing Foundation) が発足し、K8s が最初のプロジェクトとして寄贈される
  • 以降 — 主要クラウド事業者(AWS EKS, GCP GKE, Azure AKS)がすべてマネージド K8s を提供し、コンテナオーケストレーションの事実上の標準となる

「Kubernetes」はギリシャ語で 舵取り(helmsman) の意味です。略称の K8s は K と s の間の 8 文字を縮めて書いた表記です。両者はよく混在して使われ、同じものを指します。

K8s クラスタの全体像 #

K8s クラスタは 2 種類のノードで構成されます — control planeworker node です。

K8s クラスタ — コンポーネントを一覧で
                ┌───────────────────────────────────┐
                │           Control Plane            │
                │                                    │
   kubectl ───▶ │   kube-apiserver  ◀──────┐         │
                │        │                 │         │
                │        ▼                 │         │
                │       etcd               │         │
                │                          │         │
                │   kube-scheduler  ───────┤         │
                │   kube-controller-manager ─┤       │
                │   cloud-controller-manager ┘       │
                └────────────┬───────────────────────┘
        ┌────────────────────┼────────────────────┐
        ▼                    ▼                    ▼
 ┌────────────────┐  ┌────────────────┐  ┌────────────────┐
 │  Worker Node 1 │  │  Worker Node 2 │  │  Worker Node 3 │
 │                │  │                │  │                │
 │   kubelet      │  │   kubelet      │  │   kubelet      │
 │   kube-proxy   │  │   kube-proxy   │  │   kube-proxy   │
 │   containerd   │  │   containerd   │  │   containerd   │
 │                │  │                │  │                │
 │   ┌──────┐     │  │   ┌──────┐     │  │   ┌──────┐     │
 │   │ Pod  │ ... │  │   │ Pod  │ ... │  │   │ Pod  │ ... │
 │   └──────┘     │  │   └──────┘     │  │   └──────┘     │
 └────────────────┘  └────────────────┘  └────────────────┘

Control plane — クラスタの脳 #

control plane はクラスタの desired state を受け取って保管し、その状態が実際に維持されるように決定を下す部分です。

  • kube-apiserver — クラスタの正面玄関。kubectl を含むすべてのツールはこの API サーバに REST でリクエストを送ります。認証・認可・検証を経て、結果を etcd に書き込みます。
  • etcd — クラスタのすべての状態(どの Pod がどこにあるべきか、どの Service が定義されているか、など)を保管する分散 KV ストア。クラスタの単一の真実の源 (source of truth) です。
  • kube-scheduler — 新しく作られた Pod に「どの worker node に行くか」を決めます。ノードの利用可能リソース・ラベル・制約条件を見て決定します。
  • kube-controller-manager — 複数のコントローラを 1 プロセスにまとめて動かします。上で見た reconcile loop を実際に回す主体です。例: ReplicaSet コントローラは「Pod 3 個が立っているべき」という宣言と実際の数を絶えず比較します。
  • cloud-controller-manager — クラウド (AWS/GCP/Azure など) の上に立っているクラスタで、そのクラウドのロードバランサ・ディスク・ノードといったリソースを K8s リソースに繋ぎます。自前のデータセンターに立てたクラスタには無いことがあります。

Worker node — 仕事をするところ #

worker node は実際にコンテナが動くマシンです。普通、ノード 1 台に次の 3 つがインストールされています。

  • kubelet — ノードのエージェント。apiserver から「このノードでこの Pod を動かせ」という指示を受け、コンテナランタイムに実際の実行を依頼します。コンテナのヘルスを観察し、状態を apiserver に報告し直す役目もあります。
  • kube-proxy — ノードのネットワークルール (iptables/IPVS など) を管理し、Service (#5) の仮想 IP が実際の Pod に届くようにします。
  • コンテナランタイム — 実際にコンテナを実行する層。現在の標準は containerd で、CRI-O もよく使われます。かつて K8s が Docker デーモンを直接使っていたシム (dockershim) は Kubernetes 1.24 (2022) から 削除されました。なので「K8s は Docker を使う」という言い方はもう正確ではありません。K8s は OCI 互換ランタイムを使い、Docker が作ったイメージ (OCI イメージ) はそのまま動きます。

流れを一気に #

kubectl apply -f deployment.yaml を叩いた瞬間に起きることを縮めるとこうなります。

  1. kubectl が apiserver に「この Deployment が存在すべき」とリクエスト
  2. apiserver が認証・検証の後、その事実を etcd に記録
  3. controller-manager 内の Deployment コントローラが「Pod が 3 つ追加で必要」と判断し、Pod オブジェクトを 3 つ作成
  4. scheduler がそれぞれの Pod にノードを割り当て
  5. 該当ノードの kubelet がその情報を見て、containerd にコンテナの実行を指示
  6. kubelet が結果を apiserver に報告 → ユーザは kubectl get pods でその状態を確認

全体が apiserver を経由して etcd に書き込まれた desired state を、各コンポーネントが自分の領域で reconcile する という 1 つのパターンに沿って流れます。

主要リソースを 1 つの表に #

K8s で人が扱う単位は コンテナではなく、リソース (resource, object) です。Pod、Deployment、Service といった名前です。シリーズの後の記事で 1 つずつ深く扱いますが、先に 1 つの表で形を押さえておきましょう。

リソース一行定義扱う回
Podコンテナ 1 つ、または一緒に動く数個をまとめた K8s の最小実行単位。IP を共有#3
ReplicaSet「この Pod テンプレートが N 個立っているべき」を保証#4
DeploymentReplicaSet の上でローリングアップデートやロールバックを扱う上位リソース。人が一番よく触る単位#4
Service複数の Pod の前面に安定した仮想 IP/DNS 名を付けるネットワーク抽象#5
ConfigMap設定値 (環境変数、設定ファイル) をコードと分離して注入#6
Secret秘密値 (パスワード、トークン、証明書) を分離して注入。base64 エンコード + アクセス制御#6
Namespaceクラスタ内部の論理区画。権限・リソース上限・名前衝突を分離#7

この 6 つを身につければ、日常的な作業のかなりの部分を処理できます。さらに深いリソース (Job/CronJob, StatefulSet, Ingress, PersistentVolume など) は K8s 中級で扱います。

Docker Compose とは何が違うのか #

Docker 基礎シリーズの読者にまず思い浮かぶ比較対象は Docker Compose です。Compose も YAML 1 ファイルに複数のコンテナを宣言的に定義する点で K8s と似ています。しかし両者は解いている問題の範囲が異なります。

Docker ComposeKubernetes
扱う範囲1 ホスト 内のマルチコンテナ複数ノード(クラスタ) 上のコンテナ群
自己復旧同じホストでの再起動程度ノードが死んでも別ノードに自動再配置
スケーリング--scale で手動desired state で宣言 → 自動維持、オートスケーリングも可能
無停止デプロイ自前で書くDeployment のローリングアップデートが標準
設定/秘密.envsecrets 程度ConfigMap / Secret が 1 級リソース
適する用途ローカル開発、単一サーバ運用複数マシン運用 / 本格 SLA 環境

Compose は 1 ホストでの開発・運用を簡単にするツールで、K8s はその限界を超える運用環境で使われます。両者は競合するというより、異なる段階の問題 を解いています。ローカル開発は今も Compose のほうが軽く、本番運用は K8s またはそのマネージドサービスが自然です。

このシリーズで扱う範囲 #

K8s は広いトピックです。この 7 編でどこまで扱うかを先に決めておきます。

この K8s 基礎 シリーズが扱うこと:

  • ローカルに K8s クラスタを 1 台立てる (#2)
  • kubectl で初めての Pod を立てて観察する (#3)
  • Deployment で複数の Pod を安定して動かし、新バージョンをデプロイする (#4)
  • Service で内外からのアクセスを作る (#5)
  • ConfigMap/Secret で設定・秘密を扱う (#6)
  • Namespace とラベルでクラスタを整理する (#7)

この 7 編を終えると ローカルクラスタ 1 台に小さな Web アプリを K8s 流にデプロイして運用できる水準 に到達します。

扱わない部分は後続のシリーズに回します。

トピックシリーズ
Ingress, PersistentVolume, StatefulSet, Job/CronJob, RBACK8s 中級
Helm, kustomize, GitOps (Argo CD/Flux), マルチクラスタK8s 実践
クラスタの直接ブートストラップ (kubeadm)、アップグレード、バックアップ/リストア、セキュリティハードニングK8s 運用
EKS / GKE / AKS のマネージドクラスタ運用クラウド K8s シリーズ

基礎シリーズでは YAML でリソースを定義し、kubectl でクラスタに適用し、結果を覗き見る という 1 サイクルを身につけることに集中します。

まとめ #

この記事で押さえた絵:

  • 単一コンテナのツールだけでは ノード障害・自動復旧・スケーリング・無停止デプロイ・設定管理 の 5 つが解けません。ここがコンテナオーケストレーターが登場した背景。
  • K8s の核心の発想は 宣言的 desired state + reconcile loop。「何であるべきか」だけ書けば、システムがその形を絶えず維持します。
  • K8s は Google 社内システム Borg の発想をオープンソースで作り直したものであり、2015 年の 1.0 リリース以降 CNCF に移管され、事実上の標準になった。
  • クラスタは control plane (kube-apiserver / etcd / kube-scheduler / kube-controller-manager / cloud-controller-manager) と worker node (kubelet / kube-proxy / containerd) に分かれる。すべての流れは apiserver を通って etcd に書かれた desired state を、各コンポーネントが reconcile するパターンに収まる。
  • 日常的に扱う主要リソースは Pod / Deployment / Service / ConfigMap / Secret / Namespace の 6 つ。このシリーズはこの 6 つを 1 編ずつ扱う。
  • Docker Compose は 1 ホスト、K8s は複数ノード。 似て見えるが、異なる段階の問題を解く。

次 — ローカルで K8s を 1 台立てる #

この記事では K8s がなぜ必要かと全体像を整理しました。次の記事ではローカル環境で実際にクラスタを立てる方法を扱います。

#2 ローカル環境 — minikube / kind / Docker Desktop k8s では (1) ローカルで K8s を試す 3 つの方法 (minikube / kind / Docker Desktop の内蔵 K8s)、(2) それぞれの長所短所と、どの場合に何を選ぶか(3) kubectl のインストールと最初のコンテキスト切り替え(4) kubectl get nodes で最初のクラスタを確認する方法 までを扱います。ローカルクラスタを準備したら #3 に進みます。

X