日誌架構

應用程式日誌可以協助您瞭解應用程式內部發生的情況。日誌對於偵錯問題和監控叢集活動特別有用。大多數現代應用程式都有某種日誌記錄機制。同樣地,容器引擎的設計旨在支援日誌記錄。容器化應用程式最簡單且最常用的日誌記錄方法是寫入標準輸出和標準錯誤串流。

然而,容器引擎或執行期提供的原生功能通常不足以構成完整的日誌記錄解決方案。

例如,如果容器崩潰、Pod 被驅逐或節點死亡,您可能需要存取應用程式的日誌。

在叢集中,日誌應該具有獨立於節點、Pod 或容器的個別儲存空間和生命週期。此概念稱為 叢集層級日誌記錄

叢集層級日誌記錄架構需要一個獨立的後端來儲存、分析和查詢日誌。Kubernetes 沒有為日誌資料提供原生儲存解決方案。相反地,有許多與 Kubernetes 整合的日誌記錄解決方案。以下章節說明如何在節點上處理和儲存日誌。

Pod 和容器日誌

Kubernetes 從正在執行的 Pod 中的每個容器擷取日誌。

此範例使用 Pod 的 Manifest,其中包含一個容器,該容器每秒將文字寫入標準輸出串流一次。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

若要執行此 Pod,請使用以下命令

kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml

輸出為

pod/counter created

若要擷取日誌,請使用 kubectl logs 命令,如下所示

kubectl logs counter

輸出類似於

0: Fri Apr  1 11:42:23 UTC 2022
1: Fri Apr  1 11:42:24 UTC 2022
2: Fri Apr  1 11:42:25 UTC 2022

您可以使用 kubectl logs --previous 從容器先前執行個體中擷取日誌。如果您的 Pod 有多個容器,請指定您要存取哪個容器的日誌,方法是在命令中附加容器名稱和 -c 旗標,如下所示

kubectl logs counter -c count

容器日誌串流

功能狀態: Kubernetes v1.32 [alpha] (預設啟用:false)

作為 Alpha 功能,kubelet 可以將容器產生的兩個標準串流中的日誌分開:標準輸出標準錯誤。若要使用此行為,您必須啟用 PodLogsQuerySplitStreams 功能閘道。啟用該功能閘道後,Kubernetes 1.32 允許透過 Pod API 直接存取這些日誌串流。您可以透過指定串流名稱 (StdoutStderr 其中之一),使用 stream 查詢字串來擷取特定串流。您必須具有讀取該 Pod 的 log 子資源的權限。

為了示範此功能,您可以建立一個 Pod,定期將文字寫入標準輸出和錯誤串流。

apiVersion: v1
kind: Pod
metadata:
  name: counter-err
spec:
  containers:
  - name: count
    image: busybox:1.28
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; echo "$i: err" >&2 ; i=$((i+1)); sleep 1; done']

若要執行此 Pod,請使用以下命令

kubectl apply -f https://k8s.io/examples/debug/counter-pod-err.yaml

若要僅擷取 stderr 日誌串流,您可以執行

kubectl get --raw "/api/v1/namespaces/default/pods/counter-err/log?stream=Stderr"

請參閱 kubectl logs 文件 以取得更多詳細資訊。

節點如何處理容器日誌

Node level logging

容器執行期處理並重新導向任何產生到容器化應用程式的 stdoutstderr 串流的輸出。不同的容器執行期以不同的方式實作此功能;但是,與 kubelet 的整合已標準化為CRI 日誌記錄格式

預設情況下,如果容器重新啟動,kubelet 會保留一個終止的容器及其日誌。如果 Pod 從節點中驅逐,則所有對應的容器也會被驅逐,以及其日誌。

kubelet 透過 Kubernetes API 的特殊功能,使日誌可供用戶端使用。存取此功能的常用方法是執行 kubectl logs

日誌輪替

功能狀態: Kubernetes v1.21 [stable]

kubelet 負責輪替容器日誌和管理日誌記錄目錄結構。kubelet 將此資訊傳送至容器執行期 (使用 CRI),而執行期將容器日誌寫入給定的位置。

您可以使用 kubelet 組態檔,設定兩個 kubelet 組態設定containerLogMaxSize (預設值為 10Mi) 和 containerLogMaxFiles (預設值為 5)。這些設定可讓您分別設定每個日誌檔的最大大小和每個容器允許的最大檔案數。

為了在工作負載產生大量日誌的叢集中執行有效率的日誌輪替,kubelet 也提供了一種機制來調整日誌輪替的方式,包括可以執行的並行日誌輪替次數以及監視和輪替日誌的間隔 (如果需要)。您可以使用 kubelet 組態檔,設定兩個 kubelet 組態設定containerLogMaxWorkerscontainerLogMonitorInterval

當您如基本日誌記錄範例中執行 kubectl logs 時,節點上的 kubelet 會處理請求並直接從日誌檔讀取。kubelet 會傳回日誌檔的內容。

系統元件日誌

系統元件有兩種型別:通常在容器中執行的元件,以及直接參與執行容器的元件。例如

  • kubelet 和容器執行期不在容器中執行。kubelet 執行您的容器 (在 Pod 中分組在一起)
  • Kubernetes 排程器、控制器管理員和 API 伺服器在 Pod 內執行 (通常是 靜態 Pod)。etcd 元件在控制平面中執行,最常見的情況下也作為靜態 Pod 執行。如果您的叢集使用 kube-proxy,您通常會將其作為 DaemonSet 執行。

日誌位置

kubelet 和容器執行期寫入日誌的方式取決於節點使用的作業系統

在使用 systemd 的 Linux 節點上,kubelet 和容器執行期預設會寫入 journald。您可以使用 journalctl 來讀取 systemd 日誌;例如:journalctl -u kubelet

如果 systemd 不存在,kubelet 和容器執行期會寫入 /var/log 目錄中的 .log 檔案。如果您希望將日誌寫入其他位置,您可以透過輔助工具 kube-log-runner 間接執行 kubelet,並使用該工具將 kubelet 日誌重新導向到您選擇的目錄。

預設情況下,kubelet 會指示您的容器執行期將日誌寫入 /var/log/pods 內的目錄中。

如需 kube-log-runner 的更多資訊,請參閱系統日誌

預設情況下,kubelet 會將日誌寫入 C:\var\logs 目錄中的檔案 (請注意,這不是 C:\var\log)。

雖然 C:\var\log 是這些日誌的 Kubernetes 預設位置,但一些叢集部署工具會設定 Windows 節點以記錄到 C:\var\log\kubelet

如果您希望將日誌寫入其他位置,您可以透過輔助工具 kube-log-runner 間接執行 kubelet,並使用該工具將 kubelet 日誌重新導向到您選擇的目錄。

但是,預設情況下,kubelet 會指示您的容器執行期將日誌寫入 C:\var\log\pods 目錄內。

如需 kube-log-runner 的更多資訊,請參閱系統日誌


對於在 Pod 中執行的 Kubernetes 叢集元件,這些元件會寫入 /var/log 目錄內部的檔案,繞過預設的日誌記錄機制 (元件不會寫入 systemd 日誌)。您可以使用 Kubernetes 的儲存機制將持久儲存空間對應到執行元件的容器中。

Kubelet 允許將 Pod 日誌目錄從預設的 /var/log/pods 變更為自訂路徑。可以透過在 kubelet 的組態檔中設定 podLogsDir 參數來進行此調整。

如需 etcd 及其日誌的詳細資訊,請檢視 etcd 文件。同樣地,您可以使用 Kubernetes 的儲存機制將持久儲存空間對應到執行元件的容器中。

叢集層級日誌記錄架構

雖然 Kubernetes 沒有為叢集層級日誌記錄提供原生解決方案,但您可以考慮幾種常見的方法。以下是一些選項

  • 使用在每個節點上執行的節點層級日誌記錄代理程式。
  • 在應用程式 Pod 中包含專用的 Sidecar 容器以進行日誌記錄。
  • 從應用程式內部將日誌直接推送至後端。

使用節點日誌記錄代理程式

Using a node level logging agent

您可以透過在每個節點上包含節點層級日誌記錄代理程式來實作叢集層級日誌記錄。日誌記錄代理程式是一種專用工具,可公開日誌或將日誌推送至後端。通常,日誌記錄代理程式是一個容器,可以存取包含該節點上所有應用程式容器的日誌檔的目錄。

由於日誌記錄代理程式必須在每個節點上執行,因此建議將代理程式作為 DaemonSet 執行。

節點層級日誌記錄每個節點僅建立一個代理程式,且不需要對節點上執行的應用程式進行任何變更。

容器寫入 stdout 和 stderr,但沒有一致的格式。節點層級代理程式收集這些日誌並轉發以進行彙總。

將 Sidecar 容器與日誌記錄代理程式搭配使用

您可以使用 Sidecar 容器,透過以下方式之一

  • Sidecar 容器將應用程式日誌串流至其自己的 stdout
  • Sidecar 容器執行日誌記錄代理程式,該代理程式設定為從應用程式容器中擷取日誌。

串流 Sidecar 容器

Sidecar container with a streaming container

透過讓您的 Sidecar 容器寫入其自己的 stdoutstderr 串流,您可以利用 kubelet 和已在每個節點上執行的日誌記錄代理程式。Sidecar 容器從檔案、Socket 或 journald 讀取日誌。每個 Sidecar 容器都會將日誌列印到其自己的 stdoutstderr 串流。

此方法可讓您分隔來自應用程式不同部分的數個日誌串流,其中某些串流可能缺乏寫入 stdoutstderr 的支援。重新導向日誌背後的邏輯非常簡單,因此不會造成顯著的額外負荷。此外,由於 stdoutstderr 由 kubelet 處理,因此您可以使用內建工具,例如 kubectl logs

例如,一個 Pod 執行單一容器,而該容器使用兩種不同的格式寫入兩個不同的日誌檔。以下是 Pod 的 Manifest

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

不建議將不同格式的日誌項目寫入相同的日誌串流,即使您設法將兩個元件都重新導向到容器的 stdout 串流。相反地,您可以建立兩個 Sidecar 容器。每個 Sidecar 容器都可以追蹤共用磁碟區中的特定日誌檔,然後將日誌重新導向到其自己的 stdout 串流。

以下是具有兩個 Sidecar 容器的 Pod 的 Manifest

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-1
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-2
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/2.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

現在,當您執行此 Pod 時,您可以透過執行以下命令來個別存取每個日誌串流

kubectl logs counter count-log-1

輸出類似於

0: Fri Apr  1 11:42:26 UTC 2022
1: Fri Apr  1 11:42:27 UTC 2022
2: Fri Apr  1 11:42:28 UTC 2022
...
kubectl logs counter count-log-2

輸出類似於

Fri Apr  1 11:42:29 UTC 2022 INFO 0
Fri Apr  1 11:42:30 UTC 2022 INFO 0
Fri Apr  1 11:42:31 UTC 2022 INFO 0
...

如果您在叢集中安裝了節點層級代理程式,則該代理程式會自動擷取這些日誌串流,而無需任何其他組態。如果您願意,您可以設定代理程式以根據來源容器剖析日誌行。

即使對於 CPU 和記憶體使用率低的 Pod (CPU 約為數毫核心,記憶體約為數 MB),將日誌寫入檔案,然後將其串流到 stdout 也可能會使您在節點上需要的儲存空間增加一倍。如果您的應用程式寫入單一檔案,建議您將 /dev/stdout 設定為目的地,而不是實作串流 Sidecar 容器方法。

Sidecar 容器也可以用於輪替應用程式本身無法輪替的日誌檔。此方法的一個範例是定期執行 logrotate 的小型容器。但是,更直接的方法是直接使用 stdoutstderr,並將輪替和保留原則留給 kubelet。

具有日誌記錄代理程式的 Sidecar 容器

Sidecar container with a logging agent

如果節點層級日誌記錄代理程式對於您的情況不夠彈性,您可以建立一個具有個別日誌記錄代理程式的 Sidecar 容器,您已將其特別設定為與您的應用程式一起執行。

以下是兩個範例 Manifest,您可以使用它們來實作具有日誌記錄代理程式的 Sidecar 容器。第一個 Manifest 包含 ConfigMap 以組態 fluentd。

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluentd.conf: |
    <source>
      type tail
      format none
      path /var/log/1.log
      pos_file /var/log/1.log.pos
      tag count.format1
    </source>

    <source>
      type tail
      format none
      path /var/log/2.log
      pos_file /var/log/2.log.pos
      tag count.format2
    </source>

    <match **>
      type google_cloud
    </match>    

第二個 Manifest 描述一個具有執行 fluentd 的 Sidecar 容器的 Pod。Pod 掛載一個磁碟區,fluentd 可以從該磁碟區中擷取其組態資料。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-agent
    image: registry.k8s.io/fluentd-gcp:1.30
    env:
    - name: FLUENTD_ARGS
      value: -c /etc/fluentd-config/fluentd.conf
    volumeMounts:
    - name: varlog
      mountPath: /var/log
    - name: config-volume
      mountPath: /etc/fluentd-config
  volumes:
  - name: varlog
    emptyDir: {}
  - name: config-volume
    configMap:
      name: fluentd-config

直接從應用程式公開日誌

Exposing logs directly from the application

從每個應用程式直接公開或推送日誌的叢集日誌記錄超出 Kubernetes 的範圍。

下一步

上次修改時間:2024 年 10 月 17 日下午 11:21 PST:doc: document the usage of the PodLogsQuerySplitStreams feature (387153787a)