![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/bed059552ed86580939aa18fbdf1553e.jpg)
Gjennom Ärene med bruk av Kubernetes i produksjon, har vi samlet mange interessante historier om hvordan feil i ulike systemkomponenter fÞrte til ubehagelige og/eller uforstÄelige konsekvenser som pÄvirket driften av containere og pods. I denne artikkelen har vi laget et utvalg av noen av de mest vanlige eller interessante. Selv om du aldri er heldig nok til Ä mÞte slike situasjoner, er det alltid interessant Ä lese om slike korte detektivhistorier - spesielt "fÞrstehÄnds" - ikke sant?
Historie 1. Supercronic og Docker hengende
PĂ„ en av klyngene mottok vi med jevne mellomrom en frossen Docker, som forstyrret den normale funksjonen til klyngen. Samtidig ble fĂžlgende observert i Docker-loggene:
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
⊠Det som interesserer oss mest med denne feilen er meldingen: pthread_create failed: No space left on device. Rask studie forklarte at Docker ikke kunne splitte en prosess, og derfor frÞs den med jevne mellomrom.
Ved overvÄking tilsvarer fÞlgende bilde det som skjer:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/bd778052c87b338493bae54b26830ef3.jpg)
En lignende situasjon observeres pÄ andre noder:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/ef512532a95ca982e4342071115dbe9f.jpg)
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/43c32ebca78755dde348ed5e7ac75c79.jpg)
PĂ„ de samme nodene ser vi:
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] <defunct>
root 17398 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 16852 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 9473 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 4664 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 30571 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 24113 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 16475 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 7176 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>
root 1090 0.0 0.0 0 0 ? Z Feb06 0:00 | _ [curl] <defunct>Det viste seg at denne oppfĂžrselen er en konsekvens av at poden jobber med (et Go-verktĂžy som vi bruker til Ă„ kjĂžre cron-jobber i pods):
_ 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] <defunct>
| _ [curl] <defunct>
| _ [curl] <defunct>
| _ [curl] <defunct>
âŠProblemet er dette: nĂ„r en oppgave kjĂžres i supercronic, ble prosessen skapt av den kan ikke avsluttes riktig, blir til .
Note: For Ä vÊre mer presis, prosesser skapt av cron-oppgaver, men supercronic er ikke et init-system og kan ikke "adoptere" prosesser som barna har skapt. NÄr SIGHUP- eller SIGTERM-signaler heves, sendes de ikke videre til barneprosessene, noe som resulterer i at barneprosessene ikke avsluttes og forblir i zombiestatus. Alt dette kan du lese mer om, for eksempel i .
Det er et par mÄter Ä lÞse problemer pÄ:
- Som en midlertidig lĂžsning â Ăžk antallet PID-er i systemet pĂ„ et enkelt tidspunkt:
/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 - Eller start oppgaver i supercronic ikke direkte, men ved Ă„ bruke det samme , som er i stand til Ă„ avslutte prosesser pĂ„ riktig mĂ„te og ikke skape zombier.
Historie 2. "Zombies" nÄr du sletter en cgroup
Kubelet begynte Ă„ bruke mye CPU:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/6140058330faaa3785b089dcba857056.jpg)
Ingen vil like dette, sÄ vi bevÊpnet oss og begynte Ä hÄndtere problemet. Resultatene av undersÞkelsen var som fÞlger:
- Kubelet bruker mer enn en tredjedel av CPU-tiden sin pÄ Ä trekke minnedata fra alle cgroups:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20241'%3E%3C/svg%3E)
- I kjerneutviklernes e-postliste kan du finne . Kort fortalt kommer poenget ned til dette: ulike tmpfs-filer og andre lignende ting fjernes ikke fullstendig fra systemet ved sletting av en cgruppe, den sÄkalte zombie. FÞr eller siden vil de bli slettet fra sidebufferen, men det er mye minne pÄ serveren og kjernen ser ikke poenget med Ä kaste bort tid pÄ Ä slette dem. Det er derfor de stadig hoper seg opp. Hvorfor skjer dette i det hele tatt? Dette er en server med cron-jobber som stadig skaper nye jobber, og med dem nye poder. Dermed opprettes det nye cgroups for containere i dem, som snart blir slettet.
- Hvorfor kaster cAdvisor i kubelet bort sÄ mye tid? Dette er lett Ä se med den enkleste utfÞrelse
time cat /sys/fs/cgroup/memory/memory.stat. Hvis operasjonen pÄ en frisk maskin tar 0,01 sekunder, tar den pÄ den problematiske cron02 1,2 sekunder. Saken er at cAdvisor, som leser data fra sysfs veldig sakte, prÞver Ä ta hensyn til minnet som brukes i zombie cgroups. - For Ä fjerne zombier med makt, prÞvde vi Ä tÞmme cacher som anbefalt i LKML:
sync; echo 3 > /proc/sys/vm/drop_caches,- men kjernen viste seg Ă„ vĂŠre mer komplisert og krasjet bilen.
Hva Ă„ gjĂžre? Problemet blir fikset (, og for en beskrivelse se ) kjerneoppdatering Linux opp til versjon 4.16.
Historie 3. Systemd og dets montering
Igjen, kubelet bruker for mange ressurser pÄ noen noder, men denne gangen bruker den for mye minne:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/044c4e23a772c61a6206b9b20aa67c1d.jpg)
Det viste seg at det var et problem med systemd som ble brukt i Ubuntu 16.04, og det skjer nÄr du administrerer monteringer som er opprettet for tilkobling subPath fra ConfigMap's eller secret's. Etter at poden har fullfÞrt arbeidet systemd-tjenesten og dens servicemontering gjenstÄr i systemet. Over tid akkumuleres et stort antall av dem. Det er til og med problemer om dette emnet:
- ;
- .
...den siste refererer til PR i systemd: (problem i systemd - ).
Problemet er ikke lenger der Ubuntu 18.04, men hvis du vil fortsette Ä bruke Ubuntu 16.04, kan det hende du synes lÞsningen vÄr pÄ dette emnet er nyttig.
SĂ„ vi laget fĂžlgende DaemonSet:
---
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... og den bruker fĂžlgende skript:
#!/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... og den kjĂžrer hvert 5. minutt ved Ă„ bruke den tidligere nevnte supercronic. Dockerfilen ser slik ut:
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"]Historie 4. Konkurranseevne nÄr du planlegger pods
Det ble lagt merke til at: hvis vi har en pod plassert pÄ en node og bildet pumpes ut i veldig lang tid, vil en annen pod som "treffer" den samme noden ganske enkelt begynner ikke Ä trekke bildet av den nye poden. I stedet venter den til bildet av den forrige poden er trukket. Som et resultat vil en pod som allerede var planlagt og hvis bilde kunne ha blitt lastet ned pÄ bare et minutt havne i statusen til containerCreating.
Arrangementene vil se omtrent slik ut:
Normal Pulling 8m kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal pulling image "registry.example.com/infra/openvpn/openvpn:master"Det viser seg at et enkelt bilde fra et tregt register kan blokkere distribusjon per node.
Dessverre er det ikke mange veier ut av situasjonen:
- PrĂžv Ă„ bruke Docker Registry direkte i klyngen eller direkte med klyngen (for eksempel GitLab Registry, Nexus, etc.);
- Bruk verktĂžy som f.eks .
Historie 5. Noder henger pÄ grunn av mangel pÄ hukommelse
Under driften av forskjellige applikasjoner mÞtte vi ogsÄ en situasjon der en node slutter Ä vÊre tilgjengelig fullstendig: SSH reagerer ikke, alle overvÄkingsdemoner faller av, og sÄ er det ingenting (eller nesten ingenting) unormalt i loggene.
Jeg skal fortelle deg i bilder ved Ä bruke eksemplet pÄ en node der MongoDB fungerte.
Slik ser det ut pÄ toppen til ulykker:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/5de916d270a862cbcbb5ed23c31f698e.jpg)
Og sÄnn - etter ulykker:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/0f32bf1113204cf19f4639a297e40348.jpg)
I overvÄking er det ogsÄ et skarpt hopp, der noden slutter Ä vÊre tilgjengelig:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/31e770cac5be32bb7f95cfbbc6b9f1ae.jpg)
SĂ„ fra skjermbildene er det klart at:
- RAM-en pÄ maskinen er nÊr slutten;
- Det er et kraftig hopp i RAM-forbruket, hvoretter tilgangen til hele maskinen brÄtt deaktiveres;
- En stor oppgave kommer til Mongo, som tvinger DBMS-prosessen til Ă„ bruke mer minne og aktivt lese fra disk.
Det viser seg at hvis i Linux ledig minne gÄr tom (minnetrykk oppstÄr) og det ikke er noen bytte, da til NÄr OOM-morderen ankommer, kan det oppstÄ en balansegang mellom Ä kaste sider inn i sidebufferen og skrive dem tilbake til disken. Dette gjÞres av kswapd, som modig frigjÞr sÄ mange minnesider som mulig for senere distribusjon.
Dessverre, med en stor I/O-belastning kombinert med en liten mengde ledig minne, kswapd blir flaskehalsen i hele systemet, fordi de er knyttet til det alle allokeringer (sidefeil) av minnesider i systemet. Dette kan pÄgÄ i svÊrt lang tid hvis prosessene ikke Þnsker Ä bruke minne lenger, men er fikset helt pÄ kanten av OOM-killer-avgrunnen.
Det naturlige spÞrsmÄlet er: hvorfor kommer OOM-morderen sÄ sent? I sin nÄvÊrende iterasjon er OOM-morderen ekstremt dum: den vil drepe prosessen bare nÄr forsÞket pÄ Ä tildele en minneside mislykkes, dvs. hvis sidefeilen mislykkes. Dette skjer ikke pÄ ganske lenge, fordi kswapd modig frigjÞr minnesider, og dumper sidebufferen (faktisk hele disk I/O i systemet) tilbake til disken. Mer detaljert, med en beskrivelse av trinnene som kreves for Ä eliminere slike problemer i kjernen, kan du lese .
Denne oppfĂžrselen med en kjerne Linux 4.6 +.
Historie 6. Pods blir sittende fast i ventende tilstand
I noen klynger, der det virkelig er mange pods som opererer, begynte vi Ä legge merke til at de fleste av dem "henger" veldig lenge i staten Pending, selv om selve Docker-beholderne allerede kjÞrer pÄ nodene og kan arbeides med manuelt.
Dessuten, i describe Det er ikke noe galt:
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 containerEtter litt graving antok vi at kubelet rett og slett ikke har tid til Ă„ sende all informasjon om tilstanden til podene og liveness/beredskapstester til API-serveren.
Og etter Ă„ ha studert hjelp, fant vi fĂžlgende parametere:
--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)Som sett, standardverdiene er ganske smÄ, og i 90 % dekker de alle behov... Men i vÄrt tilfelle var ikke dette nok. Derfor setter vi fÞlgende verdier:
--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40... og startet kubelets pÄ nytt, hvoretter vi sÄ fÞlgende bilde i grafene av kall til API-serveren:
![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/b2ae099729e55a686f6bec3012b96195.jpg)
... og ja, alt begynte Ă„ fly!
PS
For deres hjelp med Ä samle feil og forberede denne artikkelen, uttrykker jeg min dype takknemlighet til de mange ingeniÞrene i selskapet vÄrt, og spesielt til min kollega fra vÄrt FoU-team Andrey Klimentyev ().
PPS
Les ogsÄ pÄ bloggen vÄr:
- «'.
- Kubernetes tips og triks loop:
- «";
- «";
- «";
- «'.
Kilde: www.habr.com

![6 underholdende systemfeil i driften av Kubernetes [og deres lĂžsning]](/wp-content/uploads/2019/03/0d15d1de17cd6838fc1cad19615af218.jpg)