本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
使用 Kubernetes 進行叢集級日誌記錄
Kubernetes 叢集通常會穩定執行許多系統和應用程式 Pod。系統管理員如何收集、管理和查詢系統 Pod 的日誌?使用者如何查詢其應用程式的日誌,該應用程式由許多 Pod 組成,這些 Pod 可能會被 Kubernetes 系統重啟或自動生成?這些問題由 Kubernetes 叢集級別日誌服務解決。
Kubernetes 的叢集級別日誌允許我們收集在 Pod 容器映象生命週期、Pod 生命週期甚至叢集生命週期之外持久存在的日誌。在本文中,我們假設已建立具有叢集級別日誌支援的 Kubernetes 叢集,用於將日誌傳送到 Google Cloud Logging。這是建立 Google Container Engine (GKE) 叢集時的一個選項,並且預設情況下為開源 Google Compute Engine (GCE) Kubernetes 發行版啟用。叢集建立後,您將擁有一個執行系統 Pod 的集合,這些 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,它收集同一節點上執行的容器的日誌輸出並將其傳送到 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。
當建立啟用日誌記錄到 Google Cloud Logging 的 Kubernetes 叢集時,系統會在叢集的每個節點上建立一個名為 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 的一個例項在叢集的每個節點上執行。如果此 Pod 失敗,Kubernetes 將會注意到並自動重啟它。
我們可以點選 Google Developer Console 監控部分下的日誌項,選擇計數器容器的日誌,其名稱為 kubernetes.counter_default_count。這標識了發生日誌收集的 Pod 名稱 (counter)、名稱空間 (default) 和容器名稱 (count)。使用此名稱,我們可以從下拉選單中選擇僅限我們計數器容器的日誌。
(image-counter-new-logs.png)
當我們在開發人員控制檯中檢視日誌時,我們觀察到容器兩次呼叫的日誌。
(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 儲存桶獲取到我們的桌上型電腦或筆記型電腦,然後在本地搜尋它們。以下命令獲取在名為 myproject 的 GCE 專案中執行的叢集中計數器 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 服務。