マルチテナンシーの資源と事故対応
目標
3つのテナントを作って、クォータとLimitRangeでリソースを分け、オーバーコミット率を自分で計算して記録し、スケジューリングのインシデントを再現して数字で分類し、要件を消さずに復旧し、プラットフォーム自身のレコーディングルールとアラートを作ってクラスターに上げます。
なぜ重要なのか
ここで練習するのは、ツールの操作だけでなく、証拠に基づく判断です。インシデントが起きたときに、何から数えるのか、その値をどこに書くのか、そして何を復旧と呼ぶのかが、その順序です。
特に最後の項目が重要です。制約を消してPodを起動させることと、制約を満たして起動させることは、画面では区別できません。どちらもRunningで、どちらもアラートが消えます。区別するのは人であり、その判断を身につけることが、このラボの目的です。
このクラスターのノードは3台で、それぞれ8コアです。そのため、クラスター全体のallocatable CPUは24コアです。クォータの合計をこの値で割ったものが、オーバーコミット率です。
この環境は、実際のAPIサーバー・スケジューラーとKWOKの仮想ノードを使います。Runningはスケジューリングの状態を学ぶためのものであり、GPUやledgerプロセスが実際に実行されたという証拠ではありません。Prometheus Operator・Alertmanagerは実行しません。最後の2つのステップのPromQLは、イメージに含まれるpromtool 3.0.1で実際に評価し、CRはAPIの保存内容まで検証します。
作業ディレクトリは/root/cnpe-opsです。時系列のテストは仮想の時間を使うので、実際に25分待つことはありません。各採点は45秒以内に検査し、受講生のファイルやAPIオブジェクトは修正しません。
ステップ
- ネームスペース
tenant-red、tenant-green、tenant-goldを作成し、それぞれにplatform.labhub.io/tenantラベルを付けます。値は、名前からtenant-を除いたものです。そのあと、/root/cnpe-ops/inventory.txtにtenantsとtenant_listの2行を書きます。一覧は、名前順にカンマだけを入れてつなぎます。 /root/cnpe-ops/quotas.yamlに、3つのテナントのResourceQuotaを書きます。名前は、それぞれtenant-red-quotaのように、ネームスペース名に-quotaを付けたもので、requests.cpuを必ず入れます。3つのクォータのrequests.cpuの合計を24で割った値が、1.00より大きく1.25以下になるようにし、どのテナントも6コアを下回らないようにします。そのあと、/root/cnpe-ops/capacity.txtにquota_cpu、allocatable_cpu、overcommitの3行を書きます。/root/cnpe-ops/limitranges.yamlに、3つのテナントのLimitRangeを書きます。名前はtenant-red-limitsのようにつけます。defaultRequestとdefaultを両方とも置きますが、defaultRequestのCPUがdefaultのCPUより低い必要があり、maxでコンテナ1つがCPU 4コアを要求できないようにします。/root/cnpe-ops/ledger.yamlにDeploymentledgerを書いて、tenant-redに適用します。replicasは3、Podのラベルはapp=ledger、nodeSelectorはplatform.labhub.io/pool=gpuの1つだけで、コンテナのrequestはCPU 500mとメモリ512Miで、イメージはダイジェストで固定します。この時点では、Podは起動できません。/root/cnpe-ops/triage.txtに、selector_key、selector_value、replicas、required_cpuの4行を書きます。required_cpuは、コンテナのrequestにレプリカ数を掛けた値を、ミリコアで書きます。- ノード1台にだけ
platform.labhub.io/pool=gpuラベルを付けて復旧します。DeploymentのnodeSelectorとreplicasは、そのままにしておく必要があります。Pod 3つがすべてRunningになるまで確認します。 /root/cnpe-ops/platform-rules.yamlに、グループplatform-sloとinterval: 1mを置きます。レコーディングルールplatform:provision_success:ratio5mは、直近5分の成功リクエストのrateの合計を、全リクエストのrateの合計で割った値です。系列ごとにrateを計算してから合算し、失敗の系列だけがあれば0%、成功の系列だけがあれば100%とし、無トラフィック・観測なしの場合は、割合の系列を出力しません。PlatformProvisionFailingは、この割合が95%未満の状態が10分続いたときに発火し、回復したら解除されます。severity: critical、意味のあるsummary、runbook_urlを置きます。文法の検査のあと、python3 /opt/fixtures/cnpe_slo_contract.py rulesで、13個の時系列シナリオを通過させてください。95%はこのラボのしきい値であり、本番の推奨SLOではありません。- ネームスペース
monitoringを作成し、/root/cnpe-ops/platform-rules-cr.yamlにPrometheusRuleplatform-sloを書いて適用します。ルールファイル・マニフェストのspec.groups・APIのspec.groups全体が一致している必要があります。/root/cnpe-ops/ops-report.txtに、重複なしでtenants、pool_nodes、ledger_running、alert_rulesの4行を、参照した整数で記録します。このラボの目標値は、それぞれ3、1、3、実際のアラートルール数です。python3 /opt/fixtures/cnpe_slo_contract.py deployで確認してください。APIへの保存の成功は、本番のPrometheusによるルールのロードやアラート配信の成功を意味しません。
参考
- ラベルで数えた値が、プラットフォームが自分で知っている値です。
kubectl get ns -l platform.labhub.io/tenantで数えてください。 - ノードのallocatableは、
kubectl get nodes -o json | jq '[.items[].status.allocatable.cpu | tonumber] | add'で数え直せます。 - LimitRangeが実際に何を埋めるかを見るには、requestを空にしたPodを
--dry-run=serverで入れてみて、結果を読むのが最も確実です。 - ラベルを付けたあとは、スケジューラーが1周する時間を与えてください。すぐに確認して判断すると、正しい対処を元に戻してしまいます。
- よくある間違いの1つは、Podを起動させようとして
nodeSelectorを消すことです。それは復旧ではなく、要件の削除です。 - もう1つは、検証したルールファイルと異なる内容をPrometheusRuleとして上げることです。そうなると、promtoolの通過が何も保証しなくなります。
- Prometheusルールの単体テストとアラートルールを読んでください。文法と発火の動作は別です。
- runbook.example.invalidは形式を説明するためのアドレスです。本番では、実際の担当者・診断・復旧手順のあるドキュメントに置き換える必要があります。
テナントを機械が数えられるようにする
ネームスペースtenant-red、tenant-green、tenant-goldを作成し、それぞれにplatform.labhub.io/tenantラベルを付けます。値は、名前からtenant-を除いたものです。そのあと、/root/cnpe-ops/inventory.txtにtenantsとtenant_listの2行を書きます。一覧は、名前順にカンマだけを入れてつなぎます。
名前の規則だけがあってラベルがなければ、テナントの一覧は人の記憶の中にしかありません。ラベルを付けたあと、ラベルセレクターでもう一度数えて、その結果を書いてください。
約束した量と、実際に持っている量を分けてみる
/root/cnpe-ops/quotas.yamlに、3つのテナントのResourceQuotaを書きます。名前は、それぞれtenant-red-quotaのように、ネームスペース名に-quotaを付けたもので、requests.cpuを必ず入れます。3つのクォータのrequests.cpuの合計を24で割った値が、1.00より大きく1.25以下になるようにし、どのテナントも6コアを下回らないようにします。そのあと、/root/cnpe-ops/capacity.txtにquota_cpu、allocatable_cpu、overcommitの3行を書きます。
クォータはネームスペースごとに独立してかかり、クラスターの容量を参照しません。そのため、合計は人が別に数える必要があります。ノード3台がそれぞれ8コアなので、分母は24です。
書かなかった人に埋める値を決める
/root/cnpe-ops/limitranges.yamlに、3つのテナントのLimitRangeを書きます。名前はtenant-red-limitsのようにつけます。defaultRequestとdefaultを両方とも置きますが、defaultRequestのCPUがdefaultのCPUより低い必要があり、maxでコンテナ1つがCPU 4コアを要求できないようにします。
defaultはlimit、defaultRequestはrequestのデフォルト値です。すでに宣言したrequestは、デフォルト値で上書きしません。この課題では、CPUのデフォルトrequestをlimitより小さくして、予約量を区別します。Guaranteedかどうかは、すべてのコンテナのCPU・メモリの条件を合わせて確認する必要があります。maxはコンテナごとの上限です。
インシデントを再現しておく
/root/cnpe-ops/ledger.yamlにDeploymentledgerを書いて、tenant-redに適用します。replicasは3、Podのラベルはapp=ledger、nodeSelectorはplatform.labhub.io/pool=gpuの1つだけで、コンテナのrequestはCPU 500mとメモリ512Miで、イメージはダイジェストで固定します。この時点では、Podは起動できません。
今のところ、これらのノードにはプールのラベルがありません。そのため、このワークロードは起動できません。その状態が、このステップでは正常です。Podの現在の状態ではなく、何を要求するワークロードなのかが、採点の対象です。
分類の結果を数字で書く
/root/cnpe-ops/triage.txtに、selector_key、selector_value、replicas、required_cpuの4行を書きます。required_cpuは、コンテナのrequestにレプリカ数を掛けた値を、ミリコアで書きます。
感覚ではなく、4つの数字を書きます。必要な合計CPUは、コンテナのrequestにレプリカ数を掛けた値で、ミリコアで書きます。値は、クラスターから直接読み取ってください。
制約を消さずに復旧する
ノード1台にだけplatform.labhub.io/pool=gpuラベルを付けて復旧します。DeploymentのnodeSelectorとreplicasは、そのままにしておく必要があります。Pod 3つがすべてRunningになるまで確認します。
セレクターを消せばすぐに起動しますが、それは復旧ではなく、要件の削除です。ノード側を直して、制約を満たしてください。そして、必要な分だけ付けます。2台に付けると、そのプールの意味がぼやけます。
プラットフォーム自身のレコーディングルールとアラートを書く
/root/cnpe-ops/platform-rules.yamlに、グループplatform-sloとinterval: 1mを置きます。レコーディングルールplatform:provision_success:ratio5mは、直近5分の成功リクエストのrateの合計を、全リクエストのrateの合計で割った値です。系列ごとにrateを計算してから合算し、失敗の系列だけがあれば0%、成功の系列だけがあれば100%とし、無トラフィック・観測なしの場合は、割合の系列を出力しません。PlatformProvisionFailingは、この割合が95%未満の状態が10分続いたときに発火し、回復したら解除されます。severity: critical、意味のあるsummary、runbook_urlを置きます。文法の検査のあと、python3 /opt/fixtures/cnpe_slo_contract.py rulesで、13個の時系列シナリオを通過させてください。95%はこのラボのしきい値であり、本番の推奨SLOではありません。
値が0のベクターも、アラートを有効にします。bool比較をアラートにそのまま使うと、正常なときにも系列が残ります。失敗だけがある場合の空の分子と、全体のトラフィックがない分母を区別してください。失敗メッセージの仮想のシナリオ・時点を先に読み、式とforを点検します。
検証したルールを上げて、現在の状態を記録する
ネームスペースmonitoringを作成し、/root/cnpe-ops/platform-rules-cr.yamlにPrometheusRuleplatform-sloを書いて適用します。ルールファイル・マニフェストのspec.groups・APIのspec.groups全体が一致している必要があります。/root/cnpe-ops/ops-report.txtに、重複なしでtenants、pool_nodes、ledger_running、alert_rulesの4行を、参照した整数で記録します。このラボの目標値は、それぞれ3、1、3、実際のアラートルール数です。python3 /opt/fixtures/cnpe_slo_contract.py deployで確認してください。APIへの保存の成功は、本番のPrometheusによるルールのロードやアラート配信の成功を意味しません。
グループの名前だけが同じで、式・しきい値・forが違えば、同じルールではありません。検証したファイルをそのままCRで包み、APIのspec.groups全体を比較してください。参照の失敗を0として記録せず、先にAPIのエラーを解決します。