6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

Kubernetes'i tootmises kasutamise aastate jooksul on meil kogunenud palju huvitavaid lugusid, kuidas erinevate sĂŒsteemi komponentide vead on pĂ”hjustanud ebameeldivaid ja/vĂ”i arusaamatuid tagajĂ€rgi, mis mĂ”jutavad konteinerite ja pod'ide tööd. Selles artiklis oleme koostanud valiku mĂ”nedest kĂ”ige sagedamatest vĂ”i huvitavamatest neist. Isegi kui te ei pruugi kunagi sattuda sellistesse olukordadesse, on alati huvitav lugeda sellistest lĂŒhikesest detektiivides — eriti „esimesest kĂ€est“, eks?..

Lugu 1. Supercronic ja kĂŒlmunud Docker

Ühes meie klastris saime perioodiliselt „kĂŒlmunud“ Docker, mis hĂ€iris klastrite normaalset toimimist. Samas olid Docker'i logides jĂ€rgmised kirjed

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 kĂ”ige rohkem sĂ”num: pthread_create failed: No space left on device. Kiire ĂŒlevaade dokumentatsioon nĂ€itas, et Docker ei saa protsessi forkida, mistĂ”ttu see aeg-ajalt „kĂŒlmus”.

JĂ€lgimisel, mis toimus, vastas selline pilt:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

Sarnast olukorda tÀheldati ka teistes sÔlmedes:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

Nendel samadel sÔlmedel nÀeme:

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, selline kĂ€itumine on pod'i töö tagajĂ€rg supercronic (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 supercronicis, siis protsess, mille see edasi annab, ei suuda Ă”igesti lĂ”petada, muutudes zombiks.

MĂ€rkus: TĂ€psemalt öeldes, protsessid genereeritakse cron-ĂŒlesannetest, kuid supercronic ei ole init-sĂŒsteem ja ei suuda "seda lapsendavaid" protsesse, mille tema lastega toimub. Signaalide SIGHUP vĂ”i SIGTERM korral ei edastata neid edasi genereeritud protsessidele, mille tĂ”ttu tĂŒtarprotsessid ei lĂ”petata, jÀÀdes zombistaatusele. TĂ€iendavateks lugemiseks vĂ”ib nĂ€iteks lugeda sellest artiklist.

Sorriteen on mÔningaid probleemide lahendamise viise:

  1. Ajutise lahendusena - suurendage sĂŒsteemis samal ajal PIDde arvu:
           /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
  2. VĂ”i kĂ€ivitada ĂŒlesandeid supercronicis mitte otse, vaid sama abil, tini, mis suudab Ă”igesti lĂ”petada protsesse ja ei genereeri zombisid.

Lugu 2. "Zombid" cgroupi eemaldamisel

Kubelet hakkas tarbima suures koguses CPU-d:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

See ei meeldi kellelegi, seega relvasime end perf ja hakkasime probleemiga tegelema. Uurimise tulemused olid jÀrgmised:

  • Kubelet kulutab rohkem kui kolmandiku CPU ajast mĂ€luandmete kogumisele kĂ”ikidest cgroupidest:

    6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

  • Tuletisede arendajate postil vĂ”ib leida probleemi arutelu. LĂŒhidalt, sisu seisneb selles, et erinevad tmpfs-failid ja muud sarnased asjad ei eemaldu tĂ€ielikult sĂŒsteemist cgroupi eemaldamisel - jÀÀvad nn memcg zombiks. Aega vĂ”i hiljem nad siiski kustutatakse page cache'ist, kuid serveris on palju mĂ€lu ja tuum ei nĂ€e mĂ”tet nende kustutamiseks aega raisata. SeetĂ”ttu nad jĂ€tkuvalt kuhjuvad. Miks see ĂŒldse juhtub? See on server cron-töödega, mis pidevalt loob uusi tĂ¶Ă¶ĂŒlesandeid ja nendega uusi pod'e. Seega luuakse nende konteinerite jaoks uued cgroup'id, mis varsti kustutatakse.
  • Miks cAdvisor kubeleti juures kulutab nii palju aega? Seda on lihtne nĂ€ha kĂ”ige lihtsama teostuse abil time cat /sys/fs/cgroup/memory/memory.stat. Kui tervel masinal see operatsioon kestab 0,01 sekundit, siis probleemse cron02 peal 1,2 sekundit. Asi on selles, et cAdvisor, kes loeb sysfs'ist andmeid vĂ€ga aeglaselt, proovib arvestada kasutatud mĂ€lu ja zombide cgroup'e.
  • Kuna me pĂŒĂŒdsime zombisid sunniviisiliselt kustutada, siis proovisime puhastada cache'e, nagu soovitati LKML'is: sync; echo 3 > /proc/sys/vm/drop_caches, — aga tuum osutus keerulisemaks ning masin hangus.

Mida siis teha? Probleem lahendatakse (komiteed, ja kirjelduse leiate vÀljaande teatest) Linuxi tuuma versiooni 4.16 uuendamisega.

Lugu 3. Systemd ja selle mount

Taaskord tarbib kubelet liiga palju ressursse mÔnedes sÔlmedes, aga seekord juba mÀlu:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

Selgus, et probleem on systemd's, mis on kasutusel Ubuntu 16.04-s, ja see tekib mount'ide haldamisel, mis luuakse ConfigMap'ide vĂ”i salajaste jaoks. PĂ€rast pod'i lĂ”petamist , said edasiarendust — uue vĂ€lja jÀÀb systemd teenus ja selle teenusmount sĂŒsteemi. Aja jooksul neid koguneb tohutult. Selle teema kohta on isegi probleeme: kops #5916

  1. kubernetes #57345;
  2. 
 viimasest, kus viidatakse PR-ile systemd's:.

(issue systemd's — #7811 Probleem ei esine enam Ubuntu 18.04-s, aga kui soovite endiselt kasutada Ubuntu 16.04, siis vĂ”ib meie lahendus selle teema jaoks teile Ă€ra kuluda. #7798).

Nii et me tegime jÀrgmise DaemonSet'i:

Nii, me oleme loonud jÀrgmise DaemonSet'i:

---
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 kasutatakse sellist skripti:

#!/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 see kÀivitatakse iga 5 minuti jÀrel juba mainitud supercronic'i abil. Selle Dockerfile nÀeb vÀlja jÀrgmine:

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. Üheaegsus pod'ide planeerimisel

On mÀrgatud, et: kui meil on nodis paigutatud pod ja selle pildi alla laadimine vÔtab kaua aega, siis teine pod, mis "satub" samale node'ile, lihtsalt ei alusta uue pod'i kuvatamise tÔmbamisega.Selle asemel ootab ta, kuni eelmine pod'i pilt on tÔmmatud. Tulemuseks on pod, mis on juba planeeritud ja mille pilt oleks vÔinud alla laadida vaid minutiga, jÀÀb pikaks ajaks staatusele containerCreating.

SĂŒndmustes on umbes jĂ€rgmine:

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 ainult ĂŒks pilt aeglasest registrist vĂ”ib blokeerida juurutamise node'ile.

Kahjuks ei ole olukorrast vÀljapÀÀse palju:

  1. PĂŒĂŒdke kasutada oma Docker registrit otse klastris vĂ”i otse klassi (nĂ€iteks GitLab Registry, Nexus jne);
  2. Kasutage selliseid tööriistu nagu kraken.

Lugu 5. SÔlmede hangumine mÀlu puuduse tÔttu

Erinevate rakenduste kasutamise ajal oleme samuti kogenud olukordi, kus sÔlm lakkab tÀielikult olemast kÀttesaadav: ei vasta SSH, kÔik jÀlgimisdemonid pingutavad ja logides ei ole midagi (vÔi peaaegu midagi) ebatavalist.

NĂ€itan piltidel ĂŒhe sĂ”lme nĂ€iteks, kus töötas MongoDB.

Nii nÀeb atop vÀlja kuni katastroofid:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

Ja nii — pĂ€rast katastroofid:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

JÀlgimises on samuti nÀhtav terav tÔus, mille kÀigus sÔlm lakab olemast kÀttesaadav:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]

Seega, ekraanipiltidest on nÀha, et:

  1. MĂ€lu masinas on peaaegu otsas;
  2. TÀheldatakse jÀrsku mÀlutarbimise kasvu, mille jÀrel juurdepÀÀs kogu masinale katkeb;
  3. Mongo'le tuleb suur ĂŒlesanne, mis paneb andmebaasi protsessi kasutama rohkem mĂ€lu ja aktiivselt lugema kettalt.

Selgub, et kui Linuxis lÔpeb vaba mÀlu (tekib mÀlurÔhk) ja s-wappi pole, siis kuni OOM killer'i saabumise hetk vÔib tÀhendada tasakaalu lehitsemise vahel lehtede paigutamise page cache'i ja nende kirjutamise tagasi kettale. Sellega tegeleb kswapd, kes julgeb vabastada vÔimalikult palju mÀlu lehti edasiseks jaotamiseks.

Kahjuks, kui sisend/vĂ€ljund koormus on suur koos vĂ€ikese vaba mĂ€luhulgaga, kasvab kswapd kogu sĂŒsteemi pudelikaelaks, kuna sellele sĂ”ltuvad 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 ning jÀÀvad OOM killer'i ÀÀrele.

KĂŒsimus on loomulik: miks OOM killer tuleb nii hilja? Praeguses iteratsioonis on OOM killer ÀÀrmiselt rumal: ta lĂ”petab protsessi ainult siis, kui ebaĂ”nnestub mĂ€lulehe jaotamise katse, st kui page fault lĂ€heb lĂ€bi veaga. Seda ei juhtu piisavalt kaua, kuna kswapd julgeb vabastada mĂ€lulehti, kustutades page cache'i (kogu ketta I/O sĂŒsteemis, pĂ”himĂ”tteliselt) tagasi kettale. TĂ€iendavalt, koos sammudega, mis on vajalikud sarnaste probleemide lahendamiseks tuumas, saate lugeda siin.

See kÀitumine peaks paranema Linuxi 4.6+ tuumaga.

Ajalugu 6. Pod’id jÀÀvad seisundisse Pending

MĂ”nedes klastrites, kus töötab tĂ”eliselt palju pod’e, oleme hakanud mĂ€rkama, et suur osa neist jÀÀb vĂ€ga kauaks "ripakile" seisu Pending, kuigi samal ajal on Docker-konteinerid juba sĂ”lmedes kĂ€ivitatud ja nendega saab kĂ€sitsi töötada.

Samas on describe pole midagi halba:

  Type    Reason                  Age                From                     Message
  ----    ------                  ----               ----                     -------
  Normal  Scheduled               1m                 default-scheduler        Edukalt mÀÀratud sphinx-0 ss-dev-kub07-le
  Normal  SuccessfulAttachVolume  1m                 attachdetach-controller  AttachVolume.Attach Ônnestus mahuti "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b" jaoks
  Normal  SuccessfulMountVolume   1m                 kubelet, ss-dev-kub07    MountVolume.SetUp Ônnestus mahuti "sphinx-config" jaoks
  Normal  SuccessfulMountVolume   1m                 kubelet, ss-dev-kub07    MountVolume.SetUp Ônnestus mahuti "default-token-fzcsf" jaoks
  Normal  SuccessfulMountVolume   49s (x2 over 51s)  kubelet, ss-dev-kub07    MountVolume.SetUp Ônnestus mahuti "pvc-6aaad34f-ad10-11e8-a44c-52540035a73b" jaoks
  Normal  Pulled                  43s                kubelet, ss-dev-kub07    Konteineri pilt "registry.example.com/infra/sphinx-exporter/sphinx-indexer:v1" on juba masinas olemas
  Normal  Created                 43s                kubelet, ss-dev-kub07    Kontainer loodud
  Normal  Started                 43s                kubelet, ss-dev-kub07    Kontainer kÀivitatud
  Normal  Pulled                  43s                kubelet, ss-dev-kub07    Konteineri pilt "registry.example.com/infra/sphinx/sphinx:v1" on juba masinas olemas
  Normal  Created                 42s                kubelet, ss-dev-kub07    Kontainer loodud
  Normal  Started                 42s                kubelet, ss-dev-kub07    Kontainer kÀivitatud

Uurides asja, tegime jĂ€relduse, et kubelet ei suuda lihtsalt API-serverile kĂ”iki pod’ide olekuandmeid piisavalt kiiresti edastada, liveness/readiness-testide kohta.

Ja uurides abi, leidsime jÀrgmised parameetrid:

--kube-api-qps - QPS, mida kasutada kubernetes apiserveriga suhtlemisel (vaikimisi 5)
--kube-api-burst  - Burst, mida kasutada kubernetes apiserveriga suhtlemisel (vaikimisi 10)
--event-qps - Kui > 0, piirata ĂŒrituste loomise sagedust sekundis sellele vÀÀrtusele. Kui 0, piiramatu. (vaikimisi 5)
--event-burst - Maksimaalne suurus purunevate ĂŒrituste jaoks, lubab ajutiselt ĂŒrituste rekordite puruneda sellele numbrile, ĂŒletamata siiski event-qps-i. Kasutatakse ainult juhul, kui --event-qps > 0 (vaikimisi 10)
--registry-qps - Kui > 0, piirata registri pull-i QPS-iga sellele vÀÀrtusele.
--registry-burst - Maksimaalne suurus purunevate tĂ”mbamiste jaoks, lubab ajutiselt tĂ”mbamisi puruneda sellele numbrile, ĂŒletamata siiski registry-qps-i. Kasutatakse ainult juhul, kui --registry-qps > 0 (vaikimisi 10)

Nagu nĂ€ha, vaikimisi vÀÀrtused on ĂŒsna vĂ€ikesed, ja 90% juhtudest katab see kĂ”ik vajadused... Kuid meie puhul osutus sellest vĂ€heks. SeetĂ”ttu seadsime sellised vÀÀrtused:

--event-qps=30 --event-burst=40 --kube-api-burst=40 --kube-api-qps=30 --registry-qps=30 --registry-burst=40


 ja kĂ€ivitasime kubelet’id uuesti, pĂ€rast mida nĂ€gime API-serveri mahtuvuse graafikutel jĂ€rgmist pilti:

6 huvitavat sĂŒsteemiviga Kubernetes'i kasutamise ajal [ja nende lahendamine]


 ja jah, kÔik hakkas lendama!

P.S.

Suur tÀnu meie ettevÔtte paljudele inseneridele, eriti minu kolleegile R&D meeskonnast Andreile Klimentjevile, abi eest vigade kogumisel ja artikli ettevalmistamisel.zuzzas).

P.P.S.

Lugege ka meie blogist:

Allikas: habr.com

Osta usaldusvÀÀrne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid - ProHoster