提高 K8S 監(jiān)控可觀察性最佳方式實戰(zhàn)教程
當談到云原生可觀察性時,可能每個人都會提到OpenTelemetry (OTEL),因為社區(qū)需要依賴標準來將所有集群組件開發(fā)指向到同一方向。OpenTelemetry 使我們能夠將日志、指標(metrics)、跟蹤(traces)和其他上下文信息組合到一個資源中。集群管理員或軟件工程師可以使用此資源來獲取在定義的時間段內集群中正在發(fā)生的事情的視圖。
但是 Kubernetes 本身如何利用這個技術棧呢?
Kubernetes 由多個組件組成,其中一些組件是獨立的,而另一些組件則堆疊在一起。從容器運行時的角度來看架構,那么從上到下有:
- kube-apiserver:驗證和配置 API 對象的數據
- kubelet:在每個節(jié)點上運行的代理
- CRI 運行時:容器運行時接口 (CRI) 兼容的容器運行時,如CRI-O或containerd
- OCI 運行時:較低級別的開放容器倡議 (OCI)運行時,如runc或crun
- Linux 內核或Microsoft Windows:底層操作系統
這意味著如果我們在 Kubernetes 中運行容器時遇到問題,那么我們就會開始查看其中一個組件。隨著當今集群架構復雜性不斷增加,查找問題的根本原因是我們面臨的最耗時的操作之一。即使知道可能導致問題的組件,仍須考慮其他組件。
我們如何做到這一點?大多數人可能會堅持抓取日志,過濾它們并在組件邊界上將它們組裝在一起。我們也有 metrics 指標,但是將指標值與普通日志相關聯使跟蹤正在發(fā)生的事情變得更加困難。一些指標也不是為了調試目的而制定的。
OpenTelemetry 應運而生。該項目旨在將 traces~跟蹤、metrics~指標和 logs~日志 等信號組合在一起,以維護集群狀態(tài)的統一視圖。
Kubernetes 中 OpenTelemetry 跟蹤的當前狀態(tài)是什么?從 API server 的角度來看,自 Kubernetes v1.22 以來,我們對 tracing 提供了 alpha 支持,它將在即將發(fā)布的其中一個版本中升級為 beta。不幸的是,beta 畢業(yè)錯過了 Kubernetes v1.26 版本??梢栽?API Server Tracing Kubernetes Enhancement Proposal (KEP)中找到該設計提案,其中提供了更多相關信息。
kubelet tracing 部分 在另一個 KEP 中進行跟蹤,該 KEP 在 Kubernetes v1.25 中以 alpha 狀態(tài)實現。撰寫本文時并未計劃進行 Beta 畢業(yè),但 v1.27 發(fā)布周期中可能會有更多。除了兩個 KEP 之外還有其他方面的努力,例如 klog 正在考慮 OTEL 支持,這將通過將日志消息鏈接到現有跟蹤來提高可觀察性。在 SIG Instrumentation 和 SIG Node 中,我們還在討論 如何將 kubelet traces 鏈接在一起,因為現在他們專注于 kubelet 和 CRI 容器運行時之間的 gRPC 調用。
CRI-O 從 v1.23.0 開始就支持 OpenTelemetry 跟蹤,并致力于不斷改進它們,例如通過 將日志附加到跟蹤 或將 spans 擴展到應用程序的邏輯部分。這有助于跟蹤的用戶獲得與解析日志相同的信息,但具有增強的范圍界定和過濾其他 OTEL 信號的能力。CRI-O 維護者還在開發(fā)conmon的容器監(jiān)控替代,稱為 conmon-rs 并且純用 Rust 編寫。使用 Rust 實現的一個好處是能夠添加諸如 OpenTelemetry 支持之類的功能,因為這些功能的庫已經存在。這允許與 CRI-O 緊密集成,并讓消費者從容器中看到最低級別的跟蹤數據。
containerd 從 v1.6.0 開始添加了 tracing 支持,可通過使用插件 獲得。較低級別的 OCI 運行時,如 runc 或 crun,根本不支持 OTEL,而且似乎不存在這方面的計劃。我們必須考慮到在收集 traces 并將它們導出到數據接收器時會產生性能開銷。我仍然認為在 OCI 運行時擴展的遙測收集看起來是值得評估的。讓我們看看 Rust OCI 運行時 youki 將來是否會考慮類似的事情。
下面我會展示如何做。對于下面演示,我將堅持使用 runc、conmon-rs、CRI-O 和 kubelet 的單個本地節(jié)點堆棧。要在 kubelet 中啟用跟蹤,需要在KubeletConfiguration中配置以下內容:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
KubeletTracing: true
tracing:
samplingRatePerMillion: 1000000
等于一samplingRatePerMillion百萬將在內部轉化為對所有內容進行采樣。必須將類似的配置應用于 CRI-O;我可以使用 and 啟動二進制 crio文件,或者我們使用這樣的嵌入式配置:--enable-tracing``--tracing-sampling-rate-per-million 1000000
一個 samplingRatePerMillion 等于 100 萬將在內部轉化為對所有內容的抽樣。必須對 CRI-O 應用類似的配置;我可以使用參數 --enable-tracing 和 --tracing-sampling-rate-per-million 1000000 啟動 crio 二進制文件,或者使用這樣的插入式配置:
cat /etc/crio/crio.conf.d/99-tracing.conf
[crio.tracing]
enable_tracing = true
tracing_sampling_rate_per_million = 1000000
要將 CRI-O 配置為使用 conmon-rs,至少需要最新的 CRI-O v1.25.x 和 conmon-rs v0.4.0。然后像下面這樣配置插件可以讓 CRI-O 使用 conmon-rs:
cat /etc/crio/crio.conf.d/99-runtimes.conf
[crio.runtime]
default_runtime = "runc"
[crio.runtime.runtimes.runc]
runtime_type = "pod"
monitor_path = "/path/to/conmonrs" # or will be looked up in $PATH
默認配置將指向的 OpenTelemetry 收集器 gRPC 端點:localhost:4317,它也必須啟動并運行。如文檔中所述 有多種運行 OTLP 的方法,但也可以通過 kubectl proxy 進入在 Kubernetes 中運行的現有實例。
如果一切都已設置好,那么收集器應該記錄有傳入的跟蹤:
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope go.opentelemetry.io/otel/sdk/tracer
Span #0
Trace ID : 71896e69f7d337730dfedb6356e74f01
Parent ID : a2a7714534c017e6
ID : 1d27dbaf38b9da8b
Name : github.com/cri-o/cri-o/server.(*Server).filterSandboxList
Kind : SPAN_KIND_INTERNAL
Start time : 2022-11-15 09:50:20.060325562 +0000 UTC
End time : 2022-11-15 09:50:20.060326291 +0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Span #1
Trace ID : 71896e69f7d337730dfedb6356e74f01
Parent ID : a837a005d4389579
ID : a2a7714534c017e6
Name : github.com/cri-o/cri-o/server.(*Server).ListPodSandbox
Kind : SPAN_KIND_INTERNAL
Start time : 2022-11-15 09:50:20.060321973 +0000 UTC
End time : 2022-11-15 09:50:20.060330602 +0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Span #2
Trace ID : fae6742709d51a9b6606b6cb9f381b96
Parent ID : 3755d12b32610516
ID : 0492afd26519b4b0
Name : github.com/cri-o/cri-o/server.(*Server).filterContainerList
Kind : SPAN_KIND_INTERNAL
Start time : 2022-11-15 09:50:20.0607746 +0000 UTC
End time : 2022-11-15 09:50:20.060795505 +0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Events:
SpanEvent #0
-> Name: log
-> Timestamp: 2022-11-15 09:50:20.060778668 +0000 UTC
-> DroppedAttributesCount: 0
-> Attributes::
-> id: Str(adf791e5-2eb8-4425-b092-f217923fef93)
-> log.message: Str(No filters were applied, returning full container list)
-> log.severity: Str(DEBUG)
-> name: Str(/runtime.v1.RuntimeService/ListContainers)
可以看到 spans 有一個 trace ID,并且通常有一個附加 attached。日志等事件也是輸出的一部分。在上述情況下,kubelet 的 Pod 生命周期事件生成器 (PLEG) 定期觸發(fā) ListPodSandbox 調用 CRI-O 的 RPC??梢酝ㄟ^例如 Jaeger 來顯示這些 traces 。在本地運行跟蹤堆棧時,默認情況下應公開一個 Jaeger 實例地址為:http://localhost:16686。
這些ListPodSandbox請求在 Jaeger UI 中直接可見:

這并不太令人興奮,所以我將直接通過kubectl運行工作負載:
kubectl run -it --rm --restart=Never --image=alpine alpine -- echo hi
hi
pod "alpine" deleted
現在查看 Jaeger,我們可以看到有 conmonrs,crio以及kubelet的RunPodSandbox和CreateContainer CRI RPC 的 traces:

kubelet 和 CRI-O spans 相互連接,使調查更容易。如果我們現在仔細查看這些 spans,可以看到 CRI-O 的日志正確地包含了相應的功能。例如,可以像這樣從 traces 中提取 container user:

較低級別的 conmon-rs spans 也是此跟蹤的一部分。例如 conmon-rs 維護一個內部 read_loop 處理容器和最終用戶之間的 IO。讀取和寫入字節(jié)的日志是 spans 的一部分。這同樣適用于 wait_for_exit_code span,它告訴我們容器成功退出,code 為0:

將所有這些信息與 Jaeger 的過濾功能放在一起,使整個堆棧成為調試容器問題的絕佳解決方案!提到“整個堆?!币诧@示了整體方法的最大缺點:與解析日志相比,它在集群設置之上增加了明顯的開銷。用戶必須維護一個像 Elasticsearch 這樣的接收器來持久化數據,暴露 Jaeger UI 并可能考慮到性能缺陷。無論如何,它仍然是提高 Kubernetes 可觀察性方面的最佳方法之一。
感謝您閱讀這篇博文,我很確定大家正在展望 Kubernetes 對 OpenTelemetry 支持的光明前景,以簡化故障排除。
譯自
作者:Sascha Grunert
原文:https://kubernetes.io/blog/2022/12/01/runtime-observability-opentelemetry/
說明
請關注 危 ? 工中號【進擊云原生】,更有free資源供您學習
本文由mdnice多平臺發(fā)布