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

CKAD — Kubernetesアプリケーション開発者

securityContext、リソース、クォータ、QoS

TT Labで続きを見る

目標

Podがノード上で何をできるか(securityContext、ServiceAccount)とどれだけ使えるか(resources、LimitRange、ResourceQuota)をマニフェストで決め、その結果QoSクラスがどのように決まるかを確認できるようになります。

なぜ重要なのか

コンテナはデフォルトでrootで動きます。イメージがそのように作られているからです。コンテナ脱出の脆弱性が1つでもあると、そのrootがノードのrootにつながりかねません。runAsNonRoot: trueはUID 0で起動しようとするコンテナをそもそも起動させず、capabilities.drop: ["ALL"]はカーネルが与える特権をすべて取り消します。Webサーバーがポート80を開く必要があるなら、NET_BIND_SERVICEだけを再度追加します。これが最小権限の原則の具体的な姿です。

リソース側は2つの層で理解します。requestsはスケジューラーの言語です。ノードに残っている割り当て可能量がrequestsの合計より大きいときに、Podが載ります。実際の使用量とは無関係です。limitsはノードの言語です。CPUはcgroupのクォータでスロットリングされ、メモリは超えた瞬間にOOMKilledになります。そのため、メモリのlimitsを実際のピークより低く設定すると、Podが静かに再起動を繰り返します。

LimitRangeとResourceQuotaは、必ずセットで理解する必要があります。クォータが設定されたネームスペースでrequests/limitsのないPodを作成すると、作成そのものが拒否されます。チームのネームスペースにクォータだけを設定しておくと何も起動しない事故が起きるので、LimitRangeでデフォルト値を補うことが事実上必須です。

ステップ

  1. ネームスペースckad-secureを作成し、その中にServiceAccount app-saを作成してください。
  2. Pod sa-podを作成してください。イメージはnginx:1.27、serviceAccountName: app-sa、automountServiceAccountToken: falseです。
  3. Pod nonrootを作成してください。イメージはnginx:1.27、PodレベルのsecurityContextにrunAsNonRoot: true、runAsUser: 1000、fsGroup: 2000を設定してください。
  4. Pod hardenedを作成してください。イメージはnginx:1.27、コンテナ名はapp、コンテナレベルのsecurityContextにallowPrivilegeEscalation: false、readOnlyRootFilesystem: true、capabilities.drop: ["ALL"]、capabilities.add: ["NET_BIND_SERVICE"]を設定してください。
  5. Deployment apiを作成してください。レプリカは2、ラベルはapp=api、イメージはnginx:1.27、コンテナのresources.requestsはcpu: 100m / memory: 128Mi、resources.limitsはcpu: 500m / memory: 512Miです。
  6. LimitRange defaultsを作成してください。type: Containerで、default(limits)はcpu: 200m / memory: 256Mi、defaultRequestはcpu: 100m / memory: 128Mi、maxはcpu: "1" / memory: 1Gi、minはcpu: 50m / memory: 64Miにしてください。
  7. ResourceQuota team-quotaを作成してください。requests.cpu: "2"、requests.memory: 4Gi、limits.cpu: "4"、limits.memory: 8Gi、pods: "10"を設定してください。
  8. Podをさらに2つ作成してください。qos-guaranteedはrequestsとlimitsがどちらもcpu: 250m / memory: 256Miで同じになるようにし、qos-burstableはrequestsだけをcpu: 100m / memory: 128Miにしてください。どちらもイメージはnginx:1.27です。

参考

ネームスペースとServiceAccountを作成する

ネームスペースckad-secureを作成し、その中にServiceAccount app-saを作成してください。

kubectl create serviceaccountで作成します。ネームスペースを先に作成し、その中に作成します。

PodにServiceAccountを指定し、トークンの自動マウントを無効にする

Pod sa-podを作成してください。イメージはnginx:1.27、serviceAccountName: app-sa、automountServiceAccountToken: falseです。

フィールド名はspec.serviceAccountNameです(serviceAccountは古い別名です)。トークンの自動マウントを無効にするブール値のフィールドも、Podのspecの最上位にあります。

PodレベルのsecurityContextを設定する

Pod nonrootを作成してください。イメージはnginx:1.27、PodレベルのsecurityContextにrunAsNonRoot: true、runAsUser: 1000、fsGroup: 2000を設定してください。

spec.securityContextに入るのは、Pod全体に適用される値です。ボリュームの所有グループを指定するフィールドは、Podレベルにしか存在しません。

コンテナレベルのsecurityContextとcapabilitiesを設定する

Pod hardenedを作成してください。イメージはnginx:1.27、コンテナ名はapp、コンテナレベルのsecurityContextにallowPrivilegeEscalation: false、readOnlyRootFilesystem: true、capabilities.drop: ["ALL"]、capabilities.add: ["NET_BIND_SERVICE"]を設定してください。

capabilitiesはコンテナレベルにしかありません。すべて削除してから必要なものだけを再度追加する方式が、最小権限の原則です。dropとaddは、それぞれ文字列の配列です。

Deploymentにrequestsとlimitsを指定する

Deployment apiを作成してください。レプリカは2、ラベルはapp=api、イメージはnginx:1.27、コンテナのresources.requestsはcpu: 100m / memory: 128Mi、resources.limitsはcpu: 500m / memory: 512Miです。

resourcesはコンテナの下にあります。requestsはスケジューラーが見る値、limitsはランタイムが強制する値です。CPUの単位mは1/1000コアです。

LimitRangeでデフォルト値と上下限を決める

LimitRange defaultsを作成してください。type: Containerで、default(limits)はcpu: 200m / memory: 256Mi、defaultRequestはcpu: 100m / memory: 128Mi、maxはcpu: "1" / memory: 1Gi、minはcpu: 50m / memory: 64Miにしてください。

spec.limitsは配列で、各要素にtypeがあります。デフォルトのlimitsを決めるキーとデフォルトのrequestsを決めるキーは名前が違うので、kubectl explain limitrange.spec.limitsで確認してください。

ResourceQuotaでネームスペースの総量を制限する

ResourceQuota team-quotaを作成してください。requests.cpu: "2"、requests.memory: 4Gi、limits.cpu: "4"、limits.memory: 8Gi、pods: "10"を設定してください。

spec.hardはマップで、キー名がrequests.cpu、limits.memoryのようにドットを含みます。個数の制限は、podsのように文字列の値で指定します。

QoSクラスを意図どおりに作成する(総合)

Podをさらに2つ作成してください。qos-guaranteedはrequestsとlimitsがどちらもcpu: 250m / memory: 256Miで同じになるようにし、qos-burstableはrequestsだけをcpu: 100m / memory: 128Miにしてください。どちらもイメージはnginx:1.27です。

QoSは直接指定するフィールドではなく、requests/limitsの組み合わせで決まる結果の値です。すべてのコンテナでCPU・メモリともにrequestsとlimitsが同じでなければ、最上位のクラスにはなりません。kubectl get pod -o jsonpath='{.status.qosClass}'で確認してください。