ProHoster > Blog > Pangangasiwa > 6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]
6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]
Sa paglipas ng mga taon ng paggamit ng Kubernetes sa produksyon, nakaipon kami ng maraming kawili-wiling kwento kung paano humantong ang mga bug sa iba't ibang bahagi ng system sa hindi kasiya-siya at/o hindi maintindihan na mga kahihinatnan na nakakaapekto sa pagpapatakbo ng mga container at pod. Sa artikulong ito nakagawa kami ng seleksyon ng ilan sa mga pinakakaraniwan o kawili-wili. Kahit na hindi ka mapalad na makatagpo ng mga ganitong sitwasyon, ang pagbabasa tungkol sa mga maiikling kwento ng tiktik - lalo na ang "first-hand" - ay palaging kawili-wili, hindi ba?..
Kuwento 1. Supercronic at Docker na nakabitin
Sa isa sa mga kumpol, pana-panahon kaming nakatanggap ng isang nakapirming Docker, na nakakasagabal sa normal na paggana ng kumpol. Kasabay nito, ang mga sumusunod ay naobserbahan sa mga log ng 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
β¦
Ang pinaka-interesante sa amin tungkol sa error na ito ay ang mensahe: pthread_create failed: No space left on device. Mabilis na Pag-aaral dokumentasyon ipinaliwanag na hindi maaaring i-fork ng Docker ang isang proseso, kaya naman pana-panahon itong nagyelo.
Sa pagsubaybay, ang sumusunod na larawan ay tumutugma sa kung ano ang nangyayari:
Ang isang katulad na sitwasyon ay sinusunod sa iba pang mga node:
Ito ay lumabas na ang pag-uugali na ito ay bunga ng pod na nagtatrabaho sa supercronic (isang Go utility na ginagamit namin para magpatakbo ng mga cron job sa mga pod):
Ang problema ay ito: kapag ang isang gawain ay pinapatakbo sa supercronic, ang proseso ay nabuo nito hindi maaaring wakasan ng tama, nagiging zombie.
Nota: Upang maging mas tumpak, ang mga proseso ay pinamumunuan ng mga gawain ng cron, ngunit ang supercronic ay hindi isang init system at hindi maaaring "mag-ampon" ng mga proseso na iniluwal ng mga anak nito. Kapag nakataas ang mga signal ng SIGHUP o SIGTERM, hindi ito naipapasa sa mga proseso ng bata, na nagreresulta sa hindi pagwawakas ng mga proseso ng bata at nananatili sa status ng zombie. Maaari kang magbasa nang higit pa tungkol sa lahat ng ito, halimbawa, sa ganoong artikulo.
Mayroong ilang mga paraan upang malutas ang mga problema:
Bilang pansamantalang solusyon - dagdagan ang bilang ng mga PID sa system sa isang punto ng oras:
/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
O ilunsad ang mga gawain sa supercronic hindi direkta, ngunit gamit ang pareho tini, na magagawang wakasan ang mga proseso nang tama at hindi mag-spawn ng mga zombie.
Kuwento 2. "Mga Zombie" kapag nagde-delete ng cgroup
Ang Kubelet ay nagsimulang gumamit ng maraming CPU:
Walang magugustuhan nito, kaya armado kami perpekto at nagsimulang harapin ang problema. Ang mga resulta ng imbestigasyon ay ang mga sumusunod:
Gumugugol ang Kubelet ng higit sa isang katlo ng oras ng CPU nito sa pagkuha ng data ng memorya mula sa lahat ng cgroup:
Sa mailing list ng mga developer ng kernel mahahanap mo pagtalakay sa suliranin. Sa madaling salita, ang punto ay bumababa dito: iba't ibang mga tmpfs file at iba pang katulad na mga bagay ay hindi ganap na naalis sa system kapag nagde-delete ng cgroup, ang tinatawag na memcg sombi. Maaga o huli sila ay tatanggalin mula sa cache ng pahina, ngunit mayroong maraming memorya sa server at hindi nakikita ng kernel ang punto sa pag-aaksaya ng oras sa pagtanggal ng mga ito. Kaya naman patuloy silang nagtatambak. Bakit nangyayari pa ito? Ito ay isang server na may mga cron job na patuloy na lumilikha ng mga bagong trabaho, at kasama nila ang mga bagong pod. Kaya, ang mga bagong cgroup ay nilikha para sa mga lalagyan sa mga ito, na malapit nang matanggal.
Bakit nag-aaksaya ng maraming oras ang cAdvisor sa kubelet? Ito ay madaling makita sa pinakasimpleng pagpapatupad time cat /sys/fs/cgroup/memory/memory.stat. Kung sa isang malusog na makina ang operasyon ay tumatagal ng 0,01 segundo, pagkatapos ay sa may problemang cron02 ito ay tumatagal ng 1,2 segundo. Ang bagay ay ang cAdvisor, na nagbabasa ng data mula sa sysfs nang napakabagal, ay sumusubok na isaalang-alang ang memorya na ginagamit sa mga zombie cgroup.
Upang piliting alisin ang mga zombie, sinubukan naming i-clear ang mga cache gaya ng inirerekomenda sa LKML: sync; echo 3 > /proc/sys/vm/drop_caches, - ngunit ang kernel ay naging mas kumplikado at nag-crash sa kotse.
Anong gagawin? Ang problema ay inaayos (mangako, at para sa isang paglalarawan tingnan maglabas ng mensahe) pag-update ng Linux kernel sa bersyon 4.16.
Kasaysayan 3. Systemd at ang mount nito
Muli, ang kubelet ay kumokonsumo ng napakaraming mapagkukunan sa ilang mga node, ngunit sa pagkakataong ito ito ay gumagamit ng masyadong maraming memorya:
Ito ay lumabas na may problema sa systemd na ginamit sa Ubuntu 16.04, at ito ay nangyayari kapag pinamamahalaan ang mga mount na nilikha para sa koneksyon subPath mula sa ConfigMap's o secret's. Matapos makumpleto ng pod ang trabaho nito mananatili ang systemd service at ang service mount nito sa sistema. Sa paglipas ng panahon, ang isang malaking bilang ng mga ito ay naipon. Mayroong kahit na mga isyu sa paksang ito:
...ang huli ay tumutukoy sa PR sa systemd: #7811 (isyu sa systemd - #7798).
Ang problema ay wala na sa Ubuntu 18.04, ngunit kung gusto mong magpatuloy sa paggamit ng Ubuntu 16.04, maaari mong makitang kapaki-pakinabang ang aming solusyon sa paksang ito.
#!/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
... at ito ay tumatakbo tuwing 5 minuto gamit ang naunang nabanggit na supercronic. Ang Dockerfile nito ay ganito ang hitsura:
Kuwento 4. Pagiging mapagkumpitensya kapag nag-iiskedyul ng mga pod
Napansin na: kung mayroon kaming isang pod na nakalagay sa isang node at ang imahe nito ay na-pump out nang napakatagal, pagkatapos ay isa pang pod na "hit" sa parehong node ay simpleng ay hindi nagsisimulang hilahin ang imahe ng bagong pod. Sa halip, naghihintay ito hanggang sa ma-pull ang imahe ng nakaraang pod. Bilang resulta, ang isang pod na nakaiskedyul na at kung saan ang larawan ay maaaring ma-download sa loob lamang ng isang minuto ay mapupunta sa katayuan ng containerCreating.
Magiging ganito ang hitsura ng mga kaganapan:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"
Ito ay lumiliko ang na ang isang imahe mula sa isang mabagal na pagpapatala ay maaaring harangan ang pag-deploy bawat node.
Sa kasamaang palad, walang maraming paraan sa labas ng sitwasyon:
Subukang gamitin ang iyong Docker Registry nang direkta sa cluster o direkta sa cluster (halimbawa, GitLab Registry, Nexus, atbp.);
Kuwento 5. Nakasabit ang mga node dahil sa kakulangan ng memorya
Sa panahon ng pagpapatakbo ng iba't ibang mga application, nakatagpo din kami ng isang sitwasyon kung saan ang isang node ay ganap na hindi na ma-access: SSH ay hindi tumugon, lahat ng mga daemon sa pagsubaybay ay nahuhulog, at pagkatapos ay wala (o halos walang) anomalya sa mga log.
Sasabihin ko sa iyo sa mga larawan gamit ang halimbawa ng isang node kung saan gumana ang MongoDB.
Ito ang hitsura ng nasa ibabaw sa aksidente:
At tulad nito - pagkatapos aksidente:
Sa pagsubaybay, mayroon ding isang matalim na pagtalon, kung saan ang node ay hindi na magagamit:
Kaya, mula sa mga screenshot ay malinaw na:
Ang RAM sa makina ay malapit sa dulo;
Mayroong isang matalim na pagtalon sa pagkonsumo ng RAM, pagkatapos kung saan ang pag-access sa buong makina ay biglang hindi pinagana;
Dumating ang isang malaking gawain sa Mongo, na pinipilit ang proseso ng DBMS na gumamit ng mas maraming memorya at aktibong magbasa mula sa disk.
Ito ay lumiliko na kung ang Linux ay naubusan ng libreng memorya (memory pressure set in) at walang swap, kung gayon sa Kapag dumating ang OOM killer, maaaring magkaroon ng pagbabalanse sa pagitan ng paghagis ng mga pahina sa cache ng pahina at pagsusulat ng mga ito pabalik sa disk. Ginagawa ito ng kswapd, na buong tapang na nagpapalaya ng maraming mga pahina ng memorya hangga't maaari para sa kasunod na pamamahagi.
Sa kasamaang palad, na may malaking I/O load na kasama ng maliit na halaga ng libreng memory, Ang kswapd ay nagiging bottleneck ng buong system, dahil nakatali sila dito lahat mga alokasyon (page faults) ng mga memory page sa system. Maaari itong magpatuloy nang napakatagal kung ang mga proseso ay hindi na gustong gumamit ng memorya, ngunit naayos sa pinakadulo ng OOM-killer abyss.
Ang natural na tanong ay: bakit nahuhuli ang pumapatay sa OOM? Sa kasalukuyang pag-ulit nito, ang OOM killer ay lubhang hangal: papatayin lamang nito ang proseso kapag nabigo ang pagtatangkang maglaan ng memory page, i.e. kung nabigo ang page fault. Hindi ito nangyayari sa loob ng mahabang panahon, dahil ang kswapd ay buong tapang na nagpapalaya sa mga pahina ng memorya, na nagtatapon ng cache ng pahina (ang buong disk I/O sa system, sa katunayan) pabalik sa disk. Sa mas detalyado, na may isang paglalarawan ng mga hakbang na kinakailangan upang maalis ang mga naturang problema sa kernel, maaari mong basahin dito.
Kuwento 6. Natigil ang mga pod sa Nakabinbing estado
Sa ilang mga kumpol, kung saan mayroong talagang maraming mga pod na tumatakbo, nagsimula kaming mapansin na karamihan sa kanila ay "nakabitin" nang napakatagal sa estado. Pending, kahit na ang mga lalagyan ng Docker mismo ay tumatakbo na sa mga node at maaaring gamitin nang manu-mano.
Kasabay nito, sa describe walang mali:
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
Pagkatapos ng ilang paghuhukay, ginawa namin ang pagpapalagay na ang kubelet ay walang oras para ipadala ang lahat ng impormasyon tungkol sa estado ng mga pod at liveness/readiness test sa API server.
At pagkatapos ng pag-aaral ng tulong, nakita namin ang mga sumusunod na parameter:
--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)
Tulad ng nakikita, ang mga default na halaga ay medyo maliit, at sa 90% sinasaklaw nila ang lahat ng pangangailangan... Gayunpaman, sa aming kaso hindi ito sapat. Samakatuwid, itinakda namin ang mga sumusunod na halaga:
... at i-restart ang mga kubelets, pagkatapos ay nakita namin ang sumusunod na larawan sa mga graph ng mga tawag sa API server:
... at oo, nagsimulang lumipad ang lahat!
PS
Para sa kanilang tulong sa pagkolekta ng mga bug at paghahanda ng artikulong ito, ipinapahayag ko ang aking matinding pasasalamat sa maraming mga inhinyero ng aming kumpanya, at lalo na sa aking kasamahan mula sa aming R&D team na si Andrey Klimentyev (zuzzas).