本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

使用 Kubernetes 進行叢集層級記錄

Kubernetes 叢集通常會持續運作,執行許多系統和應用程式 Pod。系統管理員如何收集、管理和查詢系統 Pod 的日誌?使用者如何查詢由許多 Pod 組成的應用程式日誌,這些 Pod 可能會重新啟動或由 Kubernetes 系統自動產生?這些問題將由 Kubernetes 叢集層級記錄服務解決。

Kubernetes 的叢集層級記錄功能讓我們能夠收集超出 Pod 容器映像生命週期、Pod 生命週期甚至叢集生命週期的日誌。在本文中,我們假設已建立具有叢集層級記錄支援的 Kubernetes 叢集,以便將日誌傳送至 Google Cloud LoggingGoogle Container Engine (GKE) 叢集建立時,這是一個選項,並且預設為開放原始碼 Google Compute Engine (GCE) Kubernetes 發行版啟用。叢集建立後,您將擁有一系列系統 Pod,用於支援 Kubernetes 服務名稱的監控、記錄和 DNS 解析

$ kubectl get pods
NAME                                           READY     REASON    RESTARTS   AGE

fluentd-cloud-logging-kubernetes-minion-0f64   1/1       Running   0          32m

fluentd-cloud-logging-kubernetes-minion-27gf   1/1       Running   0          32m

fluentd-cloud-logging-kubernetes-minion-pk22   1/1       Running   0          31m

fluentd-cloud-logging-kubernetes-minion-20ej   1/1       Running   0          31m

kube-dns-v3-pk22                               3/3       Running   0          32m


monitoring-heapster-v1-20ej                    0/1       Running   9          32m

以下是相同的資訊,以圖片顯示 Pod 可能在特定節點上的放置方式。

以下是每個節點上執行內容的近距離檢視。

第一個圖表顯示在 GCE 叢集上建立的四個節點,每個 VM 節點的名稱都以紫色背景顯示。每個節點的內部和公用 IP 位址顯示在灰色方塊中,而每個節點中執行的 Pod 顯示在綠色方塊中。每個 Pod 方塊顯示 Pod 的名稱及其執行的命名空間、Pod 的 IP 位址以及作為 Pod 執行一部分執行的映像。在這裡,我們看到每個節點都執行一個 fluentd-cloud-logging Pod,該 Pod 收集在相同節點上執行的容器的日誌輸出,並將其傳送至 Google Cloud Logging。提供 叢集 DNS 服務 的 Pod 在其中一個節點上執行,而提供監控支援的 Pod 在另一個節點上執行。

為了協助說明叢集層級記錄的運作方式,我們先從合成日誌產生器 Pod 規格 counter-pod.yaml 開始

  apiVersion : v1  
  kind : Pod  
  metadata :  
    name : counter  
  spec :  
    containers :  
   - name : count  
      image : ubuntu:14.04  
      args : [bash, -c,   
            'for ((i = 0; ; i++)); do echo "$i: $(date)"; sleep 1; done']  

此 Pod 規格有一個容器,該容器在容器誕生時執行 bash 腳本。此腳本只會每秒寫出計數器的值和日期,並無限期執行。讓我們建立 Pod。


$ kubectl create -f counter-pod.yaml


pods/counter

我們可以觀察正在執行的 Pod

$ kubectl get pods
NAME                                           READY     REASON    RESTARTS   AGE

counter                                        1/1       Running   0          5m

fluentd-cloud-logging-kubernetes-minion-0f64   1/1       Running   0          55m

fluentd-cloud-logging-kubernetes-minion-27gf   1/1       Running   0          55m

fluentd-cloud-logging-kubernetes-minion-pk22   1/1       Running   0          55m

fluentd-cloud-logging-kubernetes-minion-20ej   1/1       Running   0          55m

kube-dns-v3-pk22                               3/3       Running   0          55m

monitoring-heapster-v1-20ej                    0/1       Running   9          56m

此步驟可能需要幾分鐘才能下載 ubuntu:14.04 映像,在此期間 Pod 狀態將顯示為 Pending。

其中一個節點現在正在執行計數器 Pod

當 Pod 狀態變更為 Running 時,我們可以使用 kubectl logs 命令來檢視此計數器 Pod 的輸出。

$ kubectl logs counter

0: Tue Jun  2 21:37:31 UTC 2015

1: Tue Jun  2 21:37:32 UTC 2015

2: Tue Jun  2 21:37:33 UTC 2015

3: Tue Jun  2 21:37:34 UTC 2015

4: Tue Jun  2 21:37:35 UTC 2015

5: Tue Jun  2 21:37:36 UTC 2015

此命令從在此容器中執行的映像的 Docker 日誌檔案中擷取日誌文字。我們可以連線到正在執行的容器並觀察正在執行的計數器 bash 腳本。

$ kubectl exec -i counter bash

ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

root         1  0.0  0.0  17976  2888 ?        Ss   00:02   0:00 bash -c for ((i = 0; ; i++)); do echo "$i: $(date)"; sleep 1; done

root       468  0.0  0.0  17968  2904 ?        Ss   00:05   0:00 bash

root       479  0.0  0.0   4348   812 ?        S    00:05   0:00 sleep 1

root       480  0.0  0.0  15572  2212 ?        R    00:05   0:00 ps aux

如果此 Pod 中的映像因任何原因被終止,然後由 Kubernetes 重新啟動,會發生什麼事?我們是否仍會看到先前容器調用中的日誌行,然後是已啟動容器的日誌行?或者我們會遺失原始容器執行中的日誌行,而只看到新容器的日誌行?讓我們找出答案。首先,讓我們停止目前正在執行的計數器。

$ kubectl stop pod counter

pods/counter


Now let’s restart the counter.


$ kubectl create -f counter-pod.yaml

pods/counter

讓我們等待容器重新啟動,並再次取得日誌行。

$ kubectl logs counter

0: Tue Jun  2 21:51:40 UTC 2015

1: Tue Jun  2 21:51:41 UTC 2015

2: Tue Jun  2 21:51:42 UTC 2015

3: Tue Jun  2 21:51:43 UTC 2015

4: Tue Jun  2 21:51:44 UTC 2015

5: Tue Jun  2 21:51:45 UTC 2015

6: Tue Jun  2 21:51:46 UTC 2015

7: Tue Jun  2 21:51:47 UTC 2015

8: Tue Jun  2 21:51:48 UTC 2015

糟糕!我們遺失了此 Pod 中容器第一次調用的日誌行!理想情況下,我們希望保留 Pod 中每個容器每次調用的所有日誌行。此外,即使 Pod 重新啟動,我們仍然希望保留 Pod 中容器發出的所有日誌行。但別擔心,這是 Kubernetes 中叢集層級記錄提供的功能。建立叢集時,可以使用在每個節點上執行的 Fluentd 代理程式將每個容器的標準輸出和標準錯誤輸出擷取到 Google Cloud Logging 或 Elasticsearch 中,並使用 Kibana 檢視。本部落格文章著重於 Google Cloud Logging。

當建立 Kubernetes 叢集並啟用記錄至 Google Cloud Logging 時,系統會在叢集的每個節點上建立一個名為 fluentd-cloud-logging 的 Pod,以收集 Docker 容器日誌。這些 Pod 在本部落格文章的開頭,在對第一個 get pods 命令的回應中顯示。

此日誌收集 Pod 的規格看起來像這樣 fluentd-gcp.yaml

apiVersion: v1

kind: Pod

metadata:

  name: fluentd-cloud-logging

spec:

  containers:

  - name: fluentd-cloud-logging

    image: gcr.io/google\_containers/fluentd-gcp:1.6

    env:

    - name: FLUENTD\_ARGS

      value: -qq

    volumeMounts:

    - name: containers

      mountPath: /var/lib/docker/containers

  volumes:

  - name: containers

    hostPath:

      path: /var/lib/docker/containers

此 Pod 規格將主機上包含 Docker 日誌檔案的目錄 /var/lib/docker/containers 對應到容器內具有相同路徑的目錄。Pod 執行一個映像 gcr.io/google_containers/fluentd-gcp:1.6,該映像配置為從 logs 目錄收集 Docker 日誌檔案,並將其擷取到 Google Cloud Logging 中。此 Pod 的一個執行個體在叢集的每個節點上執行。Kubernetes 將注意到此 Pod 是否失敗,並自動重新啟動它。

我們可以按一下 Google Developer Console 監控區段下的 [日誌] 項目,然後選取計數器容器的日誌,該日誌將稱為 kubernetes.counter_default_count。這會識別 Pod 的名稱 (counter)、命名空間 (default) 和容器的名稱 (count),日誌收集即針對此容器發生。使用此名稱,我們可以從下拉式選單中僅選取計數器容器的日誌

(image-counter-new-logs.png)

當我們在 Developer Console 中檢視日誌時,我們會觀察到容器的兩個調用記錄。

(image-screenshot-2015-06-02)

請注意,第一個容器計數到 108,然後被終止。當下一個容器映像重新啟動時,計數程序從 0 恢復。同樣地,如果我們刪除 Pod 並重新啟動它,我們將擷取 Pod 中容器的所有執行個體的日誌,無論 Pod 何時執行。

擷取到 Google Cloud Logging 中的日誌可以匯出到各種其他目的地,包括 Google Cloud Storage 值區和 BigQuery。使用 Cloud Logging 主控台中的 [匯出] 標籤,指定日誌應串流至何處(或追蹤此連結至 設定標籤)。

我們可以使用 SQL 查詢從 BigQuery 查詢擷取的日誌,該查詢會報告計數器日誌行,並先顯示最新的行。

SELECT metadata.timestamp, structPayload.log FROM [mylogs.kubernetes_counter_default_count_20150611] ORDER BY metadata.timestamp DESC

以下是一些範例輸出

(image-bigquery-log-new.png)

我們也可以從 Google Cloud Storage 值區將日誌擷取到我們的桌上型電腦或筆記型電腦,然後在本機搜尋它們。以下命令擷取在 GCE 專案(名為 myproject)中的叢集中執行的計數器 Pod 的日誌。僅擷取 2015-06-11 日期的日誌。

$ gsutil -m cp -r gs://myproject/kubernetes.counter\_default\_count/2015/06/11 .

現在我們可以對擷取的日誌執行查詢。以下範例使用 jq 程式僅擷取日誌行。

$ cat 21\:00\:00\_21\:59\:59\_S0.json | jq '.structPayload.log'

"0: Thu Jun 11 21:39:38 UTC 2015\n"

"1: Thu Jun 11 21:39:39 UTC 2015\n"

"2: Thu Jun 11 21:39:40 UTC 2015\n"

"3: Thu Jun 11 21:39:41 UTC 2015\n"

"4: Thu Jun 11 21:39:42 UTC 2015\n"

"5: Thu Jun 11 21:39:43 UTC 2015\n"

"6: Thu Jun 11 21:39:44 UTC 2015\n"

"7: Thu Jun 11 21:39:45 UTC 2015\n"

本文簡要介紹了支援在 Kubernetes 部署上收集叢集層級日誌的基礎機制。此處的方法僅適用於收集 Pod 容器中執行的程序的標準輸出和標準錯誤輸出。若要收集儲存在檔案中的其他日誌,可以使用 Sidecar 容器來收集所需的檔案,如 使用 Fluentd 在容器內收集日誌檔案並將其傳送至 Google Cloud Logging 服務 頁面所述。