![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/bed059552ed86580939aa18fbdf1553e.jpg)
Selama bertahun-tahun menggunakan Kubernetes dalam produksi, kami telah mengumpulkan banyak cerita menarik tentang bagaimana bug di berbagai komponen sistem menyebabkan konsekuensi yang tidak menyenangkan dan/atau tidak dapat dipahami yang memengaruhi pengoperasian container dan pod. Pada artikel ini kami telah memilih beberapa yang paling umum atau menarik. Sekalipun Anda tidak pernah cukup beruntung untuk menghadapi situasi seperti itu, membaca cerita detektif pendek seperti itu - terutama yang “langsung” - selalu menarik, bukan?..
Cerita 1. Supercronic dan Docker gantung
Di salah satu cluster, kami secara berkala menerima Docker yang dibekukan, yang mengganggu fungsi normal cluster. Pada saat yang sama, hal berikut diamati di log 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
… Yang paling menarik bagi kami tentang kesalahan ini adalah pesannya: pthread_create failed: No space left on device. Belajar Cepat menjelaskan bahwa Docker tidak dapat melakukan fork suatu proses, itulah sebabnya proses tersebut terhenti secara berkala.
Dalam pemantauan, gambar berikut sesuai dengan apa yang terjadi:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/bd778052c87b338493bae54b26830ef3.jpg)
Situasi serupa diamati pada node lain:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/ef512532a95ca982e4342071115dbe9f.jpg)
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/43c32ebca78755dde348ed5e7ac75c79.jpg)
Pada node yang sama kita melihat:
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>Ternyata perilaku ini merupakan konsekuensi dari kerja pod tersebut (utilitas Go yang kami gunakan untuk menjalankan tugas cron di pod):
_ 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>
…Masalahnya adalah ini: ketika suatu tugas dijalankan dalam mode superkronik, proses yang dihasilkan oleh tugas tersebut tidak dapat diakhiri dengan benar, berubah menjadi .
Catatan: Lebih tepatnya, proses dihasilkan oleh tugas cron, tetapi supercronic bukanlah sistem init dan tidak dapat “mengadopsi” proses yang dihasilkan oleh turunannya. Ketika sinyal SIGHUP atau SIGTERM dimunculkan, sinyal tersebut tidak diteruskan ke proses anak, sehingga proses anak tidak berhenti dan tetap dalam status zombie. Anda dapat membaca lebih lanjut tentang semua ini, misalnya, di .
Ada beberapa cara untuk menyelesaikan masalah:
- Sebagai solusi sementara - tingkatkan jumlah PID dalam sistem pada satu waktu:
/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 - Atau meluncurkan tugas di supercronic tidak secara langsung, tetapi menggunakan hal yang sama , yang mampu menghentikan proses dengan benar dan tidak memunculkan zombie.
Cerita 2. “Zombie” saat menghapus cgroup
Kubelet mulai memakan banyak CPU:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/6140058330faaa3785b089dcba857056.jpg)
Tidak ada yang akan menyukai ini, jadi kami mempersenjatai diri dan mulai mengatasi masalahnya. Hasil penyelidikannya adalah sebagai berikut:
- Kubelet menghabiskan lebih dari sepertiga waktu CPU-nya untuk menarik data memori dari semua cgroup:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20241'%3E%3C/svg%3E)
- Di milis pengembang kernel, Anda dapat menemukannya . Singkatnya, intinya begini: berbagai file tmpfs dan hal serupa lainnya tidak sepenuhnya dihapus dari sistem saat menghapus cgroup, yang disebut zombie. Cepat atau lambat mereka akan dihapus dari cache halaman, tetapi ada banyak memori di server dan kernel tidak melihat gunanya membuang waktu untuk menghapusnya. Itu sebabnya mereka terus menumpuk. Mengapa ini bisa terjadi? Ini adalah server dengan pekerjaan cron yang terus-menerus menciptakan pekerjaan baru, dan dengan itu pod baru. Dengan demikian, cgroup baru dibuat untuk wadah di dalamnya, yang segera dihapus.
- Mengapa cAdvisor di kubelet membuang banyak waktu? Ini mudah dilihat dengan eksekusi paling sederhana
time cat /sys/fs/cgroup/memory/memory.stat. Jika pada mesin yang sehat pengoperasiannya memerlukan waktu 0,01 detik, maka pada mesin cron02 yang bermasalah memerlukan waktu 1,2 detik. Masalahnya adalah cAdvisor, yang membaca data dari sysfs dengan sangat lambat, mencoba memperhitungkan memori yang digunakan dalam grup zombie. - Untuk menghapus zombie secara paksa, kami mencoba membersihkan cache seperti yang direkomendasikan di LKML:
sync; echo 3 > /proc/sys/vm/drop_caches, - tapi kernelnya ternyata lebih rumit dan membuat mobil crash.
Apa yang harus dilakukan? Masalahnya sedang diperbaiki (, dan untuk deskripsi lihat ) обновлением ядра Linux hingga versi 4.16.
Sejarah 3. Systemd dan mountnya
Sekali lagi, kubelet memakan terlalu banyak sumber daya pada beberapa node, namun kali ini kubelet memakan terlalu banyak memori:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/044c4e23a772c61a6206b9b20aa67c1d.jpg)
Оказалось, что есть проблема в systemd, используемом в Ubuntu 16.04, и возникает она при управлении mount’ами, которые создаются для подключения subPath dari ConfigMaps atau rahasia. Setelah pod menyelesaikan tugasnya layanan systemd dan mount layanannya tetap ada dalam sistem. Seiring waktu, sejumlah besar dari mereka terakumulasi. Bahkan ada isu mengenai topik ini:
- ;
- .
...yang terakhir mengacu pada PR di systemd: (masalah di systemd - ).
Проблемы уже нет в Ubuntu 18.04, но если вы хотите и дальше использовать Ubuntu 16.04, вам может пригодиться наш workaround на эту тему.
Jadi kami membuat DaemonSet berikut:
---
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... dan menggunakan skrip berikut:
#!/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... dan itu berjalan setiap 5 menit menggunakan superkronik yang disebutkan sebelumnya. Dockerfile-nya terlihat seperti ini:
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"]Cerita 4. Daya saing saat menjadwalkan pod
Telah diketahui bahwa: jika kita mempunyai sebuah pod yang ditempatkan pada sebuah node dan gambarnya dipompa keluar dalam waktu yang sangat lama, maka pod lain yang “menabrak” node yang sama akan langsung terpompa keluar. tidak mulai menarik gambar pod baru. Sebaliknya, ia menunggu hingga gambar dari pod sebelumnya ditarik. Akibatnya, pod yang sudah dijadwalkan dan gambarnya dapat diunduh hanya dalam satu menit akan berstatus containerCreating.
Acaranya akan terlihat seperti ini:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"Ternyata bahwa satu gambar dari registri yang lambat dapat memblokir penerapan per simpul.
Sayangnya, tidak banyak jalan keluar dari situasi ini:
- Coba gunakan Docker Registry Anda secara langsung di cluster atau langsung dengan cluster (misalnya, GitLab Registry, Nexus, dll.);
- Gunakan utilitas seperti .
Cerita 5. Node hang karena kekurangan memori
Selama pengoperasian berbagai aplikasi, kami juga menghadapi situasi di mana sebuah node benar-benar tidak lagi dapat diakses: SSH tidak merespons, semua daemon pemantauan hilang, dan kemudian tidak ada (atau hampir tidak ada) apa pun yang aneh di log.
Saya akan memberi tahu Anda melalui gambar menggunakan contoh satu node tempat MongoDB berfungsi.
Seperti inilah penampakan di atas untuk kecelakaan:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/5de916d270a862cbcbb5ed23c31f698e.jpg)
Dan seperti ini - setelah kecelakaan:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/0f32bf1113204cf19f4639a297e40348.jpg)
Dalam pemantauan, ada juga lompatan tajam, di mana node tidak lagi tersedia:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/31e770cac5be32bb7f95cfbbc6b9f1ae.jpg)
Jadi, dari tangkapan layar terlihat jelas bahwa:
- RAM pada mesin hampir habis;
- Ada lonjakan tajam dalam konsumsi RAM, setelah itu akses ke seluruh mesin dinonaktifkan secara tiba-tiba;
- Tugas besar tiba di Mongo, yang memaksa proses DBMS menggunakan lebih banyak memori dan secara aktif membaca dari disk.
Оказывается, если в Linux заканчивается свободная память (наступает memory pressure) и swap’а нет, то untuk Ketika pembunuh OOM tiba, tindakan penyeimbangan mungkin muncul antara memasukkan halaman ke dalam cache halaman dan menulisnya kembali ke disk. Hal ini dilakukan oleh kswapd, yang dengan berani mengosongkan halaman memori sebanyak mungkin untuk distribusi selanjutnya.
Sayangnya, dengan beban I/O yang besar ditambah dengan jumlah memori bebas yang sedikit, kswapd menjadi penghambat keseluruhan sistem, karena mereka terikat padanya semua alokasi (kesalahan halaman) halaman memori dalam sistem. Ini bisa berlangsung sangat lama jika prosesnya tidak ingin menggunakan memori lagi, tetapi tetap berada di ujung jurang pembunuh OOM.
Pertanyaan wajarnya adalah: mengapa pembunuh OOM datang terlambat? Dalam iterasi saat ini, pembunuh OOM sangat bodoh: ia akan mematikan proses hanya ketika upaya untuk mengalokasikan halaman memori gagal, yaitu. jika kesalahan halaman gagal. Hal ini tidak terjadi dalam waktu yang cukup lama, karena kswapd dengan berani membebaskan halaman memori, membuang cache halaman (sebenarnya seluruh disk I/O dalam sistem) kembali ke disk. Anda dapat membaca lebih detail dengan deskripsi langkah-langkah yang diperlukan untuk menghilangkan masalah seperti itu di kernel .
Perilaku ini с ядром Linux 4.6 +.
Cerita 6. Pod terjebak dalam status Tertunda
Di beberapa cluster, di mana terdapat banyak sekali pod yang beroperasi, kami mulai memperhatikan bahwa sebagian besar dari mereka “menggantung” untuk waktu yang sangat lama di negara bagian tersebut. Pending, meskipun container Docker sendiri sudah berjalan di node dan dapat digunakan secara manual.
Selain itu, di describe tidak ada yang salah:
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 containerSetelah melakukan beberapa penggalian, kami berasumsi bahwa kubelet tidak mempunyai waktu untuk mengirimkan semua informasi tentang status pod dan uji keaktifan/kesiapan ke server API.
Dan setelah mempelajari bantuan, kami menemukan parameter berikut:
--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)Seperti yang terlihat, nilai defaultnya cukup kecil, dan 90% mencakup semua kebutuhan... Namun, dalam kasus kami ini tidak cukup. Oleh karena itu, kami menetapkan nilai berikut:
--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40... dan memulai ulang kubelet, setelah itu kita melihat gambar berikut di grafik panggilan ke server API:
![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/b2ae099729e55a686f6bec3012b96195.jpg)
... dan ya, semuanya mulai terbang!
PS
Atas bantuan mereka dalam mengumpulkan bug dan mempersiapkan artikel ini, saya mengucapkan terima kasih yang sebesar-besarnya kepada banyak insinyur di perusahaan kami, dan terutama kepada rekan saya dari tim R&D kami Andrey Klimentyev ().
PPS
Baca juga di blog kami:
- «'.
- Lingkaran tip & trik Kubernetes:
- «»;
- «»;
- «»;
- «'.
Sumber: www.habr.com

![6 bug sistem menghibur dalam pengoperasian Kubernetes [dan solusinya]](/wp-content/uploads/2019/03/0d15d1de17cd6838fc1cad19615af218.jpg)