6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]

6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]

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 dokumentatsioonis 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]

Sarnast olukorda tÀheldatakse ka teistel sÔlmedel:

6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]

6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]

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, 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 supercronic'is, siis sellele jĂ€rgnev protsess ei saa korrektselt lĂ”petada, muutudes zombiks.

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 sellest artiklist.

Probleemide lahendamiseks on paar vÔimalust:

  1. 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
  2. VĂ”i kĂ€ivitada ĂŒlesanded supercronicis mitte otse, vaid sama tini, 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]

Sellest ei saa keegi vaimustuda, seega varustasime end perf 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]

  • Kernal arendajate postitusse saab leida probleemi arutelu. Ü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 memcg 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 (kommitt, ning kirjeldust vaata vÀljaande teates) 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]

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:

  1. kops #5916;
  2. kubernetes #57345.


 viidates viimases PR-le systemd-s: #7811 (issue systemd-s — #7798).

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:

  1. PĂŒĂŒdke kasutada oma Docker Registry otse klastris vĂ”i otse koos klastriga (nĂ€iteks GitLab Registry, Nexus jne);
  2. Kasutage selliseid utiliite nagu kraken.

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]

Ja nii — pĂ€rast Ă”nnetused:

6 huvitavat sĂŒsteemi tĂ”rget Kubernetes'e kasutamise kĂ€igus [ja nende lahendamine]

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]

Nii et ekraanipiltidest on nÀha, et:

  1. MÀlu masinas on peaaegu lÔppemas;
  2. TÔuseb jÀrsk mÀlu tarbimise tÔus, pÀrast mida katkeb juurdepÀÀs kogu masinale;
  3. 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 siit.

See kÀitumine peab paranema 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 konteinerit

Uurides 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]


 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 (zuzzas).

P.P.S.

Lugege ka meie blogist:

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster