![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/bed059552ed86580939aa18fbdf1553e.jpg)
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 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:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/bd778052c87b338493bae54b26830ef3.jpg)
Ang isang katulad na sitwasyon ay sinusunod sa iba pang mga node:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/ef512532a95ca982e4342071115dbe9f.jpg)
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/43c32ebca78755dde348ed5e7ac75c79.jpg)
Sa parehong mga node nakikita natin:
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>Ito ay lumabas na ang pag-uugali na ito ay bunga ng pod na nagtatrabaho sa (isang Go utility na ginagamit namin para magpatakbo ng mga cron job sa mga pod):
_ 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>
…Ang problema ay ito: kapag ang isang gawain ay pinapatakbo sa supercronic, ang proseso ay nabuo nito hindi maaaring wakasan ng tama, nagiging .
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 .
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 , 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:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/6140058330faaa3785b089dcba857056.jpg)
Walang magugustuhan nito, kaya armado kami 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:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20600%20241'%3E%3C/svg%3E)
- Sa mailing list ng mga developer ng kernel mahahanap mo . 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 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 (, at para sa isang paglalarawan tingnan ) pag-update ng kernel Linux hanggang 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:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/044c4e23a772c61a6206b9b20aa67c1d.jpg)
Lumalabas na may problema sa systemd na ginamit sa Ubuntu 16.04, at nangyayari ito 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: (isyu sa systemd - ).
Wala na ang problema Ubuntu 18.04, ngunit kung gusto mong ipagpatuloy ang paggamit Ubuntu 16.04, maaaring makatulong sa iyo ang aming solusyon sa paksang ito.
Kaya ginawa namin ang sumusunod na 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... at ginagamit nito ang sumusunod na script:
#!/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:
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"]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.);
- Gumamit ng mga kagamitan tulad ng .
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:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/5de916d270a862cbcbb5ed23c31f698e.jpg)
At tulad nito - pagkatapos aksidente:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/0f32bf1113204cf19f4639a297e40348.jpg)
Sa pagsubaybay, mayroon ding isang matalim na pagtalon, kung saan ang node ay hindi na magagamit:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/31e770cac5be32bb7f95cfbbc6b9f1ae.jpg)
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.
Lumalabas na kung sa Linux nauubusan ng libreng memorya (nangyayari ang presyon ng memorya) at walang pagpapalit, 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 .
Ang ugali na ito na may core Linux 4.6 +.
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 containerPagkatapos 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:
--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40... at i-restart ang mga kubelets, pagkatapos ay nakita namin ang sumusunod na larawan sa mga graph ng mga tawag sa API server:
![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/b2ae099729e55a686f6bec3012b96195.jpg)
... 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 ().
Pps
Basahin din sa aming blog:
- «'.
- Mga tip at trick sa Kubernetes loop:
- «";
- «";
- «";
- «'.
Pinagmulan: www.habr.com

![6 na nakakaaliw na system bug sa pagpapatakbo ng Kubernetes [at ang kanilang solusyon]](/wp-content/uploads/2019/03/0d15d1de17cd6838fc1cad19615af218.jpg)