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

CNPE — クラウドネイティブプラットフォームエンジニア

リクエストは成功したのに、なぜアプリは準備できないのか

TT Labで続きを見る

目標

実際のCrossplane 2.4で、ネームスペース型のAppを作成し、誤った準備条件・チームの権限・変更の収束・削除の遅延を検証します。 宣言やステータス表示だけで成功と推定せず、実際のPodとHTTP、および所有UIDを合わせて確認します。

なぜ重要なのか

セルフサービスは、作成ボタンだけを自動化する作業ではありません。リクエストの入力と権限を制限し、作成されたアプリを観測し、 変更と削除まで責任を持つ必要があります。同じ名前の新しいオブジェクトや、ほかのチームの応答が、今回のリクエストの成功として混ざってはいけません。 個人VM内で実行されるk3s・Crossplane・関数・実際のコンテナを使います。KWOKや偽のReady Podではありません。 インストールには数分かかることがあります。想定のラボ時間は55分で、もっと必要なら、有効期限が切れる前に時間を延長してください。 セッション終了時にVMとファイルは回収されます。必要な記録は先にダウンロードし、VMの外の本番クラスターには適用しないでください。

用意されているものとツール

actは、該当のステップのファイルを適用するか、明示された実験を実施します。observeは1回だけ参照します。 captureは、現在の設定を直さずに最大75秒観測したあと、指定したJSONを保存します。 gradeは、ファイルを修正したりリソースを変更したりしません。準備中の状態なら、再インストールせずに、同じ実行をもう一度参照してください。 すでに検証された観測ファイルは、後のステップで同じ状態が消えても保存されます。ファイルを手で成功のJSONに書き換えないでください。

ステップ

  1. /root/cnpe-api/initial.jsonに、apiVersion=platform.labhub.local/v1alpha1、kind=App、metadata.name=parcel、metadata.namespace=team-a、spec.replicas=1、spec.channel=stableを書いてください。act 1は、developerアカウントでこのリクエストを適用します。capture 1でaccepted.jsonを保存し、Synced=TrueだがReady=Falseのリクエストと、実際のHTTPのstableの応答を、合わせて確認してください。
  2. /opt/fixtures/cnpe-crossplane-composition.jsonを/root/cnpe-api/composition.jsonにコピーしてください。spec.pipeline[0].input.resources[0].readinessChecks[0].matchCondition.typeのReadyだけを、Availableに直します。そのほかのテンプレート・セレクター・イメージ・権限は維持します。act 2で適用し、capture 2でready.jsonに、実際の準備完了を記録してください。
  3. team-aのdeveloperが、自分のチームのAppは作れるが、ほかのチームのAppと、直接のDeploymentは作れないことを、参照して確認してください。act 3で、実際の2つの権限の拒否と、replicas=4のスキーマ拒否をテストします。capture 3でboundaries.jsonを保存します。管理者の成功や、失敗した権限参照を、拒否の証拠として使いません。
  4. /root/cnpe-api/update.jsonに、同じApp・名前・チームで、replicas=2、channel=previewのリクエストを書いてください。act 4で適用し、capture 4でupdated.jsonを保存します。新しいリクエストを作らずに同じUIDを維持し、現在の世代・更新されたレプリカ数・利用可能なレプリカ数・Podの所有関係・実際のpreviewの応答を確認してください。
  5. act 5で、配下のDeploymentだけを3レプリカに変える実験を実施してください。このヘルパーは、実際のパッチのレスポンスのレプリカ数・世代・UIDを保管します。Appのリクエストは2のままです。capture 5でreconciled.jsonに、それより後の世代で2に戻った証拠を保存してください。最終的な数字の2だけを見て、実験したと判断しないでください。
  6. act 6で、教育用のfinalizer labhub.io/handoff-holdを入れて、parcelの削除をリクエストしてください。capture 6でdeleting.jsonを保存します。deletionTimestampはあるが、同じUIDがまだ参照できる状態です。この印が、すべての配下のリソースの生存まで保証すると解釈しないでください。
  7. act 7で、教育用の保留の印を1つだけ取り除いてください。ほかのfinalizerを強制的に消さないでください。capture 7でabsent.jsonを保存し、App・Deployment・ReplicaSet・Service・Podの一覧の参照がすべて成功し、対象がなくなったかを確認します。team-b/sentinelは、同じUIDと実際のHTTPの正常な状態を維持している必要があります。
  8. /root/cnpe-api/diagnose.pyにdiagnose(e)を書いてください。以下の診断契約に沿って、文字列を1つ返します。必須フィールドの欠落・boolの代わりの数値や文字列・観測の失敗・状態の矛盾は、unknownとして残してください。このステップは、以前のリソースを再作成しない、独立したコードの課題です。

ステップ8の診断契約

入力eには、observed・accepted・synced・ready・deleting・absentの6つのフィールドがすべてあり、値は実際のboolである必要があります。 この関数は、すでに突き合わせ済みの現在の状態の要約を入力として受け取ります。acceptedは、現在のリクエストオブジェクトが存在するという意味です。

  1. 型・必須フィールドのエラー、またはobserved=Falseなら、unknownです。
  2. absent=Trueなら、accepted・synced・ready・deletingがすべてFalseのときだけabsent、そうでなければunknownです。
  3. synced=Trueなのにaccepted=Falseの場合、またはready=Trueなのにsynced=Falseの場合は、unknownです。
  4. 前の矛盾がなくdeleting=Trueなら、accepted=Trueのときはdeleting、そうでなければunknownです。
  5. 残りは、ready=Trueならready、次にsynced=Trueならsynced、次にaccepted=Trueならaccepted、すべてFalseならnot_acceptedです。

absentは、正常な参照で確認した不在にすぎず、過去のリクエストが正常に回収された履歴までは意味しません。回収は、ステップ7で別に確認します。

参考

App→Deployment→ReplicaSet→Podの所有関係と、現在の応答Podを突き合わせます。名前だけが同じ別のオブジェクトの記録は拒否します。 観測ファイルは、同じラボの実行の過去の状態を学習するためのデータであり、暗号学的なリモート証明や不正防止の仕組みではありません。 ステップ2以降の採点は現在のCompositionを、ステップ3は現在のXRDと権限も確認します。ステップ4・5は、削除前なら現在のアプリも確認します。 削除したあとは、ステップ7の実際の回収の証拠があって初めて、過去の記録を再検査できます。ステップの準備は、既存のファイルと後続の進行を 上書きせず、必要な前のステップだけを埋めます。ステップ8は、クラスターの状態を元に戻しません。 教育用のfinalizer 1つが、すべての配下のリソースを保全するわけではありません。ネームスペースの権限分離は、ネットワークポリシー・クォータ全体の証明ではありません。 公式ドキュメント: Composition・Finalizers。

リクエストを作って、準備の失敗を観測する

/root/cnpe-api/initial.jsonに、apiVersion=platform.labhub.local/v1alpha1、kind=App、metadata.name=parcel、metadata.namespace=team-a、spec.replicas=1、spec.channel=stableを書いてください。act 1は、developerアカウントでこのリクエストを適用します。capture 1でaccepted.jsonを保存し、Synced=TrueだがReady=Falseのリクエストと、実際のHTTPのstableの応答を、合わせて確認してください。

リクエストの受理は、実行の完了ではありません。Appの条件と、該当PodのHTTPレスポンスを、別々に見てください。

アプリに合った準備条件に復旧する

/opt/fixtures/cnpe-crossplane-composition.jsonを/root/cnpe-api/composition.jsonにコピーしてください。spec.pipeline[0].input.resources[0].readinessChecks[0].matchCondition.typeのReadyだけを、Availableに直します。そのほかのテンプレート・セレクター・イメージ・権限は維持します。act 2で適用し、capture 2でready.jsonに、実際の準備完了を記録してください。

Deploymentが実際に提供する条件の名前を参照してください。準備の検査をNoneにしてなくすことは、復旧ではありません。

制限されたアカウントによる、実際の拒否のテスト

team-aのdeveloperが、自分のチームのAppは作れるが、ほかのチームのAppと、直接のDeploymentは作れないことを、参照して確認してください。act 3で、実際の2つの権限の拒否と、replicas=4のスキーマ拒否をテストします。capture 3でboundaries.jsonを保存します。管理者の成功や、失敗した権限参照を、拒否の証拠として使いません。

kubectl auth can-iには、--as=system:serviceaccount:team-a:developerを使います。拒否のあとに、管理者の一覧でも、作成されていないことを確認します。

同じリクエストを新しいchannelに変更する

/root/cnpe-api/update.jsonに、同じApp・名前・チームで、replicas=2、channel=previewのリクエストを書いてください。act 4で適用し、capture 4でupdated.jsonを保存します。新しいリクエストを作らずに同じUIDを維持し、現在の世代・更新されたレプリカ数・利用可能なレプリカ数・Podの所有関係・実際のpreviewの応答を確認してください。

metadata.generationとstatus.observedGenerationを合わせて見てください。終了中の以前のPodが残ることがあります。

配下のリソースの変更が元に戻る理由

act 5で、配下のDeploymentだけを3レプリカに変える実験を実施してください。このヘルパーは、実際のパッチのレスポンスのレプリカ数・世代・UIDを保管します。Appのリクエストは2のままです。capture 5でreconciled.jsonに、それより後の世代で2に戻った証拠を保存してください。最終的な数字の2だけを見て、実験したと判断しないでください。

続けたい変更の原本はAppです。配下のリソースの手動の変更は、次の収束で上書きされることがあります。

削除のリクエストと、実際の不在を区別する

act 6で、教育用のfinalizer labhub.io/handoff-holdを入れて、parcelの削除をリクエストしてください。capture 6でdeleting.jsonを保存します。deletionTimestampはあるが、同じUIDがまだ参照できる状態です。この印が、すべての配下のリソースの生存まで保証すると解釈しないでください。

delete --wait=falseの成功は、リクエストの成功です。finalizerとdeletionTimestampを直接参照してください。

自分のリソースだけが回収されたかを確認する

act 7で、教育用の保留の印を1つだけ取り除いてください。ほかのfinalizerを強制的に消さないでください。capture 7でabsent.jsonを保存し、App・Deployment・ReplicaSet・Service・Podの一覧の参照がすべて成功し、対象がなくなったかを確認します。team-b/sentinelは、同じUIDと実際のHTTPの正常な状態を維持している必要があります。

参照の失敗と、空の一覧は別です。ほかのチームまで消して自分のリソースをなくす答えも、失敗です。

わからない状態を残す診断ツール

/root/cnpe-api/diagnose.pyにdiagnose(e)を書いてください。以下の診断契約に沿って、文字列を1つ返します。必須フィールドの欠落・boolの代わりの数値や文字列・観測の失敗・状態の矛盾は、unknownとして残してください。このステップは、以前のリソースを再作成しない、独立したコードの課題です。

まず、型・必須フィールド・観測可能かどうかを検査し、矛盾を排除したあとで、削除・準備の状態を判定してください。