EKS 1.33 が利用できるようになったので、In-Place Pod Resource Resize を試してみた

EKS 1.33 が利用できるようになったので、In-Place Pod Resource Resize を試してみた

こんにちは。クラウド事業本部の枡川です。
EKS において Kubernetes v1.33 を利用できるようになりました。

https://aws.amazon.com/about-aws/whats-new/2025/05/amazon-eks-distro-kubernetes-version-1-33/

アップデート内容を見てみる

Kubernetes v1.33 のアップデートとして、In-Place Update of Pod Resources が Beta になったというものがあります。

In-Place Pod Resource Resize (Beta): In-place resource resize has been promoted to beta, allowing dynamic updates to CPU and memory resources for existing Pods without restarts - enabling vertical scaling of stateful workloads with zero downtime and seamless resource adjustments based on traffic patterns.

https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions-standard.html

ステートフルなリソースかつ Pod の再起動を許容できない場合でも必要に応じて垂直スケールすることができる機能です。
また、CPU の limits/requests やメモリの requests についてはスケールインも可能なので、起動時のみ一時的に大きなリソースを割り当てておくような運用も可能です。

Memory limits cannot be decreased unless the resizePolicy for memory is RestartContainer. Memory requests can generally be decreased.
https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/#limitations

メモリの limits を減少させることについては OOM killer の可能性を考えての制限ですが、今後のバージョンアップで可能にする方向のようです。

https://github.com/kubernetes/kubernetes/issues/129152

合わせて Dynamic Resource Allocation も有効化されています。
こちらは 1.32 で beta になった機能ですが、EKS では有効化されていませんでした。
GPU などの特殊なハードウェアを複数の Pod に柔軟に割り当てるための機能になります。

https://kubernetes.io/docs/concepts/scheduling-eviction/dynamic-resource-allocation/

https://github.com/aws/containers-roadmap/issues/2314

その他、 EKS 最適化 AMI が EKS 1.33 以降では公開されないことは注意が必要です。
Amazon Linux 2023 ベースのものは引き続き公開されているので、そちらを利用したり Bottlerocket を使うのが良いでしょう。

https://github.com/awslabs/amazon-eks-ami/releases

In-Place Update of Pod Resources を使ってみる

下記 Kubernetes 公式ドキュメントに沿って EKS で試してみます。

https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/

まず、eksctl で EKS クラスターを作成します。
v1.33 を選択し、非 Auto Mode とします。

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: test-cluster
  region: ap-northeast-1
  version: "1.33"

iam:
  withOIDC: true

vpc:
  clusterEndpoints:
    publicAccess: true
    privateAccess: true

cloudWatch:
  clusterLogging:
    enableTypes:
      - "audit"
      - "authenticator"
      - "controllerManager"
      - "scheduler"

managedNodeGroups:
  - name: node-group-al2023
    amiFamily: AmazonLinux2023
    instanceTypes:
      - t3.medium
    maxSize: 1
    minSize: 1
    desiredCapacity: 1
    privateNetworking: true
    volumeType: gp3
    volumeSize: 50

autoModeConfig:
  enabled: false

addons:
  - name: eks-pod-identity-agent
    version: latest
  - name: vpc-cni
    version: latest
    useDefaultPodIdentityAssociations: true
  - name: coredns
    version: latest
  - name: kube-proxy
    version: latest

create cluster コマンドを実行します。

% eksctl create cluster -f cluster.yaml
2025-05-31 18:30:41 [ℹ]  eksctl version 0.208.0
2025-05-31 18:30:41 [ℹ]  using region ap-northeast-1
2025-05-31 18:30:42 [ℹ]  setting availability zones to [ap-northeast-1d ap-northeast-1c ap-northeast-1a]
2025-05-31 18:30:42 [ℹ]  subnets for ap-northeast-1d - public:192.168.0.0/19 private:192.168.96.0/19
2025-05-31 18:30:42 [ℹ]  subnets for ap-northeast-1c - public:192.168.32.0/19 private:192.168.128.0/19
2025-05-31 18:30:42 [ℹ]  subnets for ap-northeast-1a - public:192.168.64.0/19 private:192.168.160.0/19
2025-05-31 18:30:42 [ℹ]  nodegroup "node-group-al2023" will use "" [AmazonLinux2023/1.33]
2025-05-31 18:30:42 [ℹ]  using Kubernetes version 1.33
2025-05-31 18:30:42 [ℹ]  creating EKS cluster "test-cluster" in "ap-northeast-1" region with managed nodes
2025-05-31 18:30:42 [ℹ]  1 nodegroup (node-group-al2023) was included (based on the include/exclude rules)
2025-05-31 18:30:42 [ℹ]  will create a CloudFormation stack for cluster itself and 1 managed nodegroup stack(s)
2025-05-31 18:30:42 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-1 --cluster=test-cluster'
2025-05-31 18:30:42 [ℹ]  Kubernetes API endpoint access will use provided values {publicAccess=true, privateAccess=true} for cluster "test-cluster" in "ap-northeast-1"
2025-05-31 18:30:42 [ℹ]  configuring CloudWatch logging for cluster "test-cluster" in "ap-northeast-1" (enabled types: audit, authenticator, controllerManager, scheduler & disabled types: api)
2025-05-31 18:30:42 [ℹ]  default addons metrics-server were not specified, will install them as EKS addons
2025-05-31 18:30:42 [ℹ]
2 sequential tasks: { create cluster control plane "test-cluster",
    2 sequential sub-tasks: {
        5 sequential sub-tasks: {
            1 task: { create addons },
            wait for control plane to become ready,
            associate IAM OIDC provider,
            no tasks,
            update VPC CNI to use IRSA if required,
        },
        create managed nodegroup "node-group-al2023",
    }
}
2025-05-31 18:30:42 [ℹ]  building cluster stack "eksctl-test-cluster-cluster"
2025-05-31 18:30:42 [ℹ]  deploying stack "eksctl-test-cluster-cluster"
2025-05-31 18:31:12 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:31:42 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:32:42 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:33:43 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:34:44 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:35:44 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:36:44 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:37:44 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:38:45 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:39:45 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:40:45 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-cluster"
2025-05-31 18:40:47 [ℹ]  creating addon: eks-pod-identity-agent
2025-05-31 18:40:48 [ℹ]  successfully created addon: eks-pod-identity-agent
2025-05-31 18:40:48 [ℹ]  "addonsConfig.autoApplyPodIdentityAssociations" is set to true; will lookup recommended pod identity configuration for "vpc-cni" addon
2025-05-31 18:40:48 [ℹ]  deploying stack "eksctl-test-cluster-addon-vpc-cni-podidentityrole-aws-node"
2025-05-31 18:40:48 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-addon-vpc-cni-podidentityrole-aws-node"
2025-05-31 18:41:18 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-addon-vpc-cni-podidentityrole-aws-node"
2025-05-31 18:41:19 [ℹ]  creating addon: vpc-cni
2025-05-31 18:41:20 [ℹ]  successfully created addon: vpc-cni
2025-05-31 18:41:21 [ℹ]  creating addon: coredns
2025-05-31 18:41:21 [ℹ]  successfully created addon: coredns
2025-05-31 18:41:21 [ℹ]  creating addon: kube-proxy
2025-05-31 18:41:22 [ℹ]  successfully created addon: kube-proxy
2025-05-31 18:41:22 [ℹ]  creating addon: metrics-server
2025-05-31 18:41:22 [ℹ]  successfully created addon: metrics-server
2025-05-31 18:43:25 [ℹ]  addon "vpc-cni" active
2025-05-31 18:43:26 [ℹ]  updating IAM resources stack "eksctl-test-cluster-addon-vpc-cni-podidentityrole-aws-node" for pod identity association "a-bnzlafltvfmhfyccq"
2025-05-31 18:43:27 [ℹ]  waiting for CloudFormation changeset "eksctl--aws-node-update-1748684606" for stack "eksctl-test-cluster-addon-vpc-cni-podidentityrole-aws-node"
2025-05-31 18:43:27 [ℹ]  nothing to update
2025-05-31 18:43:27 [ℹ]  IAM resources for aws-node (pod identity association ID: a-bnzlafltvfmhfyccq) are already up-to-date
2025-05-31 18:43:27 [ℹ]  updating addon
2025-05-31 18:43:38 [ℹ]  addon "vpc-cni" active
2025-05-31 18:43:38 [ℹ]  building managed nodegroup stack "eksctl-test-cluster-nodegroup-node-group-al2023"
2025-05-31 18:43:39 [ℹ]  deploying stack "eksctl-test-cluster-nodegroup-node-group-al2023"
2025-05-31 18:43:39 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-nodegroup-node-group-al2023"
2025-05-31 18:44:10 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-nodegroup-node-group-al2023"
2025-05-31 18:44:51 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-nodegroup-node-group-al2023"
2025-05-31 18:45:43 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-nodegroup-node-group-al2023"
2025-05-31 18:47:04 [ℹ]  waiting for CloudFormation stack "eksctl-test-cluster-nodegroup-node-group-al2023"
2025-05-31 18:47:04 [ℹ]  waiting for the control plane to become ready
2025-05-31 18:47:04 [✔]  saved kubeconfig as "/Users/masukawa.kentaro/.kube/config"
2025-05-31 18:47:04 [ℹ]  no tasks
2025-05-31 18:47:04 [✔]  all EKS cluster resources for "test-cluster" have been created
2025-05-31 18:47:05 [ℹ]  nodegroup "node-group-al2023" has 1 node(s)
2025-05-31 18:47:05 [ℹ]  node "ip-192-168-109-90.ap-northeast-1.compute.internal" is ready
2025-05-31 18:47:05 [ℹ]  waiting for at least 1 node(s) to become ready in "node-group-al2023"
2025-05-31 18:47:05 [ℹ]  nodegroup "node-group-al2023" has 1 node(s)
2025-05-31 18:47:05 [ℹ]  node "ip-192-168-109-90.ap-northeast-1.compute.internal" is ready
2025-05-31 18:47:05 [✔]  created 1 managed nodegroup(s) in cluster "test-cluster"
2025-05-31 18:47:06 [ℹ]  kubectl command should work with "/Users/masukawa.kentaro/.kube/config", try 'kubectl get nodes'
2025-05-31 18:47:06 [✔]  EKS cluster "test-cluster" in "ap-northeast-1" region is ready

各バージョンは下記のようになります。

% kubectl version
Client Version: v1.33.1
Kustomize Version: v5.6.0
Server Version: v1.33.1-eks-7308294

Kubernetes 公式ドキュメント で紹介されているマニフェストを適用します。

apiVersion: v1
kind: Pod
metadata:
  name: resize-demo
spec:
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.8
      resizePolicy:
        - resourceName: cpu
          restartPolicy: NotRequired # Default, but explicit here
        - resourceName: memory
          restartPolicy: RestartContainer
      resources:
        limits:
          memory: "200Mi"
          cpu: "700m"
        requests:
          memory: "200Mi"
          cpu: "700m"

CPU は limits/requests ともに 700m になっていることを Containers.pause.Limits/Requests から確認できます。

% kubectl describe pod resize-demo
Name:             resize-demo
Namespace:        default
Priority:         0
Service Account:  default
Node:             ip-192-168-109-90.ap-northeast-1.compute.internal/192.168.109.90
Start Time:       Sat, 31 May 2025 22:37:59 +0900
Labels:           <none>
Annotations:      <none>
Status:           Running
IP:               192.168.108.83
IPs:
  IP:  192.168.108.83
Containers:
  pause:
    Container ID:   containerd://dc68922bfca7e6fe3f26c71e8c15cd3d4e7c49f01744a8a6a7008445e34c662f
    Image:          registry.k8s.io/pause:3.8
    Image ID:       registry.k8s.io/pause@sha256:9001185023633d17a2f98ff69b6ff2615b8ea02a825adffa40422f51dfdcde9d
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 31 May 2025 22:38:01 +0900
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     700m
      memory:  200Mi
    Requests:
      cpu:        700m
      memory:     200Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-hd8qh (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True
  Initialized                 True
  Ready                       True
  ContainersReady             True
  PodScheduled                True
Volumes:
  kube-api-access-hd8qh:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    Optional:                false
    DownwardAPI:             true
QoS Class:                   Guaranteed
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  14s   default-scheduler  Successfully assigned default/resize-demo to ip-192-168-109-90.ap-northeast-1.compute.internal
  Normal  Pulling    13s   kubelet            Pulling image "registry.k8s.io/pause:3.8"
  Normal  Pulled     12s   kubelet            Successfully pulled image "registry.k8s.io/pause:3.8" in 1.268s (1.268s including waiting). Image size: 311286 bytes.
  Normal  Created    12s   kubelet            Created container: pause
  Normal  Started    12s   kubelet            Started container pause

下記コマンドを実行して、垂直スケールします。
この際、--subresource resize オプションが必要になります。

% kubectl patch pod nginx-6cc5dc7978-pdj42 --subresource resize --patch '{"spec":{"containers":[{"name":"nginx", "resources":{"requests":{"cpu":"0.5","memory":"512Mi"}, "limits":{"cpu":"1","memory":"1024Mi"}}}]}}'
pod/nginx-6cc5dc7978-pdj42 patched

同じ Pod のまま、CPU の limits/requests が 800m になりました。
良い感じです。

% kubectl describe pod resize-demo
Name:             resize-demo
Namespace:        default
Priority:         0
Service Account:  default
Node:             ip-192-168-109-90.ap-northeast-1.compute.internal/192.168.109.90
Start Time:       Sat, 31 May 2025 22:37:59 +0900
Labels:           <none>
Annotations:      <none>
Status:           Running
IP:               192.168.108.83
IPs:
  IP:  192.168.108.83
Containers:
  pause:
    Container ID:   containerd://dc68922bfca7e6fe3f26c71e8c15cd3d4e7c49f01744a8a6a7008445e34c662f
    Image:          registry.k8s.io/pause:3.8
    Image ID:       registry.k8s.io/pause@sha256:9001185023633d17a2f98ff69b6ff2615b8ea02a825adffa40422f51dfdcde9d
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 31 May 2025 22:38:01 +0900
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     800m
      memory:  200Mi
    Requests:
      cpu:        800m
      memory:     200Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-hd8qh (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True
  Initialized                 True
  Ready                       True
  ContainersReady             True
  PodScheduled                True
Volumes:
  kube-api-access-hd8qh:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    Optional:                false
    DownwardAPI:             true
QoS Class:                   Guaranteed
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  2m18s  default-scheduler  Successfully assigned default/resize-demo to ip-192-168-109-90.ap-northeast-1.compute.internal
  Normal  Pulling    2m17s  kubelet            Pulling image "registry.k8s.io/pause:3.8"
  Normal  Pulled     2m16s  kubelet            Successfully pulled image "registry.k8s.io/pause:3.8" in 1.268s (1.268s including waiting). Image size: 311286 bytes.
  Normal  Created    2m16s  kubelet            Created container: pause
  Normal  Started    2m16s  kubelet            Started container pause

--subresource resize オプションを省いて実行すると In-Place Update of Pod Resources を使わないので、requests/limits を変更できませんと怒られます。

% kubectl patch pod resize-demo --patch '{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"900m"}, "limits":{"cpu":"900m"}}}]}}'
The Pod "resize-demo" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`,`spec.initContainers[*].image`,`spec.activeDeadlineSeconds`,`spec.tolerations` (only additions to existing tolerations),`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)
  core.PodSpec{
        Volumes:        {{Name: "kube-api-access-hd8qh", VolumeSource: {Projected: &{Sources: {{ServiceAccountToken: &{ExpirationSeconds: 3607, Path: "token"}}, {ConfigMap: &{LocalObjectReference: {Name: "kube-root-ca.crt"}, Items: {{Key: "ca.crt", Path: "ca.crt"}}}}, {DownwardAPI: &{Items: {{Path: "namespace", FieldRef: &{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}}}, DefaultMode: &420}}}},
        InitContainers: nil,
        Containers: []core.Container{
                {
                        ... // 6 identical fields
                        EnvFrom: nil,
                        Env:     nil,
                        Resources: core.ResourceRequirements{
                                Limits: core.ResourceList{
-                                       s"cpu":    {i: resource.int64Amount{value: 800, scale: -3}, s: "800m", Format: "DecimalSI"},
+                                       s"cpu":    {i: resource.int64Amount{value: 900, scale: -3}, s: "900m", Format: "DecimalSI"},
                                        s"memory": {i: {...}, Format: "BinarySI"},
                                },
                                Requests: core.ResourceList{
-                                       s"cpu":    {i: resource.int64Amount{value: 800, scale: -3}, s: "800m", Format: "DecimalSI"},
+                                       s"cpu":    {i: resource.int64Amount{value: 900, scale: -3}, s: "900m", Format: "DecimalSI"},
                                        s"memory": {i: {...}, Format: "BinarySI"},
                                },
                                Claims: nil,
                        },
                        ResizePolicy:  {{ResourceName: s"cpu", RestartPolicy: "NotRequired"}, {ResourceName: s"memory", RestartPolicy: "RestartContainer"}},
                        RestartPolicy: nil,
                        ... // 13 identical fields
                },
        },
        EphemeralContainers: nil,
        RestartPolicy:       "Always",
        ... // 29 identical fields
  }

監査ログには requestURI/api/v1/namespaces/default/pods/resize-demo/resize?fieldManager=kubectl-patch として記録されます。
--subresource オプションを付けない場合は /api/v1/namespaces/default/pods/resize-demo へのリクエストになります。
In-Place Update of Pod Resources では Pod そのものの更新ではなく、プロパティに対する変更をリクエストしていることがここからも読み取れます。

{
  "kind": "Event",
  "apiVersion": "audit.k8s.io/v1",
  "level": "RequestResponse",
  "auditID": "37fc06a2-5c62-4550-8843-646d09de1dbb",
  "stage": "ResponseComplete",
  "requestURI": "/api/v1/namespaces/default/pods/resize-demo/resize?fieldManager=kubectl-patch",
  "verb": "patch",
  "user": {
    "username": "arn:aws:sts::xxxxxxxxxxxx:assumed-role/AWSReservedSSO_AdministratorAccess_xxxxxxxxxxxx/masukawa",
    "uid": "aws-iam-authenticator:xxxxxxxxxxxx:xxxxxxxxxxxx",
    "groups": ["system:authenticated"],
    "extra": {
      "accessKeyId": ["xxxxxxxxxxxx"],
      "arn": [
        "arn:aws:sts::xxxxxxxxxxxx:assumed-role/AWSReservedSSO_AdministratorAccess_xxxxxxxxxxxx/masukawa"
      ],
      "canonicalArn": [
        "arn:aws:iam::xxxxxxxxxxxx:role/AWSReservedSSO_AdministratorAccess_xxxxxxxxxxxx"
      ],
      "principalId": ["xxxxxxxxxxxx"],
      "sessionName": ["masukawa"],
      "sigs.k8s.io/aws-iam-authenticator/principalId": ["xxxxxxxxxxxx"]
    }
  },
  "sourceIPs": ["219.104.138.156"],
  "userAgent": "kubectl1.33.1/v1.33.1 (darwin/arm64) kubernetes/8adc0f0",
  "objectRef": {
    "resource": "pods",
    "namespace": "default",
    "name": "resize-demo",
    "apiVersion": "v1",
    "subresource": "resize"
  },
  "responseStatus": {
    "metadata": {},
    "code": 200
  },
  "requestObject": {
    "spec": {
      "containers": [
        {
          "name": "pause",
          "resources": {
            "requests": {
              "cpu": "800m"
            },
            "limits": {
              "cpu": "800m"
            }
          }
        }
      ]
    }
  },
  "responseObject": {
    "kind": "Pod",
    "apiVersion": "v1",
    "metadata": {
      "name": "resize-demo",
      "namespace": "default",
      "uid": "7bde55fb-2a7c-4151-b5dc-6f1e66bc088c",
      "resourceVersion": "39479",
      "generation": 2,
      "creationTimestamp": "2025-05-31T13:37:59Z",
      "annotations": {
        "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"resize-demo\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"registry.k8s.io/pause:3.8\",\"name\":\"pause\",\"resizePolicy\":[{\"resourceName\":\"cpu\",\"restartPolicy\":\"NotRequired\"},{\"resourceName\":\"memory\",\"restartPolicy\":\"RestartContainer\"}],\"resources\":{\"limits\":{\"cpu\":\"700m\",\"memory\":\"200Mi\"},\"requests\":{\"cpu\":\"700m\",\"memory\":\"200Mi\"}}}]}}\n"
      },
      "managedFields": [
        {
          "manager": "kubectl-client-side-apply",
          "operation": "Update",
          "apiVersion": "v1",
          "time": "2025-05-31T13:37:59Z",
          "fieldsType": "FieldsV1",
          "fieldsV1": {
            "f:metadata": {
              "f:annotations": {
                ".": {},
                "f:kubectl.kubernetes.io/last-applied-configuration": {}
              }
            },
            "f:spec": {
              "f:containers": {
                "k:{\"name\":\"pause\"}": {
                  ".": {},
                  "f:image": {},
                  "f:imagePullPolicy": {},
                  "f:name": {},
                  "f:resizePolicy": {},
                  "f:resources": {
                    ".": {},
                    "f:limits": {
                      ".": {},
                      "f:memory": {}
                    },
                    "f:requests": {
                      ".": {},
                      "f:memory": {}
                    }
                  },
                  "f:terminationMessagePath": {},
                  "f:terminationMessagePolicy": {}
                }
              },
              "f:dnsPolicy": {},
              "f:enableServiceLinks": {},
              "f:restartPolicy": {},
              "f:schedulerName": {},
              "f:securityContext": {},
              "f:terminationGracePeriodSeconds": {}
            }
          }
        },
        {
          "manager": "kubelet",
          "operation": "Update",
          "apiVersion": "v1",
          "time": "2025-05-31T13:38:02Z",
          "fieldsType": "FieldsV1",
          "fieldsV1": {
            "f:status": {
              "f:conditions": {
                "k:{\"type\":\"ContainersReady\"}": {
                  ".": {},
                  "f:lastProbeTime": {},
                  "f:lastTransitionTime": {},
                  "f:status": {},
                  "f:type": {}
                },
                "k:{\"type\":\"Initialized\"}": {
                  ".": {},
                  "f:lastProbeTime": {},
                  "f:lastTransitionTime": {},
                  "f:status": {},
                  "f:type": {}
                },
                "k:{\"type\":\"PodReadyToStartContainers\"}": {
                  ".": {},
                  "f:lastProbeTime": {},
                  "f:lastTransitionTime": {},
                  "f:status": {},
                  "f:type": {}
                },
                "k:{\"type\":\"Ready\"}": {
                  ".": {},
                  "f:lastProbeTime": {},
                  "f:lastTransitionTime": {},
                  "f:status": {},
                  "f:type": {}
                }
              },
              "f:containerStatuses": {},
              "f:hostIP": {},
              "f:hostIPs": {},
              "f:phase": {},
              "f:podIP": {},
              "f:podIPs": {
                ".": {},
                "k:{\"ip\":\"192.168.108.83\"}": {
                  ".": {},
                  "f:ip": {}
                }
              },
              "f:startTime": {}
            }
          },
          "subresource": "status"
        },
        {
          "manager": "kubectl-patch",
          "operation": "Update",
          "apiVersion": "v1",
          "time": "2025-05-31T13:40:01Z",
          "fieldsType": "FieldsV1",
          "fieldsV1": {
            "f:spec": {
              "f:containers": {
                "k:{\"name\":\"pause\"}": {
                  "f:resources": {
                    "f:limits": {
                      "f:cpu": {}
                    },
                    "f:requests": {
                      "f:cpu": {}
                    }
                  }
                }
              }
            }
          },
          "subresource": "resize"
        }
      ]
    },
    "spec": {
      "volumes": [
        {
          "name": "kube-api-access-hd8qh",
          "projected": {
            "sources": [
              {
                "serviceAccountToken": {
                  "expirationSeconds": 3607,
                  "path": "token"
                }
              },
              {
                "configMap": {
                  "name": "kube-root-ca.crt",
                  "items": [
                    {
                      "key": "ca.crt",
                      "path": "ca.crt"
                    }
                  ]
                }
              },
              {
                "downwardAPI": {
                  "items": [
                    {
                      "path": "namespace",
                      "fieldRef": {
                        "apiVersion": "v1",
                        "fieldPath": "metadata.namespace"
                      }
                    }
                  ]
                }
              }
            ],
            "defaultMode": 420
          }
        }
      ],
      "containers": [
        {
          "name": "pause",
          "image": "registry.k8s.io/pause:3.8",
          "resources": {
            "limits": {
              "cpu": "800m",
              "memory": "200Mi"
            },
            "requests": {
              "cpu": "800m",
              "memory": "200Mi"
            }
          },
          "resizePolicy": [
            {
              "resourceName": "cpu",
              "restartPolicy": "NotRequired"
            },
            {
              "resourceName": "memory",
              "restartPolicy": "RestartContainer"
            }
          ],
          "volumeMounts": [
            {
              "name": "kube-api-access-hd8qh",
              "readOnly": true,
              "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
            }
          ],
          "terminationMessagePath": "/dev/termination-log",
          "terminationMessagePolicy": "File",
          "imagePullPolicy": "IfNotPresent"
        }
      ],
      "restartPolicy": "Always",
      "terminationGracePeriodSeconds": 30,
      "dnsPolicy": "ClusterFirst",
      "serviceAccountName": "default",
      "serviceAccount": "default",
      "nodeName": "ip-192-168-109-90.ap-northeast-1.compute.internal",
      "securityContext": {},
      "schedulerName": "default-scheduler",
      "tolerations": [
        {
          "key": "node.kubernetes.io/not-ready",
          "operator": "Exists",
          "effect": "NoExecute",
          "tolerationSeconds": 300
        },
        {
          "key": "node.kubernetes.io/unreachable",
          "operator": "Exists",
          "effect": "NoExecute",
          "tolerationSeconds": 300
        }
      ],
      "priority": 0,
      "enableServiceLinks": true,
      "preemptionPolicy": "PreemptLowerPriority"
    },
    "status": {
      "phase": "Running",
      "conditions": [
        {
          "type": "PodReadyToStartContainers",
          "status": "True",
          "lastProbeTime": null,
          "lastTransitionTime": "2025-05-31T13:38:02Z"
        },
        {
          "type": "Initialized",
          "status": "True",
          "lastProbeTime": null,
          "lastTransitionTime": "2025-05-31T13:37:59Z"
        },
        {
          "type": "Ready",
          "status": "True",
          "lastProbeTime": null,
          "lastTransitionTime": "2025-05-31T13:38:02Z"
        },
        {
          "type": "ContainersReady",
          "status": "True",
          "lastProbeTime": null,
          "lastTransitionTime": "2025-05-31T13:38:02Z"
        },
        {
          "type": "PodScheduled",
          "status": "True",
          "lastProbeTime": null,
          "lastTransitionTime": "2025-05-31T13:37:59Z"
        }
      ],
      "hostIP": "192.168.109.90",
      "hostIPs": [
        {
          "ip": "192.168.109.90"
        }
      ],
      "podIP": "192.168.108.83",
      "podIPs": [
        {
          "ip": "192.168.108.83"
        }
      ],
      "startTime": "2025-05-31T13:37:59Z",
      "containerStatuses": [
        {
          "name": "pause",
          "state": {
            "running": {
              "startedAt": "2025-05-31T13:38:01Z"
            }
          },
          "lastState": {},
          "ready": true,
          "restartCount": 0,
          "image": "registry.k8s.io/pause:3.8",
          "imageID": "registry.k8s.io/pause@sha256:9001185023633d17a2f98ff69b6ff2615b8ea02a825adffa40422f51dfdcde9d",
          "containerID": "containerd://dc68922bfca7e6fe3f26c71e8c15cd3d4e7c49f01744a8a6a7008445e34c662f",
          "started": true,
          "allocatedResources": {
            "cpu": "700m",
            "memory": "200Mi"
          },
          "resources": {
            "limits": {
              "cpu": "700m",
              "memory": "200Mi"
            },
            "requests": {
              "cpu": "700m",
              "memory": "200Mi"
            }
          },
          "volumeMounts": [
            {
              "name": "kube-api-access-hd8qh",
              "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
              "readOnly": true,
              "recursiveReadOnly": "Disabled"
            }
          ]
        }
      ],
      "qosClass": "Guaranteed"
    }
  },
  "requestReceivedTimestamp": "2025-05-31T13:40:01.856824Z",
  "stageTimestamp": "2025-05-31T13:40:01.872612Z",
  "annotations": {
    "authorization.k8s.io/decision": "allow",
    "authorization.k8s.io/reason": "EKS Access Policy: allowed by ClusterRoleBinding \"arn:aws:iam::xxxxxxxxxxxx:role/aws-reserved/sso.amazonaws.com/ap-northeast-1/AWSReservedSSO_AdministratorAccess_xxxxxxxxxxxx+arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy\" of ClusterRole \"arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy\" to User \"xxxxxxxxxxxx\"",
    "pod-security.kubernetes.io/enforce-policy": "privileged:latest"
  }
}

Pod そのものをマニフェストファイルで管理していれば、マニフェストファイル書き換えに依る更新も可能です。
ただし、特に考えずに実行すると下記エラーが発生しました。

% kubectl apply -f pod-resize.yaml --subresource resize
error: --subresource can only be specified for --server-side

kubectl で --subresource オプションを利用する際、Server-Side Apply を利用していないと、適用できないとのことです。
In-Place Update of Pod Resources に限った話ではなく、status や scale も含めた subresouce を扱う時に係る制限のようです。

https://github.com/kubernetes/kubectl/commit/d6ad8b12b76e58d54dad4308b9a1e464644e7c22

下記ドキュメントに従い、Server-Side Apply に変更します。

https://kubernetes.io/docs/reference/using-api/server-side-apply/#upgrading-from-client-side-apply-to-server-side-apply

まず、マニフェストファイルを kubectl patch で更新された現在の limits/requests の値に更新します。

apiVersion: v1
kind: Pod
metadata:
  name: resize-demo
spec:
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.8
      resizePolicy:
        - resourceName: cpu
          restartPolicy: NotRequired # Default, but explicit here
        - resourceName: memory
          restartPolicy: RestartContainer
      resources:
        limits:
          memory: "200Mi"
          cpu: "800m"
        requests:
          memory: "200Mi"
          cpu: "800m"

その後、下記コマンドを実行します。

% kubectl apply -f pod-resize.yaml --server-side
pod/resize-demo serverside-applied

無事、Server-Side Apply に成功したので、マニフェストファイルを修正して limits/requests を 900m まで上げてみます。

apiVersion: v1
kind: Pod
metadata:
  name: resize-demo
spec:
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.8
      resizePolicy:
        - resourceName: cpu
          restartPolicy: NotRequired # Default, but explicit here
        - resourceName: memory
          restartPolicy: RestartContainer
      resources:
        limits:
          memory: "200Mi"
          cpu: "900m"
        requests:
          memory: "200Mi"
          cpu: "900m"

--server-side オプションを付与して、apply すると今回は成功しました。

% kubectl apply -f pod-resize.yaml --server-side --subresource resize
pod/resize-demo serverside-applied

再作成にならず、requests/limits を更新できています。

% kubectl describe pod resize-demo
Name:             resize-demo
Namespace:        default
Priority:         0
Service Account:  default
Node:             ip-192-168-109-90.ap-northeast-1.compute.internal/192.168.109.90
Start Time:       Sat, 31 May 2025 22:37:59 +0900
Labels:           <none>
Annotations:      <none>
Status:           Running
IP:               192.168.108.83
IPs:
  IP:  192.168.108.83
Containers:
  pause:
    Container ID:   containerd://dc68922bfca7e6fe3f26c71e8c15cd3d4e7c49f01744a8a6a7008445e34c662f
    Image:          registry.k8s.io/pause:3.8
    Image ID:       registry.k8s.io/pause@sha256:9001185023633d17a2f98ff69b6ff2615b8ea02a825adffa40422f51dfdcde9d
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 31 May 2025 22:38:01 +0900
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     900m
      memory:  200Mi
    Requests:
      cpu:        900m
      memory:     200Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-hd8qh (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True
  Initialized                 True
  Ready                       True
  ContainersReady             True
  PodScheduled                True
Volumes:
  kube-api-access-hd8qh:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    Optional:                false
    DownwardAPI:             true
QoS Class:                   Guaranteed
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  13m   default-scheduler  Successfully assigned default/resize-demo to ip-192-168-109-90.ap-northeast-1.compute.internal
  Normal  Pulling    13m   kubelet            Pulling image "registry.k8s.io/pause:3.8"
  Normal  Pulled     13m   kubelet            Successfully pulled image "registry.k8s.io/pause:3.8" in 1.268s (1.268s including waiting). Image size: 311286 bytes.
  Normal  Created    13m   kubelet            Created container: pause
  Normal  Started    13m   kubelet            Started container pause

最後に

EKS が Kubernetes v1.33 を利用できるようになったので、In-Place Update of Pod Resources を触ってみました。
スケールアウトに頼り辛く、Pod の再起動を許容できない場合に便利そうです。
Dynamic Resource Allocation など他にも新しく使える機能は多いので、別途試してみようと思います。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.