
2026/10/17 12:16
Kubernetes との初対面:懐疑者の物語(2024)
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
本ガイドはインフラストラクチャの観点から Kubernetes を解明し、「ブラックボックス」あるいは不必要に複雑なツールという認識への挑戦を行います。著者は、広範なシステム管理経験に基づき、Kubernetes が成功するのは、ユーザー定義の設定と実際のシステム状態を継続的に調整する自動化された「コントールループ」によることを主張しています。これらのループは、ホスト選択、自己修復、スケーリングといった重要なタスクを処理し、Services(Virtual IP と DNS によって安定したアイデンティティを提供し、Kube-proxy が単一の障害点を有さない堅牢なロードバランシングを確保する)や Deployments(レプリカを管理し、ロールアウトアップグレードを可能にする)といった主要コンポーネントに依存しています。
重要な区別として、Pods とともに消滅する Ephemeral Volumes と、独立したストレージライフサイクルを持つ Persistent Volumes/Claims の間があります。しかし、著者は現在の制限点についても批判的であり、StatefulSets における順次ライフサイクル管理に関わる混乱を招く「優先度」ロジックや、Horizontal Pod Autoscalers が現時点では単純な CPU メトリクス以上のサポートを提供していないことなどを指摘しています。本テキストはまた、宣言的な設定手法の改善、明示的なクラウドプロバイダ統合、YAML テキスト補間(Helm や KEDA における例)のようなエラー発生のしやすい慣行からの脱却を求めています。結局のところ、これらの基盤メカニズムを理解することにより、組織は表面的なチュートリアルに依存せず、ストレージ戦略やコントローラーパターンのに関する informed な判断を下すことができます。
本文
Kubernetes への懐疑から理解へ:インフラエンジニアの視点による考察
はじめに
システム管理・エンジニアリング・インフラストラクチャ開発の分野において、私は長年 Kubernetes との接点を持つことができませんでした。これは偶然ではなく、ワークロードの配置を物理的なロケーションで厳密に制御する必要がある業務上の制約からくる回避行動でした。
時が経ち、「新しいやり方」「不要な複雑さ」という漠然とした認識のまま、懐疑的見解を抱え続けていました。しかし、意見を実感ではなく事実に基づいて形成する時機が訪れました。ここでは私が学んだことを記述し、Kubernetes の本質的な仕組みを解説します。
既存のチュートリアルはコンポーネントの説明に終始し、背後にある理由やトレードオフについて語ることが不足していました。本稿は網羅性を意図するものではなく、有益と感じた視点からの解説を目指しています。
「標準的」な説明
Kubernetes は任意のワークロードの実行と、以下の機能を提供します:
- 要件の指定(CPU、ディスク、メモリ、インスタンス数など)
- 動的スケーリング(インスタンス数の自動調整)
しかし、より重要なのは Kubernetes があなた方にとって行うことです:
- スケジューリング:アプリケーションがどのホストで実行されるかを選択する
- 自己修復:クラッシュしたインスタンスの再起動、障害ノードからの自動移動
- サービス発見:DNS ベースでのサービスアクセス提供
これを実現するためには、ワークロードをDocker イメージとしてパッケージ化するだけで十分であり、これは合理的な対価と言えます。
基本概念の定義
クラスターを構成する基本要素は以下の通りです:
- Pod: ワークロードの最小単位(Docker イメージおよびその設定)
- Node: クラスターの物理的なコンピューター(Kubelet を実行中)
- Cluster: Control Plane と Nodes の集合体
- Service: Pod の一群を論理的にグループ化したもの
- Namespace: Cluster の論理的な区画(DNS サーチドメインのような機能)
これらの概念間の関係は図表形式で観察されますが、多くのインフラストラクチャプラットフォームが同様の構造を持つため、特筆すべき点はありませんでした。
多くの場合、この定義自体は正しくても、私の業務には直結する説明ではなかったと感じていました。
私にとって重要な要素
私の分析によると、Kubernetes の価値の大部分はわずか2 つの概念に起因しています。
1. Control Loops(制御ループ)
ワークロードは、Controllers という一組の制御ループによって管理されます。
制御ループの基本動作:
- 観測:特定の変数(センサーを通じて)を監視する
- 行動:望ましい状態を実現するために、必要な制御要素を通じて継続的に動作する
重要な点は、制御要素が必ずしもセンサーの値を直接変化させるわけではないことです。具体的事例は以下の通りです:
| シナリオ | 望ましい状態 | センサー(観測) | 制御要素(行動) |
|---|---|---|---|
| キュー処理 | キューが空になる | キューの深さ | イベント処理サーバーの増減 |
| 負荷分散 | P90 レイテンシーを X ms 以下に維持 | バックエンドからのレイテンシ指標 | 需要に応じた新しい Pod の起動 |
| ヘルスチェック | すべての Node が健全である | Node からのヘルス指標 | クラスターから故障ノードの除外 |
2. Services(サービス)
Service はネットワークレベルの概念であり、バックエンドとなる Pod の集合への安定したアクセスを提供します。
提供される機能
- 安定したアイデンティティ:変更されない(仮想)IP アドレスと DNS レコード
- 負荷分散:流入トラフィックを複数の Pod 間に分配する
- サービス発見:他のコンポーネントがサービスを見つけて通信できるようにする
従来の「クラシック」なクラスターとの比較
Service は、以下の組み合わせに相当する抽象化を提供します:
- Keepalived + NGINX(動的なバックエンドを持つ)を N ノードで実行
- Keepalived の仮想 IP への CNAME レコード
Service の仕組みと kube-proxy
新しい Service が作成されるたびに以下の処理が発生します:
- 仮想 IP の割り当て
- ネットワーク構成の更新:各 Node は
を更新し、宛先が Service の仮想 IP に指定されたパケットを、バックエンドの Pod へと転送するように設定します。netfilter
この設計は、すべての Node 上で動作する Controller(具体的には kube-proxy)によって管理されます。 kube-proxy の主な責任:
- すべての Service オブジェクトの状態を観測し
- 変更のたびに各 Node 上の
ルールを更新するnetfilter
この設計によるメリットとデメリット
この「すべての Node がすべての Service のルールを受信する」設計には、以下のような帰結があります。
✅ メリット
- シングルポイントオブ.FAILURE(SPOF)の排除:Service トラフィックの負荷分散においてすべての Node が役割を果たすため、転送経路に SPOF が存在しません。
- スケーラビリティ:転送容量はクラスターのサイズに合わせてスケールします。
- キャッシュ無効化の不要化:
レベルで DNS エントリのキャッシュ処理を気にする必要がありません。kube-proxy - クライアントの不具合への耐性:悪意のあるクライアント(再接続前に DNS を再解決しないもの)も引き続き動作します。
⚠️ デメリット・課題
- 実装の複雑化:
がすべてのルールを直線的に評価するため、ルールの増加によりパフォーマンス問題が生じる可能性があります。iptables - 不要なトラフィック転送:Pod が別の Node に移動した際、古い DNS エントリが期限切れになるまでトラフィックが複数回転送され、クラスターへの負荷がかかります(ただし、可用性の向上というトレードオフがあります)。
ワークロード管理・ストレージ管理
ワークロード管理(実行方法・時期・場所)
Pod は以下の YAML で手動で作成できます:
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
しかし、この方法には深刻な制限があります:
- ノード障害への耐性なし:Pod をホストする Node が死亡した場合、再スケジューリングされません。
- リソース圧力による驱逐(Eviction)への耐性なし:リソース不足で Pod が驱逐された場合も再スケジューリングされません。
- 需要対応の困難さ:「これらを 2 つ持っておきたい」といった増設需要に対応できません。
したがって、Pod を直接作成する代わりに、以下の Controller を使用すべきです。
ReplicaSet
ReplicaSet は制御ループの一部として、常に正しい数の Pod レプリカが実行されていることを保証します。ノードの故障や驱逐による Pod の消失を自動的に解決します。
apiVersion: apps/v1 kind: ReplicaSet metadata: name: nginx-replicaset spec: replicas: 3 template: spec: containers: - name: nginx image: nginx:1.14.2
制限点:望ましいレプリカ数を更新することは可能ですが、イメージのバージョン変更やポートの変更など、他の設定変更を行うにはリソースを削除して再作成する必要があります。
Deployment
これらの制限に対処するには Deployment を使用します。Deployment は、最終的に正しい数の Pod が正しいイメージで実行されていることを保証します。
「最終的に(eventually)」という点が非常に重要です。
Deployment を利用することで:
- Pod がどのイメージを実行すべきかを更新できます。
- 変更のペースを制御できます。
- 標準的なバージョンアップグレードワークフローに対応できます。
具体的には、新しい ReplicaSet の作成と古い ReplicaSet の段階的縮小により、**ロールアウト式アップグレード(Rolling Update)**が可能になります。
ストレージ管理(データ永続化と移動)
デフォルトでは、Pod 内のコンテナ同士はストレージやファイルシステムを共有しません。共有したい場合は以下の方法を選択します。
Ephemeral Volume(一時的ボリューム)
ライフサイクルが Pod に紐づくボリュームです。Pod が停止するとデータは消去されます。 ユースケース:
- サイドカーからのログ shipping
- メインアプリケーション用のキャッシュを populate するサイドカー
Persistent Volume(永続ボリューム)
ライフサイクルが Pod と独立したストレージです。
PersistentVolumeClaim で定義し、Pod の volumeMounts で使用します。
PersistentVolumeClaim の例:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nginx-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
Pod の定義(PVC のマウント例):
apiVersion: v1 kind: Pod metadata: name: nginx-with-pv spec: containers: - name: nginx image: nginx volumeMounts: - name: html-volume mountPath: /usr/share/nginx/html volumes: - name: html-volume persistentVolumeClaim: claimName: nginx-pvc
注意点:
- 共有ストレージのリスク:複数のユーザー(Pod)が同じ Claim を使用すると、複数書き込みが発生した場合に非常にリスクが高くなります。
- ドライバー依存性の不明確さ:
は NFS、SMB、iSCSI などの異なるドライバーによって裏付けられますが、Pod にはどのドライバーが使用されているかが明確でないため、原子リネームなどの機能は利用できません。Persistent Volume
StatefulSet
インスタンスごとの永続ストレージを持たせたい場合は StatefulSet を使用します。 StatefulSet は単一のリソースで、ワークロード(Pod)、レプリカ数、マウント構成、ボリューム Claim テンプレートなどを定義します。
StatefulSet の特徴:
- 安定したホスト名:Pod は
,nginx-0
などの安定した名前を持ちます。nginx-1 - 順序立てての実行:
- Pod の作成は昇順(0, 1, …)
- Pod の削除は降順(3, 2, …)
- Pod の更新も降順(3, 2, …)
この順序付けについては議論があります。「番号の小さいインスタンスほどコアで安定している」という説明がありますが、クラッシュして再起動された Pod も再番号付けされず、その理由は不明瞭です。なぜ「優先度/安定性」が後付設定になっているのか、あるいは Kubernetes が単に見るべき順序を必要としているのか、理解に苦しみます。
運用上の課題: 現状では Persistent Volume の中身を調査する方法が不明確です。稼働中のコンテナに接続してコマンドを実行する以外の方法がありません。シェルを持たないコンテナの場合、データ確認が困難になります。
スコアカード(総括)
Kubernetes の主要部分である Control Loops、Services、ワークロード、ストレージ について解説しました。これらの概念を通じて、私は以前抱えていた根拠に欠ける懐疑的な意見から、より合理的な視点を持つことができました(※まだクラスターを操作・使用したわけではありませんが)。
良い点
全体として、これらの概念は非常に理にかなっています:
- Service(ネットワークモデルや
を含む)は素晴らしい実装です。kube-proxy - リソース管理のエンジンとして Controller パターンを採用していることは正しい方向だと考えます。
未解決の課題と不満点
一方で、いくつかの疑問点と不満を残しておく必要があります:
1. コントローラーの状態安定性について
ドキュメントには「クラスターは常に変化しており、制御ループが自動的に故障を修正するため、潜在的にクラスターは決して安定した状態に達しない可能性がある」とあります。
- 質問:なぜ状態が不安定かどうかに関係ないのでしょうか?収束できない場合はすぐに知らせてほしいです。
- クラウドネイティブへの対応の欠如:
- キューの深さに基づいた単純な計算(例:
)でスケールする ReplicaSet が欲しいです。キューの深さ / 16 = レプリカ数 - イベントインターフェース(キューの深さ、オープンコネクション、レスポンスレイテンシーなど)の定義が必要です。
- CPU 以外のセンサーを持つ Horizontal Pod Autoscaler のようなものがほしいです。
- キューの深さに基づいた単純な計算(例:
2. ストレージとネットワークの実装について
なぜストレージとネットワークの実装は「ツリー外(out of tree)」である必要があるのでしょうか?(CNI / CSI)
- モジュール性と懸念の分離のためだと仮定していますが、これらを実装するクラウドプロバイダーへの明示的なサポート(例:LoadBalancer が AWS/GCP/Azure をサポートしていること)との整合性が気になります。
3. Stringy Types(文字列型)の問題
Kubernetes コミュニティは、あらゆるものをテキスト埋め込みに基づいてツールを設計しており、あえて自分の人生を難しくしようとしているように見えます。
- RabbitMQ スケーラー(KEDA など):
のタイプがvalue
の値によって変わる不整合(enum であるべきのに String になっている)は論理的ではありません。mode - Helm:テキストテンプレートを何でもかんでも使う設計ですが、なぜ構造化された言語を生成する際にコンピュータを使って文法(スペースなど)を手動で調整する必要があるのでしょうか?
のような中間的なテキストテンプレート表現は不要だと考えます。{{- nindent 4 }}
結論として、Kubernetes のアーキテクチャそのものは理にかなっていますが、ツールチェーンにおける型安全の欠如と文字列ベースの複雑さには依然として課題が残っていると言えます。