ProHoster > Blog > İdarə > Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]
Kubernetes əməliyyatında 6 əyləncəli sistem səhvi [və onların həlli]
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ə sənədləşdirmə 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:
Bənzər bir vəziyyət digər qovşaqlarda da müşahidə olunur:
Məlum oldu ki, bu davranış pod ilə işləməyin nəticəsidir superkronik (podlarda cron işlərini yerinə yetirmək üçün istifadə etdiyimiz Go yardım proqramı):
Problem belədir: tapşırıq superkronikdə icra edildikdə, proses onun tərəfindən yaranır düzgün bitirə bilməz, çevrilir zombi.
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, belə bir məqalə.
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 tini, 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ı:
Bu heç kimin xoşuna gəlməyəcək, ona görə də özümüzü silahlandırdıq perf 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:
Kernel tərtibatçılarının poçt siyahısında tapa bilərsiniz problemin müzakirəsi. 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ə memcg 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 (törətmək, və təsvir üçün baxın buraxılış mesajı) Linux nüvəsinin 4.16 versiyasına yenilənməsi.
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:
Məlum oldu ki, Ubuntu 16.04-də istifadə olunan systemd-də problem var və bu, əlaqə üçün yaradılmış montajları idarə edərkən baş verir. 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:
Problem artıq Ubuntu 18.04-də yoxdur, lakin Ubuntu 16.04-dən istifadə etməyə davam etmək istəyirsinizsə, bu mövzuda həll yolumuzu faydalı tapa bilərsiniz.
#!/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:
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.);
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:
Və belə - sonra qəzalar:
Monitorinqdə, düyünün mövcud olmağı dayandırdığı kəskin bir atlama da var:
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.
Belə çıxır ki, əgər Linux-un boş yaddaşı tükənirsə (yaddaş təzyiqi işə düşür) və dəyişdirmə yoxdursa, onda ü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 burada.
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 container
Bə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:
... 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:
... 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 (zuzzalar).