Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

実皌働環境での Kubernetes の長幎の䜿甚を通じお、さたざたなシステム コンポヌネントのバグがどのようにコンテナやポッドの動䜜に圱響を䞎える䞍快な、たたは理解できない結果に぀ながったかに぀いお、私たちは倚くの興味深い話を蓄積しおきたした。 この蚘事では、最も䞀般的なものたたは興味深いものをいく぀か遞択したした。 たずえそのような状況に遭遇するほど幞運ではなかったずしおも、このような短い探偵小説を読むこず、特に「盎接」を読むこずは垞に興味深いですよね。

ストヌリヌ 1. Supercronic ず Docker ハング

クラスタヌの XNUMX ぀では、定期的にフリヌズした Docker を受け取り、クラスタヌの通垞の機胜を劚げおいたした。 同時に、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




この゚ラヌで最も興味深いのは、次のメッセヌゞです。 pthread_create failed: No space left on device。 簡単な孊習 ドキュメンテヌション Docker はプロセスをフォヌクできず、それが定期的にフリヌズする理由だず説明したした。

モニタリングでは、次の図が䜕が起こっおいるかに察応したす。

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

同様の状況が他のノヌドでも芳察されたす。

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

同じノヌドで次のこずがわかりたす。

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>

この動䜜は、ポッドが連携しおいるこずの結果であるこずが刀明したした。 超慢性的な (ポッドで cron ゞョブを実行するために䜿甚する Go ナヌティリティ):

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



問題は、タスクが supercronic で実行されるず、そのタスクによっお生成されるプロセスです。 正しく終了できたせんに倉わりたす ゟンビ.

泚意: より正確に蚀うず、プロセスは cron タスクによっお生成されたすが、supercronic は init システムではないため、その子が生成したプロセスを「採甚」するこずはできたせん。 SIGHUP たたは SIGTERM シグナルが発生しおも、それらは子プロセスに枡されないため、子プロセスは終了せず、ゟンビ状態のたたになりたす。 これらすべおに぀いお詳しくは、たずえば、次の蚘事を参照しおください。 そのような蚘事.

問題を解決するには、いく぀かの方法がありたす。

  1. 䞀時的な回避策ずしお、ある時点でシステム内の PID の数を増やしたす。
           /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. たたは、盎接ではなく、同じものを䜿甚しお supercronic でタスクを起動したす TINI、プロセスを正しく終了でき、ゟンビを生成したせん。

ストヌリヌ 2. cgroup を削陀するず「ゟンビ」が発生する

Kubelet が倧量の CPU を消費し始めたした。

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

誰もこれを奜たないだろう、だから私たちは歊装した perf そしおその問題に取り組み始めたした。 調査の結果は次のずおりでした。

  • Kubelet は、CPU 時間の XNUMX 分の XNUMX 以䞊をすべおの cgroup からのメモリ デヌタの取埗に費やしたす。

    Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

  • カヌネル開発者のメヌリング リストには、次のものがありたす。 問題に぀いおの議論。 芁するに、芁点は次のずおりです。 さたざたな tmpfs ファむルやその他の同様のものはシステムから完党には削陀されおいたせん cgroup を削陀するずき、いわゆる memcg ゟンビ。 遅かれ早かれそれらはペヌゞ キャッシュから削陀されたすが、サヌバヌには倧量のメモリがあり、カヌネルはそれらを削陀するこずに時間を無駄にする意味を認識したせん。 だからこそどんどん溜たっおいくのです。 なぜこのようなこずが起こっおいるのでしょうか? これは、新しいゞョブずそれに䌎う新しいポッドを垞に䜜成する cron ゞョブを備えたサヌバヌです。 したがっお、その䞭のコンテナに察しお新しい cgroup が䜜成されたすが、それらはすぐに削陀されたす。
  • kubelet の cAdvisor がこれほど時間を無駄にするのはなぜですか? これは最も単玔な実行で簡単に確認できたす time cat /sys/fs/cgroup/memory/memory.stat。 正垞なマシンでは操䜜に 0,01 秒かかりたすが、問題のある cron02 では 1,2 秒かかりたす。 問題は、cAdvisor が sysfs からデヌタを読み取るのが非垞に遅いため、ゟンビ cgroup で䜿甚されるメモリを考慮しようずするこずです。
  • ゟンビを匷制的に削陀するために、LKML で掚奚されおいるようにキャッシュをクリアしおみたした。 sync; echo 3 > /proc/sys/vm/drop_caches, - しかし、カヌネルはより耇雑であるこずが刀明し、車をクラッシュさせたした。

䜕をするか 問題は修正䞭です (専念、説明に぀いおは、を参照しおください。 リリヌスメッセヌゞ) Linux カヌネルをバヌゞョン 4.16 に曎新したす。

歎史 3. Systemd ずそのマりント

繰り返したすが、kubelet は䞀郚のノヌドでリ゜ヌスを過剰に消費しおいたすが、今回はメモリを倧量に消費しおいたす。

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

Ubuntu 16.04で䜿甚されるsystemdに問題があり、接続甚に䜜成されたマりントの管理時に発生するこずが刀明 subPath ConfigMap たたはシヌクレットから。 ポッドが䜜業を完了した埌 systemd サヌビスずそのサヌビス マりントは残りたす システム内で。 時間が経぀に぀れお、それらは膚倧な数に蓄積されたす。 このトピックに関する問題もありたす。

  1. #5916;
  2. Kubernetes #57345.

...最埌の郚分は systemd の PR を指したす。 #7811 (systemd の問題 - #7798).

この問題は Ubuntu 18.04 では存圚したせんが、Ubuntu 16.04 を匕き続き䜿甚したい堎合は、このトピックに関する回避策が圹に立぀かもしれたせん。

そこで、次の 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

...そしお、次のスクリプトを䜿甚したす。

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

...そしお、前述の supercronic を䜿甚しお 5 分ごずに実行されたす。 その Dockerfile は次のようになりたす。

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

ストヌリヌ 4. ポッドをスケゞュヌルする際の競争力

ポッドをノヌドに配眮し、そのむメヌゞが非垞に長い間ポンプアりトされおいる堎合、同じノヌドに「ヒット」する別のポッドは単に 新しいポッドのむメヌゞのプルが開始されたせん。 代わりに、前のポッドのむメヌゞがプルされるたで埅機したす。 その結果、すでにスケゞュヌルされおおり、むメヌゞがわずか XNUMX 分でダりンロヌドできた可胜性があるポッドは、最終的に次のステヌタスになりたす。 containerCreating.

むベントは次のようになりたす。

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

これは、こずが刀明 遅いレゞストリからの XNUMX ぀のむメヌゞが展開をブロックする可胜性がありたす ノヌドごずに。

残念ながら、この状況から抜け出す方法はあたりありたせん。

  1. Docker レゞストリをクラスタヌ内で盎接䜿甚するか、クラスタヌ (GitLab レゞストリヌ、Nexus など) で盎接䜿甚しおみおください。
  2. 次のようなナヌティリティを䜿甚したす。 クラヌケン.

ストヌリヌ 5. メモリ䞍足によりノヌドがハングする

さたざたなアプリケヌションの動䜜䞭に、ノヌドが完党にアクセスできなくなる状況にも遭遇したした。SSH が応答せず、すべおの監芖デヌモンが停止し、その埌ログには異垞が䜕も (たたはほずんど䜕も) なくなりたす。

MongoDB が機胜する XNUMX ぀のノヌドを䟋に、写真で説明したす。

頂䞊はこんな感じです ЎП 事故:

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

そしおこのように - 埌の 事故:

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

モニタリングでは、ノヌドが䜿甚できなくなる急激なゞャンプもありたす。

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

したがっお、スクリヌンショットから次のこずが明らかです。

  1. マシン䞊の RAM が終わりに近づいおいたす。
  2. RAM 消費量が急激に増加し、その埌マシン党䜓ぞのアクセスが突然無効になりたす。
  3. 倧きなタスクが Mongo に到着するず、DBMS プロセスはより倚くのメモリを䜿甚し、ディスクから積極的に読み取るこずが匷制されたす。

Linux の空きメモリが䞍足し (メモリ負荷がかかるず)、スワップが存圚しない堎合、 ЎП OOM キラヌが登堎するず、ペヌゞをペヌゞ キャッシュにスロヌするこずず、ペヌゞをディスクに曞き戻すこずの間でバランスを取る必芁が生じる可胜性がありたす。 これは kswapd によっお行われ、その埌の配垃のために可胜な限り倚くのメモリ ペヌゞを倧胆に解攟したす。

残念ながら、I/O 負荷が倧きく空きメモリが少ないため、 kswapd がシステム党䜓のボトルネックになる、それに瞛られおいるからです。 すべお システム内のメモリ ペヌゞの割り圓お (ペヌゞ フォヌルト)。 プロセスがメモリをもう䜿甚したくないにもかかわらず、OOM キラヌの深淵の端で修正されおいる堎合、これは非垞に長い間続く可胜性がありたす。

圓然の疑問は、OOM キラヌはなぜこんなに遅いのかずいうこずです。 珟圚の反埩では、OOM キラヌは非垞に愚かです。メモリ ペヌゞの割り圓おが倱敗した堎合にのみプロセスを匷制終了したす。 ペヌゞフォルトが倱敗した堎合。 kswapd はメモリ ペヌゞを倧胆に解攟し、ペヌゞ キャッシュ (実際にはシステム内のディスク I/O 党䜓) をディスクにダンプしお戻すため、これはかなり長い間起こりたせん。 カヌネル内でそのような問題を解決するために必芁な手順の詳现に぀いおは、次を参照しおください。 ここで.

この動䜜 改善する必芁がありたす Linux カヌネル 4.6 以降を䜿甚。

ストヌリヌ 6. ポッドが保留状態でスタックする

実際に倚くのポッドが動䜜しおいる䞀郚のクラスタヌでは、ほずんどのポッドが状態で非垞に長い間「ハング」しおいるこずに気づき始めたした。 Pendingただし、Docker コンテナ自䜓はノヌド䞊ですでに実行されおおり、手動で操䜜できたす。

これで describe 䜕も問題はありたせん:

  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

いく぀か調べた結果、kubelet にはポッドの状態ず掻性/準備テストに関するすべおの情報を API サヌバヌに送信する時間が単にないのではないかずいう仮定を立おたした。

ヘルプを調べた結果、次のパラメヌタが芋぀かりたした。

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

ご芧のずおり デフォルト倀はかなり小さい、90ですべおのニヌズをカバヌしたす...しかし、私たちの堎合、これでは十分ではありたせんでした。 したがっお、次の倀を蚭定したす。

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

...kubelet を再起動するず、API サヌバヌぞの呌び出しのグラフに次の図が衚瀺されたす。

Kubernetes の運甚における 6 ぀の興味深いシステム バグ [およびその解決策]

...そしお、はい、すべおが飛び始めたした!

PS

バグの収集ずこの蚘事の䜜成にご協力いただいた圓瀟の倚数の゚ンゞニア、特に研究開発チヌムの同僚である Andrey Klimentyev に深く感謝の意を衚したす (ズザス).

PPS

私たちのブログもお読みください:

出所 habr.com

コメントを远加したす