Kubernetes系統(tǒng)組件的日志記錄集群中發(fā)生的事件對(duì)于調(diào)試非常有用。通過(guò)配置日志的精細(xì)度,可以展示更多或更少的細(xì)節(jié)。日志可以是粗粒度的,例如只顯示組件內(nèi)的錯(cuò)誤,也可以是細(xì)粒度的,例如顯示事件的每一個(gè)跟蹤步驟(如HTTP訪問(wèn)日志、pod狀態(tài)更新、控制器動(dòng)作或調(diào)度器決策)。
注意:與此處描述的命令行標(biāo)志不同,日志輸出本身不屬于 Kubernetes API 的穩(wěn)定性保證范圍: 單個(gè)日志條目及其格式可能會(huì)在不同版本之間發(fā)生變化!
一、Klog
klog 是 Kubernetes 的日志庫(kù)。 klog 為 Kubernetes 系統(tǒng)組件生成日志消息。
Kubernetes 正在進(jìn)行簡(jiǎn)化其組件日志的努力。下面的 klog 命令行參數(shù)從 Kubernetes v1.23 開(kāi)始已被廢棄, 在 Kubernetes v1.26 中被移除:
- –add-dir-header
- –alsologtostderr
- –log-backtrace-at
- –log-dir
- –log-file
- –log-file-max-size
- –logtostderr
- –one-output
- –skip-headers
- –skip-log-headers
- –stderrthreshold
輸出總會(huì)被寫(xiě)到標(biāo)準(zhǔn)錯(cuò)誤輸出(stderr)之上,無(wú)論輸出格式如何。 對(duì)輸出的重定向?qū)⒂烧{(diào)用 Kubernetes 組件的軟件來(lái)處理。 這一軟件可以是 POSIX Shell 或者類似 systemd 這樣的工具。
在某些場(chǎng)合下,例如對(duì)于無(wú)發(fā)行主體的(distroless)容器或者 Windows 系統(tǒng)服務(wù), 這些替代方案都是不存在的。那么可以使用 kube-log-runner 可執(zhí)行文件來(lái)作為 Kubernetes 的封裝層,完成對(duì)輸出的重定向。 在很多 Kubernetes 基礎(chǔ)鏡像中,都包含一個(gè)預(yù)先構(gòu)建的可執(zhí)行程序。 這個(gè)程序原來(lái)稱作 /go-runner,而在服務(wù)器和節(jié)點(diǎn)的發(fā)行版本庫(kù)中,稱作 kube-log-runner。
下表展示的是 kube-log-runner 調(diào)用與 Shell 重定向之間的對(duì)應(yīng)關(guān)系:
用法 | POSIX Shell(例如 Bash) | kube-log-runner <options> <cmd> |
合并 stderr 與 stdout,寫(xiě)出到 stdout | 2>&1 | kube-log-runner(默認(rèn)行為 ) |
將 stderr 與 stdout 重定向到日志文件 | 1>>/tmp/log 2>&1 | kube-log-runner -log-file=/tmp/log |
輸出到 stdout 并復(fù)制到日志文件中 | 2>&1 | tee -a /tmp/log | kube-log-runner -log-file=/tmp/log -also-stdout |
僅將 stdout 重定向到日志 | >/tmp/log | kube-log-runner -log-file=/tmp/log -redirect-stderr=false |
1、klog輸出
傳統(tǒng)的 klog 原生格式示例:
I1025 00:15:15.525108 1 httplog.go:79] GET /api/v1/namespaces/kube-system/pods/metrics-server-v0.3.1-57c75779f-9p8wg: (1.512ms) 200 [pod_nanny/v0.0.0 (linux/amd64) kubernetes/$Format 10.56.1.19:51756]
消息字符串可能包含換行符:
I1025 00:15:15.525108 1 example.go:79] This is a message which has a line break.
2、結(jié)構(gòu)化日志
注意:
遷移到結(jié)構(gòu)化日志消息是一個(gè)正在進(jìn)行的過(guò)程。在此版本中,并非所有日志消息都是結(jié)構(gòu)化的。 解析日志文件時(shí),也必須要處理非結(jié)構(gòu)化日志消息。
日志格式和值的序列化可能會(huì)發(fā)生變化。
結(jié)構(gòu)化日志記錄旨在日志消息中引入統(tǒng)一結(jié)構(gòu),以便以編程方式提取信息。 可以方便地用更小的開(kāi)銷來(lái)處理結(jié)構(gòu)化日志。 生成日志消息的代碼決定其使用傳統(tǒng)的非結(jié)構(gòu)化的 klog 還是結(jié)構(gòu)化的日志。
默認(rèn)的結(jié)構(gòu)化日志消息是以文本形式呈現(xiàn)的,其格式與傳統(tǒng)的 klog 保持向后兼容:
<klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...
示例:
I1025 00:15:15.525108 1 controller_utils.go:116] "Pod status updated" pod="kube-system/kubedns" status="ready"
字符串在輸出時(shí)會(huì)被添加引號(hào)。其他數(shù)值類型都使用 %+v 來(lái)格式化,因此可能導(dǎo)致日志消息會(huì)延續(xù)到下一行, 具體取決于數(shù)據(jù)本身。
I1025 00:15:15.525108 1 example.go:116] "Example" data="This is text with a line break\nand \"quotation marks\"." someInt=1 someFloat=0.1 someStruct={StringField: First line, second line.}
3、上下文日志
上下文日志建立在結(jié)構(gòu)化日志之上。 它主要是關(guān)于開(kāi)發(fā)人員如何使用日志記錄調(diào)用:基于該概念的代碼將更加靈活, 并且支持在結(jié)構(gòu)化日志 KEP 中描述的額外用例。
如果開(kāi)發(fā)人員在他們的組件中使用額外的函數(shù),比如 WithValues 或 WithName, 那么日志條目將會(huì)包含額外的信息,這些信息會(huì)被調(diào)用者傳遞給函數(shù)。
目前這一特性是由 StructuredLogging 特性門控所控制的,默認(rèn)關(guān)閉。 這個(gè)基礎(chǔ)設(shè)施是在 1.24 中被添加的,并不需要修改組件。 該 component-base/logs/example 命令演示了如何使用新的日志記錄調(diào)用以及組件如何支持上下文日志記錄。
$ cd $GOPATH/src/k8s.io/kubernetes/staging/src/k8s.io/component-base/logs/example/cmd/ $ go run . --help ... --feature-gates mapStringBool A set of key=value pairs that describe feature gates for alpha/experimental features. Options are: AllAlpha=true|false (ALPHA - default=false) AllBeta=true|false (BETA - default=false) ContextualLogging=true|false (ALPHA - default=false) $ go run . --feature-gates ContextualLogging=true ... I0404 18:00:02.916429 451895 logger.go:94] "example/myname: runtime" foo="bar" duration="1m0s" I0404 18:00:02.916447 451895 logger.go:95] "example: another runtime" foo="bar" duration="1m0s"
example 前綴和 foo=”bar” 會(huì)被函數(shù)的調(diào)用者添加上, 不需修改該函數(shù),它就會(huì)記錄 runtime 消息和 duration=”1m0s” 值。
禁用上下文日志后,WithValues 和 WithName 什么都不會(huì)做, 并且會(huì)通過(guò)調(diào)用全局的 klog 日志記錄器記錄日志。 因此,這些附加信息不再出現(xiàn)在日志輸出中:
$ go run . --feature-gates ContextualLogging=false ... I0404 18:03:31.171945 452150 logger.go:94] "runtime" duration="1m0s" I0404 18:03:31.171962 452150 logger.go:95] "another runtime" duration="1m0s"
4、JSON日志格式
注意:
- JSON 輸出并不支持太多標(biāo)準(zhǔn) klog 參數(shù);
- 并不是所有日志都保證寫(xiě)成 JSON 格式(例如,在進(jìn)程啟動(dòng)期間)。 如果打算解析日志,請(qǐng)確??梢蕴幚矸?JSON 格式的日志行;
- 字段名和 JSON 序列化可能會(huì)發(fā)生變化。
–logging-format=json 參數(shù)將日志格式從 klog 原生格式改為 JSON 格式。 JSON 日志格式示例(美化輸出):
{ "ts": 1580306777.04728, "v": 4, "msg": "Pod status updated", "pod":{ "name": "nginx-1", "namespace": "default" }, "status": "ready" }
具有特殊意義的 key:
- ts – Unix 時(shí)間風(fēng)格的時(shí)間戳(必選項(xiàng),浮點(diǎn)值);
- v – 精細(xì)度(僅用于 info 級(jí)別,不能用于錯(cuò)誤信息,整數(shù));
- err – 錯(cuò)誤字符串(可選項(xiàng),字符串);
- msg – 消息(必選項(xiàng),字符串)。
當(dāng)前支持 JSON 格式的組件列表:
- kube-controller-manager
- kube-apiserver
- kube-scheduler
- kubelet
5、日志精細(xì)度級(jí)別
參數(shù) -v 控制日志的精細(xì)度。增大該值會(huì)增大日志事件的數(shù)量。 減小該值可以減小日志事件的數(shù)量。增大精細(xì)度會(huì)記錄更多的不太嚴(yán)重的事件。 精細(xì)度設(shè)置為 0 時(shí)只記錄關(guān)鍵(critical)事件。
6、日志位置
有兩種類型的系統(tǒng)組件:運(yùn)行在容器中的組件和不運(yùn)行在容器中的組件。例如:
- Kubernetes 調(diào)度器和 kube-proxy 在容器中運(yùn)行;
- kubelet 和容器運(yùn)行時(shí)不在容器中運(yùn)行。
在使用 systemd 的系統(tǒng)中,kubelet 和容器運(yùn)行時(shí)寫(xiě)入 journald。 在別的系統(tǒng)中,日志寫(xiě)入 /var/log 目錄下的 .log 文件中。 容器中的系統(tǒng)組件總是繞過(guò)默認(rèn)的日志記錄機(jī)制,寫(xiě)入 /var/log 目錄下的 .log 文件。
與容器日志類似,應(yīng)該輪轉(zhuǎn) /var/log 目錄下系統(tǒng)組件日志。 在 kube-up.sh 腳本創(chuàng)建的 Kubernetes 集群中,日志輪轉(zhuǎn)由 logrotate 工具配置。 logrotate 工具,每天或者當(dāng)日志大于 100MB 時(shí),輪轉(zhuǎn)日志。
二、日志查詢
為了幫助在節(jié)點(diǎn)上調(diào)試問(wèn)題,Kubernetes v1.27 引入了一個(gè)特性來(lái)查看節(jié)點(diǎn)上當(dāng)前運(yùn)行服務(wù)的日志。 要使用此特性,請(qǐng)確保已為節(jié)點(diǎn)啟用了 NodeLogQuery 特性門控, 且 kubelet 配置選項(xiàng) enableSystemLogHandler 和 enableSystemLogQuery 均被設(shè)置為 true。 在 Linux 上,我們假設(shè)可以通過(guò) journald 查看服務(wù)日志。 在 Windows 上,我們假設(shè)可以在應(yīng)用日志提供程序中查看服務(wù)日志。 在兩種操作系統(tǒng)上,都可以通過(guò)讀取 /var/log/ 內(nèi)的文件查看日志。
假如被授權(quán)與節(jié)點(diǎn)對(duì)象交互,可以在所有節(jié)點(diǎn)或只是某個(gè)子集上試用此 Alpha 特性。 這里有一個(gè)從節(jié)點(diǎn)中檢索 kubelet 服務(wù)日志的例子:
# 從名為 node-1.example 的節(jié)點(diǎn)中獲取 kubelet 日志 kubectl get --raw "/api/v1/nodes/node-1.example/proxy/logs/?query=kubelet"
也可以獲取文件,前提是日志文件位于 kubelet 允許進(jìn)行日志獲取的目錄中。 例如可以從 Linux 節(jié)點(diǎn)上的 /var/log 中獲取日志。
kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=/<insert-log-file-name-here>"
kubelet 使用啟發(fā)方式來(lái)檢索日志。 如果還未意識(shí)到給定的系統(tǒng)服務(wù)正將日志寫(xiě)入到操作系統(tǒng)的原生日志記錄程序(例如 journald) 或 /var/log/ 中的日志文件,這會(huì)很有幫助。這種啟發(fā)方式先檢查原生的日志記錄程序, 如果不可用,則嘗試從 /var/log/<servicename>、/var/log/<servicename>.log 或 /var/log/<servicename>/<servicename>.log 中檢索第一批日志。
可用選項(xiàng)的完整列表如下:
選項(xiàng) | 描述 |
boot | boot?顯示來(lái)自特定系統(tǒng)引導(dǎo)的消息 |
pattern | pattern?通過(guò)提供的兼容 PERL 的正則表達(dá)式來(lái)過(guò)濾日志條目 |
query | query?是必需的,指定返回日志的服務(wù)或文件 |
sinceTime | 顯示日志的 RFC3339 起始時(shí)間戳(包含) |
untilTime | 顯示日志的 RFC3339 結(jié)束時(shí)間戳(包含) |
tailLines指定要從日志的末尾檢索的行數(shù);默認(rèn)為獲取全部日志
更復(fù)雜的查詢示例:
# 從名為 node-1.example 且?guī)в袉卧~ "error" 的節(jié)點(diǎn)中獲取 kubelet 日志 kubectl get --raw "/api/v1/nodes/node-1.example/proxy/logs/?query=kubelet&pattern=error"