ProHoster > Блог > басқарма > Kubernetes жұмысындағы 6 қызықты жүйелік қателер [және олардың шешімі]
Kubernetes жұмысындағы 6 қызықты жүйелік қателер [және олардың шешімі]
Kubernetes-ті өндірісте пайдаланған жылдар ішінде біз әртүрлі жүйе құрамдас бөліктеріндегі қателердің контейнерлер мен бұршақтардың жұмысына әсер ететін жағымсыз және/немесе түсініксіз салдарға әкелгені туралы көптеген қызықты оқиғаларды жинақтадық. Бұл мақалада біз ең көп таралған немесе қызықты нәрселердің кейбірін таңдадық. Мұндай жағдайларға кезікпесеңіз де, мұндай қысқа детективтер туралы оқу, әсіресе «бірінші қолмен» - әрқашан қызықты, солай емес пе?..
Әңгіме 1. Supercronic және Docker ілулі
Кластерлердің бірінде біз мезгіл-мезгіл кластердің қалыпты жұмысына кедергі келтіретін мұздатылған Docker алдық. Сонымен қатар, 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
…
Бұл қате туралы бізді ең қызықтыратыны мына хабарлама: pthread_create failed: No space left on device. Жылдам оқу құжаттама Докер процесті тоқтата алмайтынын түсіндірді, сондықтан ол мезгіл-мезгіл қатып қалады.
Мониторинг кезінде келесі сурет болып жатқан жағдайға сәйкес келеді:
Мәселе мынада: тапсырма суперкроникада іске қосылғанда, процесс одан пайда болады дұрыс аяқтай алмайды, айналады зомби.
ескерту: Дәлірек айтқанда, процестер cron тапсырмаларымен туындайды, бірақ суперкроника бастама жүйесі емес және оның балалары пайда болған процестерді «қабылдай» алмайды. SIGHUP немесе SIGTERM сигналдары көтерілгенде, олар еншілес процестерге берілмейді, нәтижесінде еншілес процестер аяқталмайды және зомби күйінде қалады. Мұның бәрі туралы көбірек оқи аласыз, мысалы, мына жерден мұндай мақала.
Мәселені шешудің бірнеше жолы бар:
Уақытша шешім ретінде - бір уақытта жүйедегі PID санын көбейтіңіз:
/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
Немесе суперкроникада тапсырмаларды тікелей емес, сонымен бірге іске қосыңыз тини, ол процестерді дұрыс аяқтай алады және зомбилерді тудырмайды.
2-әңгіме. Топты жою кезіндегі «Зомбилер».
Kubelet көп процессорды тұтына бастады:
Бұл ешкімге ұнамайды, сондықтан біз қаруландық Perf және мәселемен айналыса бастады. Тергеу нәтижелері келесідей болды:
Kubelet процессорлық уақытының үштен бірінен астамын барлық топтардан жад деректерін алуға жұмсайды:
Ядро әзірлеушілерінің жіберу тізімінде сіз таба аласыз мәселені талқылау. Қысқасы, мәселе мынаған келіп тіреледі: әртүрлі tmpfs файлдары және басқа ұқсас нәрселер жүйеден толығымен жойылмайды топты жою кезінде, деп аталатын memcg зомби. Ерте ме, кеш пе, олар бет кэшінен жойылады, бірақ серверде жад көп және ядро оларды жоюға уақыт жоғалтудың мәнін көрмейді. Сондықтан олар үйіліп жатыр. Неге бұл тіпті болып жатыр? Бұл үнемі жаңа жұмыс орындарын және олармен бірге жаңа подкасттарды жасайтын cron тапсырмалары бар сервер. Осылайша, олардағы контейнерлер үшін жаңа топтар құрылады, олар көп ұзамай жойылады.
Неліктен kubelet ішіндегі cAdvisor көп уақытты босқа өткізеді? Мұны қарапайым орындау арқылы байқау оңай time cat /sys/fs/cgroup/memory/memory.stat. Егер сау машинада операция 0,01 секундқа созылса, проблемалы cron02-де 1,2 секунд кетеді. Мәселе мынада, sysfs деректерін өте баяу оқитын cAdvisor зомби топтарында қолданылатын жадты есепке алуға тырысады.
Зомбилерді күштеп жою үшін біз LKML-де ұсынылған кэштерді тазалауға тырыстық: sync; echo 3 > /proc/sys/vm/drop_caches, - бірақ ядросы күрделірек болып шықты және көлікті соқтырды.
Енді не істеу керек? Мәселе шешілуде (міндеттеу, және сипаттаманы қараңыз хабарды шығару) Linux ядросын 4.16 нұсқасына жаңарту.
Тарих 3. Systemd және оның тірегі
Тағы да, kubelet кейбір түйіндерде тым көп ресурстарды тұтынады, бірақ бұл жолы ол тым көп жадты тұтынады:
Ubuntu 16.04 жүйесінде қолданылатын жүйеде ақаулық бар екені анықталды және ол қосылу үшін жасалған қондырғыларды басқару кезінде пайда болады. subPath ConfigMap немесе құпиядан. Бұршақ жұмысын аяқтағаннан кейін systemd қызметі және оның қызметтік қондырғысы қалады жүйеде. Уақыт өте келе олардың үлкен саны жинақталады. Бұл тақырыпта тіпті мәселелер бар:
...соның соңғысы systemd ішіндегі PR-ға қатысты: #7811 (жүйедегі мәселе - #7798).
Мәселе Ubuntu 18.04 нұсқасында енді жоқ, бірақ Ubuntu 16.04 пайдалануды жалғастырғыңыз келсе, осы тақырып бойынша біздің уақытша шешімімізді пайдалы деп таба аласыз.
#!/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
... және ол бұрын айтылған суперкрониканы пайдаланып 5 минут сайын жұмыс істейді. Оның Dockerfile келесідей көрінеді:
4-әңгіме. Бұршақтарды жоспарлау кезіндегі бәсекеге қабілеттілік
Байқағандай: егер бізде түйінге орналастырған поддон болса және оның кескіні өте ұзақ уақыт бойы сорылып тұрса, сол түйінге «соққы» басқа бір поддон жай ғана болады. жаңа бөтелкенің суретін тартуға кіріспейді. Оның орнына, ол алдыңғы бөлімнің суреті тартылғанша күтеді. Нәтижесінде, әлдеқашан жоспарланған және суретін бір минут ішінде жүктеп алуға болатын бөлім келесі күйде аяқталады. containerCreating.
Оқиғалар келесідей болады:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"
Көрсетіледі баяу тізілімдегі жалғыз сурет орналастыруды блоктай алады түйінге.
Өкінішке орай, жағдайдан шығудың көптеген жолдары жоқ:
Docker тізілімін тікелей кластерде немесе тікелей кластермен бірге пайдалануға тырысыңыз (мысалы, GitLab Registry, Nexus және т.б.);
Әртүрлі қосымшалардың жұмысы кезінде біз түйін толығымен қол жетімділікті тоқтататын жағдайға тап болдық: SSH жауап бермейді, барлық бақылау демондары құлап кетеді, содан кейін журналдарда аномальды ештеңе (немесе ештеңе дерлік) жоқ.
Мен MongoDB жұмыс істейтін бір түйіннің мысалын пайдаланып суреттерде айтып беремін.
Төбесі осылай көрінеді қарай апаттар:
Және осылай - после апаттар:
Мониторингте түйін қол жетімді болмайтын күрт секіріс бар:
Осылайша, скриншоттардан анық:
Құрылғыдағы жедел жады соңына жақын;
ЖЖҚ тұтынуында күрт секіру бар, содан кейін бүкіл машинаға қол жеткізу кенеттен ажыратылады;
Mongo-ға үлкен тапсырма келеді, ол ДҚБЖ процесін көбірек жадты пайдалануға және дискіден белсенді оқуға мәжбүр етеді.
Егер Linux-тың бос жады бітсе (жад қысымы орнатылса) және айырбастау болмаса, онда қарай OOM өлтірушісі келгенде, беттерді бет кэшіне тастау және оларды дискіге қайта жазу арасында теңдестіру әрекеті туындауы мүмкін. Бұл kswapd арқылы жасалады, ол кейіннен тарату үшін мүмкіндігінше көп жад беттерін босатады.
Өкінішке орай, үлкен енгізу/шығару жүктемесі және шағын бос жад көлемімен, kswapd бүкіл жүйенің тар мойнына айналады, өйткені олар оған байланған барлық жүйедегі жад беттерінің бөлінуі (бет ақаулары). Бұл процесстер жадты бұдан былай пайдаланғысы келмесе, бірақ OOM-өлтіруші тұңғиықтың ең шетінде бекітілген болса, бұл өте ұзақ уақытқа созылуы мүмкін.
Табиғи сұрақ: OOM өлтіруші неге кеш келеді? Қазіргі итерациясында OOM өлтірушісі өте ақымақ: ол жад бетін бөлу әрекеті сәтсіз болғанда ғана процесті жояды, яғни. егер бет қатесі орындалмаса. Бұл ұзақ уақыт бойы болмайды, өйткені kswapd жад беттерін батыл босатып, бет кэшін (жүйедегі бүкіл диск енгізу/шығару, шын мәнінде) дискіге қайтарады. Толығырақ, ядродағы осындай проблемаларды жою үшін қажетті қадамдардың сипаттамасымен сіз оқи аласыз осында.
Кейбір кластерлерде, оларда шын мәнінде көп жұмыс істейді, біз олардың көпшілігі штатта өте ұзақ уақыт «ілулі тұрғанын» байқадық. Pending, дегенмен Docker контейнерлерінің өзі түйіндерде жұмыс істеп тұр және олармен қолмен жұмыс істеуге болады.
Оның үстіне, ішке describe ештеңе жоқ:
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
Біраз іздегеннен кейін біз кубелеттің API серверіне подкасттардың күйі және өмірлік/дайындық сынақтары туралы барлық ақпаратты жіберуге уақыты жоқ деген болжам жасадық.
Ал анықтаманы зерттегеннен кейін біз келесі параметрлерді таптық:
--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)
Көріп отырғанымыздай, әдепкі мәндер өте аз, ал 90% олар барлық қажеттіліктерді өтейді... Алайда, біздің жағдайда бұл жеткіліксіз болды. Сондықтан біз келесі мәндерді орнаттық:
... және кубелеттерді қайта іске қостық, содан кейін API серверіне қоңыраулар графиктерінде келесі суретті көрдік:
... және иә, бәрі ұша бастады!
PS
Қателерді жинауға және осы мақаланы дайындауға көмектескені үшін мен компаниямыздың көптеген инженерлеріне, әсіресе ҒЗТКЖ тобымыздағы әріптесім Андрей Климентьевке үлкен алғысымды білдіремін (зузалар).