GKE Autopilotによるresourcesの書き換え
Google Kubernetes EngineのAutopilotモードはワークロードリソースのspec.containers[].resources
の値を書き換えることがあるのですが、そのルールが初見だと少しわかりづらかったので簡単な入門記事を書いてみました。
Autopilotによるresourcesの書き換えのルール概要
どのようにresources
が制御されるかについては、Autopilotでのリソースリクエストに詳細に書いてありますが、ざっくり説明すると以下のようになっています。
-
requests
を指定していないと、limits
が設定されていればその値が、されてなければデフォルト値が設定される - クラスタがバースト機能をサポートしていない場合は、
requests=limits
となるように上書きされる - 何かしらの値を指定した場合でも最小値と最大値、CPUとメモリの比率の3つの制約を満たすように上書きされる
- デフォルト値、最大・最小値、CPUとメモリの比率は指定するコンピューティングクラス、リソースタイプによって決定される
コンピューティングクラスはPodをどのようなノードで実行するかを指定するためのもので、何も指定しない場合は汎用
になります。詳細はこちら。
クラスタがバースト機能をサポートしているかどうかは、こちらに条件が記載されています。クラスタの要件に加えて、特定のコンピューティングクラスを指定する必要があり、リソース管理も難しくなるので利用するケースは少ないと思います。
書き換えの確認
ルール概要を踏まえた上で実際に書き換えられる様子を確認してみます。なお、動作環境はバースト非対応のクラスタとなっています。
以下のDeploymentマニフェストではrequests.cpu
に125m
, requests.memory
に256Mi
を指定、requests.ephemeral-storage
を未指定、limits
は全て未指定としています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-deployment
namespace: default
labels:
app: sample
spec:
replicas: 1
selector:
matchLabels:
app: sample
template:
metadata:
labels:
app: sample
spec:
containers:
- name: ubuntu
image: ubuntu:24.04
command: ["sleep"]
args: ["infinity"]
resources:
requests:
cpu: "125m"
memory: "256Mi"
コンピューティングクラスは指定していないため汎用
になりますが、このクラスの制約を見てみると以下のようになっており、比率は問題ないですが、CPUとメモリが最小値を下回っています。また、エフェメラルストレージのデフォルト値は1GiB
です。
制約対象 | 値の範囲 |
---|---|
CPU | 250m ~ 30 |
メモリ | 512 MiB ~ 110 GiB |
CPUとメモリの比率 | 1:1 ~ 1:6.5 |
次に、これをAutopilotが制約を満たすように書き換える様子を見ていきますが、実際にデプロイせずとも --dry-run=server
を利用することで確認できます。
実行すると出力の一行目に以下のようにWarningが出てAutopilotの制約を満たしていないためresources
が書き換えられたという警告がでます。
$ kubectl apply -f sample-deployment-ng.yaml --dry-run=server -o yaml
Warning: autopilot-default-resources-mutator:Autopilot updated Deployment default/sample-deployment: adjusted 'cpu' resource to meet requirements for containers [ubuntu] (see http://g.co/gke/autopilot-defaults).
出力されたyamlを見てみると、annotations
のautopilot.gke.io/resource-adjustment
にこの書き換え処理のinputとoutputが記載されており、どのように書き変わったかが確認できます。また、spec.containers[].resources
をみると、実際にCPUとメモリが最小値まで増加されており、エフェメラルストレージはデフォルト値に設定され、設定していなかったlimits
がrequests=limits
となるように設定されてることが確認できます。
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
autopilot.gke.io/resource-adjustment: '{"input":{"containers":[{"requests":{"cpu":"125m","memory":"256Mi"},"name":"ubuntu"}]},"output":{"containers":[{"limits":{"cpu":"250m","ephemeral-storage":"1Gi","memory":"512Mi"},"requests":{"cpu":"250m","ephemeral-storage":"1Gi","memory":"512Mi"},"name":"ubuntu"}]},"computeClassAtAdmission":"Default","modified":true}'
autopilot.gke.io/warden-version: xx.yy.zz-gke.1
...
spec:
...
template:
...
spec:
containers:
- args:
- infinity
command:
- sleep
image: ubuntu:24.04
imagePullPolicy: IfNotPresent
name: ubuntu
resources:
limits:
cpu: 250m
ephemeral-storage: 1Gi
memory: 512Mi
requests:
cpu: 250m
ephemeral-storage: 1Gi
memory: 512Mi
...
書き換え対象と注意事項
次に、この書き換えが何によって行われ、書き換え対象となるリソースは何かを見ていきます。
まず、書き換えが何によって行われているかですが、先ほどのdry-runで出力されたyamlに autopilot.gke.io/warden-version: xx.yy.zz-gke.1
というアノテーションがついていましたが、GKEのAutopilotモードにはGKE Wardenというカスタムアドミッションコントローラが存在し、これはそのバージョンです(値はマスクしています)。
そして、このGKE Wardenは公式ドキュメントではセキュリティの文脈(例: Kubernetes Pod セキュリティ標準の適用)で出てきますが、resources
の書き換えも担っています。
このことを確認するために、先ほどdry-runしたコマンドを実際にapplyします。
$ kubectl apply -f sample-deployment-ng.yaml
Warning: autopilot-default-resources-mutator:Autopilot updated Deployment default/sample-deployment: adjusted 'cpu' resource to meet requirements for containers [ubuntu] (see http://g.co/gke/autopilot-defaults).
deployment.apps/sample-deployment created
この操作の監査ログをCloudLoggingで以下のクエリを用いて検索すると、
protoPayload.@type="type.googleapis.com/google.cloud.audit.AuditLog"
protoPayload.methodName="io.k8s.apps.v1.deployments.create"
以下の監査ログがヒットします。
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"methodName": "io.k8s.apps.v1.deployments.create",
"labels": {
...,
"patch.webhook.admission.k8s.io/round_0_index_19": "...後述...",
...
},
このログのlabelの"patch.webhook.admission.k8s.io/round_0_index_19"
の値は以下のようになっており、リソースを書き換えているのは warden-mutating.common-webhooks.networking.gke.io
、つまりGKE Wardenであることが確認できます。
"patch.webhook.admission.k8s.io/round_0_index_19"の値
{
"configuration": "warden-mutating.config.common-webhooks.networking.gke.io",
"webhook": "warden-mutating.common-webhooks.networking.gke.io",
"patch": [
{
"op": "add",
"path": "/metadata/annotations/autopilot.gke.io~1resource-adjustment",
"value": "{\"input\":{\"containers\":[{\"requests\":{\"cpu\":\"125m\",\"memory\":\"256Mi\"},\"name\":\"ubuntu\"}]},\"output\":{\"containers\":[{\"limits\":{\"cpu\":\"250m\",\"ephemeral-storage\":\"1Gi\",\"memory\":\"512Mi\"},\"requests\":{\"cpu\":\"250m\",\"ephemeral-storage\":\"1Gi\",\"memory\":\"512Mi\"},\"name\":\"ubuntu\"}]},\"computeClassAtAdmission\":\"Default\",\"modified\":true}"
},
{
"op": "add",
"path": "/metadata/annotations/autopilot.gke.io~1warden-version",
"value": "xx.yy.zz-gke.1"
},
{
"op": "add",
"path": "/spec/template/spec/securityContext/seccompProfile",
"value": {
"type": "RuntimeDefault"
}
},
{
"op": "add",
"path": "/spec/template/spec/tolerations",
"value": [
{
"effect": "NoSchedule",
"key": "kubernetes.io/arch",
"operator": "Equal",
"value": "amd64"
}
]
},
{
"op": "add",
"path": "/spec/template/spec/containers/0/resources/limits",
"value": {
"cpu": "250m",
"ephemeral-storage": "1Gi",
"memory": "512Mi"
}
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/resources/requests/memory",
"value": "512Mi"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/resources/requests/cpu",
"value": "250m"
},
{
"op": "add",
"path": "/spec/template/spec/containers/0/resources/requests/ephemeral-storage",
"value": "1Gi"
},
{
"op": "add",
"path": "/spec/template/spec/containers/0/securityContext",
"value": {
"capabilities": {
"drop": [
"NET_RAW"
]
}
}
}
],
"patchType": "JSONPatch"
}
このwebhookの設定は以下のコマンドで確認できます。
$ kubectl mutatingwebhookconfiguration warden-mutating.config.common-webhooks.networking.gke.io -o yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
...
labels:
networking.gke.io/common-webhooks: "true"
name: warden-mutating.config.common-webhooks.networking.gke.io
...
webhooks:
...
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- daemonsets
- deployments
- replicasets
- statefulsets
scope: '*'
...
これだけでは具体的にどの操作に何がどう適用されているかまではわかりませんが、あくまで標準のリソースのみが対象となっており、CustomResourceDefinitionは書き換えの対象になっていないことがわかります。
例えば、Deploymentの代わりにArgo RolloutsのRolloutsリソースを使っている場合は書き換えられません。ただし、Rolloutsが生成するReplicasetリソースは書き換えの対象となります。この挙動の影響を受ける例としてArgoCDがあります。Rolloutsが書き換えられないことによりOutOfSync判定されない一方で、Podは書き換えられているといった不一致が発生し得る点に注意が必要です。
Discussion