[EKS] Amazon Linux 2023 への移行
2024/2/29 に Amazon Linux 2023 が EKS で正式サポートされました。全てのリージョンの Karpenter Node、マネージドノードグループ、セルフマネージドノードグループで利用可能です。現在 EKS でサポート対象の 1.25 以降に加えて、延長サポートに入っている EKS 1.23 / 1.24 でも利用できます。Amazon Linux 2023 のサポートに関しては Amazon EKS-Optimized Amazon Linux 2023 AMIs Now Available のブログに詳細がまとまっています。
セキュリティ機能の強化
Amazon Linux 2023 では、Security Enhanced Linux (SELinux) や OpenSSL v3、Instance Metadata Service Version 2 (IMDSv2) がデフォルトで利用されます。SELinux に関しては permissive
モードが有効になっており、ポリシーに違反したアクセスに対してログを書き出すのみです。
パフォーマンスの改善
Amazon Linux 2023 では、起動時間が最適化されており、ワークロードが実行されるまでの時間が短くなっています。詳細は Optimizations に記載されています。
cgroup v2
Amazon Linux 2023 の中で EKS にとって大きな変更点の一つです。Amazon Linux 2 では、cgroup v1 を利用してきました。Kubernetes 1.25 から cgroup v2 でコンテナリソースを管理する機能が GA となり、GKE では 1.26 から新規に作成したノードプールで cgroup v2 がデフォルト有効となりました。AWS では、Amazon Linux 2 で cgroup v2 への移行を行うのではなく、Amazon Linux 2023 で cgroup v2 を有効化する方針を取りました。
# Amazon Linux 2023 は cgroup2fs なので cgroup v2 が有効
[root@ip-172-30-22-222 /]# stat -fc %T /sys/fs/cgroup/
cgroup2fs
アプリケーションやプログラミング言語への影響
プログラミング言語やランタイム、ライブラリ、アプリケーションによって cgroup v2 のサポート状況が異なります。Migrating to cgroup v2 とTwo things to prepare for workloads も併せて確認すると良いです。
- Go で uber-go/automaxprocs を利用している場合は、v1.5.1 以降に更新
- OpenJDK / HotSpot: jdk8u372, 11.0.16, 15 and later
- Node.js 20.3.0 or later
- Wrong memory ceiling in cgroup v2 environments で報告されているように libuv が cgroup v2 に対応しておらず、正確にヒープサイズを設定できない問題が報告されています。libuv v1.45.0 に cgroup v2 サポートが取り込まれ、Node.js 20.3.0 で libuv v1.45.0 が取り込まれました。
- IBM Semeru Runtimes: jdk8u345-b01, 11.0.16.0, 17.0.4.0, 18.0.2.0 and later
- IBM SDK Java Technology Edition Version (IBM Java): 8.0.7.15 and later
- EKS の場合、cAdvisor は kubelet に組み込まれているため影響なし
cgroup v2 に関連する Kubernetes の機能
Kubernetes における cgroup v2 のサポートの中でユーザー影響のある機能は Kubernetes と cgroup v2 の記事にまとまっています。
- Memory QoS (アルファ機能)
- memory.oom.group
Memory QoS (アルファ機能)
Memory QoS は、Kubernetes 1.29 時点でアルファ機能のため有効になっていません。EKS でも同様に Memory QoS の機能は有効になっていないので、cgroup v2 移行の障壁とはなりません。
# EKS 1.28 時点での Memory QoS の FeatureGate の確認
❯ kubectl get --raw /metrics | grep kubernetes_feature_enabled | grep MemoryQoS
kubernetes_feature_enabled{name="MemoryQoS",stage="ALPHA"} 0
現状は resources.limits.memory で指定したメモリ使用量を超えると OOMKill されますが、resources.limits.memory の 90% を超えるとカーネルがページキャッシュを解放するなどしてメモリを出来るだけ回収しようとします。これにより、resources.requests.memory と resources.limits.memory が同じ値でない場合に OOMKill の発生を減らせます。
ただ、当初の想定とは異なり、メモリ消費の激しいワークロードで、カーネルによるページキャッシュの解放とワークロードのメモリ割り当ての要求が拮抗し、ワークロードのプロセスがスタックする問題が浮き彫りになりました。カーネルによる OOMKill が発生することなく、ワークロードが永遠にスタックする状態となるため、Memory QoS の機能はベータ昇格が止まっています。(Latest Update [Stalled])
PSI メトリクスでワークロードがスタックしたことを検知して OOM を発生させることでこの問題を解決しようとしていますが、前提条件の KEP-4205: Support PSI based on cgroupv2 が Kubernetes 1.30 時点でも機能として入っていないため、Memory QoS の機能のベータ昇格に向けた再始動は時間が掛かりそうです。
memory.oom.group
Kubernetes 1.27 以前は Pod の中で複数の子プロセスを実行した場合 (e.g. PHP FPM, Python / uWSGI, PostgreSQL, JupyterLab, ML ジョブ, …) 、子プロセスが OOM となるとその子プロセスだけが Kill されていました。Kubernetes 1.28 から Pod 内の全てのプロセスが Kill されるようになります。
Container with multiple processes not terminated when OOM で機能要望があり、Kubernetes 1.28 で use the cgroup aware OOM killer if available により機能が入った形ですが、クラスタ管理者やユーザーが ON / OFF を切り替えられる設計となっていません。memory.oom.group
を使用する設定がハードコードされているため、いくつかの問題が報告されています。影響を受けるワークロードを動かしている場合は、Amazon Linux 2023 への移行を諦めるしかありません。
- ML ジョブの多くで OOMKill が発生。子プロセスが OOM となった場合に、ML ジョブ全てが Kill されてしまう。これまでは子プロセスが OOM となっても、また別の子プロセスが処理を継続できていた。
- JupyterLab などの Notebook 系のワークロードでユーザーが実行したコマンドの中で OOM が発生すると Notebook 自体も OOMKill されてしまう。
- PostgreSQL の bg_writer のプロセスで OOM が発生し、PostgreSQL の Pod が OOMKill されたため、レプリカ間でデータの不整合が発生してしまった。
memory.oom.group を ON / OFF する機能については kubelet: new kubelet config option for disabling group oom kill で議論が進められていますが、結論はまだ出ておらず時間が掛かりそうです。Pod レベルの ON / OFF の機能は KEP-3008: QoS-class resources で実装すると言う話も出ており、そうなるとかなり時間が掛かりそうです。
Kubernetes における cgroup v2 の既知の問題
Node のメモリ使用量の計算の違い
Node memory usage on cgroupv2 reported higher than cgroupv1 で Azure の中の人から報告されている問題で、kubectl top nodes
で表示されるノードのメモリ使用量が cgroup v1 よりも cgroup v2 の方が高く表示されます。cgroup v1 の時は root の cgroup の memory.usage_in_bytes
のファイルを見ていますが、cgroup v2 の場合は /proc/meminfo の情報から total - free
を計算しています。この計算方法の違いにより cgroup v2 の方は inactive_anon
(アクティブじゃないキャッシュ) が含まれ、その分高く表示されています。
runc 1.1.9 で libct/cg/fs2: use file + anon + swap for usage の修正が入り、cAdvisor v0.48.0 で v0.47: vendor: bump runc to 1.1.9 により runc 1.1.9 に更新が取り込まれました。Kubernetes 1.29 で Update to new cadvisor v0.48.1 により cAdvisor v0.48.0 の変更が取り込まれたため、Kubernetes 1.29 でこの問題は修正されました。
ただし、cgroup v1 と cgroup v2 でメモリ管理の設計が異なることから cgroup v1 と v2 でメモリ使用量に違いが出てくるのは正しい挙動なのでは?と疑問視する声が上がっており (comment - opencontainers/runc#3933) 、今後修正が revert される可能性があります。また、この問いに対する答えが出ていないため、Kubernetes ≤ 1.28 に修正が cherry-pick される予定はありません。
Kubernetes における cgroup v1 のサポート
KEP-4569: Moving cgroup v1 support into maintenance mode で cgroup v1 のサポートに関する議論が進んでいましたが、Kubernetes としてはすぐに非推奨化は行わず、新機能の追加がなくバグ修正もベストエフォートなメンテナンスモードに移行することになりました。Kubernetes としては cgroup v2 に関わる機能の開発に注力し、ユーザーの cgroup v2 への移行が進むことを期待しているようです。Kubernetes の cgroup v1 と cgroup v2 の機能の違いにより Amazon Linux 2023 に移行できないとしても、猶予期間ができた形です。
VPC CNI plugin の対応バージョン
Amazon Linux 2023 を利用するには VPC CNI plugin の v1.16.2 以降に更新する必要があります。理由は言及されていないため不明です。
Node のカスタマイズ方法の変更
Amazon Linux 2023 から nodeadm と呼ばれるプロセスが systemd に登録され、Node 起動時に NodeConfig の YAML ファイルで定義した設定に従って、Node をカスタマイズしてくれます。NodeConfig で反映可能な設定は API リファレンスを参照して下さい。起動テンプレートのユーザーデータで Node をカスタマイズしている場合は、対応が必要です。
セルフマネージドノードグループを利用している場合は、NodeConfig で必須な設定をユーザーデータとして渡す必要があります。詳細は Amazon EC2 user data を確認して下さい。マネージドノードグループや Karpenter を利用している場合、EC2 インスタンスが EKS クラスタに参加するために必須な設定は EKS 側で自動生成してくれます。
マネージドノードグループや Karpenter を利用していて、ユーザーデータで Node をカスタマイズしていない場合は特に対応は不要です。Amazon Linux 2023 を利用するには AMI を置き換えるだけで良いです。
-
マネージドノードグループの場合
resource "aws_eks_node_group" "default" { ... ... # EKS に最適化された Amazon Linux 2023 が GA したので利用 # https://aws.amazon.com/jp/blogs/containers/amazon-eks-optimized-amazon-linux-2023-amis-now-available/ ami_type = "AL2023_x86_64_STANDARD" }
-
Karpenter の場合
apiVersion: karpenter.k8s.aws/v1beta1 kind: EC2NodeClass metadata: name: default spec: amiFamily: AL2023 (...)
ただし、マネージドノードグループの場合、amiType
を変更するとマネージドノードグループが再作成されてしまうため、サービス影響を抑えるには以下の方法を検討して下さい。
-
新しいマネージドノードグループを作成
- 必要な台数と最小台数を現在の旧マネージドノードグループの台数に揃える
-
旧マネージドノードグループの Node をスケールしないように Cluster Autoscaler を設定
- Cluster Autoscaler の --nodes から旧マネージドノードグループの ASG を削除
- タグによる自動検出の機能 (Auto-Discovery Setup) を利用している場合は、タグの切り替える
-
旧マネージドノードグループの Node を全て cordon
# 新しく Pod がスケジュールしないようにする NODE_GROUP_NAME= kubectl cordon -l eks.amazonaws.com/nodegroup=${NODE_GROUP_NAME} --dry-run kubectl cordon -l eks.amazonaws.com/nodegroup=${NODE_GROUP_NAME}
-
旧マネージドノードグループを drain
NODE_GROUP_NAME= kubectl drain -l eks.amazonaws.com/nodegroup=${NODE_GROUP_NAME} --dry-run kubectl drain -l eks.amazonaws.com/nodegroup=${NODE_GROUP_NAME}
-
旧マネージドノードグループを削除
- 数分で別の Node に移動できる Pod しか存在していない場合は 4. の手順を飛ばして、マネージドノードグループ削除するでも良い (マネージド型ノードグループの削除)
Kubernetes 1.30 からマネージドノードグループを新規に作成すると Amazon Linux 2023 の AMI がデフォルトで使用されるようです。amiType
で Amazon Linux 2 を利用するように変更は可能です。
マネージドノードグループや Karpenter を利用中かつ Node をカスタマイズしている場合は、ユーザーデータの移行が必要です。例えば、Kubernetes の Graceful Node Shutdown の機能を有効にする場合は、以下のように設定します。
-
マネージドノードグループの場合
resource "aws_eks_node_group" "default" { ... launch_template { id = aws_launch_template.default.id version = aws_launch_template.default.latest_version } # EKS に最適化された Amazon Linux 2023 が GA したので利用 # https://aws.amazon.com/jp/blogs/containers/amazon-eks-optimized-amazon-linux-2023-amis-now-available/ ami_type = "AL2023_x86_64_STANDARD" } locals { # AL2023 で Graceful Node Shutdown を有効化するための userdata # https://github.com/kubernetes-sigs/karpenter/issues/944#issuecomment-2048825692 eks_default_userdata = <<USERDATA MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="//" --// Content-Type: application/node.eks.aws apiVersion: node.eks.aws/v1alpha1 kind: NodeConfig spec: kubelet: config: shutdownGracePeriod: 45s shutdownGracePeriodCriticalPods: 15s --// Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash echo -e "InhibitDelayMaxSec=45\n" >> /etc/systemd/logind.conf systemctl restart systemd-logind --// USERDATA } resource "aws_launch_template" "default" { ... user_data = base64encode(local.eks_default_userdata) }
-
Karpenter の場合
apiVersion: karpenter.k8s.aws/v1beta1 kind: EC2NodeClass metadata: name: default spec: amiFamily: AL2023 (...) userData: | MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="//" --// Content-Type: application/node.eks.aws apiVersion: node.eks.aws/v1alpha1 kind: NodeConfig spec: kubelet: config: shutdownGracePeriod: 45s shutdownGracePeriodCriticalPods: 15s --// Content-Type: text/x-shellscript; charset="us-ascii" #!/bin/bash echo -e "InhibitDelayMaxSec=45\n" >> /etc/systemd/logind.conf systemctl restart systemd-logind --//
NodeConfig で containerd をカスタマイズしている場合の設定のして方法は Configuring containerd にサンプルがあります。
Amazon Linux 2023 の既知の問題
ユーザーデータの gzip 圧縮の非サポート
NodeConfig を含むユーザーデータを Base64 エンコードして gzip 圧縮すると nodeadm が kubelet などの設定を正常に生成できない問題です。おそらく cloudinit_config の Datasource を使ってユーザーデータを設定した場合です。gzip = false
を明示的に指定することでこの問題は回避できます。Decompress GZIP'd user data でユーザーデータ全体を gzip で圧縮している場合の問題は修正されるようです。
Amazon Linux 2 と比べて Node の起動時間が遅い
Slow startup of AL2023 AMI で報告されている問題です。
comment - awslabs/amazon-eks-ami#1696 でコンテナイメージをプリロードしたカスタム AMI の AL2 で 45 秒だった起動時間が、AL2023 だと 100 秒掛かるようになったようです。systemd の起動シークエンスの中で nodeadm-config が後続の cloud-init のプロセスの開始を 20 秒程度ブロックしている部分と nodeadm-run のプロセスの起動に 10 秒程度掛かっている部分を改善しようとしています。
- nodeadm-config の中の kubelet —version でバージョンを取得している処理で時間が掛かっている。Node 起動後に EBS ブロックが遅延読み込みされる影響でバイナリの実行が遅くなっている説があったが、EBS の高速スナップショット復元の機能を有効化しても改善されず別の原因と思われる。
- nodeadm-run では
containerd config dump
の結果から Sandbox イメージを正規表現で探す処理に時間が掛かっているらしい。
2024/4/15 現在、原因は特定されておらず、報告者と EKS チームが調査中です。
Discussion