![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/bed059552ed86580939aa18fbdf1553e.jpg)
Aastate jooksul, mil me oleme Kubernetes't tootmises kasutanud, on meil kogunenud palju huvitavaid lugusid, kuidas erinevate sĂŒsteemikomponentide vead on toonud kaasa ebameeldivaid ja/vĂ”i arusaamatuid tagajĂ€rgi konteinerite ja pod'ide töös. Selles artiklis oleme vĂ€lja valinud mĂ”ned kĂ”ige sagedamad vĂ”i huvitavamad neist. Isegi kui te kunagi ei satuks selliste olukordadega kokku, on selliste lĂŒhikeste detektiivlugude lugemine - eriti «esmakĂ€eliselt» - alati huvitav, eks ole?...
Lugu 1. Supercronic ja kinni jÀÀnud Docker
Ăhel meie klastritest said me aeg-ajalt «kinni jÀÀnud» Dockerit, mis takistas klastrite normaalset toimimist. Samal ajal oli Docker'i logides jĂ€rgmine teade
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
⊠Selles veas huvitab meid enim sĂ”num: pthread_create failed: No space left on device. Kiire ĂŒlevaade selgitas, et Docker ei suuda protsessi forkida, mistĂ”ttu see aeg-ajalt "hangub".
JÀlgitavale vastab jÀrgmine pilt:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/bd778052c87b338493bae54b26830ef3.jpg)
Sarnast olukorda tÀheldatakse ka teistel sÔlmedel:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/ef512532a95ca982e4342071115dbe9f.jpg)
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/43c32ebca78755dde348ed5e7ac75c79.jpg)
NÀeme samadel sÔlmedel:
root@kube-node-1 ~ # ps auxfww | grep curl -c
19782
root@kube-node-1 ~ # ps auxfww | grep curl | head
root 16688 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 17398 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 16852 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 9473 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 4664 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 30571 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 24113 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 16475 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 7176 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]
root 1090 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl]Selgus, et selline kĂ€itumine on pod'i töö tagajĂ€rg, (Go utiliit, mida kasutame cron-ĂŒlesannete kĂ€ivitamiseks pod'ides):
_ docker-containerd-shim 833b60bb9ff4c669bb413b898a5fd142a57a21695e5dc42684235df907825567 /var/run/docker/libcontainerd/833b60bb9ff4c669bb413b898a5fd142a57a21695e5dc42684235df907825567 docker-runc
| _ /usr/local/bin/supercronic -json /crontabs/cron
| _ /usr/bin/newrelic-daemon --agent --pidfile /var/run/newrelic-daemon.pid --logfile /dev/stderr --port /run/newrelic.sock --tls --define utilization.detect_aws=true --define utilization.detect_azure=true --define utilization.detect_gcp=true --define utilization.detect_pcf=true --define utilization.detect_docker=true
| | _ /usr/bin/newrelic-daemon --agent --pidfile /var/run/newrelic-daemon.pid --logfile /dev/stderr --port /run/newrelic.sock --tls --define utilization.detect_aws=true --define utilization.detect_azure=true --define utilization.detect_gcp=true --define utilization.detect_pcf=true --define utilization.detect_docker=true -no-pidfile
| _ [newrelic-daemon]
| _ [curl]
| _ [curl]
| _ [curl]
âŠProbleem on jĂ€rgmine: kui ĂŒlesanne kĂ€ivitub supercronic'is, siis sellele jĂ€rgnev protsess ei saa korrektselt lĂ”petada, muutudes .
MĂ€rkus: TĂ€psemalt öeldes, protsessid tekivad cron-ĂŒlesannetest, kuid supercronic ei ole init-sĂŒsteem ja ei saa âkasvatadaâ protsesse, mille ta on sĂŒnnitanud. Kui ilmnevad SIGHUP vĂ”i SIGTERM signaalid, ei edastata neid loodud protsessidele, mistĂ”ttu allprotsessid ei lĂ”peta tegevust ja jÀÀvad zombistaatusesse. Selle kohta saab rohkem lugeda nĂ€iteks .
Probleemide lahendamiseks on paar vÔimalust:
- Ajutise lahendusena â suurendada sĂŒsteemi PID'ide arvu korraga:
/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Ă”i kĂ€ivitada ĂŒlesanded supercronicis mitte otse, vaid sama , mis suudab protsesse Ă”igesti lĂ”petada ja ei tekita zombie'id.
Lugu 2. «Zombid» cgroup'i eemaldamisel
Kubelet hakkas kasutama suurt hulka CPU-d:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/6140058330faaa3785b089dcba857056.jpg)
Sellest ei saa keegi vaimustuda, seega varustasime end ja hakkasime probleemi uurima. Uurimise tulemused olid jÀrgmised:
- Kubelet kulutab ĂŒle kolmandiku protsessoriajast, et tĂ”mmata mĂ€luandmeid kĂ”igist cgroup'idest:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%20241%22%3E%3C%2Fsvg%3E)
- Kernal arendajate postitusse saab leida . Ătlemata lĂŒhidalt, seisneb asi selles, et erinevad tmpfs-failid ja teised sarnased asjad ei kustutata sĂŒsteemist tĂ€ielikult cgroup'i eemaldamisel â jÀÀvad ellu nn zombid. Varakult vĂ”i hiljem nad siiski kustutatakse page cache'ist, kuid serveris on palju mĂ€lu ja tuuma ei nĂ€e vajadust nende kustutamiseks aega raisata. SeetĂ”ttu nad jĂ€tkavad kogunemist. Miks see ĂŒldse juhtub? See on server cron-töödega, mis pidevalt loob uusi tĂ¶Ă¶ĂŒlesandeid ja nendega â uusi pod'e. Nii luuakse konteineritele uusi cgroup'e, mis peagi kustutatakse.
- Miks cAdvisor kubelet'is kulutab nii palju aega? Seda on kerge nÀha jÀrgmise lihtsa kÀsu tÀitmisega
time cat /sys/fs/cgroup/memory/memory.stat. Kui terves masinas vĂ”tab operatsioon 0,01 sekundit, siis probleemses cron02-s â 1,2 sekundit. KĂ”ik on selles, et cAdvisor loeb andmeid sysf'st vĂ€ga aeglaselt ja pĂŒĂŒab arvesse vĂ”tta kasutatud mĂ€lu zombie cgroup'ides. - Et sunniviisiliselt eemaldada zombi, proovisime puhastada vahemĂ€lu, nagu soovitati LKML-is:
sync; echo 3 > /proc/sys/vm/drop_caches, â kuid kernel osutus keerulisemaks ja hangestas masina.
Mida siis teha? Probleem lahendatakse (, ning kirjeldust vaata ) uuendades Linuxi tuuma versioonile 4.16.
Lugu 3. Systemd ja selle mount
JĂ€llegi kubelet tarbib liiga palju ressursse mĂ”nel sĂ”lmel, kuid seekord â juba mĂ€lu:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/044c4e23a772c61a6206b9b20aa67c1d.jpg)
Tuvastati, et Ubuntu 16.04-s kasutatavas systemd-s on probleem, mis ilmneb mountâide haldamisel, mis luuakse ConfigMapâide vĂ”i secretâite ĂŒhendamiseks. subPath PĂ€rast podâi lĂ”petamist jÀÀb systemd teenus ja selle teenuse mount sĂŒsteemi. Aja jooksul koguneb neid tohutult palju. Selle kohta on isegi kĂŒsimusi:
- ;
- .
⊠viidates viimases PR-le systemd-s: (issue systemd-s â ).
Probleemi ei ole enam Ubuntu 18.04-s, kuid kui soovite jĂ€tkuvalt kasutada Ubuntu 16.04, vĂ”ib meie töö ĂŒmber selle olla teile kasulik.
Nii et koostasime jÀrgmise DaemonSeti:
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
labels:
app: systemd-slices-cleaner
name: systemd-slices-cleaner
namespace: kube-system
spec:
updateStrategy:
type: RollingUpdate
selector:
matchLabels:
app: systemd-slices-cleaner
template:
metadata:
labels:
app: systemd-slices-cleaner
spec:
containers:
- command:
- /usr/local/bin/supercronic
- -json
- /app/crontab
Image: private-registry.org/systemd-slices-cleaner/systemd-slices-cleaner:v0.1.0
imagePullPolicy: Always
name: systemd-slices-cleaner
resources: {}
securityContext:
privileged: true
volumeMounts:
- name: systemd
mountPath: /run/systemd/private
- name: docker
mountPath: /run/docker.sock
- name: systemd-etc
mountPath: /etc/systemd
- name: systemd-run
mountPath: /run/systemd/system/
- name: lsb-release
mountPath: /etc/lsb-release-host
imagePullSecrets:
- name: antiopa-registry
priorityClassName: cluster-low
tolerations:
- operator: Exists
volumes:
- name: systemd
hostPath:
path: /run/systemd/private
- name: docker
hostPath:
path: /run/docker.sock
- name: systemd-etc
hostPath:
path: /etc/systemd
- name: systemd-run
hostPath:
path: /run/systemd/system/
- name: lsb-release
hostPath:
path: /etc/lsb-release⊠ja seda kasutatakse sellise skripti jaoks:
#!/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⊠ja seda kÀivitatakse iga 5 minuti jÀrel juba varem mainitud supercronic abil. Selle Dockerfile nÀeb vÀlja jÀrgmiselt:
FROM ubuntu:16.04
COPY rootfs /
WORKDIR /app
RUN apt-get update &&
apt-get upgrade -y &&
apt-get install -y gnupg curl apt-transport-https software-properties-common wget
RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" &&
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - &&
apt-get update &&
apt-get install -y docker-ce=17.03.0*
RUN wget https://github.com/aptible/supercronic/releases/download/v0.1.6/supercronic-linux-amd64 -O
/usr/local/bin/supercronic && chmod +x /usr/local/bin/supercronic
ENTRYPOINT ["/bin/bash", "-c", "/usr/local/bin/supercronic -json /app/crontab"]Lugu 4. Konkurents podâide planeerimisel
On mĂ€rgatud, et: kui meil on nodeâis pod ja selle pilt allalaadimine kestab vĂ€ga kaua, siis teine pod, mis sattus samale nodeâile, lihtsalt ei alusta uue podâi pilti allalaadimist. Selle asemel ootab ta, kuni eelmise podâi pilt on allalaaditud. Tulemuseks on see, et pod, mis on juba planeeritud ja mille pilt saaks alla laadida vaid minutiga, jÀÀb pikaks ajaks staatusesse containerCreating.
Eventâides on enam-vĂ€hem jĂ€rgmist:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"Selgub, et ainus halb pilt aeglasest registrist vĂ”ib blokeerida juurutuse nodeâile.
Kahjuks ei ole olukorrast vÀljapÀÀse palju:
- PĂŒĂŒdke kasutada oma Docker Registry otse klastris vĂ”i otse koos klastriga (nĂ€iteks GitLab Registry, Nexus jne);
- Kasutage selliseid utiliite nagu .
Lugu 5. SÔlmede seiskumine mÀlupuudusel
Erinevate rakenduste kasutamise ajal oleme samuti kogenud olukordi, kus sÔlm ei ole enam kergesti ligipÀÀsetav: SSH ei vasta, kÔik jÀlgimise deemonid kukuvad vÀlja ja logides ei ole seejuures midagi (vÔi peaaegu mitte midagi) anomaalset.
Taas jutustan piltide nĂ€itel ĂŒhest sĂ”lmest, kus töötas MongoDB.
Nii nÀeb atop vÀlja kuni Ônnetused:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/5de916d270a862cbcbb5ed23c31f698e.jpg)
Ja nii â pĂ€rast Ă”nnetused:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/0f32bf1113204cf19f4639a297e40348.jpg)
JÀlgimises on samuti nÀha jÀrsk tÔus, mille kÀigus sÔlm lÔpetab ligipÀÀsu:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/31e770cac5be32bb7f95cfbbc6b9f1ae.jpg)
Nii et ekraanipiltidest on nÀha, et:
- MÀlu masinas on peaaegu lÔppemas;
- TÔuseb jÀrsk mÀlu tarbimise tÔus, pÀrast mida katkeb juurdepÀÀs kogu masinale;
- Mongo'de saab suur ĂŒlesanne, mis sunnib andmebaasi protsessi kasutama rohkem mĂ€lu ja aktiivselt lugema kettalt.
Selgub, et kui Linuxis lÔpeb vaba mÀlu (tekib memory pressure) ja swap'i ei ole, siis kuni OOM killer'i saabumisel vÔib leida tasakaalu lehtede juhuslikus hoidmisel vahemÀlus ja nende tagasi kirjutamisel kettale. Sellega tegeleb kswapd, kes piiritletult vabastab vÔimalikult palju mÀlu lehti edasiseks jaotamiseks.
Kahjuks, kui sisend/vĂ€ljund on suure koormuse all koos vĂ€ikese vaba mĂ€luhulga, muutub kswapd kogu sĂŒsteemi kitsaskohaks, kuna sellele toetuvad kĂ”ik mĂ€lu lehtede jaotused (page faults) sĂŒsteemis. See vĂ”ib kesta vĂ€ga kaua, kui protsessid ei soovi enam mĂ€lu kasutada ja jÀÀvad OOM-killer'i ÀÀrele.
On loomulik kĂŒsimus: miks OOM killer tuleb nii hilja? Oma praeguses iteratsioonis on OOM killer ÀÀrmiselt rumal: ta lĂ”petab protsessi ainult siis, kui mĂ€lu lehe jaotamine ebaĂ”nnestub, st kui page fault lĂ”ppeb veaga. Seda ei juhtu piisavalt kaua, kuna kswapd piiritletult vabastab mĂ€lu lehti, kirjutades page cache (sisuliselt kogu sĂŒsteemi ketta I/O) tagasi kettale. TĂ€iendavat teavet koos sammudega, mis on vajalikud sarnaste probleemide lahendamiseks kernelis, leiate .
See kÀitumine Linux 4.6+ tuumaga.
Ajalugu 6. Pod'id ripuvad ooteolekus
MÔnedes klastrites, kus töötab tÔeliselt palju pod'e, oleme hakanud tÀhele panema, et suur osa neist jÀÀb pikaks ajaks "ripakile" Ootel, kuigi Docker-konteinerid on juba sÔlmedes kÀivitatud ja nendega saab kÀsitsi töötada.
Samas, describe sellest pole midagi halba:
TĂŒĂŒp PĂ”hjendus Vanus From SĂ”num
---- ------ ---- ---- -------
Tavaline Planeeritud 1m default-scheduler Sphinx-0 on edukalt mÀÀratud ss-dev-kub07-le
Tavaline Mahutamine edukalt 1m attachdetach-controller AttachVolume.Attach Ônnestus mahuti "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b" jaoks
Tavaline Mahutamine edukalt 1m kubelet, ss-dev-kub07 MountVolume.SetUp Ônnestus mahuti "sphinx-config" jaoks
Tavaline Mahutamine edukalt 1m kubelet, ss-dev-kub07 MountVolume.SetUp Ônnestus mahuti "default-token-fzcsf" jaoks
Tavaline Mahutamine edukalt 49s (x2 51s jooksul) kubelet, ss-dev-kub07 MountVolume.SetUp Ônnestus mahuti "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b" jaoks
Tavaline TÔmmatud 43s kubelet, ss-dev-kub07 Mahuti pilt "registry.example.com/infra/sphinx-exporter/sphinx-indexer:v1" on masinas juba olemas
Tavaline Loome 43s kubelet, ss-dev-kub07 Loome konteineri
Tavaline Alustas 43s kubelet, ss-dev-kub07 Alustas konteinerit
Tavaline TÔmmatud 43s kubelet, ss-dev-kub07 Mahuti pilt "registry.example.com/infra/sphinx/sphinx:v1" on masinas juba olemas
Tavaline Loome 42s kubelet, ss-dev-kub07 Loome konteineri
Tavaline Alustas 42s kubelet, ss-dev-kub07 Alustas konteineritUurides jĂ”udsime jĂ€reldusele, et kubelet lihtsalt ei jĂ”ua edastada API-serverile kogu teavet podâide, elujĂ”udluse/readiness-proovide kohta.
Ja uurides abimaterjale, leidsime jÀrgmised parameetrid:
--kube-api-qps - QPS, mida kasutatakse Kubernetes apiserveriga suhtlemiseks (vaikimisi 5)
--kube-api-burst - Burst, mida kasutatakse Kubernetes apiserveriga suhtlemiseks (vaikimisi 10)
--event-qps - Kui > 0, piira sĂŒndmuste loomist sekundis sellele vÀÀrtusele. Kui 0, on piirang puudub. (vaikimisi 5)
--event-burst - Maksimaalne suurus Ă€kiliste sĂŒndmuste rekordite jaoks, mis ajutiselt lubab sĂŒndmuste rekorditel ulatuda sellesse numbrisse, ĂŒletamata siiski event-qps-i. Kasutatakse ainult siis, kui --event-qps > 0 (vaikimisi 10)
--registry-qps - Kui > 0, piira registri tÔmbamise QPS sellele vÀÀrtusele.
--registry-burst - Maksimaalne suurus Ă€kiliste tĂ”mbamiste jaoks, mis ajutiselt lubab tĂ”mbamisi ulatuda sellesse numbrisse, ĂŒletamata siiski registry-qps-i. Kasutatakse ainult siis, kui --registry-qps > 0 (vaikimisi 10)Nagu nĂ€ha, vaikimisi vÀÀrtused - ĂŒsna vĂ€ikesed, ja 90% neist katab kĂ”ik vajadused⊠Siiski, meie puhul osutus see liiga vĂ€ikeseks. SeetĂ”ttu seadsime jĂ€rgmised vÀÀrtused:
--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40⊠ja taaskĂ€ivitame kubeletâid, pĂ€rast mida nĂ€gime API-serveri kĂ”nede graafikutel jĂ€rgmist pilti:
![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/b2ae099729e55a686f6bec3012b96195.jpg)
⊠ja jah, kÔik hakkas toimima!
P.S.
AitÀh kÔigile ettevÔtte inseneridele vigade kogumise ja artikli ettevalmistamise eest, eriti meie R&D meeskonna kolleegile Andreile Klimentjevile ().
P.P.S.
Lugege ka meie blogist:
- «».
- Kubernetesi nĂ€punĂ€idete ja nippe tsĂŒkkel:
- «»;
- «»;
- «»;
- «».
Allikas: habr.com

![6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]](/wp-content/uploads/2019/03/0d15d1de17cd6838fc1cad19615af218.jpg)