6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Dros y blynyddoedd o ddefnyddio Kubernetes wrth gynhyrchu, rydym wedi cronni llawer o straeon diddorol am sut mae bygiau mewn gwahanol gydrannau system wedi arwain at ganlyniadau annymunol a / neu annealladwy sy'n effeithio ar weithrediad cynwysyddion a chodennau. Yn yr erthygl hon rydym wedi gwneud detholiad o rai o'r rhai mwyaf cyffredin neu ddiddorol. Hyd yn oed os nad ydych byth yn ddigon ffodus i ddod ar draws sefyllfaoedd o’r fath, mae darllen am straeon ditectif byr o’r fath - yn enwedig “yn uniongyrchol” - bob amser yn ddiddorol, onid yw?..

Stori 1. Supercronic a Docker yn hongian

Ar un o'r clystyrau, cawsom Dociwr wedi'i rewi o bryd i'w gilydd, a oedd yn ymyrryd â gweithrediad arferol y clwstwr. Ar yr un pryd, gwelwyd y canlynol yn logiau'r 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

…

Yr hyn sydd o ddiddordeb i ni fwyaf am y gwall hwn yw'r neges: pthread_create failed: No space left on device. Astudio Cyflym dogfennaeth eglurodd na allai Docker fforchio proses, a dyna pam y mae'n rhewi o bryd i'w gilydd.

Wrth fonitro, mae'r darlun canlynol yn cyfateb i'r hyn sy'n digwydd:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Gwelir sefyllfa debyg ar nodau eraill:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Ar yr un nodau gwelwn:

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>

Mae'n troi allan bod yr ymddygiad hwn yn ganlyniad i'r pod yn gweithio gyda uwchcronig (cyfleuster Go a ddefnyddiwn i redeg swyddi cron mewn codennau):

 _ 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>
…

Y broblem yw hyn: pan fydd tasg yn cael ei rhedeg mewn uwchgronig, mae'r broses yn silio ganddo methu terfynu yn gywir, yn troi i mewn zombie.

Nodyn: I fod yn fwy manwl gywir, mae prosesau'n cael eu silio gan dasgau cron, ond nid yw uwchgronig yn system init ac ni all “fabwysiadu” prosesau y mae ei blant yn eu silio. Pan godir signalau SIGHUP neu SITERM, nid ydynt yn cael eu trosglwyddo i'r prosesau plentyn, gan arwain at y prosesau plentyn yn peidio â therfynu ac yn aros mewn statws zombie. Gallwch ddarllen mwy am hyn i gyd, er enghraifft, yn erthygl o'r fath.

Mae dwy ffordd i ddatrys problemau:

  1. Fel ateb dros dro - cynyddwch nifer y PIDs yn y system ar un adeg:
           /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. Neu lansio tasgau yn supercronic nid yn uniongyrchol, ond gan ddefnyddio'r un peth tini, sy'n gallu terfynu prosesau yn gywir a pheidio â silio zombies.

Stori 2. “Zombies” wrth ddileu cgroup

Dechreuodd Kubelet ddefnyddio llawer o CPU:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Fydd neb yn hoffi hyn, felly fe wnaethon ni arfogi ein hunain perff a dechreuodd ddelio â'r broblem. Roedd canlyniadau’r ymchwiliad fel a ganlyn:

  • Mae Kubelet yn treulio mwy na thraean o'i amser CPU yn tynnu data cof o bob cgroup:

    6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

  • Yn y rhestr bostio datblygwyr cnewyllyn gallwch ddod o hyd trafodaeth ar y broblem. Yn fyr, daw'r pwynt i lawr i hyn: nid yw ffeiliau tmpfs amrywiol a phethau tebyg eraill yn cael eu tynnu'n llwyr o'r system wrth ddileu cgroup, yr hyn a elwir memcg zombie. Yn hwyr neu'n hwyrach byddant yn cael eu dileu o storfa'r dudalen, ond mae llawer o gof ar y gweinydd ac nid yw'r cnewyllyn yn gweld pwynt gwastraffu amser ar eu dileu. Dyna pam maen nhw'n dal i bentyrru. Pam fod hyn hyd yn oed yn digwydd? Mae hwn yn weinydd gyda swyddi cron sy'n creu swyddi newydd yn gyson, a gyda nhw codennau newydd. Felly, crëir cgroups newydd ar gyfer cynwysyddion ynddynt, sy'n cael eu dileu yn fuan.
  • Pam mae cAdvisor mewn kubelet yn gwastraffu cymaint o amser? Mae hyn yn hawdd i'w weld gyda'r gweithredu symlaf time cat /sys/fs/cgroup/memory/memory.stat. Os yw'r llawdriniaeth yn cymryd 0,01 eiliad ar beiriant iach, yna ar y cron02 problemus mae'n cymryd 1,2 eiliad. Y peth yw bod cAdvisor, sy'n darllen data o sysfs yn araf iawn, yn ceisio cymryd i ystyriaeth y cof a ddefnyddir mewn cgroups zombie.
  • Er mwyn cael gwared â zombies yn rymus, fe wnaethom geisio clirio caches fel yr argymhellir yn LKML: sync; echo 3 > /proc/sys/vm/drop_caches, - ond trodd y cnewyllyn yn fwy cymhleth a chwalodd y car.

Beth i'w wneud? Mae'r broblem yn cael ei datrys (ymrwymo, ac am ddisgrifiad gw rhyddhau neges) diweddaru'r cnewyllyn Linux i fersiwn 4.16.

Hanes 3. Systemd a'i fynydd

Unwaith eto, mae'r kubelet yn defnyddio gormod o adnoddau ar rai nodau, ond y tro hwn mae'n cymryd gormod o gof:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Mae'n troi allan bod yna broblem mewn systemd a ddefnyddir yn Ubuntu 16.04, ac mae'n digwydd wrth reoli mowntiau sy'n cael eu creu ar gyfer cysylltiad subPath o ConfigMaps neu gyfrinachau. Ar ôl i'r pod orffen ei waith mae'r gwasanaeth systemd a'i mount gwasanaeth yn parhau mewn system. Dros amser, mae nifer fawr ohonynt yn cronni. Mae hyd yn oed problemau ar y pwnc hwn:

  1. #5916;
  2. ciwbernetes #57345.

...mae'r olaf yn cyfeirio at y cysylltiadau cyhoeddus yn systemd: #7811 (mater yn systemd - #7798).

Nid yw'r broblem yn bodoli bellach yn Ubuntu 18.04, ond os ydych chi am barhau i ddefnyddio Ubuntu 16.04, efallai y bydd ein datrysiad ar y pwnc hwn yn ddefnyddiol i chi.

Felly gwnaethom y DaemonSet canlynol:

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

... ac mae'n defnyddio'r sgript ganlynol:

#!/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

... ac mae'n rhedeg bob 5 munud gan ddefnyddio'r supercronic a grybwyllwyd yn flaenorol. Mae ei Dockerfile yn edrych fel hyn:

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

Stori 4. Cystadleurwydd wrth amserlennu codennau

Sylwyd: os oes gennym god wedi'i osod ar nod a bod ei ddelwedd yn cael ei bwmpio allan am amser hir iawn, yna bydd pod arall sy'n “taro” yr un nod yn syml. ddim yn dechrau tynnu delwedd y pod newydd. Yn lle hynny, mae'n aros nes bod delwedd y pod blaenorol yn cael ei dynnu. O ganlyniad, bydd pod a oedd eisoes wedi'i amserlennu ac y gallai ei ddelwedd fod wedi'i lawrlwytho mewn dim ond munud yn dod i ben yn statws containerCreating.

Bydd y digwyddiadau yn edrych fel hyn:

Normal  Pulling    8m    kubelet, ip-10-241-44-128.ap-northeast-1.compute.internal  pulling image "registry.example.com/infra/openvpn/openvpn:master"

Mae'n ymddangos bod gall delwedd sengl o gofrestrfa araf rwystro defnydd y nod.

Yn anffodus, nid oes llawer o ffyrdd allan o'r sefyllfa:

  1. Ceisiwch ddefnyddio'ch Cofrestrfa Docker yn uniongyrchol yn y clwstwr neu'n uniongyrchol gyda'r clwstwr (er enghraifft, Cofrestrfa GitLab, Nexus, ac ati);
  2. Defnyddiwch gyfleustodau fel kraken.

Stori 5. Mae nodau'n hongian oherwydd diffyg cof

Yn ystod gweithrediad amrywiol gymwysiadau, rydym hefyd wedi dod ar draws sefyllfa lle mae nod yn peidio â bod yn hygyrch yn llwyr: nid yw SSH yn ymateb, mae'r holl ddaemonau monitro yn disgyn i ffwrdd, ac yna nid oes dim (neu bron dim) yn anghyson yn y logiau.

Fe ddywedaf wrthych mewn lluniau gan ddefnyddio'r enghraifft o un nod lle roedd MongoDB yn gweithredu.

Dyma sut olwg sydd ar ben i damweiniau:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Ac fel hyn - ar ôl damweiniau:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Wrth fonitro, mae naid sydyn hefyd, lle mae'r nod yn peidio â bod ar gael:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

Felly, o'r sgrinluniau mae'n amlwg:

  1. Mae'r RAM ar y peiriant yn agos at y diwedd;
  2. Mae naid sydyn yn y defnydd o RAM, ac ar ôl hynny mae mynediad i'r peiriant cyfan wedi'i analluogi'n sydyn;
  3. Mae tasg fawr yn cyrraedd Mongo, sy'n gorfodi'r broses DBMS i ddefnyddio mwy o gof a darllen yn weithredol o ddisg.

Mae'n troi allan, os yw Linux yn rhedeg allan o gof am ddim (pwysau cof yn gosod i mewn) ac nad oes cyfnewid, yna i Pan fydd y llofrudd OOM yn cyrraedd, efallai y bydd gweithred gydbwyso yn codi rhwng taflu tudalennau i mewn i'r storfa dudalen a'u hysgrifennu yn ôl i ddisg. Gwneir hyn gan kswapd, sy'n rhyddhau cymaint o dudalennau cof â phosibl i'w dosbarthu wedyn.

Yn anffodus, gyda llwyth I / O mawr ynghyd ag ychydig bach o gof am ddim, kswapd yn dod yn dagfa'r system gyfan, oherwydd eu bod yn gysylltiedig ag ef holl dyraniadau (diffygion tudalennau) o dudalennau cof yn y system. Gall hyn fynd ymlaen am amser hir iawn os nad yw'r prosesau eisiau defnyddio cof mwyach, ond eu bod wedi'u gosod ar ymyl yr affwys lladd OOM.

Y cwestiwn naturiol yw: pam mae'r llofrudd OOM yn dod mor hwyr? Yn ei iteriad presennol, mae'r llofrudd OOM yn hynod o dwp: bydd yn lladd y broses dim ond pan fydd yr ymgais i ddyrannu tudalen cof yn methu, h.y. os bydd nam ar y dudalen yn methu. Nid yw hyn yn digwydd am amser eithaf hir, oherwydd mae kswapd yn rhyddhau tudalennau cof yn ddewr, gan ddympio storfa'r dudalen (y ddisg gyfan I/O yn y system, mewn gwirionedd) yn ôl i'r ddisg. Yn fwy manwl, gyda disgrifiad o'r camau sydd eu hangen i ddileu problemau o'r fath yn y cnewyllyn, gallwch ddarllen yma.

Yr ymddygiad hwn ddylai wella gyda chnewyllyn Linux 4.6+.

Stori 6. Podiau'n mynd yn sownd yn yr arfaeth

Mewn rhai clystyrau, lle mae llawer o godennau'n gweithredu mewn gwirionedd, dechreuon ni sylwi bod y mwyafrif ohonyn nhw'n “hongian” yn y wladwriaeth am amser hir iawn. Pending, er bod y cynwysyddion Docker eu hunain eisoes yn rhedeg ar y nodau a gellir gweithio gyda nhw â llaw.

Ar ben hynny, yn describe does dim byd o'i le:

  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 container

Ar ôl rhywfaint o gloddio, gwnaethom y dybiaeth nad oes gan y kubelet amser i anfon yr holl wybodaeth am gyflwr y codennau a phrofion bywiogrwydd / parodrwydd i'r gweinydd API.

Ac ar ôl astudio cymorth, canfuwyd y paramedrau canlynol:

--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)

Fel y gwelir, gwerthoedd diofyn yn eithaf bach, ac mewn 90% maent yn cwmpasu'r holl anghenion... Fodd bynnag, yn ein hachos ni nid oedd hyn yn ddigon. Felly, rydym yn gosod y gwerthoedd canlynol:

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

... ac ailgychwyn y kubelets, ac ar ôl hynny gwelsom y llun canlynol yn y graffiau o alwadau i'r gweinydd API:

6 byg system difyr yng ngweithrediad Kubernetes [a'u datrysiad]

... a do, dechreuodd popeth hedfan!

PS

Am eu cymorth i gasglu chwilod a pharatoi'r erthygl hon, mynegaf fy niolch dwfn i beirianwyr niferus ein cwmni, ac yn enwedig i'm cydweithiwr o'n tîm Ymchwil a Datblygu Andrey Klimentyev (swzzas).

Pps

Darllenwch hefyd ar ein blog:

Ffynhonnell: hab.com

Ychwanegu sylw