Kubernetes問題調查:failed to get cgroup stats for /systemd/system.slice
說明
Kubelet日誌中不停的出現下面的錯誤:
Jan 25 ... summary.go:92] Failed to get system container stats for "/systemd/system.slice": failed to get cgroup stats for "/systemd/system.slice": failed to get container info for "/systemd/system.slice": unknown container "/systemd/system.slice" Jan 25 ... summary.go:92] Failed to get system container stats for "/systemd/system.slice": failed to get cgroup stats for "/systemd/system.slice": failed to get container info for "/systemd/system.slice": unknown container "/systemd/system.slice"
又是和cgroup有關的錯誤,見到cgroup就頭大,之前學習過cgroup( 《cgroup:linux的cgroup的使用》 ),但是一直沒形成很清晰的認識,藉著排查這個問題的機會深入瞭解一下。
Kubernetes版本1.9.11。
掃程式碼
kubelet版本是1.9.11,錯誤日誌來自這裡:
// pkg/kubelet/server/stats/summary.go // Get provides a new Summary with the stats from Kubelet. func (sp *summaryProviderImpl) Get() (*statsapi.Summary, error) { ...省略... systemContainers := map[string]string{ statsapi.SystemContainerKubelet: nodeConfig.KubeletCgroupsName, statsapi.SystemContainerRuntime: nodeConfig.RuntimeCgroupsName, statsapi.SystemContainerMisc:nodeConfig.SystemCgroupsName, } for sys, name := range systemContainers { // skip if cgroup name is undefined (not all system containers are required) if name == "" { continue } s, _, err := sp.provider.GetCgroupStats(name) if err != nil { glog.Errorf("Failed to get system container stats for %q: %v", name, err) continue } // System containers don't have a filesystem associated with them. s.Logs, s.Rootfs = nil, nil s.Name = sys nodeStats.SystemContainers = append(nodeStats.SystemContainers, *s) } ...省略... }
通過閱讀程式碼上下文,得知這裡查詢的是 --runtime-cgroups
和 --kubelet-cgroups
指定的cgroup。
之前調查的時候,網上有人說,將這兩個引數的數值都設定為 /systemd/system.slice
, Github Issue #56850: Failed to get system container stats for.. :
--runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice
對這個解決方法很懷疑,他設定的引數值明顯和引數名稱不搭,並且在我的環境中,這個方法無效,上面列印的日誌就是這樣設定以後列印的。
是不是 --runtime-cgroups
和 --kubelet-cgroups
應該設定為其它數值呢?
沒找到具體的例子,從引數說明中也看不到要怎樣設定,隨便嘗試了幾種:
/ /sys/fs/cgroup/systemd/system.slice /system.slice/kubelet.service /sys/fs/cgroup/systemd/system.slice/kubelet.service
結果都不行,設定為哪個值,日誌裡就顯示名稱為哪個值的容器找不到。
繼續調查
沒辦法,只能繼續爬程式碼,具體過程就不說了,總之最後發現, sp.provider.GetCgroupStats(name)
是用cadvisor client從cadvisor中查詢容器資訊的。
cadvisor client的建立程式碼:
// cmd/kubelet/app/server.go: 418 func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies) (err error) { ... if kubeDeps.CAdvisorInterface == nil { imageFsInfoProvider := cadvisor.NewImageFsInfoProvider(s.ContainerRuntime, s.RemoteRuntimeEndpoint) kubeDeps.CAdvisorInterface, err = cadvisor.New(s.Address, uint(s.CAdvisorPort), imageFsInfoProvider, s.RootDirectory, cadvisor.UsingLegacyCadvisorStats(s.ContainerRuntime, s.RemoteRuntimeEndpoint)) if err != nil { return err } }
參照kubelet的做法,寫一個從cadvisor中查詢容器資訊的小程式,看一下從cadvisor中到底能不能查詢到目標容器:
// Copyright (C) 2019 lijiaocn <[email protected]> // // Distributed under terms of the GPL license. package main import ( "encoding/json" "flag" "fmt" "github.com/golang/glog" cadvisorapiv2 "github.com/google/cadvisor/info/v2" "k8s.io/kubernetes/pkg/kubelet/cadvisor" "time" ) type CmdLine struct { ContainerName string } var cmdline CmdLine func init() { flag.StringVar(&cmdline.ContainerName, "name", "/", "container name") } func main() { flag.Parse() ContainerRuntime := "docker" RemoteRuntimeEndpoint := "" imagefs := cadvisor.NewImageFsInfoProvider(ContainerRuntime, RemoteRuntimeEndpoint) usingLegacyStats := cadvisor.UsingLegacyCadvisorStats(ContainerRuntime, RemoteRuntimeEndpoint) ca, err := cadvisor.New("127.0.0.1", 4000, imagefs, "/var/lib/kubelet", usingLegacyStats) if err != nil { glog.Error(err) } if err := ca.Start(); err != nil { glog.Error(err) } options := cadvisorapiv2.RequestOptions{ IdType:cadvisorapiv2.TypeName, Count:2, // 2 samples are needed to compute "instantaneous" CPU Recursive: true,//設定為true,遞迴查詢,深入到目錄中 } machineInfo, err := ca.MachineInfo() if err != nil { glog.Error(err.Error()) } bytes, err := json.Marshal(&machineInfo) fmt.Printf("machineInfo: %s\n", bytes) rootfsInfo, err := ca.RootFsInfo() if err != nil { glog.Error(err.Error()) } bytes, err = json.Marshal(&rootfsInfo) fmt.Printf("rootfsInfo: %s\n", bytes) infoMap, err := ca.ContainerInfoV2(cmdline.ContainerName, options) if err != nil { glog.Error(err.Error()) } if infoMap == nil { glog.Error("infoMap is null") } for name, value := range infoMap { fmt.Printf("%s: %s%s\n", name, value.Spec.Namespace, value.Spec.Image) } time.Sleep(600 * time.Second) }
上面的程式碼需要放到$GOPATH/src/k8s.io/kubernetes目錄或者它的子目錄中,例如放在新建的子目錄中lijiaocn/cadvisor中:
$ ls $GOPATH/src/k8s.io/kubernetes/lijiaocn/cadvisor/main.go main.go $ cd $GOPATH/src/k8s.io/kubernetes/lijiaocn/cadvisor/ $ go build
編譯得到檔案cadvisor,將它拷貝到目標機器上,檢視cadvisor發現的容器:
$ ./cadvisor kube-proxy /system.slice/rsyslog.service /system.slice/polkit.service /kubepods/besteffort/podca8a190e-156f-11e9-97b9-52540064c479/450f1b5757d45c2367fa06ec65de03508a47e9d9037cd5769a4c17e29299f10a /system.slice/kmod-static-nodes.service /system.slice/crond.service /system.slice/system-getty.slice /user.slice /system.slice/rhel-import-state.service /system.slice/systemd-udevd.service /kubepods /system.slice/system-selinux\x2dpolicy\x2dmigrate\x2dlocal\x2dchanges.slice /system.slice/kubelet.service ...省略... /systemd/system.slice ... /systemd/system.slice/docker.service ...
可以確定兩件事情:第一,不需要在名稱前面加上cgroup的掛載路徑/sys/fs/cgroup;第二,傳入的引數 /
、 /systemd/system.slice
以及 /system.slice/kubelet.service
等都是存在的。
直接用cadvisor能查到容器的cgroup,為什麼kubelet無論如何也查不到呢?這裡寫的查詢方法是直接抄的kubelet,為什麼我們能查到,kubelet查不到?:
用./cadvisor能查到 /system.slice/kubelet.service
:
$ ./cadvisor --name=/system.slice/kubelet.service /system.slice/kubelet.service
陷入僵局。
繼續調查
猛然想起還有另一個叢集,在那個叢集中似乎沒有見過這個錯誤,兩個叢集使用的是同一個版本的kubelet。
比對兩個叢集的kubelet命令列引數,發現了不同:沒有報該錯誤的叢集上,沒有使用 --docker-only
引數。
趕緊去看這個引數的作用:
--docker-onlyOnly report docker containers in addition to root stats
莫非使用了該引數之後,cadvisor不採集docker以外的cgroup資訊?
將這個引數去掉之後,錯誤消失!猜測大概是對的。
另外結合前面的分析過程,如果要設定–kubelet-cgroups和–runtime-cgroups,應該設定為如下的數值:
# 根據自己環境設定,我這裡是CentOS7,用systemd啟動kubelet和docker --kubelet-cgroups=/system.slice/kubelet.service --runtime-cgroups=/system.slice/docker.service
需要特別注意的是,這裡的kubelet和docker,是在CentOS上用systemctl啟動的,這兩個cgroup都是systemd建立的。
如果不用systemctl啟動,而是直接在命令列執行kubelet,例如:
./kubelet --kubelet-cgroups=/system.slice/kubelet.service --XXXX(省略)
會因為/system.slice/kubelet.service沒有被建立,導致繼續列印本頁開頭貼上的錯誤日誌,這時候才是真的找不到指定的cgroup。
參考
本文 原創首發 於網站:www.lijiaocn.com