6 izklaidÄjoÅ”as sistÄmas kļūdas Kubernetes darbÄ«bÄ [un to risinÄjums]
Gadu gaitÄ, izmantojot Kubernetes ražoÅ”anÄ, esam uzkrÄjuÅ”i daudz interesantu stÄstu par to, kÄ dažÄdu sistÄmas komponentu kļūdas noveda pie nepatÄ«kamÄm un/vai nesaprotamÄm sekÄm, kas ietekmÄ konteineru un podiÅu darbÄ«bu. Å ajÄ rakstÄ mÄs esam apkopojuÅ”i dažus no visizplatÄ«tÄkajiem vai interesantÄkajiem. Pat ja jums nekad nav paveicies saskarties ar Å”ÄdÄm situÄcijÄm, lasÄ«t par Å”Ädiem Ä«siem detektÄ«vstÄstiem - Ä«paÅ”i "no pirmavotiem" - vienmÄr ir izklaidÄjoÅ”i, vai ne?
StÄsts 1. Supercronic un iestrÄdzis Docker
VienÄ no klasteriem mÄs periodiski saÅÄmÄm āpiekÄrtuā Docker, kas traucÄja klastera normÄlu darbÄ«bu. TajÄ paÅ”Ä laikÄ Docker žurnÄlos tika novÄrots sekojoÅ”ais
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
ā¦
Å ajÄ kļūdÄ mÅ«s visvairÄk interesÄ ziÅojums: pthread_create failed: No space left on device. PavirÅ”s pÄtÄ«jums dokumentÄcija paskaidroja, ka Docker nevar sadalÄ«t procesu, tÄpÄc tas periodiski āuzkarasā.
UzraudzÄ«bÄ notiekoÅ”ais atbilst Å”Ädam attÄlam:
LÄ«dzÄ«ga situÄcija tiek novÄrota citos mezglos:
ProblÄma ir Å”Äda: kad uzdevums tiek sÄkts superkronikÄ, process ir saistÄ«ts ar to nevar pareizi pabeigt, pÄrvÄrÅ”oties par zombijs.
PiezÄ«mePrecÄ«zÄk sakot, procesus rada cron uzdevumi, taÄu superkroniks nav sÄkotnÄjÄ sistÄma un nevar "pieÅemt" procesus, ko radÄ«juÅ”i tÄ bÄrni. Kad parÄdÄs SIGHUP vai SIGTERM signÄli, tie netiek nodoti pakÄrtotajiem procesiem, kÄ rezultÄtÄ pakÄrtotie procesi netiek pÄrtraukti, paliekot zombiju statusÄ. VairÄk par to visu varat lasÄ«t, piemÄram, iekÅ” tÄds raksts.
Ir vairÄki veidi, kÄ atrisinÄt problÄmas:
KÄ pagaidu risinÄjums ā palieliniet PID skaitu sistÄmÄ vienÄ brÄ«dÄ«:
/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
Vai arÄ« veiciet uzdevumu palaiÅ”anu superkronikÄ nevis tieÅ”i, bet izmantojot to paÅ”u Tini, kas spÄj graciozi pÄrtraukt procesus un neradÄ«t zombijus.
StÄsts 2. "Zombiji" dzÄÅ”ot cgroup
Kubelet sÄka patÄrÄt daudz CPU:
Tas nevienam nepatÄ«k, tÄpÄc mÄs bruÅojÄmies ideÄls un sÄka risinÄt problÄmu. IzmeklÄÅ”anas rezultÄti bija Å”Ädi:
Kubelet pavada vairÄk nekÄ treÅ”daļu CPU laika, lai iegÅ«tu atmiÅas datus no visÄm cgrupÄm:
Kodola izstrÄdÄtÄju adresÄtu sarakstÄ varat atrast problÄmas apsprieÅ”ana. ÄŖsÄk sakot, bÅ«tÄ«ba ir tÄda dažÄdi tmpfs faili un citas lÄ«dzÄ«gas lietas netiek pilnÄ«bÄ izÅemtas no sistÄmas dzÄÅ”ot cgrupu, t.s memcg zombijs. Agri vai vÄlu tie tomÄr tiks dzÄsti no lapas keÅ”atmiÅas, tomÄr serverÄ« ir daudz atmiÅas un kodols neredz jÄgu tÄrÄt laiku to dzÄÅ”anai. TÄpÄc viÅi turpina krÄties. KÄpÄc tas vispÄr notiek? Å is ir serveris ar cron darbiem, kas pastÄvÄ«gi rada jaunas darbavietas un lÄ«dz ar to arÄ« jaunus podziÅus. TÄdÄjÄdi tajÄs esoÅ”ajiem konteineriem tiek izveidotas jaunas cgrupas, kuras drÄ«z vien tiek izdzÄstas.
KÄpÄc cAdvisor kubeletÄ pavada tik daudz laika? To ir viegli redzÄt, veicot vienkÄrÅ”Äko izpildi time cat /sys/fs/cgroup/memory/memory.stat. Ja veselÄ maŔīnÄ darbÄ«ba aizÅem 0,01 sekundi, tad problÄmai cron02 tas aizÅem 1,2 sekundes. Lieta tÄda, ka cAdvisor, kas ļoti lÄni nolasa datus no sysfs, cenÅ”as Åemt vÄrÄ arÄ« zombiju cgrupÄs izmantoto atmiÅu.
Lai piespiedu kÄrtÄ noÅemtu zombijus, mÄs mÄÄ£inÄjÄm notÄ«rÄ«t keÅ”atmiÅas, kÄ ieteica LKML: sync; echo 3 > /proc/sys/vm/drop_caches, - bet kodols izrÄdÄ«jÄs sarežģītÄks un apÄakarÄja maŔīnu.
Ko darÄ«t? ProblÄma tiek novÄrstaapÅemties, un skatiet aprakstu sadaÄ¼Ä atbrÄ«voÅ”anas ziÅojums), atjauninot Linux kodolu uz versiju 4.16.
StÄsts 3. Systemd un tÄ stiprinÄjums
Atkal, kubelet dažos mezglos patÄrÄ pÄrÄk daudz resursu, taÄu Å”oreiz tas ir vairÄk atmiÅas:
IzrÄdÄ«jÄs, ka ir problÄma ar Ubuntu 16.04 izmantoto sistÄmu, un tÄ rodas, pÄrvaldot stiprinÄjumus, kas izveidoti savienojuma izveidei subPath no ConfigMap'ov vai secret'ov. PÄc podiÅa izslÄgÅ”anas paliek sistÄmas pakalpojums un tÄ servisa stiprinÄjums sistÄmÄ. Laika gaitÄ tie uzkrÄjas milzÄ«gs daudzums. Par Å”o tÄmu ir pat problÄmas:
#!/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
... un tas darbojas ik pÄc 5 minÅ«tÄm ar iepriekÅ” minÄtÄ superkronika palÄ«dzÄ«bu. TÄ Dockerfile izskatÄs Å”Ädi:
Tika pamanÄ«ts, ka: ja mums ir uzlikts pods uz mezgla un tÄ attÄls tiek izsÅ«knÄts ļoti ilgi, tad cits pods, kas "trÄpÄ«ja" tajÄ paÅ”Ä mezglÄ vienkÄrÅ”i nesÄk vilkt jaunu pÄksts attÄlu. TÄ vietÄ tas gaida, lÄ«dz tiks āizvilktsā iepriekÅ”ÄjÄ aplikuma attÄls. RezultÄtÄ pods, kas jau bija ieplÄnots un kura attÄlu varÄja lejupielÄdÄt tikai minÅ«tes laikÄ, nonÄks statusÄ uz ilgu laiku. containerCreating.
PasÄkumi izskatÄ«sies apmÄram Å”Ädi:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"
IzrÄdÄs, ka viens attÄls no lÄna reÄ£istra var bloÄ·Ät izvietoÅ”anu uz mezglu.
DiemžÄl nav daudz izeju no situÄcijas:
MÄÄ£iniet izmantot savu Docker reÄ£istru tieÅ”i klasterÄ« vai tieÅ”i kopÄ ar klasteru (piemÄram, GitLab reÄ£istrs, Nexus utt.);
DažÄdu aplikÄciju darbÄ«bas laikÄ saÅÄmÄm arÄ« situÄciju, kad mezgls pilnÄ«bÄ pÄrstÄj bÅ«t pieejams: SSH nereaÄ£Ä, nokrÄ«t visi uzraudzÄ«bas dÄmoni, un tad žurnÄlos nav nekÄ (vai gandrÄ«z nekÄ) nenormÄla.
Es jums pastÄstÄ«Å”u attÄlos, izmantojot piemÄru vienam mezglam, kurÄ darbojÄs MongoDB.
Å Ädi izskatÄs virsotne lÄ«dz nelaimes gadÄ«jumi:
Un Å”Ädi - pÄc nelaimes gadÄ«jumi:
UzraudzÄ«bÄ ir arÄ« straujÅ” lÄciens, pie kura mezgls vairs nav pieejams:
TÄtad no ekrÄnuzÅÄmumiem varat redzÄt, ka:
IekÄrtas operatÄ«vÄ atmiÅa ir gandrÄ«z beigusies;
StraujÅ” lÄciens RAM patÄriÅÄ, pÄc kura piekļuve visai maŔīnai tiek pÄkÅ”Åi atspÄjota;
Mongo ierodas liels uzdevums, kas liek DBVS procesam izmantot vairÄk atmiÅas un aktÄ«vi lasÄ«t no diska.
IzrÄdÄs, ja Linux beidzas brÄ«vÄ atmiÅa (iestÄjas atmiÅas spiediens) un nav mijmaiÅas, tad lÄ«dz PÄc OOM slepkavas ieraÅ”anÄs var bÅ«t lÄ«dzsvars starp lapu iemetÄ«Å”anu lapas keÅ”atmiÅÄ un ierakstÄ«Å”anu atpakaļ diskÄ. To apstrÄdÄ kswapd, kas varonÄ«gi atbrÄ«vo pÄc iespÄjas vairÄk atmiÅas lappuÅ”u vÄlÄkai pieŔķirÅ”anai.
DiemžÄl ar lielu I/O slodzi kopÄ ar nelielu brÄ«vas atmiÅas daudzumu, kswapd kļūst par visas sistÄmas saÅ”aurinÄjumujo viÅi ir ar to saistÄ«ti viss atmiÅas lapu sadalÄ«jums (lapu defekti) sistÄmÄ. Tas var turpinÄties ļoti ilgi, ja procesi vairs nevÄlas izmantot atmiÅu, bet fiksÄjas uz OOM slepkavas bezdibena paÅ”as malas.
JautÄjums ir dabisks: kÄpÄc OOM slepkava nÄk tik vÄlu? PaÅ”reizÄjÄ iterÄcijÄ OOM killer ir ÄrkÄrtÄ«gi stulbs: tas nogalinÄs procesu tikai tad, ja mÄÄ£inÄjums pieŔķirt atmiÅas lapu neizdodas, t.i. ja lapas kļūda pÄriet ar kļūdu. Tas nenotiek pietiekami ilgi, jo kswapd varonÄ«gi atbrÄ«vo lappuses no atmiÅas, izskalojot lapu keÅ”atmiÅu (bÅ«tÄ«bÄ visu sistÄmas disku I/O) atpakaļ diskÄ. SÄ«kÄk varat izlasÄ«t ar aprakstu par darbÄ«bÄm, kas nepiecieÅ”amas, lai novÄrstu Å”Ädas kodola problÄmas Å”eit.
Dažos klasteros, kuros darbojas patieÅ”Äm liels skaits pÄkstÄ«m, mÄs sÄkÄm pamanÄ«t, ka lielÄkÄ daļa no tÄm karÄjas stÄvoklÄ« ļoti ilgu laiku. Pending, lai gan paÅ”i Docker konteineri jau darbojas mezglos, un jÅ«s varat ar tiem strÄdÄt manuÄli.
TurklÄt iekÅ”Ä describe nav nekÄ slikta:
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
PÄc rakÅ”anas mÄs izdarÄ«jÄm pieÅÄmumu, ka kubelet vienkÄrÅ”i nav laika nosÅ«tÄ«t API serverim visu informÄciju par podiÅu stÄvokli, dzÄ«vÄ«guma / gatavÄ«bas paraugiem.
Un, izpÄtot palÄ«dzÄ«bu, mÄs atradÄm Å”Ädus parametrus:
--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)
KÄ redzams, noklusÄjuma vÄrtÄ«bas ir diezgan mazas, un 90% tie sedz visas vajadzÄ«bas... TomÄr mÅ«su gadÄ«jumÄ ar to nepietika. TÄpÄc mÄs iestatÄm Å”Ädas vÄrtÄ«bas:
... un restartÄja kubelets, pÄc tam mÄs redzÄjÄm Å”Ädu attÄlu grafikos, lai piekļūtu API serverim:
... un jÄ, viss sÄka lidot!
PS
Par palÄ«dzÄ«bu kļūdu apkopoÅ”anÄ un raksta sagatavoÅ”anÄ es izsaku lielu pateicÄ«bu mÅ«su uzÅÄmuma daudzajiem inženieriem un jo Ä«paÅ”i mÅ«su kolÄÄ£im no mÅ«su pÄtniecÄ«bas un attÄ«stÄ«bas komandas Andrejam Klimentjevam (zuzzas).