![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/bed059552ed86580939aa18fbdf1553e.jpg)
Kubernetes’in istehsalda istifadəsi illəri ərzində, müxtəlif sistem komponentlərindəki xətaların konteynerlərə və pod-lara təsir edən xoşagəlməz və ya başa düşülməz nəticələrə səbəb olduğu bir çox maraqlı hekayə toplandı. Bu məqalədə onların bəzilərinin ən çox rast gəlinən və ya maraqlı olanlarını topladıq. Hətta belə bir vəziyyətlə heç vaxt rastlaşmasanız belə, bu cür qısa detektiv hekayələri oxumaq — xüsusən də «birbaşa» — həmişə maraqlıdır, elmi də deyilmi?..
Hekayə 1. Supercronic və dayanan Docker
Küçük klasterlərimizdən birində periodik olaraq «dayanan» Docker ilə rastlaşırdıq ki, bu da klasterin normal fəaliyyətinə mane olurdu. Bu zaman Docker loglarında aşağıdakıları görürdük.
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 xətada bizi daha çox maraqlandıran mesajdır: pthread_create failed: No space left on device. Tez bir araşdırma Docker-in prosesi fork-laması mümkün olmadığını, buna görə də periodik olaraq «dayandığını» aydınlaşdırdı.
Müşahidə olunan monitorinqdə aşağıdakı görünüş var:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/bd778052c87b338493bae54b26830ef3.jpg)
Oxşar vəziyyət digər düyünlərdə də müşahidə olunur:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/ef512532a95ca982e4342071115dbe9f.jpg)
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/43c32ebca78755dde348ed5e7ac75c79.jpg)
Eyni düyünlərdə 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]
root 17398 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 16852 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 9473 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 4664 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 30571 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 24113 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 16475 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 7176 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 1090 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]Məlum oldu ki, bu cür davranış — pod-un (pod-larda cron tapşırıqlarını icra etmək üçün istifadə etdiyimiz Go proqramı) işi ilə bağlıdır:
_ 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]
| _ [curl]
| _ [curl]
| _ [curl]
…Problemin mahiyyəti budur: supercronic-da tapşırıq işə salındıqda, onun yaratdığı proses düzgün şəkildə başa çatmır, zombiyə çevrilərək .
Qeyd: Dəqiq desək, proseslər cron tapşırıqları tərəfindən yaradılır, lakin supercronic init sistemi deyil və ona öz övladlarını "adopte etmək" imkanı vermir. SIGHUP və ya SIGTERM siqnalları meydana gəldikdə, onlar yaradılmış proseslərə ötürülmür, nəticədə övlad prosesləri tamamlanmır, zombi vəziyyətində qalır. Bunun haqqında daha ətraflı oxumaq üçün, məsələn, .
Var bir neçə həll yolu:
- Müvəqqəti bir workaround olaraq — sistemdə eyni anda PID-lərin sayını artırmaq:
/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 - Yoxsa tapşırıqları supercronic-da birbaşa deyil, eyni , prosesi düzgün tamamlayaraq zombi yaratmayan bir vasitə vasitəsilə işə salmaq.
Hekayə 2. Cgroup-ları silərkən "zombilər"
Kubelet çox CPU istehlak etməyə başladı:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/6140058330faaa3785b089dcba857056.jpg)
Bu, heç kəsə xoş gəlməz, ona görə biz ilə problemi araşdırmağa başladıq. Araşdırmanın yekunları belə oldu:
- Kubelet bütün cgroup-lardan yaddaş məlumatlarını çıxarmaq üçün CPU vaxtının üçdə birindən çoxunu sərf edir:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%20241%22%3E%3C%2Fsvg%3E)
- Kernel inkişaf etdiricilərinin poçt siyahısında . Qısa desək, məsələ odur ki, müxtəlif tmpfs-fayllar və digər oxşar şeylər cgroup silindikdə tamamilə sistemdən silinmir memcg-lardir — zombi Niyə cAdvisor kubelet-in bu qədər vaxt sərf etdiyini görmək asandır? Bu, sadə bir əməliyyat icra etməklə asanlıqla görünürtime cat /sys/fs/cgroup/memory/memory.stat
- . Sağlam bir maşında bu əməliyyat 0,01 saniyə çəkirsə, problemli cron02-də 1,2 saniyəyə başa gəlir. Bunun səbəbi cAdvisor-un sysfs-dən məlumatları yavaş oxumasıdır, o, zombil cgroup-lardakı istifadə olunan yaddaşı hesaba almağa çalışır.
.. - Zombi məcburi silmək üçün, LKML-də tövsiyə edildiyi kimi, önbellekleri təmizləməyi sınadıq:
sync; echo 3 > /proc/sys/vm/drop_caches, — amma nüvənin daha çətin olduğunu və maşını asdığını gördük.
Bəs nə etməli? Problemin həlli (, və təsvirini ) Linux nüvəsini 4.16 versiyasına yeniləməklə əldə edilir.
Tarix 3. Systemd və onun mount’ı
Yenidən kubelet bəzi düyünlərdə çoxlu resurs istehlak edir, amma bu dəfə — artıq yaddaş:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/044c4e23a772c61a6206b9b20aa67c1d.jpg)
Məlum oldu ki, Ubuntu 16.04-də istifadə olunan systemd-də problem var və bu, mount’ların idarə edilməsi zamanı yaranır ConfigMap’lardan və ya secret’lardan yaradılan . Pod bitdikdən sonra systemd xidməti və onun yardımçı mount’u sistemdə qalır. Zamanla onların sayı çoxalır. Bu barədə məsələlər də mövcuddur:
- ;
- .
… ən sonuncusunda systemd-dəki PR-ə istinad edilir: (systemd-də məsələlər — ).
Problem artıq Ubuntu 18.04-də yoxdur, amma əgər Ubuntu 16.04-dən istifadə etməyə davam etmək istəyirsinizsə, bu barədə workaround-mız yaара bilər.
Beləliklə, biz aşağıdakı DaemonSet-i yaratdıq:
---
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ə burada belə bir skript istifadə olunur:
#!/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ə bu, artıq qeyd edilən supercronic ilə hər 5 dəqiqədən bir işə düşür. 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"]Tarix 4. Pod’ların planlaşdırılması zamanı rəqabət
Müşahidə edildi ki: əgər bizim bir pod düyünə yerləşdirilirsə və onun imici yüklənməsi çox uzun sürərsə, digər pod, həmin düyünə 'düşərsə' sadəcə yeni pod’un imicini yükləməyə başlamır.Bunun yerine, önceki pod'un görüntüsünün 'çekilmesini' bekliyor. Sonuç olarak, daha önce planlanmış ve yalnızca bir dakikada çekilebilecek bir pod, uzun süre 'containerCreating' durumunda kalacak. containerCreating.
Olaylarda yaklaşık olarak şu şekilde olacak:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal görüntüyü çekiyor "registry.example.com/infra/openvpn/openvpn:master"Yani tek bir yavaş registry'den gelen bir görüntü, dağıtımı engelleyebilir düğümde.
Maalesef, durumdan çıkış yolu çok değil:
- Kendi Docker Registry'nizi doğrudan kümede veya küme ile birlikte kullanmaya çalışın (örneğin, GitLab Registry, Nexus vb.);
- Böyle araçlardan yararlanın, .
Hikaye 5. Bellek yetersizliğinde düğüm donması
Farklı uygulamaların kullanım süresince, bir düğümün tamamen erişilemez hale geldiği bir durumu da yaşadık: SSH yanıt vermiyor, tüm izleme daemonları devre dışı kalıyor ve loglarda sonra hiçbir (veya neredeyse hiçbir) anomali yok.
MongoDB'nin çalıştığı bir düğüm örneğinde, bunu görsellerle anlatacağım.
Atop böyle görünüyor ile kaza:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/5de916d270a862cbcbb5ed23c31f698e.jpg)
Ve işte böyle — sonra kaza:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/0f32bf1113204cf19f4639a297e40348.jpg)
İzlemede düğümün erişilemez hale geldiği keskin bir artış da gözlemleniyor:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/31e770cac5be32bb7f95cfbbc6b9f1ae.jpg)
Bu şekilde, ekran görüntülerinden şunlar görülüyor:
- Makinedeki RAM neredeyse bitmiş;
- RAM tüketiminde keskin bir artış gözlemleniyor, ardından makinaya erişim aniden kesiliyor;
- Mongo'ya büyük bir görev geliyor, bu da veritabanı sürecinin daha fazla RAM kullanmasına ve diskte aktif olarak okuma yapmasına neden oluyor.
Görülüyor ki, Linux'ta boş bellek kalmadığında (bellek baskısı gerçekleştiğinde) ve swap yoksa, ile OOM killer'ın gelişi, sayfaları page cache'e atma ile bunları diske geri yazma arasında bir dengede gerçekleşebilir. Bunu, mümkün olduğunca fazla bellek sayfasını serbest bırakmak için kswapd yapar.
Maalesef, girdi/çıktı üzerindeki büyük yük ile birlikte az sayıda boş bellek olduğunda, kswapd, tüm sistemin şişme noktası haline geliyor, çünkü sistemdeki bellek sayfalarının tahsislerini (page faults) etkiliyor. İşlemler daha fazla bellek kullanmak istemezse ve OOM killer çukurunun kenarında takılı kalırlarsa, bu çok uzun sürebilir. tüm Soru şudur: OOM killer neden bu kadar geç geliyor? Şu anki iterasyonunda, OOM killer oldukça aptaldır: bir bellek sayfası tahsis etmeye çalışırken başarısız olduğunda yani bir page fault hatası aldığında işlemi sonlandırır. Bunun gerçekleşmesi uzun sürmez, çünkü kswapd cesurca bellek sayfalarını serbest bırakır ve page cache'i (temel olarak sistemdeki tüm disk I/O) diske geri gönderir. Benzer sorunların çözümü için gerekli adımları daha ayrıntılı olarak açıklayan bilgiler okuyabilirsiniz.
Bu davranış .
geliştirilmelidir. Linux 4.6+ nüvəsi ilə.
6. Tarix. Pod’lar Pending vəziyyətində asılı qaldı
Həqiqətən çox sayda pod’un fəaliyyət göstərdiyi bəzi klasterlərdə, əksər pod-ların çox uzun müddət "asılı" qaldığını qeyd etməyə başladıq. Pending, halbuki Docker konteynerləri artıq nodlarda başlandı və onlarla əllə işləmək mümkündür.
Bu arada describe pis bir şey yoxdur:
Tip Səbəb Yaş Gələn Mesaj
---- ------ ---- ---- -------
Normal Cədvəli 1d default-scheduler müvəffəqiyyətlə sphinx-0 i ss-dev-kub07 yerinə təyin etdi
Normal Uğurlu əlavə həcmi 1d attachdetach-controller AttachVolume.Attach müvəffəqiyyətlə "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b" üçün həcmi əlavə etdi
Normal Uğurlu montaj həcmi 1d kubelet, ss-dev-kub07 MountVolume.SetUp müvəffəqiyyətlə "sphinx-config" üçün həcmi qurdu
Normal Uğurlu montaj həcmi 1d kubelet, ss-dev-kub07 MountVolume.SetUp müvəffəqiyyətlə "default-token-fzcsf" üçün həcmi qurdu
Normal Uğurlu montaj həcmi 49s (x2 51s ərzində) kubelet, ss-dev-kub07 MountVolume.SetUp müvəffəqiyyətlə "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b" üçün həcmi qurdu
Normal Çəkildi 43s kubelet, ss-dev-kub07 Kontainer şəkli "registry.example.com/infra/sphinx-exporter/sphinx-indexer:v1" artıq maşında mövcuddur
Normal Yaradıldı 43s kubelet, ss-dev-kub07 Kontainer yaradıldı
Normal Başlandı 43s kubelet, ss-dev-kub07 Kontainer başlandı
Normal Çəkildi 43s kubelet, ss-dev-kub07 Kontainer şəkli "registry.example.com/infra/sphinx/sphinx:v1" artıq maşında mövcuddur
Normal Yaradıldı 42s kubelet, ss-dev-kub07 Kontainer yaradıldı
Normal Başlandı 42s kubelet, ss-dev-kub07 Kontainer başlandıAraşdırmalar apararaq, kubelet-in sadəcə pod-ların vəziyyəti haqqında API-serverə bütün məlumatları göndərməyə yetərincə vaxtı olmadığını düşündük.
Və kömək bildirərək aşağıdakı parametrləri tapdıq:
--kube-api-qps - kubernetes apiserveri ilə danışarkən istifadə etmək üçün QPS (default 5)
--kube-api-burst - kubernetes apiserveri ilə danışarkən istifadə olunan Burst (default 10)
--event-qps - 0-dan böyükdürsə, saniyədə bu dəyərə qədər hadisələrin yaradılmasını məhdudlaşdırır. 0 olanlar, sərhəd yoxdur. (default 5)
--event-burst - Partlayıcı hadisə qeydiyyatlarının maksimum ölçüsü, hadisə qeydiyyatlarının bu sayına qədər partlamasına icazə verir, hələ də event-qps-i aşmadan. Yalnız --event-qps > 0 olduqda istifadə olunur (default 10)
--registry-qps - 0-dan böyükdürsə, registri çəkmək üçün QPS-i bu dəyərə qədər məhdudlaşdırır.
--registry-burst - Partlayıcı çəkmələrin maksimum ölçüsü, registri çəkməyə mənsub olan registri-qps kateqoriyasını aşmadan bu sayına qədər çəkməyə icazə verir. Yalnız --registry-qps > 0 olduqda istifadə olunur (default 10)Göründüyü kimi, default dəyərləri - olduqca kiçikdir, və 90 % hallarda bütün tələbləri qarşılayır… Lakin bizim vəziyyətimizdə bu yetərsiz oldu. Bu səbəbdən aşağıdakı dəyərləri təqdim etdik:
--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40… və kubelet-ləri yenidən işə saldıq, bundan sonra API-serverə müraciətlərin qrafiklərində aşağıdakı mənzərəni gördük:
![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/b2ae099729e55a686f6bec3012b96195.jpg)
… və bəli, hər şey uçdu!
P.S.
Xətaların toplanması və məqalənin hazırlanması üçün böyük təşəkkürümü bildirmək istəyirəm, xüsusilə də R&D komandamızdan həmkarım Andrey Klimentyevə ().
P.P.S.
Blogumuzda oxuyun:
- «».
- Kubernetes ipuçları və trikləri dövrü:
- «»;
- «»;
- «»;
- «».
Mənbə: habr.com

![Kubernetes-də baş verən 6 maraqlı sistem xətası [və onların həlli]](/wp-content/uploads/2019/03/0d15d1de17cd6838fc1cad19615af218.jpg)