YasuBlog

中年インフラエンジニアの備忘録です。

Kubernetes の Eviction

Kubernetes の Eviction について整理し、実際の挙動を確認しました。

1. Eviction

Eviction とは、Node のリソースを確保するために kubelet が Pod を終了する事です。

kubelet は定期的に Node のリソースを監視しており、空きリソースが閾値(Eviction Threshold)を下回るとPod を Evict します。

f:id:dunkshoot:20220202231210p:plain

具体的には Eviction Signal と Eviction Threshold を比較して Pod を Evict するかどうかを判断します。

Eviction Signal とは、ある特定時点での特定リソースの状態です。

Eviction Signal 説明
memory.available 空きメモリ
nodefs.available (ローカルディスク、emptyDir,ログストレージ等に使われる)メインのファイルシステムの空き容量
nodefs.inodesFree (ローカルディスク、emptyDir,ログストレージ等に使われる)メインのファイルシステムの空き inode 量
imagefs.available (コンテナランタイムがコンテナイメージとコンテナ書き込み可能レイヤーを格納するために使用する)オプションのファイルシステムの空き容量
imagefs.inodesFree (コンテナランタイムがコンテナイメージとコンテナ書き込み可能レイヤーを格納するために使用する)オプションのファイルシステムの空き inode 量
pid.available 空き pid

なお、Eviction Signal の監視間隔は kubelet フラグ --housekeeping-interval で設定可能です。デフォルトでは 10 秒です。

Eviction Threshold には soft と hard があります。

Eviction Threshold Eviction Signal が Eviction Threshold を下回った際の挙動 デフォルト値
eviction-soft ・Pod に SIGTERM が送られる
・SIGTERM 後、Pod に設定された terminationGracePeriodSeconds または kubelete に設定された --eviction-max-pod-grace-period の短い方の時間を待った後に SIGKILL が送られる
・Evict 対象になってから Evict を実行するまでの時間を--eviction-soft-grace-period で設定できる
 例えば eviction-soft-grace-period=memory.available=1m30s を設定した場合、Pod が Evict 対象になってから 1分30秒後に Pod に SIGTERM が送られる
なし
eviction-hard ・Pod に SIGKILL が送られる memory.available<100Mi
nodefs.available<10%
imagefs.available<15%
nodefs.inodesFree<5%

続いて、どのように Evict 対象の Pod を選定しているかという点ですが、以下の基準で決まります。

  1. requests を超えてリソースを使用しているか
  2. Pod に設定されている Pod Priority
  3. requests を超えている量がより多いもの

結果的には、次の順序で Pod をランク付けして Evict します。簡単に言うと requests/limits の両方とも設定していない Pod が最初に Evict されます。

  1. requests を超えてリソースを使用している BestEffort または Burstable の Pod
  2. requests を超えていない Guaranteed または Burstable の Pod

※ requests/BestEffort/Burstable/Guaranteed については Kubernetes のリソース制限とその挙動確認 - YasuBlog に整理しています

2. Eviction 発生後の挙動

Pod が Evict されると Node の Condition が変化し Taint が付与されます。 Taint とは直訳すると「汚れ」の意味で、Taint が付与された Node には Pod をスケジュールさせないといった使い方ができます。

Node Condition と Eviction Signal のマッピングは以下です。Eviction が発生していない状態ではそれぞれの Node Condition は False です。Eviction が発生すると True に変わります。

Node Condition Eviction Signal 説明
MemoryPressure memory.available Node で使用可能なメモリが Eviction Threshold を満たした
DiskPressure nodefs.available
nodefs.inodesFree
imagefs.available
imagefs.inodesFree
Node の root or image ファイルシステム の 使用可能ディスク量 or inode が Eviction Threshold を満たした
PIDPressure pid.available Node で使用可能なプロセス ID が Eviction Threshold を満たした

なお、Node Condition の更新間隔は kubelet フラグ --node-status-update-frequency で設定可能です。デフォルトでは 10 秒です。

Node Condition が True になる事により付与される Taint は以下です。

Node Condition Taint
MemoryPressure node.kubernetes.io/memory-pressure:NoSchedule
DiskPressure node.kubernetes.io/disk-pressure:NoSchedule
PIDPressure node.kubernetes.io/pid-pressure:NoSchedule

例えば、Deployment や StatefulSet の場合は Pod が Evict されると新しい Pod が起動してきます。すぐに Pod が起動するとまたすぐに Evict される事になり、Pod 作成 -> Evict が繰り返し発生する事になります。これを防ぐために Node Condition を変更するまでのタイムラグを --eviction-pressure-transition-period フラグで設定可能です。デフォルトでは 5 分です。

3. 検証

3.1. 検証環境構築

eksctl コマンドで EKS Cluster を作成する - YasuBlog で作成した EKS Cluster を使用します。

3.2. EKS の 設定値

まずは eviction-soft/eviction-hard の確認方法です。Node の情報なので KubernetesAPI サーバから取得できます。

kubectl proxy コマンドでプロキシをローカルに起動すると API サーバにアクセスできます。

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

デフォルトでローカルの 8001 ポートでプロキシが起動するので、別ターミナルで http://localhost:8001/api/v1/nodes/<Node 名>/proxy/configz にアクセスします。json なので jq で整形すると見やすいです。

$ kubectl get node
NAME                                              STATUS   ROLES    AGE    VERSION
ip-10-0-101-27.ap-northeast-1.compute.internal    Ready    <none>   19m   v1.21.5-eks-9017834
ip-10-0-102-128.ap-northeast-1.compute.internal   Ready    <none>   20m   v1.21.5-eks-9017834
ip-10-0-103-229.ap-northeast-1.compute.internal   Ready    <none>   19m   v1.21.5-eks-9017834
$ curl -sSL "http://localhost:8001/api/v1/nodes/ip-10-0-101-27.ap-northeast-1.compute.internal/proxy/configz" | jq .
{
  "kubeletconfig": {
    "enableServer": true,
    "syncFrequency": "1m0s",
    "fileCheckFrequency": "20s",
    "httpCheckFrequency": "20s",
    "address": "0.0.0.0",
    "port": 10250,
    "tlsCipherSuites": [
      "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
      "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
      "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
      "TLS_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_RSA_WITH_AES_128_GCM_SHA256"
    ],
    "serverTLSBootstrap": true,
    "authentication": {
      "x509": {
        "clientCAFile": "/etc/kubernetes/pki/ca.crt"
      },
      "webhook": {
        "enabled": true,
        "cacheTTL": "2m0s"
      },
      "anonymous": {
        "enabled": false
      }
    },
    "authorization": {
      "mode": "Webhook",
      "webhook": {
        "cacheAuthorizedTTL": "5m0s",
        "cacheUnauthorizedTTL": "30s"
      }
    },
    "registryPullQPS": 5,
    "registryBurst": 10,
    "eventRecordQPS": 5,
    "eventBurst": 10,
    "enableDebuggingHandlers": true,
    "healthzPort": 10248,
    "healthzBindAddress": "127.0.0.1",
    "oomScoreAdj": -999,
    "clusterDomain": "cluster.local",
    "clusterDNS": [
      "172.20.0.10"
    ],
    "streamingConnectionIdleTimeout": "4h0m0s",
    "nodeStatusUpdateFrequency": "10s",
    "nodeStatusReportFrequency": "5m0s",
    "nodeLeaseDurationSeconds": 40,
    "imageMinimumGCAge": "2m0s",
    "imageGCHighThresholdPercent": 85,
    "imageGCLowThresholdPercent": 80,
    "volumeStatsAggPeriod": "1m0s",
    "cgroupRoot": "/",
    "cgroupsPerQOS": true,
    "cgroupDriver": "cgroupfs",
    "cpuManagerPolicy": "none",
    "cpuManagerReconcilePeriod": "10s",
    "memoryManagerPolicy": "None",
    "topologyManagerPolicy": "none",
    "topologyManagerScope": "container",
    "runtimeRequestTimeout": "2m0s",
    "hairpinMode": "hairpin-veth",
    "maxPods": 17,
    "podPidsLimit": -1,
    "resolvConf": "/etc/resolv.conf",
    "cpuCFSQuota": true,
    "cpuCFSQuotaPeriod": "100ms",
    "nodeStatusMaxImages": 50,
    "maxOpenFiles": 1000000,
    "contentType": "application/vnd.kubernetes.protobuf",
    "kubeAPIQPS": 5,
    "kubeAPIBurst": 10,
    "serializeImagePulls": false,
    "evictionHard": {
      "memory.available": "100Mi",
      "nodefs.available": "10%",
      "nodefs.inodesFree": "5%"
    },
    "evictionPressureTransitionPeriod": "5m0s",
    "enableControllerAttachDetach": true,
    "protectKernelDefaults": true,
    "makeIPTablesUtilChains": true,
    "iptablesMasqueradeBit": 14,
    "iptablesDropBit": 15,
    "featureGates": {
      "RotateKubeletServerCertificate": true
    },
    "failSwapOn": true,
    "containerLogMaxSize": "10Mi",
    "containerLogMaxFiles": 5,
    "configMapAndSecretChangeDetectionStrategy": "Watch",
    "kubeReserved": {
      "cpu": "70m",
      "ephemeral-storage": "1Gi",
      "memory": "442Mi"
    },
    "enforceNodeAllocatable": [
      "pods"
    ],
    "volumePluginDir": "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
    "logging": {
      "format": "text"
    },
    "enableSystemLogHandler": true,
    "shutdownGracePeriod": "0s",
    "shutdownGracePeriodCriticalPods": "0s",
    "enableProfilingHandler": true,
    "enableDebugFlagsHandler": true
  }
}
 

86 行目から 90 行目に eviction-hard が表示されています。eviction-soft は設定されていません。 91 行目から evictionPressureTransitionPeriod がデフォルトの 5 分に設定されていることもわかります。

なお、API サーバではなく Node にログインして設定ファイルから値を確認する事も可能です。

@node$ cat /etc/kubernetes/kubelet/kubelet-config.json
{
  "kind": "KubeletConfiguration",
  "apiVersion": "kubelet.config.k8s.io/v1beta1",
  "address": "0.0.0.0",
  "authentication": {
    "anonymous": {
      "enabled": false
    },
    "webhook": {
      "cacheTTL": "2m0s",
      "enabled": true
    },
    "x509": {
      "clientCAFile": "/etc/kubernetes/pki/ca.crt"
    }
  },
  "authorization": {
    "mode": "Webhook",
    "webhook": {
      "cacheAuthorizedTTL": "5m0s",
      "cacheUnauthorizedTTL": "30s"
    }
  },
  "clusterDomain": "cluster.local",
  "hairpinMode": "hairpin-veth",
  "readOnlyPort": 0,
  "cgroupDriver": "cgroupfs",
  "cgroupRoot": "/",
  "featureGates": {
    "RotateKubeletServerCertificate": true
  },
  "protectKernelDefaults": true,
  "serializeImagePulls": false,
  "serverTLSBootstrap": true,
  "tlsCipherSuites": [
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
    "TLS_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_RSA_WITH_AES_128_GCM_SHA256"
  ],
  "maxPods": 20,
  "clusterDNS": [
    "172.20.0.10"
  ],
  "evictionHard": {
    "memory.available": "100Mi",
    "nodefs.available": "10%",
    "nodefs.inodesFree": "5%"
  },
  "kubeReserved": {
    "cpu": "70m",
    "ephemeral-storage": "1Gi",
    "memory": "442Mi"
  }
}

インスタンスタイプ毎の設定値を確認してみました。Node のリソース量に関わらず固定値が設定されているようです。

インスタンスタイプ eviction-soft eviction-hard
t3.small なし memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
t3.medium なし memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
m5.large なし memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
m5.xlarge なし memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%

3.3. 設定変更

AWS マネージドではないセルフマネージド NodeGroup の場合は以下の通り設定できそうです。

Customizing kubelet configuration - eksctl

AWS マネージドの場合は yaml での設定方法は見つかりませんでした。Node 上の設定ファイル(/etc/kubernetes/kubelet/kubelet-config.json や /etc/systemd/system/kubelet.service)を直接修正する事で設定変更が可能です。 Node 再作成時にも同様に設定されるように、カスタム起動テンプレートかカスタム AMI を使用する必要があります。

起動テンプレートのサポート - Amazon EKS

Amazon EKS ワーカーノードを設定して特定のディスク使用率でイメージキャッシュをクリーンアップする

個人的には AWS マネージドという事は AWS が良かれと思って設計した値なのでユーザ側がカスタマイズする必要は無いと思っています。

3.4. Eviction を発生させる

メモリ制限をかけない Pod を起動して空きメモリが eviction-hard の閾値である 100Mi 以下になった際の挙動を確認します。

以下の manifest で deployment を起動します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-a
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-a
  template:
    metadata:
      labels:
        app: app-a
    spec:
      containers:
      - name: amazonlinux
        image: public.ecr.aws/amazonlinux/amazonlinux:latest
        command:
          - "bin/bash"
          - "-c"
          - "sleep 3600"

Pod が起動している事を確認します。

$ kubectl get pod -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP            NODE                                              NOMINATED NODE   READINESS GATES
deployment-a-75979d4477-ssffj   1/1     Running   0          13s     10.0.103.15   ip-10-0-101-27.ap-northeast-1.compute.internal   <none>           <none>

Eviction が発生する前の Node の状態を確認します。

$ kubectl describe node ip-10-0-101-27.ap-northeast-1.compute.internal
Name:               ip-10-0-101-27.ap-northeast-1.compute.internal
Roles:              <none>
Labels:             alpha.eksctl.io/cluster-name=ekstest
                    alpha.eksctl.io/nodegroup-name=managed-ng
                    beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/instance-type=t3.medium
                    beta.kubernetes.io/os=linux
                    eks.amazonaws.com/capacityType=ON_DEMAND
                    eks.amazonaws.com/nodegroup=managed-ng
                    eks.amazonaws.com/nodegroup-image=ami-0b49509d917c6649b
                    eks.amazonaws.com/sourceLaunchTemplateId=lt-0e28d03ab5eb48995
                    eks.amazonaws.com/sourceLaunchTemplateVersion=1
                    failure-domain.beta.kubernetes.io/region=ap-northeast-1
                    failure-domain.beta.kubernetes.io/zone=ap-northeast-1a
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=ip-10-0-101-27.ap-northeast-1.compute.internal
                    kubernetes.io/os=linux
                    node.kubernetes.io/instance-type=t3.medium
                    topology.kubernetes.io/region=ap-northeast-1
                    topology.kubernetes.io/zone=ap-northeast-1a
Annotations:        node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Wed, 02 Feb 2022 22:56:40 +0900
Taints:             <none>
Unschedulable:      false
Lease:
  HolderIdentity:  ip-10-0-101-27.ap-northeast-1.compute.internal
  AcquireTime:     <unset>
  RenewTime:       Wed, 02 Feb 2022 23:34:49 +0900
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Wed, 02 Feb 2022 23:31:12 +0900   Wed, 02 Feb 2022 22:56:38 +0900   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Wed, 02 Feb 2022 23:31:12 +0900   Wed, 02 Feb 2022 22:56:38 +0900   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Wed, 02 Feb 2022 23:31:12 +0900   Wed, 02 Feb 2022 22:56:38 +0900   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            True    Wed, 02 Feb 2022 23:31:12 +0900   Wed, 02 Feb 2022 22:57:11 +0900   KubeletReady                 kubelet is posting ready status
Addresses:
  InternalIP:   10.0.101.27
  ExternalIP:   54.250.92.70
  Hostname:     ip-10-0-101-27.ap-northeast-1.compute.internal
  InternalDNS:  ip-10-0-101-27.ap-northeast-1.compute.internal
Capacity:
  attachable-volumes-aws-ebs:  25
  cpu:                         2
  ephemeral-storage:           31444972Ki
  hugepages-1Gi:               0
  hugepages-2Mi:               0
  memory:                      3967460Ki
  pods:                        17
Allocatable:
  attachable-volumes-aws-ebs:  25
  cpu:                         1930m
  ephemeral-storage:           27905944324
  hugepages-1Gi:               0
  hugepages-2Mi:               0
  memory:                      3412452Ki
  pods:                        17
System Info:
  Machine ID:                 ec21395734297aab934fe1f5ab059326
  System UUID:                ec213957-3429-7aab-934f-e1f5ab059326
  Boot ID:                    ed9c469c-a158-4b4c-a677-8f6688cc53a0
  Kernel Version:             5.4.172-90.336.amzn2.x86_64
  OS Image:                   Amazon Linux 2
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  docker://20.10.7
  Kubelet Version:            v1.21.5-eks-9017834
  Kube-Proxy Version:         v1.21.5-eks-9017834
ProviderID:                   aws:///ap-northeast-1a/i-07d2e46ad433eeb9d
Non-terminated Pods:          (3 in total)
  Namespace                   Name                             CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                             ------------  ----------  ---------------  -------------  ---
  default                     deployment-a-75979d4477-ssffj    0 (0%)        0 (0%)      0 (0%)           0 (0%)         4m30s
  kube-system                 aws-node-b7xfp                   25m (1%)      0 (0%)      0 (0%)           0 (0%)         38m
  kube-system                 kube-proxy-qqzbs                 100m (5%)     0 (0%)      0 (0%)           0 (0%)         38m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                    Requests   Limits
  --------                    --------   ------
  cpu                         125m (6%)  0 (0%)
  memory                      0 (0%)     0 (0%)
  ephemeral-storage           0 (0%)     0 (0%)
  hugepages-1Gi               0 (0%)     0 (0%)
  hugepages-2Mi               0 (0%)     0 (0%)
  attachable-volumes-aws-ebs  0          0
Events:
  Type    Reason                   Age                From        Message
  ----    ------                   ----               ----        -------
  Normal  Starting                 38m                kubelet     Starting kubelet.
  Normal  NodeHasSufficientMemory  38m (x2 over 38m)  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasSufficientMemory
  Normal  NodeHasNoDiskPressure    38m (x2 over 38m)  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasNoDiskPressure
  Normal  NodeHasSufficientPID     38m (x2 over 38m)  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasSufficientPID
  Normal  NodeAllocatableEnforced  38m                kubelet     Updated Node Allocatable limit across pods
  Normal  Starting                 38m                kube-proxy  Starting kube-proxy.
  Normal  NodeReady                37m                kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeReady

34 行目の通り MemoryPressure は False です。また 25 行目から Taint が何も付与されていないことがわかります。

では、Pod にメモリ負荷をかけるため、Pod にログインして yes コマンドを実行します。

$ kubectl exec -it deployment-a-75979d4477-ssffj -- /bin/bash
@pod$ /dev/null < $(yes) &
@pod$ /dev/null < $(yes) &
@pod$ /dev/null < $(yes) &
@pod$ /dev/null < $(yes) &
@pod$ /dev/null < $(yes) &

しばらくすると Pod が Evict されて他の Node で再起動しました。

$ kubectl get pod -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP            NODE                                              NOMINATED NODE   READINESS GATES
deployment-a-75979d4477-2n8mt   0/1     Evicted   0          112s    <none>        ip-10-0-101-27.ap-northeast-1.compute.internal    <none>           <none>
deployment-a-75979d4477-6ls9v   1/1     Running   0          111s    10.0.103.15   ip-10-0-103-229.ap-northeast-1.compute.internal   <none>           <none>

最初に Pod が起動していた Node の状態を確認します。

$ kubectl describe node ip-10-0-101-27.ap-northeast-1.compute.internal
Name:               ip-10-0-101-27.ap-northeast-1.compute.internal

~省略~

CreationTimestamp:  Wed, 02 Feb 2022 22:56:40 +0900
Taints:             node.kubernetes.io/memory-pressure:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  ip-10-0-101-27.ap-northeast-1.compute.internal
  AcquireTime:     <unset>
  RenewTime:       Wed, 02 Feb 2022 23:35:09 +0900
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                         Message
  ----             ------  -----------------                 ------------------                ------                         -------
  MemoryPressure   True    Wed, 02 Feb 2022 23:35:04 +0900   Wed, 02 Feb 2022 23:35:04 +0900   KubeletHasInsufficientMemory   kubelet has insufficient memory available
  DiskPressure     False   Wed, 02 Feb 2022 23:35:04 +0900   Wed, 02 Feb 2022 22:56:38 +0900   KubeletHasNoDiskPressure       kubelet has no disk pressure
  PIDPressure      False   Wed, 02 Feb 2022 23:35:04 +0900   Wed, 02 Feb 2022 22:56:38 +0900   KubeletHasSufficientPID        kubelet has sufficient PID available
  Ready            True    Wed, 02 Feb 2022 23:35:04 +0900   Wed, 02 Feb 2022 22:57:11 +0900   KubeletReady                   kubelet is posting ready status

~省略~

Events:
  Type     Reason                     Age                From        Message
  ----     ------                     ----               ----        -------
  Normal   Starting                   38m                kubelet     Starting kubelet.
  Normal   NodeHasSufficientMemory    38m (x2 over 38m)  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasSufficientMemory
  Normal   NodeHasNoDiskPressure      38m (x2 over 38m)  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasNoDiskPressure
  Normal   NodeHasSufficientPID       38m (x2 over 38m)  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasSufficientPID
  Normal   NodeAllocatableEnforced    38m                kubelet     Updated Node Allocatable limit across pods
  Normal   Starting                   38m                kube-proxy  Starting kube-proxy.
  Normal   NodeReady                  38m                kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeReady
  Warning  EvictionThresholdMet       17s                kubelet     Attempting to reclaim memory
  Normal   NodeHasInsufficientMemory  14s                kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasInsufficientMemory

MemorryPressure が True になり、Taint に node.kubernetes.io/memory-pressure:NoSchedule が付与されました。NoSchedule が付与されると Pod をスケジュールできなくなります。(Pod に合致する toleration がない場合)

しばらくして再度確認すると Taint がなくなって MemoryPressure が False に戻っていました。Condition の 更新時間は LastTransitionTime の列で確認できますが、evictionPressureTransitionPeriod の設定通り 5 分後に更新されたことがわかります。

yasu eviction %k describe node ip-10-0-101-27.ap-northeast-1.compute.internal
Name:               ip-10-0-101-27.ap-northeast-1.compute.internal

~省略~

CreationTimestamp:  Wed, 02 Feb 2022 22:56:40 +0900
Taints:             <none>
Unschedulable:      false
Lease:
  HolderIdentity:  ip-10-0-101-27.ap-northeast-1.compute.internal
  AcquireTime:     <unset>
  RenewTime:       Wed, 02 Feb 2022 23:47:27 +0900
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Wed, 02 Feb 2022 23:45:08 +0900   Wed, 02 Feb 2022 23:40:05 +0900   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Wed, 02 Feb 2022 23:45:08 +0900   Wed, 02 Feb 2022 22:56:38 +0900   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Wed, 02 Feb 2022 23:45:08 +0900   Wed, 02 Feb 2022 22:56:38 +0900   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            True    Wed, 02 Feb 2022 23:45:08 +0900   Wed, 02 Feb 2022 22:57:11 +0900   KubeletReady                 kubelet is posting ready status

~省略~

Events:
  Type     Reason                     Age                  From        Message
  ----     ------                     ----                 ----        -------
  Normal   Starting                   50m                  kubelet     Starting kubelet.
  Normal   NodeHasNoDiskPressure      50m (x2 over 50m)    kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasNoDiskPressure
  Normal   NodeHasSufficientPID       50m (x2 over 50m)    kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasSufficientPID
  Normal   NodeAllocatableEnforced    50m                  kubelet     Updated Node Allocatable limit across pods
  Normal   Starting                   50m                  kube-proxy  Starting kube-proxy.
  Normal   NodeReady                  50m                  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeReady
  Warning  EvictionThresholdMet       12m                  kubelet     Attempting to reclaim memory
  Normal   NodeHasInsufficientMemory  12m                  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasInsufficientMemory
  Normal   NodeHasSufficientMemory    7m25s (x3 over 50m)  kubelet     Node ip-10-0-101-27.ap-northeast-1.compute.internal status is now: NodeHasSufficientMemory

4. まとめ

Eviction まわりの挙動を確認してみました。EKS 等のマネージドサービスの場合は特別な要件がない限り設定値は変更しなくて良いと思います。

5. 参考

Node-pressure Eviction | Kubernetes

Configure Out of Resource Handling | Kubernetes

TaintとToleration | Kubernetes