6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]
Dros y blynyddoedd o ddefnyddio Kubernetes wrth gynhyrchu, rydym wedi cronni llawer o straeon diddorol am sut mae bygiau mewn gwahanol gydrannau system wedi arwain at ganlyniadau annymunol a / neu annealladwy sy'n effeithio ar weithrediad cynwysyddion a chodennau. Yn yr erthygl hon rydym wedi gwneud detholiad o rai o'r rhai mwyaf cyffredin neu ddiddorol. Hyd yn oed os nad ydych byth yn ddigon ffodus i ddod ar draws sefyllfaoedd o’r fath, mae darllen am straeon ditectif byr o’r fath - yn enwedig “yn uniongyrchol” - bob amser yn ddiddorol, onid yw?..
Stori 1. Supercronic a Docker yn hongian
Ar un o'r clystyrau, cawsom Dociwr wedi'i rewi o bryd i'w gilydd, a oedd yn ymyrryd â gweithrediad arferol y clwstwr. Ar yr un pryd, gwelwyd y canlynol yn logiau'r 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
…
Yr hyn sydd o ddiddordeb i ni fwyaf am y gwall hwn yw'r neges: pthread_create failed: No space left on device. Astudio Cyflym dogfennaeth eglurodd na allai Docker fforchio proses, a dyna pam y mae'n rhewi o bryd i'w gilydd.
Wrth fonitro, mae'r darlun canlynol yn cyfateb i'r hyn sy'n digwydd:
Mae'n troi allan bod yr ymddygiad hwn yn ganlyniad i'r pod yn gweithio gyda uwchcronig (cyfleuster Go a ddefnyddiwn i redeg swyddi cron mewn codennau):
Y broblem yw hyn: pan fydd tasg yn cael ei rhedeg mewn uwchgronig, mae'r broses yn silio ganddo methu terfynu yn gywir, yn troi i mewn zombie.
Nodyn: I fod yn fwy manwl gywir, mae prosesau'n cael eu silio gan dasgau cron, ond nid yw uwchgronig yn system init ac ni all “fabwysiadu” prosesau y mae ei blant yn eu silio. Pan godir signalau SIGHUP neu SITERM, nid ydynt yn cael eu trosglwyddo i'r prosesau plentyn, gan arwain at y prosesau plentyn yn peidio â therfynu ac yn aros mewn statws zombie. Gallwch ddarllen mwy am hyn i gyd, er enghraifft, yn erthygl o'r fath.
Mae dwy ffordd i ddatrys problemau:
Fel ateb dros dro - cynyddwch nifer y PIDs yn y system ar un adeg:
/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
Neu lansio tasgau yn supercronic nid yn uniongyrchol, ond gan ddefnyddio'r un peth tini, sy'n gallu terfynu prosesau yn gywir a pheidio â silio zombies.
Stori 2. “Zombies” wrth ddileu cgroup
Dechreuodd Kubelet ddefnyddio llawer o CPU:
Fydd neb yn hoffi hyn, felly fe wnaethon ni arfogi ein hunain perff a dechreuodd ddelio â'r broblem. Roedd canlyniadau’r ymchwiliad fel a ganlyn:
Mae Kubelet yn treulio mwy na thraean o'i amser CPU yn tynnu data cof o bob cgroup:
Yn y rhestr bostio datblygwyr cnewyllyn gallwch ddod o hyd trafodaeth ar y broblem. Yn fyr, daw'r pwynt i lawr i hyn: nid yw ffeiliau tmpfs amrywiol a phethau tebyg eraill yn cael eu tynnu'n llwyr o'r system wrth ddileu cgroup, yr hyn a elwir memcg zombie. Yn hwyr neu'n hwyrach byddant yn cael eu dileu o storfa'r dudalen, ond mae llawer o gof ar y gweinydd ac nid yw'r cnewyllyn yn gweld pwynt gwastraffu amser ar eu dileu. Dyna pam maen nhw'n dal i bentyrru. Pam fod hyn hyd yn oed yn digwydd? Mae hwn yn weinydd gyda swyddi cron sy'n creu swyddi newydd yn gyson, a gyda nhw codennau newydd. Felly, crëir cgroups newydd ar gyfer cynwysyddion ynddynt, sy'n cael eu dileu yn fuan.
Pam mae cAdvisor mewn kubelet yn gwastraffu cymaint o amser? Mae hyn yn hawdd i'w weld gyda'r gweithredu symlaf time cat /sys/fs/cgroup/memory/memory.stat. Os yw'r llawdriniaeth yn cymryd 0,01 eiliad ar beiriant iach, yna ar y cron02 problemus mae'n cymryd 1,2 eiliad. Y peth yw bod cAdvisor, sy'n darllen data o sysfs yn araf iawn, yn ceisio cymryd i ystyriaeth y cof a ddefnyddir mewn cgroups zombie.
Er mwyn cael gwared â zombies yn rymus, fe wnaethom geisio clirio caches fel yr argymhellir yn LKML: sync; echo 3 > /proc/sys/vm/drop_caches, - ond trodd y cnewyllyn yn fwy cymhleth a chwalodd y car.
Beth i'w wneud? Mae'r broblem yn cael ei datrys (ymrwymo, ac am ddisgrifiad gw rhyddhau neges) diweddaru'r cnewyllyn Linux i fersiwn 4.16.
Hanes 3. Systemd a'i fynydd
Unwaith eto, mae'r kubelet yn defnyddio gormod o adnoddau ar rai nodau, ond y tro hwn mae'n cymryd gormod o gof:
Mae'n troi allan bod yna broblem mewn systemd a ddefnyddir yn Ubuntu 16.04, ac mae'n digwydd wrth reoli mowntiau sy'n cael eu creu ar gyfer cysylltiad subPath o ConfigMaps neu gyfrinachau. Ar ôl i'r pod orffen ei waith mae'r gwasanaeth systemd a'i mount gwasanaeth yn parhau mewn system. Dros amser, mae nifer fawr ohonynt yn cronni. Mae hyd yn oed problemau ar y pwnc hwn:
...mae'r olaf yn cyfeirio at y cysylltiadau cyhoeddus yn systemd: #7811 (mater yn systemd - #7798).
Nid yw'r broblem yn bodoli bellach yn Ubuntu 18.04, ond os ydych chi am barhau i ddefnyddio Ubuntu 16.04, efallai y bydd ein datrysiad ar y pwnc hwn yn ddefnyddiol i chi.
#!/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
... ac mae'n rhedeg bob 5 munud gan ddefnyddio'r supercronic a grybwyllwyd yn flaenorol. Mae ei Dockerfile yn edrych fel hyn:
Sylwyd: os oes gennym god wedi'i osod ar nod a bod ei ddelwedd yn cael ei bwmpio allan am amser hir iawn, yna bydd pod arall sy'n “taro” yr un nod yn syml. ddim yn dechrau tynnu delwedd y pod newydd. Yn lle hynny, mae'n aros nes bod delwedd y pod blaenorol yn cael ei dynnu. O ganlyniad, bydd pod a oedd eisoes wedi'i amserlennu ac y gallai ei ddelwedd fod wedi'i lawrlwytho mewn dim ond munud yn dod i ben yn statws containerCreating.
Bydd y digwyddiadau yn edrych fel hyn:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"
Mae'n ymddangos bod gall delwedd sengl o gofrestrfa araf rwystro defnydd y nod.
Yn anffodus, nid oes llawer o ffyrdd allan o'r sefyllfa:
Ceisiwch ddefnyddio'ch Cofrestrfa Docker yn uniongyrchol yn y clwstwr neu'n uniongyrchol gyda'r clwstwr (er enghraifft, Cofrestrfa GitLab, Nexus, ac ati);
Yn ystod gweithrediad amrywiol gymwysiadau, rydym hefyd wedi dod ar draws sefyllfa lle mae nod yn peidio â bod yn hygyrch yn llwyr: nid yw SSH yn ymateb, mae'r holl ddaemonau monitro yn disgyn i ffwrdd, ac yna nid oes dim (neu bron dim) yn anghyson yn y logiau.
Fe ddywedaf wrthych mewn lluniau gan ddefnyddio'r enghraifft o un nod lle roedd MongoDB yn gweithredu.
Dyma sut olwg sydd ar ben i damweiniau:
Ac fel hyn - ar ôl damweiniau:
Wrth fonitro, mae naid sydyn hefyd, lle mae'r nod yn peidio â bod ar gael:
Felly, o'r sgrinluniau mae'n amlwg:
Mae'r RAM ar y peiriant yn agos at y diwedd;
Mae naid sydyn yn y defnydd o RAM, ac ar ôl hynny mae mynediad i'r peiriant cyfan wedi'i analluogi'n sydyn;
Mae tasg fawr yn cyrraedd Mongo, sy'n gorfodi'r broses DBMS i ddefnyddio mwy o gof a darllen yn weithredol o ddisg.
Mae'n troi allan, os yw Linux yn rhedeg allan o gof am ddim (pwysau cof yn gosod i mewn) ac nad oes cyfnewid, yna i Pan fydd y llofrudd OOM yn cyrraedd, efallai y bydd gweithred gydbwyso yn codi rhwng taflu tudalennau i mewn i'r storfa dudalen a'u hysgrifennu yn ôl i ddisg. Gwneir hyn gan kswapd, sy'n rhyddhau cymaint o dudalennau cof â phosibl i'w dosbarthu wedyn.
Yn anffodus, gyda llwyth I / O mawr ynghyd ag ychydig bach o gof am ddim, kswapd yn dod yn dagfa'r system gyfan, oherwydd eu bod yn gysylltiedig ag ef holl dyraniadau (diffygion tudalennau) o dudalennau cof yn y system. Gall hyn fynd ymlaen am amser hir iawn os nad yw'r prosesau eisiau defnyddio cof mwyach, ond eu bod wedi'u gosod ar ymyl yr affwys lladd OOM.
Y cwestiwn naturiol yw: pam mae'r llofrudd OOM yn dod mor hwyr? Yn ei iteriad presennol, mae'r llofrudd OOM yn hynod o dwp: bydd yn lladd y broses dim ond pan fydd yr ymgais i ddyrannu tudalen cof yn methu, h.y. os bydd nam ar y dudalen yn methu. Nid yw hyn yn digwydd am amser eithaf hir, oherwydd mae kswapd yn rhyddhau tudalennau cof yn ddewr, gan ddympio storfa'r dudalen (y ddisg gyfan I/O yn y system, mewn gwirionedd) yn ôl i'r ddisg. Yn fwy manwl, gyda disgrifiad o'r camau sydd eu hangen i ddileu problemau o'r fath yn y cnewyllyn, gallwch ddarllen yma.
Yr ymddygiad hwn ddylai wella gyda chnewyllyn Linux 4.6+.
Stori 6. Podiau'n mynd yn sownd yn yr arfaeth
Mewn rhai clystyrau, lle mae llawer o godennau'n gweithredu mewn gwirionedd, dechreuon ni sylwi bod y mwyafrif ohonyn nhw'n “hongian” yn y wladwriaeth am amser hir iawn. Pending, er bod y cynwysyddion Docker eu hunain eisoes yn rhedeg ar y nodau a gellir gweithio gyda nhw â llaw.
Ar ben hynny, yn describe does dim byd o'i le:
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
Ar ôl rhywfaint o gloddio, gwnaethom y dybiaeth nad oes gan y kubelet amser i anfon yr holl wybodaeth am gyflwr y codennau a phrofion bywiogrwydd / parodrwydd i'r gweinydd API.
Ac ar ôl astudio cymorth, canfuwyd y paramedrau canlynol:
--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)
Fel y gwelir, gwerthoedd diofyn yn eithaf bach, ac mewn 90% maent yn cwmpasu'r holl anghenion... Fodd bynnag, yn ein hachos ni nid oedd hyn yn ddigon. Felly, rydym yn gosod y gwerthoedd canlynol:
... ac ailgychwyn y kubelets, ac ar ôl hynny gwelsom y llun canlynol yn y graffiau o alwadau i'r gweinydd API:
... a do, dechreuodd popeth hedfan!
PS
Am eu cymorth i gasglu chwilod a pharatoi'r erthygl hon, mynegaf fy niolch dwfn i beirianwyr niferus ein cwmni, ac yn enwedig i'm cydweithiwr o'n tîm Ymchwil a Datblygu Andrey Klimentyev (swzzas).