入場できても動かせないサーバー:リソースの境界
目標
個人のk3sで、デフォルト値の変更 → クォータによる拒否 → CPU不足によるPending → 復旧 → チームごとのAPI権限を、自分で操作します。
なぜ重要なのか
プラットフォームがリクエストを受け付けたことと、実行する席があることは別です。ResourceQuotaはチームのrequestの合計を制限しますが、特定のノードの実行枠を予約するわけではありません。LimitRangeが空のrequestを埋めても、過去のPodまでは修正しません。この2つを混同すると、運用者はクォータを上げ続け、ユーザーはPendingの画面の前で待つことになります。CNPEのマルチテナンシーと容量判断の一部を練習するもので、公式の試験問題や試験範囲全体の代わりにはなりません。
用意されているもの
Ubuntuの個人VMとk3s v1.36.4+k3s1、busyboxの実コンテナ、5つの専用Namespaceがあります。kubectlのリソース参照・JSON・jq・RBACの基礎が必要です。KUBECONFIGは/etc/rancher/k3s/k3s.yamlで、相対ファイルパスはすべて/root/cnpe-capacity以下です。baseline.jsonとbaseline-origin.jsonは初期UID・ノード容量・oldのデータであり、修正しません。new-pod.jsonはリソースを省略したPod、oversized-pod.jsonは修正する1コアのPodの素材です。old・memory-only・first・second・sampleはあらかじめ実行されています。空のRBACから、必要な権限だけを追加します。外部クラスターのkubeconfigや実際の認証情報は入れないでください。すべての変更は個人VMの中で行います。
75分のラボなので、デフォルトの60分セッションでは「+時間」で延長してください。セッションが終わるとVMとファイルが消えます。必要なレポートは、終了前に別に保管してください。最初の準備には数分かかることがあります。
ステップ
- 既存のPodの身分証を保管する: kubectl get nodesとcnpe-cap-defaultsのold Podを参照してください。baseline.jsonのoldオブジェクトをbefore.jsonに保存します。UIDとrequest量の25m・32Miを確認し、oldを削除したり再作成したりしないでください。
- 新しい入場者のデフォルト値だけを変更する: cnpe-cap-defaultsのLimitRange defaultsを修正してください。limitsにはtype Containerのルールを1つだけ置きます。defaultRequestはcpu 50m・memory 48Mi、defaultはcpu 100m・memory 64Miです。既存のoldは、request量とUIDがそのままである必要があります。
- メモリだけが同じならGuaranteedになるのか: 用意されているnew-pod.jsonをリソース項目なしで提出し、cnpe-cap-defaults/newを作成してください。Readyになるのを待ち、新しいrequestの50m・48Miを確認します。cnpe-cap-memory/memory-onlyは、メモリがrequest=limit=64Miで、CPUはありません。qos.jsonのnewとmemory_onlyに、それぞれのPodのstatus.qosClassを記録してください。
- 3人目の客を入口で拒否する: quota-manifest.jsonでcnpe-cap-quotaにResourceQuotaのbudgetを作成してください。hardはrequests.cpu 100m、requests.memory 256Mi、pods 4です。既存のfirst・secondは、それぞれ50mをrequestしています。status.used.requests.cpuが100mになったら、collect quotaを実行してください。3つ目の50mのrequestはCPUクォータで拒否され、オブジェクトが存在しない状態になっている必要があります。
- 入場券はあるのに席がないサーバー: oversized-pod.jsonのtoo-large Podで、CPUのrequestとlimitをどちらもbaseline.jsonのwanted_cpuに変更してください。これはノードのallocatableコア数を切り上げたうえで1を足した値です。cnpe-cap-scheduleの既存クォータはそのままにして、Podを作成します。PodScheduled=False、Unschedulable、Insufficient cpu、および未配置であることを確認し、クォータのused CPUがrequest量になったら、collect pendingを実行してください。
- クォータを増やさずに復旧する: recovery-pod.jsonに、同じPod名・Namespace・セキュリティ設定を維持したまま、requests cpu 25m・memory 32Mi、limits cpu 25m・memory 64Miを書いてください。too-largeだけを削除し、このファイルで再作成します。新しいUIDとReadyを確認してから、collect recoveredを実行してください。pendingのデータと既存のscheduleクォータは保存しておきます。
- チームメンバーには読み取り権限だけを与える: role.jsonとbinding.jsonに、cnpe-cap-quotaのRole readerとRoleBinding readerを書いて適用してください。core APIのpods get・listだけを、既存のServiceAccount tenantに許可します。collect rbacで、自分のfirstの参照は成功、cnpe-cap-other/sampleの参照は拒否、budgetの変更は拒否という結果を収集してください。ClusterRoleBindingやワイルドカード権限を追加しないでください。
- 証明できたこととわからないことを分ける: report.jsonに、node_allocatable_m、pending_request_m、quota_hard_m、recovered_request_mをmillicoreの整数で記録してください。old_pod_recreatedは初期UIDと現在のUIDを比較した真偽値、memory_only_qosは実際のQoSです。recovery_provesはplacement_only、isolation_provesはapi_permissions_only、cost_slo_verifiedはfalseと記録します。フィールドはこの9個だけを使い、最終的な復旧と権限を維持してください。
参考
収集の形式はpython3 /opt/fixtures/cnpe-capacity-lab.py collect <단계>です(プレースホルダーはステップ名です)。ステップ名はquota、pending、recovered、rbacです。コレクターは、テストリクエストと観測データを、各ステップの.jsonおよび-origin.jsonに保存します。quotaは小さなPodの作成が拒否されることをテストし、許可された場合はそのテスト用Podだけを回収します。rbacはtenantの参照と、同じ上限値でのPATCHをテストします。ファイルの保存に成功しても、求められる状態が成功したことにはなりません。採点結果まで確認してください。採点はデータと現在のAPIを読み取るだけで、答えや権限は修正しません。過去のPendingのデータを別に残すため、復旧後でもステップ5を再採点できます。誤った条件で収集した場合は、その条件を直したうえで、もう一度収集する必要があります。old以外の初期Podを削除しないでください。
保存版との突き合わせは、誤って編集することを防ぐためのもので、受講生のrootによる改ざんを防ぐものではありません。このラボは、実使用量・レイテンシ・コスト・SLO・ネットワーク遮断を測定しません。Readyが性能の保証である、あるいはAPI権限の分離が完全なテナント分離であるという結論を出さないでください。
公式ドキュメント
- https://kubernetes.io/docs/concepts/policy/limit-range/
- https://kubernetes.io/docs/concepts/policy/resource-quotas/#quota-and-cluster-capacity
- https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/
- https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#troubleshooting
- https://kubernetes.io/docs/concepts/security/multi-tenancy/
既存のPodの身分証を保管する
kubectl get nodesとcnpe-cap-defaultsのold Podを参照してください。baseline.jsonのoldオブジェクトをbefore.jsonに保存します。UIDとrequest量の25m・32Miを確認し、oldを削除したり再作成したりしないでください。
Podの名前は再利用できますが、UIDは再作成すると変わります。jqの.oldで入れ子のオブジェクトを取り出してください。
新しい入場者のデフォルト値だけを変更する
cnpe-cap-defaultsのLimitRange defaultsを修正してください。limitsにはtype Containerのルールを1つだけ置きます。defaultRequestはcpu 50m・memory 48Mi、defaultはcpu 100m・memory 64Miです。既存のoldは、request量とUIDがそのままである必要があります。
デフォルト値はAPIのアドミッション段階で埋められます。LimitRangeを変えても、既存のPodがもう一度アドミッションを通ることはありません。
メモリだけが同じならGuaranteedになるのか
用意されているnew-pod.jsonをリソース項目なしで提出し、cnpe-cap-defaults/newを作成してください。Readyになるのを待ち、新しいrequestの50m・48Miを確認します。cnpe-cap-memory/memory-onlyは、メモリがrequest=limit=64Miで、CPUはありません。qos.jsonのnewとmemory_onlyに、それぞれのPodのstatus.qosClassを記録してください。
このラボのコンテナ単位のリソースでは、GuaranteedはCPUとメモリの両方の条件を見ます。RunningとQoSは別のフィールドです。
3人目の客を入口で拒否する
quota-manifest.jsonでcnpe-cap-quotaにResourceQuotaのbudgetを作成してください。hardはrequests.cpu 100m、requests.memory 256Mi、pods 4です。既存のfirst・secondは、それぞれ50mをrequestしています。status.used.requests.cpuが100mになったら、collect quotaを実行してください。3つ目の50mのrequestはCPUクォータで拒否され、オブジェクトが存在しない状態になっている必要があります。
Podの数が4に達していなくても、request CPUの合計が上限に届きます。CPU使用率を測定するステップではありません。
入場券はあるのに席がないサーバー
oversized-pod.jsonのtoo-large Podで、CPUのrequestとlimitをどちらもbaseline.jsonのwanted_cpuに変更してください。これはノードのallocatableコア数を切り上げたうえで1を足した値です。cnpe-cap-scheduleの既存クォータはそのままにして、Podを作成します。PodScheduled=False、Unschedulable、Insufficient cpu、および未配置であることを確認し、クォータのused CPUがrequest量になったら、collect pendingを実行してください。
APIがオブジェクトを作成したかどうかと、スケジューラーがノードに配置したかどうかは、別々に確認してください。describe podとResourceQuotaのstatusは、それぞれ別の証拠です。
クォータを増やさずに復旧する
recovery-pod.jsonに、同じPod名・Namespace・セキュリティ設定を維持したまま、requests cpu 25m・memory 32Mi、limits cpu 25m・memory 64Miを書いてください。too-largeだけを削除し、このファイルで再作成します。新しいUIDとReadyを確認してから、collect recoveredを実行してください。pendingのデータと既存のscheduleクォータは保存しておきます。
ここでは再作成で復旧します。requestを減らして配置できたという事実だけで、サービスの性能が十分だと結論づけないでください。
チームメンバーには読み取り権限だけを与える
role.jsonとbinding.jsonに、cnpe-cap-quotaのRole readerとRoleBinding readerを書いて適用してください。core APIのpods get・listだけを、既存のServiceAccount tenantに許可します。collect rbacで、自分のfirstの参照は成功、cnpe-cap-other/sampleの参照は拒否、budgetの変更は拒否という結果を収集してください。ClusterRoleBindingやワイルドカード権限を追加しないでください。
コレクターはtenantになりすまして実際にリクエストします。同じ値のPATCHでも、変更権限がなければ拒否される必要があります。
証明できたこととわからないことを分ける
report.jsonに、node_allocatable_m、pending_request_m、quota_hard_m、recovered_request_mをmillicoreの整数で記録してください。old_pod_recreatedは初期UIDと現在のUIDを比較した真偽値、memory_only_qosは実際のQoSです。recovery_provesはplacement_only、isolation_provesはapi_permissions_only、cost_slo_verifiedはfalseと記録します。フィールドはこの9個だけを使い、最終的な復旧と権限を維持してください。
1000mは1コアです。CPUのrequest量は使用量ではなく、APIによる拒否はネットワーク分離の証明ではありません。