最近碰到在 EKS cluster 中,容器對系統磁碟的壓力過大狀況,這種情況會因為系統磁碟效能達到上限,作業系統無法正常運作,CPU 使用率飆高,最後節點進入 NotReady 狀態。尤其,在這種情況發生時,我們只能看到節點處於 NotReady 狀態,無法觀測到是哪一個部分出了問題,同時,因我們也沒辦法連入節點了。儘管 Kubernetes 具備自動恢復的能力,但在節點 NotReady 過了五分鐘後,才會將 Pod 刪除並重新部署到 EKS cluster 當中。
為了避免這個反覆發生,我把節點的系統磁碟和容器資料磁碟拆開為兩個 EBS 磁區,以確保容器只會使用第二顆 EBS volume。
這麼做有什麼好處?
第二個 EBS 的目的很單純:就算容器把硬碟效能逼到上限,也只會影響容器專用的硬碟,系統硬碟的效能不受影響。這種做法有一些好處:
- 系統碟可以保持在 20 GiB 的預設大小。
- 容器資料硬碟依照工作負載調整大小(例如 200 GiB),壓力全落在這顆硬碟上。也可以根據需求,額外使用 Amazon EBS CSI Controller 為容器加上獨佔的 EBS volume。
建立新的 Launch Template 版本
我使用 Managed Node Group 搭配 Launch Template,所以先新增一個版本,裡面定義兩顆硬碟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| REGION="eu-west-1" aws ec2 create-launch-template \ --region $REGION \ --launch-template-name "eks-node-with-optimized-storage" \ --launch-template-data '{ "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", "Ebs": { "Iops": 3000, "VolumeSize": 30, "VolumeType": "gp3", "Throughput": 125 } }, { "DeviceName": "/dev/xvdb", "Ebs": { "Iops": 6000, "VolumeSize": 50, "VolumeType": "gp3", "Throughput": 250 } } ], "UserData": "TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PSIvLyIKCi0tLy8KQ29udGVudC1UeXBlOiB0ZXh0L3gtc2hlbGxzY3JpcHQKCiMhL2Jpbi9iYXNoCgpzeXN0ZW1jdGwgc3RvcCBjb250YWluZXJkLnNlcnZpY2UKbWtmcy5leHQ0IC9kZXYvbnZtZTFuMQptdiAvdmFyL2xpYi9jb250YWluZXJkLyAvdmFyL2xpYi9jb250YWluZXJkLWJhY2t1cApta2RpciAtcCAvdmFyL2xpYi9jb250YWluZXJkCmVjaG8gL2Rldi9udm1lMW4xIC92YXIvbGliL2NvbnRhaW5lcmQgZXh0NCBkZWZhdWx0cyxub2F0aW1lIDEgMiA+PiAvZXRjL2ZzdGFiCm1vdW50IC1hCmNwIC1ydmYgL3Zhci9saWIvY29udGFpbmVyZC1iYWNrdXAvKiAvdmFyL2xpYi9jb250YWluZXJkLwpjaG1vZCAwNzUwIC92YXIvbGliL2NvbnRhaW5lcmQKc3lzdGVtY3RsIHN0YXJ0IGNvbnRhaW5lcmQuc2VydmljZQoKLS0vLy0tCg==", "SecurityGroupIds": ["<your-security-group-id>"] }'
|
這一份 Userdata 經過 base64 解碼後為以下腳本。其中的內容是將第二塊硬碟格式化,並且將掛載點設定寫入 fstab,再將 containerd 的內容複製回去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="//"
--// Content-Type: text/x-shellscript
systemctl stop containerd.service mkfs.ext4 /dev/nvme1n1 mv /var/lib/containerd/ /var/lib/containerd-backup mkdir -p /var/lib/containerd echo /dev/nvme1n1 /var/lib/containerd ext4 defaults,noatime 1 2 >> /etc/fstab mount -a cp -rvf /var/lib/containerd-backup/* /var/lib/containerd/ chmod 0750 /var/lib/containerd systemctl start containerd.service
--//--
|
Managed Node Group 套用了這個啟動模板後,節點會獲得兩個 EBS volume,並且第二塊硬碟掛載為 /var/lib/containerd,這個目錄是容器的檔案系統所在的位置。
建立 EKS 管理節點組(EKS managed node group)
1 2 3 4 5 6 7 8 9 10 11 12
| LT_ID=$(aws ec2 describe-launch-templates --region eu-west-1 --query "LaunchTemplates[?LaunchTemplateName=='eks-node-with-optimized-storage'].LaunchTemplateId" --output text)
aws eks create-nodegroup \ --region eu-west-1 \ --cluster-name <your-cluster-name> \ --nodegroup-name <your-nodegroup-name> \ --subnets subnet-xxx subnet-yyy \ --node-role arn:aws:iam::<account-id>:role/<node-instance-role> \ --launch-template id=$LT_ID,version=1 \ --instance-types t3.medium \ --scaling-config minSize=1,maxSize=3,desiredSize=2
|
等待節點建立完成後,我們可以登入節點當中確認掛載狀況:
1 2 3 4 5 6 7
| sh-5.2$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS nvme1n1 259:0 0 50G 0 disk /var/lib/containerd nvme0n1 259:1 0 30G 0 disk ├─nvme0n1p1 259:2 0 30G 0 part / ├─nvme0n1p127 259:3 0 1M 0 part └─nvme0n1p128 259:4 0 10M 0 part /boot/efi
|
即使容器大量讀取寫入檔案系統,也不會對 nvme0n1 造成壓力,因此也不會讓節點進入故障狀態,但在相同節點上的其他容器可能也會受到影響就是了。不過,至少我們仍能連入到系統當中,檢查問題。
建立測試容器驗證結果
我讓 ChatGPT 寫了一個 Pod,部署到 EKS cluster 當中,這個 Pod 做的事情就是使用工具對硬碟大量大量寫入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| apiVersion: v1 kind: Pod metadata: name: disk-stress-pod labels: app: disk-stress spec: containers: - name: stress-ng image: ubuntu:22.04 command: ["/bin/bash", "-c"] args: - | apt update && apt install -y stress-ng && \ echo "Starting disk stress test..." && \ stress-ng --hdd 4 --hdd-ops 10000000 --timeout 30m --metrics-brief resources: requests: cpu: "500m" memory: "512Mi" limits: cpu: "1" memory: "1Gi" volumeMounts: - name: test-volume mountPath: /data volumes: - name: test-volume emptyDir: {} restartPolicy: Never
|
最後,我們使用 sar (system activity report) 確認 disk 的壓力。可以看到負載目前全部都集中於 nvme1n1,也就是我們的第二塊硬碟。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| sh-5.2$ sar -d 1 5 Linux 6.12.46-66.121.amzn2023.x86_64 (ip-192-168-94-143.eu-west-1.compute.internal) 10/31/25 _x86_64_ (2 CPU)
21:15:28 DEV tps rkB/s wkB/s dkB/s areq-sz aqu-sz await %util 21:15:29 nvme0n1 22.00 0.00 244.00 0.00 11.09 0.02 0.91 2.00 21:15:29 nvme1n1 1059.00 0.00 262688.00 0.00 248.05 16.35 15.44 100.00
21:15:29 DEV tps rkB/s wkB/s dkB/s areq-sz aqu-sz await %util 21:15:30 nvme0n1 20.00 0.00 224.00 0.00 11.20 0.02 0.95 2.00 21:15:30 nvme1n1 1022.00 0.00 254640.00 0.00 249.16 16.63 16.27 100.00 ^C
Average: DEV tps rkB/s wkB/s dkB/s areq-sz aqu-sz await %util Average: nvme0n1 21.00 0.00 234.00 0.00 11.14 0.02 0.93 2.00 Average: nvme1n1 1040.50 0.00 258664.00 0.00 248.60 16.49 15.85 100.00
|
收穫
調整完之後,節點在碰到容器的大量讀寫壓力時,也不會因為效能降級而導致節點進入故障狀態。我們也能根據不同的 application 特性,建立不同類型的節點,並且設定不同規格的 EBS 磁區,例如,在高 IO 的應用程式,就放在配備 io1 類型 EBS 磁區的節點上。