介绍一个支持案例,该案例涉及在执行”kubectl logs”时,输出”无法检索容器日志”的问题

首先

在Zettl提供Kubernetes作为一项服务给Yahoo! JAPAN。

The_Story_of_Managing_Common_Add-ons_on_1000__Kubernetes_Clusters_-_Speaker_Deck.png

当用户使用Kubernetes作为服务时,可能会出现一些用户自己无法理解的情况,导致需要提出咨询的情况。

这次我们将对一件处理的支持案例进行整理,将信息整理成外部人员也能理解的形式,并写成了一篇文章。

記事中提到的版本等信息是通过样本提供的,不是真实环境中发生的。此事件在Kubernetes v1.8及以上版本中是可以再现的。

利用者向我们提出的询问内容

有人问我以下问题。

当尝试使用 kubectl logs 获取日志时,会输出错误消息。
$ kubectl logs <Pod名称> 无法获取容器的日志,容器为 containerd://4703b9db4177926a2c963fa9e635e5931587f6e0e3b7d7165e94c6f4689d5c35
请告知无法获取容器日志的原因和解决方法。

利用者希望通过执行kubectl logs命令来获取以下日志消息,但由于出现错误,希望了解原因和解决方法。

$ kubectl logs <Pod名>                        
Hello, Kubernetes!

初動

当出现支持升级时,许多情况下会执行以下操作。

    • 利用者へのヒアリング

 

    k8sのクラスタに残っている情報の収集

对用户进行调查

当实际有需要来寻求支持的情况时,通常只能提供简明扼要的内容,不足以提供所需的充分信息进行调查。因此,为了缩小调查范围,我们会进行以下类似的询问。

    • Kubernetes上でどういったサービスを提供しているか (Webアプリケーション?バッチ処理?)

 

    • サービスのアーキテクチャ

 

    • 事象の発生時刻や発生頻度

 

    • 事象の再現性の有無

 

    • 事象発生前後での変更内容

 

    etc

根据不同的情况,有不同的听取内容,但会向用户确认类似上述的事项。

在许多确认的信息中,目前所关注的是用户在这次事务中主要使用Kubernetes的Job。 (在实际调查时,这一点尚不清楚。)

这里的做法也适用于OSS,在提出问题时,需要按照以下模板填写必要事项。

以下是OSS起票的模板参考:

Kubernetes的起票模板
Node-exporter的起票模板
kube-state-metrics的起票模板

k8sのクラスタに残っている情報の収集

因为此次查询是关于执行 “kubectl logs” 命令,所以首先需要收集相关 Pod 的信息。

$ kubectl logs <Pod名>

Podの情報を収集
kubectl get -oyaml kubectl describe etc
Podの情報を確認するとownerReferencesからJobとCronJobを利用していることが分かったので合わせて情報を取集します。

Job,CronJobのの情報を収集
kubectl get -oyaml kubectl describe etc
合わせて利用者がapplyしたマニフェストの情報も確認します。

利用者のGithub上のマニフェストの情報を収集
Podの情報から起動していたノードも確認できるので、Kuberntes上のノードオブジェクトの情報も収集します。

ノードの情報を収集
kubectl get -oyaml kubectl describe etc

Control Planeのログの収集
kube-apiserver kubelet etc のログ

Eventの情報を収集
kubectl get events Eventrouter etc
Kubernetesのトラブルシュートの際にはよく取得する情報になります。

如果我对 Kubernetes 有任何疑问,我通常会获取类似上述的信息。

考虑到用户能够自行解决问题的情况,最理想的情况是用户能够理解各自的需求并自行进行信息收集。然而,直接实现这一目标是困难的,因此我们首先会提供关于如何让用户们仅凭自身能力解决问题的建议。

此外,还有一种情况是事件发生的概率较低,并且在k8s集群内已经没有与该事件相关的信息。在这种情况下,我们会说明信息搜集的方法,并且可能需要等待再次发生。

对无法检索到容器日志的原因进行调查

关于调查方法的说明

首先,需要执行kubectl logs命令,以了解每个组件执行的处理过程。

Getting_Started_with_Kubernetes_Observability_-_Speaker_Deck.png

由于我已经提前知道kubectl logs的处理是如上所述的,所以我将检查以下相关组件。

    • API Server

 

    • Kubelet

 

    Container runtime (今回のケースでは Containerdを使用)

在研究上述内容时,有两种主要的方法。

    • ソースコード上で検索をかけてunable to retrieve container logsの出力箇所を確認する

 

    事象発生時刻付近の該当ログを確認していく

关于上述的两个问题,我们将同时进行两项措施来确认事实,而不是选择其中的一个。

调查结果的说明

当我查看日志等信息时,我发现kubectl命令执行时显示的消息是在kubelet的以下部分输出的。

// GetContainerLogs returns logs of a specific container.
func (m *kubeGenericRuntimeManager) GetContainerLogs(ctx context.Context, pod *v1.Pod, containerID kubecontainer.ContainerID, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) (err error) {
	status, err := m.runtimeService.ContainerStatus(containerID.ID)
	if err != nil {
		klog.V(4).InfoS("Failed to get container status", "containerID", containerID.String(), "err", err)
		return fmt.Errorf("unable to retrieve container logs for %v", containerID.String())
	}
	return m.ReadLogs(ctx, status.GetLogPath(), containerID.ID, logOptions, stdout, stderr)
}

参考链接中的代码段可以用以下方式进行中文翻译:

“`go
// 获取容器对应Kubernetes Pod的UID
podUID := containerStatus.PodSandboxId
// 检查container是否开启sandbox对容器的隔离功能
if !containerRuntime.useSandbox {
// 不使用sandbox时直接返回Pod的UID
return podUID, nil
}

// 获取对应PodSandbox的UUID
podSandboxUUID, err := containerRuntime.podManager.GetPodSandboxAdditionalInfo(podUID, “uuid”)
if err != nil {
return “”, err
}

// 返回PodSandbox的UUID
return podSandboxUUID, nil
“`

请注意,此处提供的是代码段的中文翻译,不包含完整上下文,可能会导致意思不够准确。翻译结果仅供参考,请根据实际需求进行调整。

从上述处理可以看出,当Kubelet向Containerd查询<容器ID>时,很明显无法获取到容器信息,因此导致了错误。

以下是每个组件实际输出的日志。

kubectl logs実行時のkubeletログ

Apr 07 09:23:18 <ノード名> docker[5013]: E0407 09:23:18.813824    5126 remote_runtime.go:597] "ContainerStatus from runtime service failed" err="rpc error: code = NotFound desc = an error occurred when try to find container \"<コンテナID>\": not found" containerID="<コンテナID>"
Apr 07 09:23:18 <ノード名> docker[5013]: I0407 09:23:18.816897    5126 log.go:184] http: superfluous response.WriteHeader call from k8s.io/kubernetes/vendor/github.com/emicklei/go-restful.(*Response).WriteHeader (response.go:220)

kubectl logs実行時のcontainerdログ

Apr 07 09:23:18 <ノード名> containerd[4556]: time="2023-04-07T09:23:18.813255753Z" level=error msg="ContainerStatus for \"<コンテナID>\" failed" error="rpc error: code = NotFound desc = an error occurred when try to find container \"<コンテナID>\": not found"

如果我们知道目标容器的ID,可以使用nerdctl等命令来确认在上述情况下该容器在Containerd上的状态。

$ sudo nerdctl ps -a
CONTAINER    ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES  
                    :                                         

通过以上结果,我们确认了不存在相关的容器。

我发现输出“kubectl logs无法检索容器日志”的错误消息是因为“在Containerd上不存在具有相应容器ID的容器”。

接下来要讨论的问题是,为什么没有集装箱呢?

Kubernetes研究的难度

kubectl logsの処理を順番に追っていくことでunable to retrieve container logsがどこで出力されており、それが該当のコンテナが存在しないことに起因していることが分かりました。

ただ、これだけでは本当の原因である「該当のコンテナが消えている原因」が分かりません。

在Kubernetes中,由于多个处理过程是异步执行的,所以仅对部分处理进行追踪无法完全了解整体情况的情况很常见。

研究此容器消失的原因。

调查方式 chá shì)

调查方案的方法

在这种情况下,我想可能有以下的调查方法。

過去の問い合わせ内容等のナレッジを確認する
自分達が過去にサポートした際のissueやKubernetes公式のGithubのissueや公式のSlack等の情報を確認することで、解決のヒントを得られることがあります。

ソースコードから処理を確認していく
ソースコードを読んでいって該当の処理が起こりそうな箇所を特定するというやり方もあるかと思います。ただこの方法はKubernetesに対するアプローチとしては難しいと思います。
Kubernetesのコード量が多いのと、該当する処理を読んだとしてそれが今回の事象につながる箇所だと気付けるかというと難しいと思います。たまたま自分が知っているKubernetesのコードの箇所で該当しそうな箇所があったら読み直してみるぐらいが使い所で、安定して使える方法ではないと思います。

再現条件を特定する
事象の再現条件を特定して、再現した結果を元に原因を調査する方法もあります。多くの場合の調査としてはこの方法を実施します。

实际的调查方法

本次案例中实际采取的调查方法如下所示。

    1. 确认过去的咨询内容等的知识。

 

    1. 关于这次事务,我确认了Kubernetes官方的问题等,但没有找到相关内容。

 

    1. 确定复现条件。

 

    通过与用户讨论其环境和使用方法,确定复现条件并进行原因调查是本次进展的流程。

再現方法的具體定義

环境建立

実際の商用環境で色々と実験をするわけにはいかないので、手元で自由に操作できる環境を構築します。

确认用户环境中发生的事件的版本信息,并在相同版本上构建环境。由于不同版本的组合可能导致不发生相关事件,这是一个重要的基本操作。

本次事件根据情况,将使用以下命令获取Kubelet和Containerd的相关版本信息,并创建相同的环境。

请你给我一杯水,谢谢。
– 请你帮我拿一杯水,非常感谢。

$ kubectl get node -o wide
NAME     STATUS   ROLES    AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
<ノード名>   Ready    <none>   41m     v1.23.9   <IPアドレス>   <none>        Ubuntu 20.04.5 LTS   5.4.0-139-generic   containerd://1.6.16
$ runc --version
runc version 1.1.4
commit: v1.1.4-0-g5fd4c4d
spec: 1.0.2-dev
go: go1.18.10
libseccomp: 2.5.1

在有关再现的信息中进行筛选

根据收集到的信息和进行问询,我们将提出假设,并试图通过试验和错误来重现事件。

只是漫不经心地查看整个Kubernetes的信息会很庞大,并且需要很长时间,因此我们会给需要确认的信息设定优先级并筛选。

对组件进行筛选

由于这个话题涉及到与容器相关的操作,所以很可能是kubelet或containerd造成的影响较大,因此我们决定优先查看与这些组件相关的信息。

image.png

然而,在这个时点上,我们无法消除其他进程删除容器的担忧,但由于信息量较大,我们需要设定优先级并逐步审视信息,因此我们制定了这样的方针。

只需一种选择:按时区筛选

当查看无法检索容器日志的详细信息时,发现以下内容:

$ kubectl describe po <Pod名>
         :
Status:           Succeeded
Containers:
  <コンテナ名>:
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Mon, 10 Apr 2023 18:27:25 +0900
      Finished:     Mon, 10 Apr 2023 18:27:25 +0900

該当のコンテナのプロセスがExit Codeの0を返してきて、それがPodの情報として記録されているということは、その時間帯までは該当のコンテナのプロセスが存在していたことを示しています。

そのことから以下の時間帯に何かがあったのだろうということが推測されるので、該当時間に絞って調査していく方針としました。

    • 開始:Jobで管理するPodのFinishedに記載されている時刻

 

    終了:利用者がkubectl logsを実行してunable to retrieve container logsを確認した時刻

仮説

該当時間のkubeletのログを1行ずつ確認していると気になるログを見つけました。

eviction_manager.go:338] "Eviction manager: attempting to reclaim" resourceName="ephemeral-storage"

我们根据上述情况推测,可能是由于DiskPressure导致了驱逐,从而导致了本次事件的发生。

検証

    nodeSelectorで対象ノードを固定してログを出力するJobを用意する
apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  backoffLimit: 1
  parallelism: 5
  completions: 20
  template:
    spec:
      nodeSelector:
        kubernetes.io/hostname: <ノード名>
      containers:
      - name: hello
        image: busybox
        command: ["echo", "Hello, Kubernetes!"]
      restartPolicy: Never

Disk Pressureがクラスタ全体で発生するとAPI Serverに接続しづらくなったり、確認すべき対象のノードが複数になったりして確認しにくいので、確認しやすくするために対象ノードの限定しています。

为了使事情更容易发生,我会部署大约20个Pod进行试验。

    ログが正常に取得できることを確認します。
$ kubectl logs -l job-name=hello
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
Hello, Kubernetes!
    対象のノードがDisk Pressureの状態になるように、ディスク容量を消費するJobを用意する
apiVersion: v1
kind: ConfigMap
metadata:
  name: scripts
data:
  test.sh: |
    #!/bin/sh
    dd if=/dev/zero of=5G.dummy bs=1M count=5000
---
apiVersion: batch/v1
kind: Job
metadata:
  name: disk-pressure-test
spec:
  backoffLimit: 3
  parallelism: 1
  completions: 15
  template:
    spec:
      nodeSelector:
        kubernetes.io/hostname: <ノード名>
      containers:
      - name: disk-pressure-test
        image: busybox
        command: ["/bin/sh", "/shell/test.sh"]
        volumeMounts:
        - name: config-volume
          mountPath: /shell
      volumes:
      - name: config-volume
        configMap:
          name: scripts
          items:
            - key: test.sh
              path: test.sh
      restartPolicy: Never
    Evictedが発生することを確認
$ kubectl get po
NAME                       READY   STATUS      RESTARTS   AGE
disk-pressure-test-4h6wj   0/1     Completed   0          36s
disk-pressure-test-6b684   0/1     Completed   0          57s
disk-pressure-test-g6j92   0/1     Completed   0          80s
disk-pressure-test-kf8k5   0/1     Completed   0          91s
disk-pressure-test-n4rgw   0/1     Evicted     0          20s
disk-pressure-test-pvms4   0/1     Pending     0          10s
disk-pressure-test-rdx8v   0/1     Completed   0          69s
disk-pressure-test-s9m2q   0/1     Evicted     0          20s
disk-pressure-test-z2zth   0/1     Completed   0          46s
hello-26fmz                0/1     Completed   0          6m7s
         :
    Evictedの原因がDisk Pressureであることを確認
$ kubectl describe po disk-pressure-test-n4rgw 
Name:             disk-pressure-test-n4rgw
   :
Status:           Failed
Reason:           Evicted
Message:          Pod The node had condition: [DiskPressure]. 
   :
Events:
  Type     Reason     Age   From               Message
  ----     ------     ----  ----               -------
  Normal   Scheduled  29s   default-scheduler  Successfully assigned job-test/disk-pressure-test-n4rgw to demo-yosshi-cj-w-default-03a7f1b0-fqtj8
  Warning  Evicted    30s   kubelet            The node had condition: [DiskPressure].
    この状態でkubectl logsを実行してログを取得できるか確認
$ kubectl logs -l job-name=hello
unable to retrieve container logs for containerd://a39e46811fd9cb371853d60f27a40321428a1d0dff2a6587a0e2207d225dfcba

unable to retrieve container logsのメッセージが取得でき、申告にあった状態を再現することに成功

對於結果進行更深入的探索

查看kubelet的日志记录

手元で再現ができるようになると、ノイズが少ない状態のログを取得できるようになります。

Disk Pressure発生時のkubeletのログ

I0214 08:38:39.199718    5148 eviction_manager.go:338] "Eviction manager: attempting to reclaim" resourceName="ephemeral-storage"
I0214 08:38:39.201508    5148 container_gc.go:85] "Attempting to delete unused containers"
I0214 08:38:39.203363    5148 scope.go:110] "RemoveContainer" containerID="aa176ae956aa102c5a65b83c9c21f95f5cb19adf77e1205b189663252fd859d7"
I0214 08:38:39.212115    5148 scope.go:110] "RemoveContainer" containerID="7a0531f9dea7aeac6d18eece63bc6127cabd744bbd993feade307dcd27ad5e87"
I0214 08:38:39.219660    5148 scope.go:110] "RemoveContainer" containerID="5f6f972e2110d22004f6d6cb65ef762b4263e53fa45a3fa34dd33dc393a5399e"
I0214 08:38:39.282208    5148 scope.go:110] "RemoveContainer" containerID="4471fe782da67ad8ac3871108b45eb9342d85a3156996534d55d7f450b5dd352"
I0214 08:38:39.301176    5148 scope.go:110] "RemoveContainer" containerID="095d21344362bda473daf55c9835a5252f21bd68934ba93b0ffe947f8a129c63"
I0214 08:38:39.315522    5148 scope.go:110] "RemoveContainer" containerID="48a095de20e37a4ad729eb15372dfd617fe6c0373b1cb67d6705f186a9cf08a6"
I0214 08:38:39.617581    5148 image_gc_manager.go:327] "Attempting to delete unused images"
I0214 08:38:39.722762    5148 eviction_manager.go:345] "Eviction manager: able to reduce resource pressure without evicting pods." resourceName="ephemeral-storage"

ログにはeviction_manager.go:338のように、ログを出力した該当のソースコードの情報が記載されているので、該当のソースコードを順番に読んでいくことで実際に起こった処理の内容を把握していくことができます。

进行源代码阅读

当查看日志中的源代码时,情况如下。

    eviction_manager.go:338
	// rank the thresholds by eviction priority
	sort.Sort(byEvictionPriority(thresholds))
	thresholdToReclaim, resourceToReclaim, foundAny := getReclaimableThreshold(thresholds)
	if !foundAny {
		return nil
	}
	klog.InfoS("Eviction manager: attempting to reclaim", "resourceName", resourceToReclaim)

ref https://github.com/kubernetes/kubernetes/blob/v1.23.9/pkg/kubelet/eviction/eviction_manager.go#L332-L338

    container_gc.go:85
func (cgc *realContainerGC) DeleteAllUnusedContainers() error {
	klog.InfoS("Attempting to delete unused containers")
	return cgc.runtime.GarbageCollect(cgc.policy, cgc.sourcesReadyProvider.AllReady(), true)
}

似乎发现了在发生磁盘压力时终止容器的删除操作。

确认实施的经过

由于它是开源软件,所以有很多我不了解的事情,但往往包含了有关变更的PR的参考信息,因此我会使用git blame等工具进行确认。

    git blame
Blaming_kubernetes_container_gc_go_at_v1_23_9_·kubernetes_kubernetes·_GitHub.png
    変更時のPR
Delete_all_dead_containers_and_sandboxes_when_under_disk_pressure__by_dashpole_·Pull_Request__45896·_kubernetes_kubernetes.png

ref https://github.com/kubernetes/kubernetes/pull/45896

変更時のPRを読んでいくと、Kubernetes v1.8からDisk Pressure発生時にterminatedのコンテナを削除する処理が実装されていることがわかります。変更理由がDisk Pressure発生時に使ってないコンテナを削除することでディスクの空き容量を増やすための実施しているということもわかりました。

对原因的思考

Kubernetes v1.8からDisk Pressure発生時にterminatedのコンテナを削除する処理が実装されているため、以下の条件が重なった時にunable to retrieve container logsが出力されると考えられます。

    • terminatedのコンテナが存在する

 

    • ノードでDisk PressureによるEvictionが発生する

kubectl logsでPodのログを参照する

該当機能がKubernetes v1.8から実装されているにも関わらずに、新規のサポートケースとして問い合わせがきたのかについても少し考えてみます。(このサポートケースが他のユースケースでもよく起こるものなのか考える)

KubernetesのユースケースではWebアプリケーションを動かすケースが多く、Deploymentを使用するケースが多数を占めます。その場合はPodのステータスがRunningになっており、コンテナの状態もterminatedではないという場合が多くなります。

そのため、上記の条件を全部満たすというのがあまり多くないので、今回が初めてのサポートケースになったのだと考えられます。

确认事实

由于上述内容是基于我手中的确认内容的假设,所以目前还没有确切的证实是否为实际发生的事实。

因此,我们要确认从用户环境中获取的kubelet是否输出了与我手边创建Disk Pressure时kubelet日志中相同的内容。

    再掲:Disk Pressure発生時のkubeletのログ
I0214 08:38:39.199718    5148 eviction_manager.go:338] "Eviction manager: attempting to reclaim" resourceName="ephemeral-storage"
I0214 08:38:39.201508    5148 container_gc.go:85] "Attempting to delete unused containers"
I0214 08:38:39.203363    5148 scope.go:110] "RemoveContainer" containerID="aa176ae956aa102c5a65b83c9c21f95f5cb19adf77e1205b189663252fd859d7"
I0214 08:38:39.212115    5148 scope.go:110] "RemoveContainer" containerID="7a0531f9dea7aeac6d18eece63bc6127cabd744bbd993feade307dcd27ad5e87"
I0214 08:38:39.219660    5148 scope.go:110] "RemoveContainer" containerID="5f6f972e2110d22004f6d6cb65ef762b4263e53fa45a3fa34dd33dc393a5399e"
I0214 08:38:39.282208    5148 scope.go:110] "RemoveContainer" containerID="4471fe782da67ad8ac3871108b45eb9342d85a3156996534d55d7f450b5dd352"
I0214 08:38:39.301176    5148 scope.go:110] "RemoveContainer" containerID="095d21344362bda473daf55c9835a5252f21bd68934ba93b0ffe947f8a129c63"
I0214 08:38:39.315522    5148 scope.go:110] "RemoveContainer" containerID="48a095de20e37a4ad729eb15372dfd617fe6c0373b1cb67d6705f186a9cf08a6"
I0214 08:38:39.617581    5148 image_gc_manager.go:327] "Attempting to delete unused images"
I0214 08:38:39.722762    5148 eviction_manager.go:345] "Eviction manager: able to reduce resource pressure without evicting pods." resourceName="ephemeral-storage"

問い合わせ時に十分に情報が取得できていない場合は、この工程は実施できない場合がありますが、できればやっておきたい内容になります。

原因に対する対処

unable to retrieve container logsの原因については分かりましたが、原因が分かっただけでは利用者としては以下に2つの課題が依然として存在します。

kubectl logsでコンテナのログが取得できない

Disk Pressureが定期的に発生している

それぞれに対する対処方法を考えていきます。

「kubectl logsでコンテナのログが取得できない」件の対処

そもそもの話になってしまいますが、kubectl logsが該当のコンテナのログを必ず取得できることを保証するような設計になっていません。

コンテナのログについては各ノードに閉じたものになっているので、以下のようにローリングアップデート時に該当のサーバがなくなると取得できなくなります。

Kubernetes_Logging入門_pdf_-_Speaker_Deck.png

Jobが完了して、terminatedになっているコンテナについては、UnusedContainersとソースコード上で書かれたりしており、そもそも使用していないものとして扱われています。

そのため、ログが取得できることを期待する場合は以下のようにLogging Beckend(e.g. Grafana Loki)を使用する必要があります。

Kubernetes_Logging入門_pdf_-_Speaker_Deck.png

kubectl logs只是一个辅助命令,在商业运营中如果要集成日志记录,建议用户考虑使用日志后端,并进行指导。

对于「磁盘压力定期发生」的处理措施。

Disk Pressureが指しているDiskは、kubernetes上ではnodefsとimagefsの2つになります。
それぞれ以下のようなものが含まれます。

    1. 节点文件系统

 

    1. 本地磁盘卷

 

    1. 空目录

 

    1. 日志存储

 

镜像文件系统
容器镜像
容器可写层

ref https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/

もちろんクラスタ全体のディスク使用量を増やすというアプローチもありますが、原因に合わせて対策しないと効果があまり出ないケースもあるので注意してください。

nodefsの対策

如果由于Node.js文件系统模块(nodefs)的原因导致了磁盘压力的发生,可以考虑以下对策。

    • resource.requests/limitの設定見直し

 

    PVの利用の検討
资源请求/限制设置的审查和调整

在设置ephemeral storage的requests/limits时,请注意以下两点。

    • requestsを設定する

 

    • ephemeral storageに関しては設定されていないケースが結構多く、設定していたら防げたなというケースを見ることも時々あります。

 

    • 運用時に定期的にrequests/limitの見直しを行う

 

    初回リリース時は設定したけど、機能追加等により使用するリソースが増えるケースが結構出てくると思います。その際にrequests/limitを修正しているケースは少ない印象を受けているので、できれば見直ししたほうが良いです。

参考:Kubernetes 的临时存储

PVの利用の検討

ノード上のディスクで足りない場合はPVを使って外部ストレージを使用する方法があります。
CPUやメモリの増加量としてディスクの増加量が多い場合は、外部ストレージの利用を検討するという選択肢があります。

通常のPV以外にもGeneric Ephemeral Volumeを使うという選択肢もあるので検討してみるという方法もあります。

参考:Kubernetes: CSI を使ったEphemeral Volumeの動作検証

图像文件系统的防御措施

如果出现由于图像文件系统引起的磁盘压力问题,可以考虑以下对策。

    • Image GCの設定の見直し

 

    emptyDirの利用の検討
重新审查Image GC的设置。

Kubelet 在图像垃圾收集方面有一个机制,根据以下设置执行垃圾收集。

    • imageMinimumGCAge: デフォルト2min

 

    • imageGCHighThresholdPercent : デフォルト 85%

 

    imageGCLowThresholdPercent: デフォルト 80%
	if obj.ImageMinimumGCAge == zeroDuration {
		obj.ImageMinimumGCAge = metav1.Duration{Duration: 2 * time.Minute}
	}
	if obj.ImageGCHighThresholdPercent == nil {
		// default is below docker's default dm.min_free_space of 90%
		obj.ImageGCHighThresholdPercent = utilpointer.Int32Ptr(85)
	}
	if obj.ImageGCLowThresholdPercent == nil {
		obj.ImageGCLowThresholdPercent = utilpointer.Int32Ptr(80)

参考链接 https://github.com/kubernetes/kubernetes/blob/v1.23.9/pkg/kubelet/apis/config/v1beta1/defaults.go#L135-L143

Please note that the provided link is a specific location in the GitHub repository of Kubernetes, and cannot be paraphrased directly.

当使用量高于HighThresholdPercent时,Image GC 会按照从旧到新的顺序,删除图像,直到使用量低于或等于LowThresholdPercent。

在当前的默认设置中,当磁盘使用率超过85%时,将从旧图像中删除以使磁盘使用率回到80%以下。

在Kubernetes的情况下,每3到4个月会发布一个次要版本,并且旧的次要版本将被弃用。为了跟随这一情况,建议定期使用丢弃节点的方式进行滚动更新,这样每个节点的寿命通常最长为4个月。

使用Web应用程序作为主体时,在四个月的时间内,大量拉取图像的情况不会那么常见,所以即使使用默认值也不会出现太多问题。

然而,有些人更倾向于以Job为主体来使用,而不是多次执行相同的容器镜像,这种使用方式会导致容器镜像的使用量急剧增加。

在这个支持案例中,这个因素所占比例很高。(只是简单地解决这个问题并不能保证一切都会好转。)

参考:Kubernetes 中的垃圾回收处理

考虑使用emptyDir

如果要使用容器可写层,请考虑使用emptyDir。

由于emptyDir是以Pod为单位进行管理的,所以如果容器崩溃,写入容器可写层的数据将与容器一起消失,但emptyDir将会继承Pod级别的管理。此外,如果由于某种原因而仍保留旧容器,那么写入容器的可写层的数据将不会消失,而会占用磁盘空间。

我认为,使用 emptyDir 比使用容器的可写层更符合要求的情况更多,因为 emptyDir 还可以使用 sizeLimit 等功能。

最后

由于经常听到有人说他们对Kubernetes的故障排除不太了解,所以我决定为外部撰写一份总结。

这次我写了一个花了一些时间的案例,所以文章会比较长,但在升级调查时,我认为调查的流程通常都是一样的。

如果需要,在Kubernetes故障排除时,可以参考一下这个。

bannerAds