問題背景
工業(yè)云部署使用k8s集群
- k8s:
01746170708faa599113b027772802bcbb594ee8 - containerd:
v1.5.4 - OS:
Tencent tlinux 2.2 (Final) - kernel:
Linux TENCENT64.site 3.10.0-693_4003268.tl2
問題描述
工業(yè)云同學通過控制臺升級業(yè)務容器鏡像版本,升級后導致容器創(chuàng)建失敗,失敗的error log如下所示:
Jun 06 20:45:29 TENCENT64.site containerd[5327]: time="2022-06-06T20:45:29.816213573+08:00" level=error msg="RunPodSandbox for &PodSandboxMetadata{Name:edge-56c86c995f-tqzz2,Uid:da70d1c2-0978-40c1-a9a6-c5a25a7f41a3,Namespace:edge-adapter,Attempt:0,} failed, error" error="failed to reserve sandbox name \"edge-56c86c995f-tqzz2_edge-adapter_da70d1c2-0978-40c1-a9a6-c5a25a7f41a3_0\": name \"edge-56c86c995f-tqzz2_edge-adapter_da70d1c2-0978-40c1-a9a6-c5a25a7f41a3_0\" is reserved for \"762b66093089b50109f74fa5a4cc6e7165d916b18dfd1b5c877fc4effff1e558\""
問題排查
-
創(chuàng)建一個測試nginx的容器也創(chuàng)建失敗,報錯相同也是卡在create pod sandbox:
image.png 通過nerdctl run創(chuàng)建容器發(fā)現(xiàn)也創(chuàng)建失敗,卡在creating 狀態(tài):
-
查看containerd的error log:
image.png 查看kubelet日志:
Jun 06 20:45:29 TENCENT64.site containerd[5327]: time="2022-06-06T20:45:29.816213573+08:00" level=error msg="RunPodSandbox for &PodSandboxMetadata{Name:edge-56c86c995f-tqzz2,Uid:da70d1c2-0978-40c1-a9a6-c5a25a7f41a3,Namespace:edge-adapter,Attempt:0,} failed, error" error="failed to reserve sandbox name \"edge-56c86c995f-tqzz2_edge-adapter_da70d1c2-0978-40c1-a9a6-c5a25a7f41a3_0\": name \"edge-56c86c995f-tqzz2_edge-adapter_da70d1c2-0978-40c1-a9a6-c5a25a7f41a3_0\" is reserved for \"762b66093089b50109f74fa5a4cc6e7165d916b18dfd1b5c877fc4effff1e558\""
-
環(huán)境中發(fā)現(xiàn)kworker占用CPU的使用率很高:
image.png -
集群上的Pod IP分配情況:
image.png -
內(nèi)存使用情況
image.png 有個containerd的僵尸進程是5月25號啟動的,被systemd 1號進程托管,kill -9也殺不掉該進程
環(huán)境中存在chaosmesh 進程使用containerd,
ps -ef | grep chao
root 752 101375 0 16:05 pts/1 00:00:00 grep --color=auto chao
root 60780 59079 0 06:30 ? 00:00:03 /usr/local/bin/chaos-daemon --runtime containerd --http-port 31766 --grpc-port 31767 --pprof --ca /etc/chaos-daemon/cert/ca.crt --cert /etc/chaos-daemon/cert/tls.crt --key /etc/chaos-daemon/cert/tls.key --runtime-socket-path /host-run/containerd.sock
問題處理
- GitHub上搜索相關(guān)的issue,發(fā)現(xiàn)有類似error的issue:https://github.com/containerd/containerd/issues/4604
- 根據(jù)issue描述,初步懷疑是磁盤的IO過高導致容器創(chuàng)建失敗,此問題社區(qū)已經(jīng)有對應的bug fix PR:https://github.com/containerd/containerd/pull/6478/files
- 由于1.5.4版本中沒有這個fix,所以在環(huán)境上升級了containerd版本,v1.5.10,這個版本包含對應的code fix,并且重啟了機器,機器重啟后pod都正常running,kworker使用CPU也恢復正常,由于當時沒有查看IO的使用情況,所以不能排除IO不是真正的root cause,此問題需要繼續(xù)跟進。
后續(xù)問題跟進
- 環(huán)境上已經(jīng)替換為新版本的containerd,后面繼續(xù)觀察集群節(jié)點上的IO使用情況iotop,kworker的資源使用情況top;
- 在自測環(huán)境中做壓力測試復現(xiàn)問題:
stress-ng --io 30 -d 5

image.png
創(chuàng)建nginx deployment:
kubectl apply -f nginx.yaml
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-857cbc9c6-7plrb 0/1 ContainerCreating 0 7m41s
nginx-deployment-857cbc9c6-f5gjq 0/1 ContainerCreating 0 7m40s

image.png
kubelet error log:

image.png
containerd error log:
Jun 07 21:03:05 VM-71-117-ubuntu containerd[14269]: time="2022-06-07T21:03:05.036947355+08:00" level=error msg="RunPodSandbox for &PodSandboxMetadata{Name:nginx-deployment-857cbc9c6-f5gjq,Uid:29d9b646-2ed6-411d-b89a-5e1526de3393,Namespace:default,Attempt:0,} failed, error" error="failed to reserve sandbox name \"nginx-deployment-857cbc9c6-f5gjq_default_29d9b646-2ed6-411d-b89a-5e1526de3393_0\": name \"nginx-deployment-857cbc9c6-f5gjq_default_29d9b646-2ed6-411d-b89a-5e1526de3393_0\" is reserved for \"aa39cc65311ca413accf095a659c372b02e987d5b97687d3241c830f3b1091d5\""
把壓力測試進程停掉,pod就創(chuàng)建成功了:
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-857cbc9c6-7plrb 1/1 Running 0 14m
nginx-deployment-857cbc9c6-f5gjq 1/1 Running 0 14m
- 使用containerd v1.5.10做壓力測試
創(chuàng)建Pod還是會卡在creating狀態(tài)大概五分鐘,最后狀態(tài)變成RunContainerError狀態(tài),最后還是會報錯failed to reserve sandbox name \"nginx-deployment-857cbc9c6-sp5rp_default_c2c8a3d4-0e02-4688-8b6e-711a0f6525c5_0\": name \"nginx-deployment-857cbc9c6-sp5rp_default_c2c8a3d4-0e02-4688-8b6e-711a0f6525c5_0\" is reserved for \"39ab9d7b6250608fd911ff079f3d0781044674e2d6a1e58893ed422d6a2adb60\"":
Normal Scheduled 7m41s default-scheduler Successfully assigned default/nginx-deployment-857cbc9c6-sp5rp to 10.0.71.117
Warning FailedCreatePodSandBox 3m37s kubelet Failed to create pod sandbox: rpc error: code = DeadlineExceeded desc = context deadline exceeded
root@VM-71-117-ubuntu:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-857cbc9c6-sp5rp 0/1 ContainerCreating 0 7m23s
nginx-deployment-857cbc9c6-wm88t 0/1 RunContainerError 0 7m24s
kubelet log:
Jun 08 10:00:13 VM-71-117-ubuntu kubelet[11643]: E0608 10:00:13.322478 11643 pod_workers.go:190] "Error syncing pod, skipping" err="failed to \"CreatePodSandbox\" for \"nginx-deployment-857cbc9c6-sp5rp_default(c2c8a3d4-0e02-4688-8b6e-711a0f6525c5)\" with CreatePodSandboxError: \"Failed to create sandbox for pod \\\"nginx-deployment-857cbc9c6-sp5rp_default(c2c8a3d4-0e02-4688-8b6e-711a0f6525c5)\\\": rpc error: code = DeadlineExceeded desc = context deadline exceeded\"" pod="default/nginx-deployment-857cbc9c6-sp5rp" podUID=c2c8a3d4-0e02-4688-8b6e-711a0f6525c5
containerd log:

image.png
- 分析錯誤產(chǎn)生的原因
1、kubelet 發(fā)送請求給 containerd 創(chuàng)建容器,當 containerd 第一次嘗試創(chuàng)建這個容器的時候,它會創(chuàng)建名為Attempt的元數(shù)據(jù),該變量保持默認值為0代碼)
2、在kubelet和containerd之間發(fā)生context timeout
3、在下一次kubelet運行 SyncPod 時,kubelet 會嘗試再次創(chuàng)建相同的容器。但是,它不會在 CreateContainer 超時時增加Attempt次數(shù),只會在容器重新啟動時增加,在超時之后,Attempt number 不會增加,但是 CRI 已經(jīng)保留了容器名稱,因此所有后續(xù)創(chuàng)建相同容器的請求都會失敗代碼
這是kubelet和containerd的正常的行為,如果容器設置策略為restartPolicy:AlwaysorrestartPolicy:OnFailure當磁盤IO下降,containerd最終會創(chuàng)建容器成功。(根據(jù)前面的壓測結(jié)果,如果kill掉IO比較高的壓測進程,容器會很快創(chuàng)建成功。) - PR fix解析
目前社區(qū)內(nèi)針對這個問題,merge了一個PR ,此PR主要是解決在createContainer的時候調(diào)用umount觸發(fā)內(nèi)核調(diào)用sync-fs,這樣會增加磁盤的IO開銷,使用readonly mount就不會掉用sync-fs方法,此PR改用readonly mount,這樣可以減少 PodCreateContainer 中的 syncfs,減小containerd帶來的IO開銷,從而加快容器的創(chuàng)建,但它不能減少 runc 進程中的 mount 時的產(chǎn)生的磁盤IO。
所以繼續(xù)跟蹤發(fā)現(xiàn):runc init確實也卡住了
root 3516 3508 0 15:54 pts/1 00:00:00 runc init

image.png

image.png
通過ps aux | grep runc會發(fā)現(xiàn)有些runc stack是卡在do_mount上,并且runc的進程狀態(tài)是D,什么是D狀態(tài)呢?D狀態(tài)的進程通常是在等待IO,比如磁盤IO,網(wǎng)絡IO,其他外設IO,很明顯這塊是在等待磁盤IO。
- 目前的解決方法
從上面的排查和社區(qū)的分析來看,此問題主要是磁盤IO過高導致的,所以要解決問題,就是從如何降低磁盤IO出發(fā),上面的PR fix也是從containerd方面降低磁盤IO:
1、更換高性能的磁盤,提高磁盤IO,增大磁盤容量;
2、增加集群節(jié)點,對于高IO的業(yè)務,遷移到其它節(jié)點,減小節(jié)點的IO(公有云的解決方法是加cbs或者挪到eks);
3、在更新業(yè)務的時候,不要同時更新多個,可以每次保持最小化更新,防止pull image加大磁盤IO導致更新失敗。




