YasuBlog

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

Kubernetes の kube-reserved と system-reserved

Kubernetes のシステム用に割り当てられるリソースについて整理し、実際の挙動を確認しました。

1. Pod に割り当て可能なリソース

まずは、Kubernetes 上で Pod が使えるリソースについて整理します。

Node 上では OS, Kubernetes が動いており、その上で Pod が動いているため Pod が Node のリソースを全て使用すると問題(OS 全体に影響が出る、Kubernetes 自体が動作しなくなる等)が発生します。 それを回避するため、OS, Kubernetes 用リソース kube-reserved, system-reserved と、Node の最低空きリソース eviction-threshold があります。

Pod に割り当てられるリソースは Node のリソース量から、kube-reserved,system-reserved,eviction-threshold を引いた分になります。

f:id:dunkshoot:20220202230021p:plain

本記事では kube-reserved,system-reserved についてまとめています。eviction-threshold については別の記事でまとめます。

2. cgroup

kube-reserved,system-reserved の説明に cgroup が出てくるため、cgroup について簡単に整理します。

cgroup とは Control Group の略で、プロセスをグループ化し CPU やメモリ等のリソースをグループ毎に制限する Linux カーネルの機能です。

cgroupfs という仮想ファイルシステム上で cgroup を操作します。cgroupfs は /sys/fs/cgroup にマウントされています。

eksctl コマンドで EKS Cluster を作成する - YasuBlog の記事で作成した EKS Node で確認すると cgroupfs が /sys/fs/cgroup にマウントされている事が確認できます。

$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         1.9G     0  1.9G    0% /dev
tmpfs            1.9G     0  1.9G    0% /dev/shm
tmpfs            1.9G  760K  1.9G    1% /run
tmpfs            1.9G     0  1.9G    0% /sys/fs/cgroup
/dev/nvme0n1p1    30G  2.7G   28G    9% /
tmpfs            388M     0  388M    0% /run/user/1000

~省略~

$ mount -l | grep cgroup
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)

/sys/fs/cgroup 配下のファイルで cgroup を管理しています。

$ ls /sys/fs/cgroup/
blkio  cpu  cpu,cpuacct  cpuacct  cpuset  devices  freezer  hugetlb  memory  net_cls  net_cls,net_prio  net_prio  perf_event  pids  systemd

例えば、/sys/fs/cgroup/cpu/tasks に記載されたプロセス(PID)の CPU リソース は /sys/fs/cgroup/cpu/cpu.shares に記載された 1024 に制限されます。※ CPU share は CPU の相対値

$ cat /sys/fs/cgroup/cpu/tasks
1
2
3
4
6
8

~省略~

$ cat /sys/fs/cgroup/cpu/cpu.shares
1024

cgroup を追加する際はディレクトリを作成します。test という cgroup を作成してみます。

$ mkdir /sys/fs/cgroup/cpu/test

ディレクトリを作成するとその配下に自動でファイルが作成されます。

$ ls /sys/fs/cgroup/cpu/test
cgroup.clone_children  cgroup.procs  cpu.cfs_period_us  cpu.cfs_quota_us  cpu.rt_period_us  cpu.rt_runtime_us  cpu.shares  cpu.stat  cpuacct.stat  cpuacct.usage  cpuacct.usage_all  cpuacct.usage_percpu  cpuacct.usage_percpu_sys  cpuacct.usage_percpu_user  cpuacct.usage_sys  cpuacct.usage_user  notify_on_release  tasks

例えば tasks に 2000、cpu.share に 512 を記載すると PID 2000 のプロセスの CPU share を 512 に制限する事になります。

$ cat /sys/fs/cgroup/cpu/test/tasks
2000
$ cat /sys/fs/cgroup/cpu/test/cpu.shares
512

コンテナは OS から見たらただのプロセスなのでこのようにリソースを制限出来ます。

なお、systemd-cgls コマンドで cgroup 階層全体が表示されます。

$ systemd-cgls
├─   1 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
├─2913 bpfilter_umh
├─kubepods
│ └─burstable
│   ├─pod2a7eefa0-6719-4cac-82f4-6b7d58d54635
│   │ ├─510f73e18aa7dac452276e74f7acfd7087f44562eae619e05654bae567f5b460
│   │ │ ├─4276 bash /app/entrypoint.sh
│   │ │ ├─4327 ./aws-k8s-agent
│   │ │ └─4328 tee -i aws-k8s-agent.log
│   │ └─e7101431d6f2cad771a0d30c81101d06475c2b4dad6b033eaf58093e636679a8
│   │   └─3585 /pause
│   ├─pode895364f-3e61-48db-93cc-b0f8daefaa55
│   │ ├─c63c43b9ab660f4e2cab66f576e1a3208b6b97d659c0666c7c2c342daeecef8e
│   │ │ └─4891 /coredns -conf /etc/coredns/Corefile
│   │ └─aa5779e764f7fbfe68948de0ce425e7e03b2c5a2082586f6eb94e9a29b4670a3
│   │   └─4587 /pause
│   ├─poddd65f1eb-593a-4882-bb86-9f453fc1a1d2
│   │ ├─0d51784c7ac2cdc9a013d19d79a5617c27c6d4f86c3dfc28c7517f96eb9c8743
│   │ │ └─3637 /pause
│   │ └─cb2b42771c6e91de40b0cddde1e57677f5c112fa013fd9c601fa960e2e9d785d
│   │   └─3876 kube-proxy --v=2 --config=/var/lib/kube-proxy-config/config
│   └─podab6c28cc-525a-4fd2-9a69-07720eeed5ee
│     ├─73c143c3c6b7706e3ef2e45b57ca0d6484b9679ba1920c095ba3c1b9025892b2
│     │ └─4626 /pause
│     └─8dd43dd500d50180b3d287d96be60c1aa341dff864c9ce1903825dfeae6a7c04
│       └─4925 /coredns -conf /etc/coredns/Corefile
├─user.slice
│ └─user-1000.slice
│   ├─session-4.scope
│   │ ├─28624 sshd: ec2-user [priv]
│   │ ├─28655 sshd: ec2-user@pts/1
│   │ ├─28656 -bash
│   │ ├─28700 sudo -s
│   │ ├─28701 /bin/bash
│   │ └─29383 systemd-cgls
│   └─session-1.scope
│     ├─5724 sshd: ec2-user [priv]
│     ├─5779 sshd: ec2-user@pts/0
│     ├─5794 -bash
│     ├─7556 sudo -s
│     ├─7557 /bin/bash
│     ├─8704 systemd-cgls
│     └─8705 less
└─system.slice
  ├─rngd.service
  │ └─1875 /sbin/rngd -f --fill-watermark=0 --exclude=jitter
  ├─irqbalance.service
  │ └─1853 /usr/sbin/irqbalance --foreground
  ├─amazon-ssm-agent.service
  │ ├─2361 /usr/bin/amazon-ssm-agent
  │ └─2518 /usr/bin/ssm-agent-worker
  ├─containerd.service
  │ ├─2781 /usr/bin/containerd
  │ ├─3545 /usr/bin/containerd-shim-runc-v2 -namespace moby -id e7101431d6f2c...
  │ ├─3546 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 0d51784c7ac2c...
  │ ├─3856 /usr/bin/containerd-shim-runc-v2 -namespace moby -id cb2b42771c6e9...
  │ ├─4257 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 510f73e18aa7d...
  │ ├─4528 /usr/bin/containerd-shim-runc-v2 -namespace moby -id aa5779e764f7f...
  │ ├─4530 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 73c143c3c6b77...
  │ ├─4826 /usr/bin/containerd-shim-runc-v2 -namespace moby -id c63c43b9ab660...
  │ └─4844 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 8dd43dd500d50...
  ├─systemd-udevd.service
  │ └─1305 /usr/lib/systemd/systemd-udevd
  ├─system-serial\x2dgetty.slice
  │ └─serial-getty@ttyS0.service
  │   └─2377 /sbin/agetty --keep-baud 115200,38400,9600 ttyS0 vt220
  ├─docker.service
  │ └─2870 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd....
  ├─chronyd.service
  │ └─1861 /usr/sbin/chronyd
  ├─auditd.service
  │ └─1826 /sbin/auditd
  ├─kubelet.service
  │ └─3054 /usr/bin/kubelet --cloud-provider aws --config /etc/kubernetes/kub...
  ├─systemd-journald.service
  │ └─1284 /usr/lib/systemd/systemd-journald
  ├─sshd.service
  │ └─2423 /usr/sbin/sshd -D
  ├─crond.service
  │ └─2376 /usr/sbin/crond -n
  ├─gssproxy.service
  │ └─1887 /usr/sbin/gssproxy -D
  ├─rsyslog.service
  │ └─2367 /usr/sbin/rsyslogd -n
  ├─rpcbind.service
  │ └─1851 /sbin/rpcbind -w
  ├─network.service
  │ ├─2087 /sbin/dhclient -q -lf /var/lib/dhclient/dhclient--eth0.lease -pf /...
  │ └─2122 /sbin/dhclient -6 -nw -lf /var/lib/dhclient/dhclient6--eth0.lease ...
  ├─lvm2-lvmetad.service
  │ └─1302 /usr/sbin/lvmetad -f
  ├─postfix.service
  │ ├─ 2268 /usr/libexec/postfix/master -w
  │ ├─ 2270 qmgr -l -t unix -u
  │ └─13892 pickup -l -t unix -u
  ├─dbus.service
  │ └─1857 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidf...
  ├─system-getty.slice
  │ └─getty@tty1.service
  │   └─2373 /sbin/agetty --noclear tty1 linux
  └─systemd-logind.service
    └─1864 /usr/lib/systemd/systemd-logind

3. kube-reserved/system-reserved

公式ドキュメント Reserve Compute Resources for System Daemons | Kubernetes には以下のように記載されています。(意訳)

項目 概要
kube-reserved ・kubelet や container runtime, node problem detector などの Kubernetes system daemon 用に確保されるリソース
・Pod として動く system deamon は対象外
・CPU/メモリ/エフェメラルストレージ/PID 数 を指定可能
・kubelet フラグ例
 --kube-reserved=cpu=100m,memory=100Mi,ephemeral-storage=1Gi,pid=1000
Kubernetes system daemon--kube-reserved を強制するには --enforce-node-allocatable=kube-reserved--kube-reserved-cgroup=<cgroup> が必要
system-reserved sshd や udev などの OS system daemon 用に確保されるリソース
カーネルメモリは Pod に割り当てられないためカーネル用のメモリも確保した方がよい
・ユーザのログインセッション用リソースを確保する事も推奨(systemd の世界では user.slice)
・CPU/メモリ/エフェメラルストレージ/PID 数 を指定可能
・kubelet フラグ例
 --system-reserved=cpu=100m,memory=100Mi,ephemeral-storage=1Gi,pid=1000
・OS system daemon--system-reserved を強制するには --enforce-node-allocatable=system-reserved--system-reserved-cgroup=<cgroup> が必要

Kubernetes/OS system daemon--kube-reserved/--system-reserved を強制するには --enforce-node-allocatable=kube-reserved/system-reserved--kube-reserved-cgroup/--systemreserved-cgroup=<cgroup> が必要」がいまいちピンと来なかったです。

--kube-reserved/--system-reserved だけでは Kubernetes/OS system daemon 用にリソースが確保されて強制はされない?

他にも、Kubernetes/OS が設定値以上使おうとするとどうなるのか?推奨値はあるのか?

などなど気になる点が多いため検証してみました。

4. 検証

4.1. 検証環境構築

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

4.2. EKS の kube-reserved/system-reserved 設定値

まずは kube-reserved/system-reserved の確認方法です。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-249.ap-northeast-1.compute.internal   Ready    <none>   2m36s   v1.21.5-eks-bc4871b
ip-10-0-102-241.ap-northeast-1.compute.internal   Ready    <none>   2m27s   v1.21.5-eks-bc4871b
ip-10-0-103-143.ap-northeast-1.compute.internal   Ready    <none>   2m12s   v1.21.5-eks-bc4871b
$ curl -sSL "http://localhost:8001/api/v1/nodes/ip-10-0-101-249.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
  }
}

104 行目から 108 行目に kube-reserved が表示されています。 kubeReservedCgroup/systemReservedCgroup が無いため、--kube-reserved-cgroup/--system-reserved-cgroup が設定されていない事がわかります。 また、enforceNodeAllocatablepods のみ設定されています。(kube-reserved/system-reserved は設定されていない)

なお、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 のリソース量から自動で計算されているようです。

インスタンスタイプ kube-reserved system-reserved kube-reserved-cgroup system-reserved-cgroup
t3.small cpu:70m
memory:376Mi
ephemeral-storage:1Gi
なし なし なし
t3.medium cpu:70m
memory:442Mi
ephemeral-storage:1Gi
なし なし なし
m5.large cpu:80m
memory:574Mi
ephemeral-storage:1Gi
なし なし なし
m5.xlarge cpu:80m
memory:893Mi
ephemeral-storage:1Gi
なし なし なし

4.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 が良かれと思って設計した値なのでユーザ側がカスタマイズする必要は無いと思っています。

4.4. --enforce-node-allocatable/--kube-reserved-cgroup/--system-reserved-cgroup 無しの場合の制限

EKS のデフォルト状態(--enforce-node-allocatable--kube-reserved-cgroup/--system-reserved-cgroup が設定されていない状態)での制限を確認します。

検証用 Node(t3.medium)の設定値は以下です。

cpu memory ephemeral-storage
kube-reserved 70m 442Mi 1Gi
system-reserved なし なし なし

Kubernetes system daemon 用に cpu 70M, memory 442Mi, ephemeral-storage 1Gi が確保されています。最初に記載した通り、Pod に割り当てられるリソース = Node - (kube-reserved + system-reserved + eviction-threshold) のため、「確保されている」と言えると思います。

ただし、--enforce-node-allocatable--kube-reserved-cgroup/--system-reserved-cgroup が設定されていないため強制はされていないはずです。 cpu.shares に 70m 、memory.limit_in_bytes に 442Mi が設定されている cgroup の有無を確認しましたが存在しなかったので確かに「強制されていない(リソースを制限していない)」という事がわかります。同様に --system-reserved を設定して検証しても同じ結果でした。

@node$ find /sys/fs/cgroup/ -type f -name 'cpu.shares' -exec cat {} \; | sort | uniq
102
1024
128
1976
2
25
@node$ find /sys/fs/cgroup/ -type f -name 'memory.limit_in_bytes' -exec cat {} \; | sort | uniq
3135741952
9223372036854771712

つまり、--kube-reserved/--system-reservedKubernetes 用とか OS 用とか関係なく、単純に Pod に割り当てられない領域として確保しているだけという事になります。これは --kube-reserved--system-reserved が同じ意味を表しているという事になるかと思います。※--enforce-node-allocatable--kube-reserved-cgroup/--system-reserved-cgroup が無い場合

マネージドサービスの EKS が --system-reserved を設定していない理由はこれかと思います。--kube-reserved 一つ設定しておけば Kubernetes/OS 用のリソースとして確保できるからです。(--kube-reserved--system-reserved が同じ意味なので --system-reserved を設定する必要がない)

当然ですが、リソースを制限しているわけではないので、Kubernetes/OS が --kube-reserved/--system-reserved に設定されている値より多くのリソースを使用する事が可能です。

4.5. --enforce-node-allocatable/--kube-reserved-cgroup/--system-reserved-cgroup ありの場合の制限

まずは --kube-reserved-cgroup に設定する cgroup を作成します。プロセスは dockerd を設定します。

@node$ # find /sys/fs/cgroup/ -maxdepth 1 ! -type l -exec mkdir {}/K8s \;
mkdir: ディレクトリ `/sys/fs/cgroup//K8s` を作成できません: Read-only file system
@node$ pgrep -fl dockerd
2877 dockerd
@node$ find /sys/fs/cgroup/ -type d -name K8s -exec sh -c 'echo 2877 > {}/tasks' \;
sh: 0 行: echo: 書き込みエラー: No space left on device

systemd-cgls で cgroup を確認します。

@node$ systemd-cgls
├─   1 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
├─2986 bpfilter_umh
├─kubepods
│ └─burstable
│   ├─pode887cc03-8c8a-47fe-94ce-77f75b71b7e2
│   │ ├─5724c33afacf82531d99d78fcea7b5b07b54105d2729b20953eb3e6a3da9dcfb
│   │ │ └─3674 /pause
│   │ └─b263bef8f5b3aff3df5430cc05551a42d72ffbd93a8e56ed10b3b2e854f2f160
│   │   ├─4309 bash /app/entrypoint.sh
│   │   ├─4357 ./aws-k8s-agent
│   │   └─4358 tee -i aws-k8s-agent.log
│   └─pod637abaec-5fc8-4855-9952-ac08730384df
│     ├─1ed3616be7204d70dda58c0ac3a6060fd26b6d595b0a832604cb9b5b777bb91e
│     │ └─3629 /pause
│     └─f703824f768510f7b8dfd989c8f64d8784cb201d7fe81949a000baed59c869c3
│       └─3911 kube-proxy --v=2 --config=/var/lib/kube-proxy-config/config
├─user.slice
│ └─user-1000.slice
│   └─session-1.scope
│     ├─4928 sshd: ec2-user [priv]
│     ├─5012 sshd: ec2-user@pts/0
│     ├─5036 -bash
│     ├─5058 sudo -s
│     ├─5060 /bin/bash
│     ├─5640 systemd-cgls
│     └─5641 less
├─system.slice
│ ├─rngd.service
│ │ └─1866 /sbin/rngd -f --fill-watermark=0 --exclude=jitter
│ ├─irqbalance.service
│ │ └─1853 /usr/sbin/irqbalance --foreground
│ ├─amazon-ssm-agent.service
│ │ ├─2363 /usr/bin/amazon-ssm-agent
│ │ └─2519 /usr/bin/ssm-agent-worker
│ ├─containerd.service
│ │ ├─2800 /usr/bin/containerd
│ │ ├─3577 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 1ed3616be7204d70dda58c0ac3a6060fd26b6d595b0a832604cb9b5b777bb91e -address /run/containerd/containerd.sock
│ │ ├─3578 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 5724c33afacf82531d99d78fcea7b5b07b54105d2729b20953eb3e6a3da9dcfb -address /run/containerd/containerd.sock
│ │ ├─3887 /usr/bin/containerd-shim-runc-v2 -namespace moby -id f703824f768510f7b8dfd989c8f64d8784cb201d7fe81949a000baed59c869c3 -address /run/containerd/containerd.sock
│ │ └─4279 /usr/bin/containerd-shim-runc-v2 -namespace moby -id b263bef8f5b3aff3df5430cc05551a42d72ffbd93a8e56ed10b3b2e854f2f160 -address /run/containerd/containerd.sock
│ ├─systemd-udevd.service
│ │ └─1686 /usr/lib/systemd/systemd-udevd
│ ├─system-serial\x2dgetty.slice
│ │ └─serial-getty@ttyS0.service
│ │   └─2375 /sbin/agetty --keep-baud 115200,38400,9600 ttyS0 vt220
│ ├─docker.service
│ │ └─2877 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
│ ├─chronyd.service
│ │ └─1884 /usr/sbin/chronyd
│ ├─auditd.service
│ │ └─1827 /sbin/auditd
│ ├─kubelet.service
│ │ └─3146 /usr/bin/kubelet --cloud-provider aws --config /etc/kubernetes/kubelet/kubelet-config.json --kubeconfig /var/lib/kubelet/kubeconfig --container-runtime docker --network-plugin cni --node-ip=10.0.102.145 --pod-infra-container-image=602401143452.dkr.ecr.ap-northe
│ ├─systemd-journald.service
│ │ └─1285 /usr/lib/systemd/systemd-journald
│ ├─sshd.service
│ │ └─2416 /usr/sbin/sshd -D
│ ├─crond.service
│ │ └─2373 /usr/sbin/crond -n
│ ├─gssproxy.service
│ │ └─1888 /usr/sbin/gssproxy -D
│ ├─rsyslog.service
│ │ └─2365 /usr/sbin/rsyslogd -n
│ ├─rpcbind.service
│ │ └─1867 /sbin/rpcbind -w
│ ├─network.service
│ │ ├─2087 /sbin/dhclient -q -lf /var/lib/dhclient/dhclient--eth0.lease -pf /var/run/dhclient-eth0.pid eth0
│ │ └─2123 /sbin/dhclient -6 -nw -lf /var/lib/dhclient/dhclient6--eth0.lease -pf /var/run/dhclient6-eth0.pid eth0
│ ├─lvm2-lvmetad.service
│ │ └─1301 /usr/sbin/lvmetad -f
│ ├─postfix.service
│ │ ├─2285 /usr/libexec/postfix/master -w
│ │ ├─2286 pickup -l -t unix -u
│ │ └─2287 qmgr -l -t unix -u
│ ├─dbus.service
│ │ └─1860 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
│ ├─system-getty.slice
│ │ └─getty@tty1.service
│ │   └─2374 /sbin/agetty --noclear tty1 linux
│ └─systemd-logind.service
│   └─1864 /usr/lib/systemd/systemd-logind
└─K8s
  └─2877 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

dockerd servcie 用 cgroup の K8s が作成されている事がわかります。cgroup K8s のリソースが --kube-reserved に設定した値で制限されるように etc/systemd/system/kubelet.service を修正して kubelet を再起動します。

@node$ cat /etc/systemd/system/kubelet.service 
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service iptables-restore.service
Requires=docker.service

[Service]
ExecStartPre=/sbin/iptables -P FORWARD ACCEPT -w 5
ExecStart=/usr/bin/kubelet --cloud-provider aws \
    --config /etc/kubernetes/kubelet/kubelet-config.json \
    --kubeconfig /var/lib/kubelet/kubeconfig \
    --container-runtime docker \
    --network-plugin cni $KUBELET_ARGS $KUBELET_EXTRA_ARGS \
    --enforce-node-allocatable=pods,kube-reserved \
    --kube-reserved-cgroup=/K8s

Restart=always
RestartSec=5
KillMode=process

[Install]
WantedBy=multi-user.target
@node$ systemctl daemon-reload
@node$ systemctl restart kubelet

kubelet 再起動後に cgroup K8s を確認すると cpu が --kube-reserved に設定した 70m で制限されていました。

@node$ cat /sys/fs/cgroup/cpu/K8s/cpu.shares
71

--enforce-node-allocatable=kube-reserved,system-reserved--kube-reserved-cgroup/--system-reserved-cgroup を設定する事で、--kube-reserved/--system-reserved に設定した値で Kubernetes/OS のリソースを制限できる事がわかりました。

マネージドではない手組みの Kubernetes の場合は、Kubernetes/OS 用に cgroup を作成してそれぞれのリソースを制限するという使い方が可能という事かと思います。

なお、cpu.shares は相対値のためリソースに空きがあれば設定された数値より多くのリソースを使用する事が可能です。

5. まとめ

上記の検証結果によるまとめです。

  • --enforce-node-allocatable=kube-reserved,system-reserved なし --kube-reserved-cgroup/--system-reserved-cgroup なしの場合
    • 特定のプロセス用にリソースを確保しているわけではなく、単純に Pod が使えないリソースというだけ
    • --kube-reserved--system-reserved は同じ意味(そのため EKS では --kube-reserved のみ設定)
  • --enforce-node-allocatable=kube-reserved,system-reserved あり --kube-reserved-cgroup/--system-reserved-cgroup ありの場合
    • 指定した cgroup に対してリソースを制限する事が可能
    • どのプロセスをどのようにグループ(cgroup)化し、どれぐらいリソースを割り当てるかという設計が必要
  • (個人的には)EKS 等のマネージドサービスの場合はこれらの値を気にしなくて良い(クラウドベンダの設計にまかせて、何か問題が起きた際に考える)

6. 参考

Reserve Compute Resources for System Daemons | Kubernetes

design-proposals-archive/node-allocatable.md at main · kubernetes/design-proposals-archive · GitHub