Kubernetes问题调查:failed to get cgroup stats for /systemd/system.slice

栏目: 编程工具 · 发布时间: 5年前

内容简介:Kubelet日志中不停的出现下面的错误:又是和cgroup有关的错误,见到cgroup就头大,之前学习过cgroup(Kubernetes版本1.9.11。

说明

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.sliceGithub 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 <lijiaocn@foxmail.com>
//
// 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-only   Only 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。

参考

  1. 《cgroup:linux的cgroup的使用》
  2. Github Issue #56850: Failed to get system container stats for..

Kubernetes问题调查:failed to get cgroup stats for /systemd/system.slice

本文 原创首发 于网站:www.lijiaocn.com


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Types and Programming Languages

Types and Programming Languages

Benjamin C. Pierce / The MIT Press / 2002-2-1 / USD 95.00

A type system is a syntactic method for automatically checking the absence of certain erroneous behaviors by classifying program phrases according to the kinds of values they compute. The study of typ......一起来看看 《Types and Programming Languages》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具