![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/bed059552ed86580939aa18fbdf1553e.jpg)
Kubernetes-dən istehsalda istifadə etdiyimiz illər ərzində müxtəlif sistem komponentlərindəki səhvlərin qabların və podların işinə təsir edən xoşagəlməz və/və ya anlaşılmaz nəticələrə necə gətirib çıxardığına dair çoxlu maraqlı hekayələr topladıq. Bu yazıda ən çox yayılmış və ya maraqlı olanlardan bəzilərini seçdik. Heç vaxt belə hallarla rastlaşmasanız belə, belə qısa detektiv hekayələri oxumaq, xüsusən də “birinci əl” - həmişə maraqlıdır, elə deyilmi?..
Hekayə 1. Supercronic və Docker asma
Klasterlərdən birində biz vaxtaşırı klasterin normal fəaliyyətinə mane olan dondurulmuş Docker aldıq. Eyni zamanda, Docker qeydlərində aşağıdakılar müşahidə edildi:
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
… Bu səhvlə bağlı bizi ən çox maraqlandıran mesajdır: pthread_create failed: No space left on device. Sürətli öyrənmə izah etdi ki, Docker bir prosesi çəngəlləyə bilmədi, buna görə də vaxtaşırı dondu.
Monitorinq zamanı baş verənlərə aşağıdakı şəkil uyğun gəlir:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/bd778052c87b338493bae54b26830ef3.jpg)
Bənzər bir vəziyyət digər qovşaqlarda da müşahidə olunur:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/ef512532a95ca982e4342071115dbe9f.jpg)
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/43c32ebca78755dde348ed5e7ac75c79.jpg)
Eyni qovşaqlarda biz görürük:
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>Məlum oldu ki, bu davranış pod ilə işləməyin nəticəsidir (podlarda cron işlərini yerinə yetirmək üçün istifadə etdiyimiz Go yardım proqramı):
_ 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>
…Problem belədir: tapşırıq superkronikdə icra edildikdə, proses onun tərəfindən yaranır düzgün bitirə bilməz, çevrilir .
Qeyd: Daha dəqiq desək, proseslər cron tapşırıqları ilə əmələ gəlir, lakin supercronic başlanğıc sistemi deyil və uşaqlarının yaratdığı prosesləri “qəbul edə” bilməz. SIGHUP və ya SIGTERM siqnalları qaldırıldıqda, onlar uşaq proseslərə ötürülmür, nəticədə uşaq prosesləri dayandırılmır və zombi statusunda qalır. Bütün bunlar haqqında daha çox oxuya bilərsiniz, məsələn, .
Problemləri həll etməyin bir neçə yolu var:
- Müvəqqəti həll yolu kimi - bir anda sistemdəki PID-lərin sayını artırın:
/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 - Və ya superkronikdə tapşırıqları birbaşa deyil, eyni istifadə edərək işə salın , prosesləri düzgün şəkildə dayandıra bilən və zombi yaratmayan.
Hekayə 2. Qrupu silərkən “Zombilər”
Kubelet çox CPU istehlak etməyə başladı:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/6140058330faaa3785b089dcba857056.jpg)
Bu heç kimin xoşuna gəlməyəcək, ona görə də özümüzü silahlandırdıq və problemlə məşğul olmağa başladı. Araşdırmanın nəticələri aşağıdakı kimi olub:
- Kubelet CPU vaxtının üçdə birindən çoxunu bütün qruplardan yaddaş məlumatlarını çəkməyə sərf edir:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20241'%3E%3C/svg%3E)
- Kernel tərtibatçılarının poçt siyahısında tapa bilərsiniz . Qısacası, məsələ belədir: müxtəlif tmpfs faylları və digər oxşar şeylər sistemdən tamamilə silinmir bir qrup silərkən, sözdə zombi. Gec-tez onlar səhifənin ön yaddaşından silinəcəklər, lakin serverdə çoxlu yaddaş var və nüvə onları silmək üçün vaxt itirməyin mənasını görmür. Ona görə də yığışmağa davam edirlər. Niyə belə baş verir? Bu, daim yeni iş yerləri və onlarla birlikdə yeni podlar yaradan cron işləri olan bir serverdir. Beləliklə, onlarda olan konteynerlər üçün tezliklə silinən yeni qruplar yaradılır.
- Niyə kubeletdəki cAdvisor bu qədər vaxt itirir? Bunu ən sadə icra ilə görmək asandır
time cat /sys/fs/cgroup/memory/memory.stat. Sağlam bir maşında əməliyyat 0,01 saniyə çəkirsə, problemli cron02-də 1,2 saniyə çəkir. İş ondadır ki, sysfs-dən məlumatları çox yavaş oxuyan cAdvisor, zombi qruplarında istifadə olunan yaddaşı nəzərə almağa çalışır. - Zombiləri zorla silmək üçün LKML-də tövsiyə edildiyi kimi keşləri təmizləməyə çalışdıq:
sync; echo 3 > /proc/sys/vm/drop_caches, - lakin ləpə daha mürəkkəb olduğu ortaya çıxdı və maşını qəzaya uğratdı.
Nə etməli? Problem həll olunur (, və təsvir üçün baxın ) обновлением ядра Linux 4.16 versiyasına qədər.
Tarix 3. Systemd və onun montajı
Yenə kubelet bəzi qovşaqlarda həddən artıq çox resurs istehlak edir, lakin bu dəfə çox yaddaş sərf edir:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/044c4e23a772c61a6206b9b20aa67c1d.jpg)
Оказалось, что есть проблема в systemd, используемом в Ubuntu 16.04, и возникает она при управлении mount’ами, которые создаются для подключения subPath ConfigMap-dan və ya gizlidən. Pod öz işini bitirdikdən sonra systemd xidməti və onun xidmət qurğusu qalır sistemdə. Zamanla onların çoxu yığılır. Bu mövzuda hətta problemlər var:
- ;
- .
...sonuncusu systemd-də PR-ə aiddir: (systemd-də məsələ - ).
Проблемы уже нет в Ubuntu 18.04, но если вы хотите и дальше использовать Ubuntu 16.04, вам может пригодиться наш workaround на эту тему.
Beləliklə, aşağıdakı DaemonSet-i etdik:
---
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... və aşağıdakı skriptdən istifadə edir:
#!/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... və əvvəllər qeyd olunan superkronikdən istifadə edərək hər 5 dəqiqədən bir işləyir. Onun Dockerfile belə görünür:
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"]Hekayə 4. Podları planlaşdırarkən rəqabət qabiliyyəti
Diqqət edildi ki, əgər bir qovşaq üzərində yerləşdirilmiş bir pod varsa və onun təsviri çox uzun müddət çıxarılırsa, eyni node "vuran" başqa bir pod sadəcə olaraq yeni podun şəklini çəkməyə başlamaz. Bunun əvəzinə, əvvəlki podun şəkli çəkilənə qədər gözləyir. Nəticədə, artıq planlaşdırılan və şəkli bir dəqiqə ərzində endirilə bilən pod statusunda sona çatacaq. containerCreating.
Hadisələr belə görünəcək:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"O çıxır ki, yavaş reyestrdən bir şəkil yerləşdirməni blok edə bilər node başına.
Təəssüf ki, vəziyyətdən çıxış yolları çox deyil:
- Docker Registry-ni birbaşa klasterdə və ya birbaşa klasterlə birlikdə istifadə etməyə çalışın (məsələn, GitLab Registry, Nexus və s.);
- kimi kommunal vasitələrdən istifadə edin .
Hekayə 5. Yaddaşın olmaması səbəbindən qovşaqlar asılır
Müxtəlif proqramların işləməsi zamanı bir node tamamilə əlçatan olmağı dayandırdığı bir vəziyyətlə də qarşılaşdıq: SSH cavab vermir, bütün monitorinq demonları düşür və sonra qeydlərdə anormal bir şey (və ya demək olar ki, heç bir şey) yoxdur.
MongoDB-nin işlədiyi bir node nümunəsindən istifadə edərək şəkillərdə sizə xəbər verəcəyəm.
Bu, yuxarıdakı kimi görünür üzrə qəzalar:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/5de916d270a862cbcbb5ed23c31f698e.jpg)
Və belə - sonra qəzalar:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/0f32bf1113204cf19f4639a297e40348.jpg)
Monitorinqdə, düyünün mövcud olmağı dayandırdığı kəskin bir atlama da var:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/31e770cac5be32bb7f95cfbbc6b9f1ae.jpg)
Beləliklə, ekran görüntülərindən aydın olur ki:
- Maşındakı RAM sona yaxındır;
- RAM istehlakında kəskin bir sıçrayış var, bundan sonra bütün maşına giriş kəskin şəkildə dayandırılır;
- Mongo-ya böyük bir tapşırıq gəlir ki, bu da DBMS prosesini daha çox yaddaş istifadə etməyə və diskdən aktiv oxumağa məcbur edir.
Оказывается, если в Linux заканчивается свободная память (наступает memory pressure) и swap’а нет, то üzrə OOM qatili gəldikdə, səhifələri səhifə keşinə atmaq və onları yenidən diskə yazmaq arasında balanslaşdırma aktı yarana bilər. Bu, sonrakı paylama üçün mümkün qədər çox yaddaş səhifəsini cəsarətlə azad edən kswapd tərəfindən edilir.
Təəssüf ki, böyük I/O yükü və az miqdarda boş yaddaşla, kswapd bütün sistemin darboğazına çevrilir, çünki onlar buna bağlıdırlar bütün sistemdə yaddaş səhifələrinin ayrılması (səhifə xətaları). Proseslər artıq yaddaşdan istifadə etmək istəmirsə, lakin OOM-qatil uçurumunun ən kənarında sabitləşərsə, bu çox uzun müddət davam edə bilər.
Təbii sual budur: OOM qatili niyə belə gec gəlir? Hazırkı iterasiyasında OOM qatili son dərəcə axmaqdır: o, prosesi yalnız yaddaş səhifəsini ayırmaq cəhdi uğursuz olduqda, yəni. səhifə xətası uğursuz olarsa. Bu, kifayət qədər uzun müddət baş vermir, çünki kswapd cəsarətlə yaddaş səhifələrini azad edir, səhifənin önbelleğini (əslində sistemdəki bütün disk I/O) diskə geri qaytarır. Daha ətraflı olaraq, nüvədəki bu cür problemləri aradan qaldırmaq üçün lazım olan addımların təsviri ilə oxuya bilərsiniz .
Bu davranış с ядром Linux 4.6 +.
Hekayə 6. Podlar Gözləmə vəziyyətində ilişib qalır
Həqiqətən çoxlu podların fəaliyyət göstərdiyi bəzi qruplarda, onların əksəriyyətinin dövlətdə çox uzun müddət "asıldığını" görməyə başladıq. Pending, baxmayaraq ki, Docker konteynerlərinin özləri artıq qovşaqlarda işləyir və əl ilə işlənə bilər.
Üstəlik, içində describe səhv bir şey yoxdur:
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 containerBəzi qazıntılardan sonra biz fərz etdik ki, kubeletin sadəcə olaraq podların vəziyyəti və canlılıq/hazırlıq testləri haqqında bütün məlumatları API serverinə göndərməyə vaxtı yoxdur.
Və köməyi öyrəndikdən sonra aşağıdakı parametrləri tapdıq:
--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)Göründüyü kimi, default dəyərlər olduqca kiçikdir, 90%-də isə bütün ehtiyacları ödəyir... Ancaq bizim vəziyyətimizdə bu kifayət deyildi. Beləliklə, biz aşağıdakı dəyərləri təyin edirik:
--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40... və kubeletləri yenidən başladın, bundan sonra API serverinə edilən zənglərin qrafiklərində aşağıdakı şəkli gördük:
![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/b2ae099729e55a686f6bec3012b96195.jpg)
... və bəli, hər şey uçmağa başladı!
PS
Səhvlərin toplanmasında və bu məqalənin hazırlanmasında göstərdikləri köməyə görə şirkətimizin çoxsaylı mühəndislərinə və xüsusən də R&D komandamızdakı həmkarım Andrey Klimentyevə dərin təşəkkürümü bildirirəm ().
PPS
Bloqumuzda da oxuyun:
- «.
- Kubernetes məsləhətləri və fəndləri döngəsi:
- «";
- «";
- «";
- «.
Mənbə: www.habr.com

![Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]](/wp-content/uploads/2019/03/0d15d1de17cd6838fc1cad19615af218.jpg)