![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/bed059552ed86580939aa18fbdf1553e.jpg)
在生產中使用 Kubernetes 的這些年裡,我們累積了許多有趣的故事,說明各種系統組件中的錯誤如何導致令人不快和/或難以理解的後果,從而影響容器和 Pod 的運作。 在本文中,我們選擇了一些最常見或最有趣的內容。 即使你從來沒有足夠幸運遇到這樣的情況,閱讀這樣的短篇偵探故事 - 尤其是“第一手” - 總是很有趣,不是嗎?
故事 1. Supercronic 和 Docker 掛起
在其中一個叢集上,我們定期收到凍結的 Docker,這幹擾了叢集的正常運作。 同時,在Docker日誌中觀察到以下內容:
level=error msg="containerd: start init process" error="exit status 2: "runtime/cgo: pthread_create failed: No space left on device
SIGABRT: abort
PC=0x7f31b811a428 m=0
goroutine 0 [idle]:
goroutine 1 [running]:
runtime.systemstack_switch() /usr/local/go/src/runtime/asm_amd64.s:252 fp=0xc420026768 sp=0xc420026760
runtime.main() /usr/local/go/src/runtime/proc.go:127 +0x6c fp=0xc4200267c0 sp=0xc420026768
runtime.goexit() /usr/local/go/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc4200267c8 sp=0xc4200267c0
goroutine 17 [syscall, locked to thread]:
runtime.goexit() /usr/local/go/src/runtime/asm_amd64.s:2086 +0x1
… 我們對該錯誤最感興趣的是以下訊息: pthread_create failed: No space left on device。 快速學習 解釋說 Docker 無法分叉進程,這就是它定期凍結的原因。
在監控中,對應的情況如下圖:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/bd778052c87b338493bae54b26830ef3.jpg)
在其他節點上也觀察到類似的情況:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/ef512532a95ca982e4342071115dbe9f.jpg)
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/43c32ebca78755dde348ed5e7ac75c79.jpg)
在相同的節點上我們看到:
root@kube-node-1 ~ # ps auxfww | grep curl -c
19782
root@kube-node-1 ~ # ps auxfww | grep curl | head
root 16688 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 17398 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 16852 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 9473 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 4664 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 30571 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 24113 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 16475 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 7176 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 1090 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>事實證明,這種行為是 pod 與 (我們用來在 pod 中執行 cron 作業的 Go 實用程式):
_ docker-containerd-shim 833b60bb9ff4c669bb413b898a5fd142a57a21695e5dc42684235df907825567 /var/run/docker/libcontainerd/833b60bb9ff4c669bb413b898a5fd142a57a21695e5dc42684235df907825567 docker-runc
| _ /usr/local/bin/supercronic -json /crontabs/cron
| _ /usr/bin/newrelic-daemon --agent --pidfile /var/run/newrelic-daemon.pid --logfile /dev/stderr --port /run/newrelic.sock --tls --define utilization.detect_aws=true --define utilization.detect_azure=true --define utilization.detect_gcp=true --define utilization.detect_pcf=true --define utilization.detect_docker=true
| | _ /usr/bin/newrelic-daemon --agent --pidfile /var/run/newrelic-daemon.pid --logfile /dev/stderr --port /run/newrelic.sock --tls --define utilization.detect_aws=true --define utilization.detect_azure=true --define utilization.detect_gcp=true --define utilization.detect_pcf=true --define utilization.detect_docker=true -no-pidfile
| _ [newrelic-daemon] <defunct>
| _ [curl] <defunct>
| _ [curl] <defunct>
| _ [curl] <defunct>
…問題是這樣的:當一個任務在 supercronic 中運行時,它產生的進程 無法正確終止, 轉變為 .
注意:更準確地說,進程是由 cron 任務產生的,但 supercronic 不是 init 系統,不能「採用」其子進程產生的進程。 當發出 SIGHUP 或 SIGTERM 訊號時,它們不會傳遞給子進程,導致子進程不會終止並保持殭屍。 您可以閱讀有關這一切的更多信息,例如, .
有幾種方法可以解決問題:
- 作為臨時解決方法 - 在單一時間點增加系統中 PID 的數量:
/proc/sys/kernel/pid_max (since Linux 2.5.34) This file specifies the value at which PIDs wrap around (i.e., the value in this file is one greater than the maximum PID). PIDs greater than this value are not allo‐ cated; thus, the value in this file also acts as a system-wide limit on the total number of processes and threads. The default value for this file, 32768, results in the same range of PIDs as on earlier kernels - 或不直接在 supercronic 中啟動任務,而是使用相同的 ,它能夠正確終止進程並且不會產生殭屍。
故事 2. 刪除 cgroup 時出現“殭屍”
Kubelet 開始消耗大量 CPU:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/6140058330faaa3785b089dcba857056.jpg)
沒有人會喜歡這樣,所以我們武裝自己 並開始處理這個問題。 調查結果如下:
- Kubelet 花了超過三分之一的 CPU 時間從所有 cgroup 中提取記憶體資料:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20241'%3E%3C/svg%3E)
- 在核心開發者的郵件列表中你可以找到 。 簡而言之,要點可以歸結為: 各種 tmpfs 檔案和其他類似的東西沒有從系統中完全刪除 當刪除一個cgroup時,所謂的 殭屍。 它們遲早會從頁面快取中刪除,但伺服器上有大量內存,核心認為沒有必要浪費時間刪除它們。 這就是為什麼它們不斷堆積。 為什麼會發生這種情況? 這是一個有 cron 作業的伺服器,它不斷創建新作業以及新的 Pod。 因此,會為其中的容器建立新的 cgroup,但很快就會被刪除。
- 為什麼 kubelet 中的 cAdvisor 會浪費這麼多時間? 透過最簡單的執行就很容易看出這一點
time cat /sys/fs/cgroup/memory/memory.stat。 如果在健康的機器上該操作需要 0,01 秒,那麼在有問題的 cron02 上則需要 1,2 秒。 問題是 cAdvisor 從 sysfs 讀取資料的速度非常慢,它試圖考慮殭屍 cgroup 中使用的記憶體。 - 為了強制刪除殭屍,我們嘗試按照 LKML 中的建議清除快取:
sync; echo 3 > /proc/sys/vm/drop_caches, - 但結果發現內核更複雜,導致車子崩潰。
怎麼辦? 問題正在修復(,有關說明,請參閱 核心更新 Linux 最高版本 4.16。
歷史 3. Systemd 及其掛載
同樣,kubelet 在某些節點上消耗了太多資源,但這次它消耗了太多記憶體:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/044c4e23a772c61a6206b9b20aa67c1d.jpg)
原來是 systemd 使用了有問題。 Ubuntu 16.04,當管理為連線所建立的掛載點時會發生這種情況。 subPath 來自 ConfigMap 或機密。 Pod 完成其工作後 systemd 服務及其服務掛載仍然存在 在系統中。 隨著時間的推移,它們會累積大量。 甚至還有關於這個主題的問題:
- ;
- .
……最後一個指的是 systemd 中的 PR: (systemd 中的問題 - ).
問題已不存在。 Ubuntu 18.04,但如果您想繼續使用 Ubuntu 16.04,您可能會發現我們針對此問題提供的解決方法很有用。
所以我們做瞭如下的DaemonSet:
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
labels:
app: systemd-slices-cleaner
name: systemd-slices-cleaner
namespace: kube-system
spec:
updateStrategy:
type: RollingUpdate
selector:
matchLabels:
app: systemd-slices-cleaner
template:
metadata:
labels:
app: systemd-slices-cleaner
spec:
containers:
- command:
- /usr/local/bin/supercronic
- -json
- /app/crontab
Image: private-registry.org/systemd-slices-cleaner/systemd-slices-cleaner:v0.1.0
imagePullPolicy: Always
name: systemd-slices-cleaner
resources: {}
securityContext:
privileged: true
volumeMounts:
- name: systemd
mountPath: /run/systemd/private
- name: docker
mountPath: /run/docker.sock
- name: systemd-etc
mountPath: /etc/systemd
- name: systemd-run
mountPath: /run/systemd/system/
- name: lsb-release
mountPath: /etc/lsb-release-host
imagePullSecrets:
- name: antiopa-registry
priorityClassName: cluster-low
tolerations:
- operator: Exists
volumes:
- name: systemd
hostPath:
path: /run/systemd/private
- name: docker
hostPath:
path: /run/docker.sock
- name: systemd-etc
hostPath:
path: /etc/systemd
- name: systemd-run
hostPath:
path: /run/systemd/system/
- name: lsb-release
hostPath:
path: /etc/lsb-release....它使用以下腳本:
#!/bin/bash
# we will work only on xenial
hostrelease="/etc/lsb-release-host"
test -f ${hostrelease} && grep xenial ${hostrelease} > /dev/null || exit 0
# sleeping max 30 minutes to dispense load on kube-nodes
sleep $((RANDOM % 1800))
stoppedCount=0
# counting actual subpath units in systemd
countBefore=$(systemctl list-units | grep subpath | grep "run-" | wc -l)
# let's go check each unit
for unit in $(systemctl list-units | grep subpath | grep "run-" | awk '{print $1}'); do
# finding description file for unit (to find out docker container, who born this unit)
DropFile=$(systemctl status ${unit} | grep Drop | awk -F': ' '{print $2}')
# reading uuid for docker container from description file
DockerContainerId=$(cat ${DropFile}/50-Description.conf | awk '{print $5}' | cut -d/ -f6)
# checking container status (running or not)
checkFlag=$(docker ps | grep -c ${DockerContainerId})
# if container not running, we will stop unit
if [[ ${checkFlag} -eq 0 ]]; then
echo "Stopping unit ${unit}"
# stoping unit in action
systemctl stop $unit
# just counter for logs
((stoppedCount++))
# logging current progress
echo "Stopped ${stoppedCount} systemd units out of ${countBefore}"
fi
done……並且它使用前面提到的 supercronic 每 5 分鐘運行一次。 它的 Dockerfile 如下所示:
FROM ubuntu:16.04
COPY rootfs /
WORKDIR /app
RUN apt-get update &&
apt-get upgrade -y &&
apt-get install -y gnupg curl apt-transport-https software-properties-common wget
RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" &&
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - &&
apt-get update &&
apt-get install -y docker-ce=17.03.0*
RUN wget https://github.com/aptible/supercronic/releases/download/v0.1.6/supercronic-linux-amd64 -O
/usr/local/bin/supercronic && chmod +x /usr/local/bin/supercronic
ENTRYPOINT ["/bin/bash", "-c", "/usr/local/bin/supercronic -json /app/crontab"]故事 4. 調度 Pod 時的競爭力
人們注意到:如果我們將一個 pod 放置在一個節點上,並且它的鏡像被抽出很長時間,那麼「擊中」同一節點的另一個 pod 就會簡單地 不開始拉取新 Pod 的鏡像。 相反,它會等待直到拉取前一個 pod 的映像。 結果,一個已經調度的 pod,其鏡像在一分鐘內就可以下載完畢,最終會處於以下狀態: containerCreating.
事件看起來像這樣:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"事實證明, 來自慢速註冊表的單一映像可能會阻止部署 每個節點。
不幸的是,擺脫這種情況的方法並不多:
- 嘗試直接在叢集中或直接與叢集一起使用您的 DockerRegistry(例如,GitLabRegistry、Nexus 等);
- 使用諸如 .
故事 5. 節點因記憶體不足而掛起
在各種應用程式的運行過程中,我們也遇到節點完全無法存取的情況:SSH沒有回應,所有監控守護程式都掉了,然後日誌中沒有任何(或幾乎沒有)異常。
我將以 MongoDB 運行的節點為例,透過圖片告訴您。
這就是上面的樣子 對 事故:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/5de916d270a862cbcbb5ed23c31f698e.jpg)
就像這樣—— 後 事故:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/0f32bf1113204cf19f4639a297e40348.jpg)
在監控中,也有一個急劇的跳躍,此時節點不再可用:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/31e770cac5be32bb7f95cfbbc6b9f1ae.jpg)
因此,從截圖中可以清楚看出:
- 機器上的RAM已接近尾聲;
- RAM 消耗急劇增加,然後突然禁用對整個電腦的存取;
- 一個大任務到達 Mongo,這迫使 DBMS 進程使用更多記憶體並主動從磁碟讀取。
結果表明,如果在 Linux 可用記憶體耗盡(出現記憶體壓力)且沒有交換空間時, 對 當 OOM 殺手到來時,將頁面放入頁面快取和將其寫回磁碟之間可能會出現平衡行為。 這是由 kswapd 完成的,它勇敢地釋放盡可能多的記憶體頁以供後續分配。
不幸的是,由於 I/O 負載較大且可用記憶體較少, kswapd成為整個系統的瓶頸,因為他們與它聯繫在一起 所有 系統中記憶體頁面的分配(頁面錯誤)。 如果進程不想再使用內存,而是固定在 OOM 殺手深淵的邊緣,這種情況可能會持續很長時間。
自然的問題是:為什麼 OOM 殺手來得這麼晚? 在當前的迭代中,OOM 殺手非常愚蠢:只有當嘗試分配記憶體頁面失敗時(即,它才會終止進程)。 如果頁面錯誤失敗。 這種情況在很長一段時間內都不會發生,因為 kswapd 勇敢地釋放記憶體頁面,將頁面快取(實際上是系統中的整個磁碟 I/O)轉儲回磁碟。 更詳細地,您可以閱讀消除內核中此類問題所需步驟的描述 .
這種行為 核心 Linux 4.6 +。
故事 6.Pod 陷入 Pending 狀態
在某些叢集中,確實有很多 pod 正在運行,我們開始注意到大多數 pod 都處於「掛起」狀態很長時間 Pending,儘管 Docker 容器本身已經在節點上運行並且可以手動使用。
同時,在 describe 沒有任何錯誤:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 1m default-scheduler Successfully assigned sphinx-0 to ss-dev-kub07
Normal SuccessfulAttachVolume 1m attachdetach-controller AttachVolume.Attach succeeded for volume "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b"
Normal SuccessfulMountVolume 1m kubelet, ss-dev-kub07 MountVolume.SetUp succeeded for volume "sphinx-config"
Normal SuccessfulMountVolume 1m kubelet, ss-dev-kub07 MountVolume.SetUp succeeded for volume "default-token-fzcsf"
Normal SuccessfulMountVolume 49s (x2 over 51s) kubelet, ss-dev-kub07 MountVolume.SetUp succeeded for volume "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b"
Normal Pulled 43s kubelet, ss-dev-kub07 Container image "registry.example.com/infra/sphinx-exporter/sphinx-indexer:v1" already present on machine
Normal Created 43s kubelet, ss-dev-kub07 Created container
Normal Started 43s kubelet, ss-dev-kub07 Started container
Normal Pulled 43s kubelet, ss-dev-kub07 Container image "registry.example.com/infra/sphinx/sphinx:v1" already present on machine
Normal Created 42s kubelet, ss-dev-kub07 Created container
Normal Started 42s kubelet, ss-dev-kub07 Started container經過一番挖掘,我們假設 kubelet 根本沒有時間將有關 pod 狀態和活動/就緒測試的所有資訊傳送到 API 伺服器。
並且研究了help後,我們發現了以下參數:
--kube-api-qps - QPS to use while talking with kubernetes apiserver (default 5)
--kube-api-burst - Burst to use while talking with kubernetes apiserver (default 10)
--event-qps - If > 0, limit event creations per second to this value. If 0, unlimited. (default 5)
--event-burst - Maximum size of a bursty event records, temporarily allows event records to burst to this number, while still not exceeding event-qps. Only used if --event-qps > 0 (default 10)
--registry-qps - If > 0, limit registry pull QPS to this value.
--registry-burst - Maximum size of bursty pulls, temporarily allows pulls to burst to this number, while still not exceeding registry-qps. Only used if --registry-qps > 0 (default 10)如所見, 預設值相當小,並且 90% 的情況滿足了所有需求...但是,在我們的案例中這還不夠。 因此,我們設定以下值:
--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40……並重新啟動 kubelet,之後我們在 API 伺服器的呼叫圖中看到了下圖:
![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/b2ae099729e55a686f6bec3012b96195.jpg)
……是的,一切都開始飛翔!
聚苯乙烯
感謝我們公司的眾多工程師,特別是我們研發團隊的同事 Andrey Klimentyev(Andrey Klimentyev)在收集 bug 和準備本文方面提供的協助。).
聚苯硫醚
另請閱讀我們的博客:
- «“。
- Kubernetes 提示與技巧循環:
- «“;
- «“;
- «“;
- «“。
來源: www.habr.com

![Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案]](/wp-content/uploads/2019/03/0d15d1de17cd6838fc1cad19615af218.jpg)