Murre: 一個輕量化的 K8S 監控工具

Murre 是一個爲 k8s 提供按需、可擴展的容器監控指標工具。Murre 直接從每個 K8s 節點上的 kubelet 組件獲取容器或節點的 CPU 和內存資源指標,並使用來自每個 PodSpec 的相關 K8s 請求和限制來豐富資源信息。

極簡主義。我知道這個詞很誇張,但請耐心聽我說。基本上,你只需擁有那些能給你的生活帶來價值和意義的東西,把其他的都拿走。極簡主義是關於清除多餘的東西,把你的時間和精力用在最有價值的事情上,因爲我們在生活中只有有限的精力、時間和空間。

最好的軟件開發者本質上都是極簡主義者。極簡主義並不意味着寫更少的代碼,而是寫優雅而緊湊的代碼,並做好一件事。在這個意義上,極簡主義意味着設計使用盡可能少的硬件和軟件資源的系統——因爲你能做到。而且你認爲事情應該是這樣的。
像許多開發人員一樣,我們將這種方法應用到我們設計和構建的所有軟件中。

當我們希望爲客戶提供 K8s 節點資源和使用指標時,我們就開始構建 Murre 項目。作爲一家構建全面的 Kuberenetes 應用程序監控解決方案的公司,需求很明確。我們希望監控和導出集羣基礎設施層中的資源——採集每個節點上容器的 CPU、內存和磁盤使用情況。

聽起來很簡單,對吧?問題是,作爲一個嵌入 Kubernetes 集羣核心的監控平臺,我們總是帶着極簡主義的標誌。我們的目標是儘可能做到輕量化——這也意味着如果沒有必要的話,我們更傾向於不在集羣上安裝任何第三方工具。

因此,我們開始了 k8s 節點指標的採集工作。但不會在集羣中安裝衆所周知的 Metric Server[https://kubernetes-sigs.github.io/metrics-server/]。

常規的 K8s 指標監控方法

我們經常看到客戶在監控被 k8 抽象過的物理層資源 (CPU、內存、磁盤等) 時遇到困難。Kubernetes 生態系統包括一些附加組件,用於聚合和採集來自 Kubernetes 集羣的監控數據。Metrics Server 就是這些實用的附加組件之一。

Metrics Server 是一個集羣範圍的資源使用數據聚合器。它從 Summary API 收集容器或節點的 CPU 或內存消耗等指標,這些指標由 Kubelet 在每個節點上提供。但是,它是一個附加組件,這意味着它不是開箱即用的,也不是默認部署在標準託管的 Kubernetes 平臺上。

Metrics Server 的目標是可擴展而且高效,對於一個典型的 100 個節點的集羣,它要要大約 100m 的 CPU 和 200 MiB 的內存。它只允許在內存中存儲接近實時的指標,支持對 CPU 或內存使用情況進行專項檢查,或者通過監控服務進行定期查詢,以便在較長時間內保留數據。

雖然它的設計可以很好地擴展 K8s 集羣,但也會存在問題。在集羣中運行第三方服務意味着你必須不時對其進行維護和故障排除。它可能會崩潰,佔用比預期更多的資源,或者無法工作。Metrics Server 存在的問題,對一個已經高負載的集羣來說可謂是雪上加霜。還會讓我們的服務嚴重依賴 Metric Server 存在單點故障。

選擇輕量化

首先,我們通過直接在集羣中的每個節點上使用 Linux 操作系統工具 (如 top、ps 等) 來讀取節點的資源。這解決了一個難題,即不需要任何預安裝或維護就能對節點資源監控。然而,它確實帶來了效率問題。top 和 ps 等工具的輸出需要解析。它們也是 Linux 而不是 K8s 工具,因此需要上層來理解進程資源,並將其轉化爲我們的客戶所瞭解和理解的容器資源。

Metrics Server 是以集中式方式構建的,每個集羣就一個。那麼,它如何從所有不同的節點獲得所需的所有指標呢?快速瀏覽一下代碼就能清楚地找到答案——只需查詢每個節點上運行的 Kubelet。突然之間,這一切都變得有意義了,因爲 K8s 本身也必須查詢節點資源,以便 Kubelet 工作流能夠運行!

Kubelet 是什麼?

Kubelet 是一個運行在 Kubernetes 集羣的每個節點上的進程,並根據請求爲給定節點創建、銷燬或更新 pod 及其容器。基本上,Kubelet 是運行在每個節點上的主要 “節點代理”,它使用 PodSpecs(描述 pod 的 YAML 或 JSON 對象) 工作。使用通過各種機制 (主要是通過 apiserver) 提供的 PodSpecs,並確保這些 PodSpecs 中描述的容器正常運行。

在 Kubernetes 中,調度、搶佔和驅逐是集羣功能的重要組成部分。調度是指確保 pod 與節點匹配,以便 Kubelet 可以運行容器。搶佔是終止低優先級 pod 的過程,以便高優先級 pod 可以調度到節點上,而驅逐是終止節點上的一個或多個 pod 的過程。

Kubelet 在這些主要場景中發揮着關鍵作用。例如,一個稱爲節點壓力驅逐的場景。Kubernetes 經常檢查節點資源,如磁盤壓力、CPU 或內存不足 (OOM)。如果節點中的資源(如 CPU 或內存) 消耗達到一定閾值,Kubelet 將開始驅逐 pod 以釋放資源。

這正是 Kubelet 必須不斷獲取 Kubernetes 資源指標來完成其工作的原因,並將這些指標提供給其他可能也需要這些信息的服務。太好了!因此,如果 Kubelet 已經向 Metrics Server 公開了它的指標 API,這意味着我們自己也可以使用這個 API。

真是一石二鳥啊!我們可以直接調用該 API 獲取指標數據,而無需在集羣上部署 Metrics Server,但是我們也可以在每個節點內部查詢——而無需離開節點。

深入 Kubelet 源碼

我們開始研究 Kubelet 源碼,以便弄清楚它如何獲得 K8s 資源指標,以及它如何將這些數據作爲 API 公開。我們發現 Kubelet 不僅讀取每個集羣節點上的資源使用情況,而且還使用 Prometheus 格式的度量標準公開數據——這也是我們希望看到的!

Kubelet API 沒有文檔,但是從它的源碼中我們找到了接口。還有更多不用於度量 / 統計的 API,但這些超出了本文研究的範圍。當然還有更多的特性有待發掘,但我們現在將專注於 K8s 的指標。
你可以在這裏找到一些指標定義
pkg/kubelet/server/server.go:

爲了訪問 kubelet API,我們可以看到數據:

Kubelet 的一些 API 返回 json,一些輸出實際的度量數據。以下是一些值得注意的地方:
/metrics/cadvisor:該接口源自 cadvisor,它提供了容器資源消耗的所有度量,例如:CPU、內存、文件系統、網絡。

type Summary struct {
    // Overall node stats.
    Node NodeStats `json:"node"`
    // Per-pod stats.
    Pods []PodStats `json:"pods"`
}

// NodeStats holds node-level unprocessed sample stats.
type NodeStats struct {
    // Reference to the measured Node.
    NodeName string `json:"nodeName"`
    // Stats of system daemons tracked as raw containers.
    // The system containers are named according to the SystemContainer* constants.
    SystemContainers []ContainerStats `json:"systemContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
    // The time at which data collection for the node-scoped (i.e. aggregate) stats was (re)started.
    StartTime metav1.Time `json:"startTime"`
    // Stats pertaining to CPU resources.
    CPU *CPUStats `json:"cpu,omitempty"`
    // Stats pertaining to memory (RAM) resources.
    Memory *MemoryStats `json:"memory,omitempty"`
    // Stats pertaining to network resources.
    Network *NetworkStats `json:"network,omitempty"`
    // Stats pertaining to total usage of filesystem resources on the rootfs used by node k8s components.
    // NodeFs.Used is the total bytes used on the filesystem.
    Fs *FsStats `json:"fs,omitempty"`
    // Stats about the underlying container runtime.
    Runtime *RuntimeStats `json:"runtime,omitempty"`
    // Stats about the rlimit of system.
    Rlimit *RlimitStats `json:"rlimit,omitempty"`
}

// PodStats holds pod-level unprocessed sample stats.
type PodStats struct {
    // Reference to the measured Pod.
    PodRef PodReference `json:"podRef"`
    // The time at which data collection for the pod-scoped (e.g. network) stats was (re)started.
    StartTime metav1.Time `json:"startTime"`
    // Stats of containers in the measured pod.
    Containers []ContainerStats `json:"containers" patchStrategy:"merge" patchMergeKey:"name"`
    // Stats pertaining to CPU resources consumed by pod cgroup (which includes all containers' resource usage and pod overhead).
    CPU *CPUStats `json:"cpu,omitempty"`
    // Stats pertaining to memory (RAM) resources consumed by pod cgroup (which includes all co

現在看看 Murre 項目

Murre 是一個 OSS(開源軟件) 工具,它可以幫助你獲得容器的 CPU 和內存指標,而不需要在集羣上安裝任何東西。它的工作方式與 metrics-server 相同,只是不存儲指標。使用 Murre,你可以過濾特定的命名空間,pod 甚至容器名稱,以專注於你想要的確切指標。
Murre 利用 3 種不同的 API 來獲取並提供這些指標所需的數據:
1、NodeList: 爲了發現和維護所有可用節點的列表。
2、/metrics/cadvisor:獲得容器實際的資源使用指標。如前所述,這個 API 用於每個節點。
3、PodList:用 K8s 對每個容器的資源請求和限制來豐富數據。
下面是 Murre 的處理流程:

項目地址:

https://github.com/groundcover-com/murre
https://github.com/groundcover-com/murre/blob/main/images/demo.gif

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/8nG592ov6j9YByuLi2MOvg