TT Lab
はじめる
学ぶ 学習パス コース

K8sを壊してみよう — 犯人は自分のYAML

正常なのに配送されないのはなぜ?

TT Labで続きを見る

一言でいうと

Serviceが存在するという事実、Podが実行中であるという事実、ユーザーのリクエストが成功するという事実は、それぞれ別の証拠です。

なぜ必要なのか

宅配便の照会サービスの画面が止まりました。担当者は、kubectl get podsでRunningを見て、「Kubernetesは正常」と答えます。ところが、顧客のリクエストは依然として失敗します。誰の観測が間違っているのでしょうか。どちらも事実である可能性があります。RunningはPodのライフサイクルの状態であり、ユーザーがリクエストしたアドレスで正しいレスポンスを受け取ったという意味ではありません。異なる問いの答えを1つの単語にまとめてしまうと、障害分析が見当違いの方向へ進みます。

このコースでは、自分自身が犯人になります。ラボ用のVMの中で小さなHTTPサービスを作成したあと、Serviceのセレクター、readinessのパス、コンテナのコマンドを1つずつ変更します。症状を予想し、実際のレスポンスを観測したうえで、原因を絞り込んで復旧します。実験の対象は、labhub-mysteryネームスペースのparcelだけです。ホストクラスターのノード・CoreDNS・CNI・etcdを止める課題ではなく、他の人のサーバーに触れる理由もありません。

YAMLのインデントと、Deployment・Serviceの基本的な役割、kubectl get・describe・patchの使い方を理解してから始めてください。初めてコマンドラインを学ぶコースではなく、コマンドが成功したあとでも業務が失敗しうることを学ぶ、中級のラボです。すべての障害は学習者が意図的に作り、復旧よりも先に、障害発生時の証拠を残します。

どう動くのか

Deploymentは、望ましいPodテンプレートとレプリカ数を宣言します。コントローラーがReplicaSetとPodを作成して、その宣言を実現します。Serviceは別の役割です。app=parcelのようなセレクターで対象のPodを選び、一定のClusterIPとポートを提供します。EndpointSliceには、そのServiceが選択したアドレスと準備状態が表示されます。Serviceオブジェクトを作成したからといって、自動的に正しいPodが選択されるわけではありません。

Service selector ── 비교 ── Pod metadata.labels
        │ 일치한 대상
        ▼
EndpointSlice의 주소와 ready 조건
        │ 대상 포트로 전달
        ▼
컨테이너의 HTTP 응답

ラボの正常値は、ServiceとPodの両方がapp=parcel、Serviceポートとターゲットポートの両方が8080です。アプリは、/へのリクエストに対して、labhub-mystery-v1という1行を返します。ポートが開いているかだけを見るのではなく、レスポンスの本文も確認する理由は、ほかのプロセスや誤ったサービスの成功レスポンスと区別するためです。200という数字1つも、どのリクエストでどの本文を確認したのかがなければ、十分な証拠ではありません。

Serviceのselectorをapp=missingに変えると、何が最初に変わるでしょうか。DeploymentのPodテンプレートは変えていないので、Podを再起動する理由はありません。アプリのPod IPに直接リクエストすれば、依然として応答します。一方、セレクターに合うPodがないため、ServiceのEndpointSliceから対象が消え、Service IPへのリクエストは失敗します。このとき、コンテナイメージを再ビルドしたり、Podを削除したりするのは、原因に合わない行動です。

空のEndpointSliceを読むプログラムも注意が必要です。endpointsが空の配列であることもあれば、nullで表現されることもあります。どちらも、選択されたアドレスがない状態として処理すべきで、パーサーの例外で観測そのものを諦めてはいけません。逆に、アドレスが1つあるからといって、すぐに正常だと考えてもいけません。次のモジュールで見るように、readyがfalseのアドレスが記録されることがあります。アドレスの存在と、ルーティング可能な対象の数を分けて見てください。

実験中は、変化がすぐにすべての場所で同時に現れるわけではありません。Serviceを変更した直後、コントローラーがEndpointSliceを更新するのに、短い時間が必要です。コマンドの終了コード0は、APIが変更を受け付けたという意味であって、データパス全体への反映が終わったという意味ではありません。観測ツールは一定時間、状態を読み直しますが、エラーを修正したり、状態を偽造したりはしません。時間が経っても条件に合わなければ、変更した値と観測した値を、もう一度比較する必要があります。

現場での姿

ロールアウトのあとにリクエストが失敗するなら、まず範囲を絞り込みます。すべてのリクエストなのか特定のパスなのか、すべてのPodなのか一部なのか、Service IPでも失敗するのか、Pod IPは応答するのかを確認します。DNS・Ingress・TLS・アプリケーションの依存関係を一度に推測すると、仮説が多くなりすぎます。このラボは、DNS名や外部のIngressを経由しないClusterIPへのリクエストと、Podへの直接リクエストを比較します。したがって、ここで通過したからといって、インターネット上のDNSやTLSまで検証したわけではありません。

たとえば、Podへの直接のレスポンスは成功して、Serviceだけが失敗するなら、サーバープロセスが完全に死んでいるという仮説は弱まります。次に、セレクター、EndpointSlice、targetPortを見ます。セレクターが同じで、Readyなアドレスもあるのにサービスだけが失敗するなら、ターゲットポートの数字が、実際の待ち受けポートと同じかどうかを問えます。反対に、Podへの直接リクエストも失敗するなら、プロセスの待ち受けアドレス・ポート・ログ・終了状態を先に確認する根拠が生まれます。

仮説を支持する証拠と、仮説を確定する証拠は異なります。Pod IPへのリクエストが成功したからといって、すべてのネットワーク層が正常なわけではありません。観測したリクエストの経路だけが成功したのです。運用では、複数のレプリカ、NetworkPolicy、サービスメッシュ、外部のロードバランサーが加わることがあります。このコースは、1つの変数を変えて症状と原因を結び付ける基礎的な実験であり、すべての障害を3つのタイプに分類する万能の診断法ではありません。

職務との関連についても、範囲を区別します。2026-09-10に確認したCanonical SREの求人は、Linux・Python・ネットワークとKubernetesの運用能力を扱っています。このコースの観測・原因の切り分け・復旧の練習は、その要件を学習課題に置き換えたものであり、企業の採用試験や提携課程ではありません。コマンドをたくさん覚えることよりも、どの層を検査したのかを説明し、次の人が同じ観測を再現できるようにする能力に焦点を置きます。

詳細な診断の順序は、Kubernetes公式のServiceデバッグドキュメントと比べてみてください。このラボのポート・本文・ネームスペースは、教育用に定めた値です。

次の確認ですること

クイズで、Running・EndpointSlice・実際のHTTPの意味を区別します。最後のモジュールのラボのステップ1–3では、正常の基準、セレクターの誤り、セレクターの復旧を、それぞれ観測JSONとして保存します。障害を修正したあとは、当時の空の対象一覧を再び見ることができないので、復旧の前に記録してください。その記録は、学習者が作成する実験ノートであり、偽造できない監査ログではありません。