Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >cgroup 挂载失败是什么鬼???

cgroup 挂载失败是什么鬼???

作者头像
米开朗基杨
发布于 2021-04-01 23:35:09
发布于 2021-04-01 23:35:09
1.3K00
代码可运行
举报
文章被收录于专栏:云原生实验室云原生实验室
运行总次数:0
代码可运行

问题

线上 k8s 集群在进行容器创建时报如下错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Failed create pod sandbox: rpc error: code = Unknown desc = failed to start sandbox container for pod “xxx-sf-32c80-0: Error response from  daemon: cgroups: cannot find cgroup mount destination: unknown

之前遇到过 cgroup 相关问题,但是这个问题还是头一次见,网上搜索了关键字,社区有类似报错的 issue,如cgroups: cannot found cgroup mount destination: unknown[1],联系最近做过的线上变更及问题,怀疑跟某自定义组件有关,详细背景参考这篇[2]

排查过程

光看问题云里雾里的,只知道和 cgroup 有关,登陆宿主查看此错误是 kubelet 请求 docker 时 docker 返回的,docker 18.06 版本,没有更详细的日志了,但是开源的一个好处在于查问题的时候有源码,这大大降低了查问题的难度,直接去 docker 项目中搜索关键词,最终发现是在 containerd 的源码中,相关代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// PidPath will return the correct cgroup paths for an existing process running inside a cgroup
// This is commonly used for the Load function to restore an existing container
func PidPath(pid int) Path {
 p := fmt.Sprintf("/proc/%d/cgroup", pid)
 paths, err := parseCgroupFile(p)
 if err != nil {
  return errorPath(errors.Wrapf(err, "parse cgroup file %s", p))
 }
 return existingPath(paths, "")
}

func existingPath(paths map[string]string, suffix string) Path {
 // localize the paths based on the root mount dest for nested cgroups
 for n, p := range paths {
  dest, err := getCgroupDestination(string(n))
  if err != nil {
   return errorPath(err)
  }
  rel, err := filepath.Rel(dest, p)
  if err != nil {
   return errorPath(err)
  }
  if rel == "." {
   rel = dest
  }
  paths[n] = filepath.Join("/", rel)
 }
 return func(name Name) (string, error) {
  root, ok := paths[string(name)]
  if !ok {
   if root, ok = paths[fmt.Sprintf("name=%s", name)]; !ok {
    return "", fmt.Errorf("unable to find %q in controller set", name)
   }
  }
  if suffix != "" {
   return filepath.Join(root, suffix), nil
  }
  return root, nil
 }
}

func getCgroupDestination(subsystem string) (string, error) {
 f, err := os.Open("/proc/self/mountinfo")
 if err != nil {
  return "", err
 }
 defer f.Close()
 s := bufio.NewScanner(f)
 for s.Scan() {
  fields := strings.Split(s.Text(), " ")
  if len(fields) < 10 {
   // broken mountinfo?
   continue
  }
  if fields[len(fields)-3] != "cgroup" {
   continue
  }
  for _, opt := range strings.Split(fields[len(fields)-1], ",") {
   if opt == subsystem {
    return fields[3], nil
   }
  }
 }
 if err := s.Err(); err != nil {
  return "", err
 }
 return "", ErrNoCgroupMountDestination
}

func parseCgroupFile(path string) (map[string]string, error) {
 f, err := os.Open(path)
 if err != nil {
  return nil, err
 }
 defer f.Close()
 return parseCgroupFromReader(f)
}

func parseCgroupFromReader(r io.Reader) (map[string]string, error) {
 var (
  cgroups = make(map[string]string)
  s       = bufio.NewScanner(r)
 )
 for s.Scan() {
  if err := s.Err(); err != nil {
   return nil, err
  }
  var (
   text  = s.Text()
   parts = strings.SplitN(text, ":", 3)
  )
  if len(parts) < 3 {
   return nil, fmt.Errorf("invalid cgroup entry: %q", text)
  }
  for _, subs := range strings.Split(parts[1], ",") {
   if subs != "" {
    cgroups[subs] = parts[2]
   }
  }
 }
 return cgroups, nil
}

逻辑比较清晰,先从/proc/id/cgroup 中解析得到所有的 subsystem,对应上面 parseCgroupFromReader 函数,/proc/id/cgroup 内容如下

先按冒号分隔每行字符串,然后取第 2 列,再根据逗号分隔得到所有的子系统,最终返回所有子系统。然后调用 existingPath 检查是否所有子系统都存在,内部又调用 getCgroupDestination,最终的报错就是在这个函数里报出来的。

getCgroupDestination 的逻辑是读取/proc/id/mountinfo 信息,判断是否传入的子系统存在

先根据空格分隔,找到所有 cgroup 类型的目录,然后再根据逗号分隔遍历所有的子系统是否是传入的子系统。找不到的话就会报错,但是不得不吐槽的就是这个报错报的太没有诚意了,要是直接把找不到的子系统报出来,问题会直观很多。

结论

到此可以明白是 agent 隔离程序先 mount 了自定义目录 cpu_mirror 到 cgroup 目录下,然后影响到了 java 程序去获取正确的核数,为了修复特意执行了 umount 的操作,但是 umount 之后/proc/id/cgroup 还是存在 cpu_mirror 相关信息而/proc/id/mountinfo 中已经不存在了,在容器重新创建的时候进行检查进而报错。

对比线上其他 docker 版本,比如 1.13.1 中就没有此问题,因为 1.13.1 用的 containerd 中并没有上面提到的检验逻辑

通过这个问题也暴露出来我们在测试、灰度过程中的问题,由于线上环境复杂,系统版本众多、组件版本也不统一,在上线一个功能或者执行线上操作的时候,测试用例需要充分覆盖所有场景,灰度时也需要所有类型的机器至少都覆盖到了之后才可以放量继续靠扩大灰度范围,否则很容易出现类似的问题。

参考资料

[1]

cgroups: cannot found cgroup mount destination: unknown: https://github.com/docker/for-linux/issues/219

[2]

这篇: https://www.likakuli.com/posts/docker-java-cpu

原文链接:https://www.likakuli.com/posts/docker-cgroup-unknown/

你可能还喜欢

点击下方图片即可阅读

GitHub 又又又多了一个新主题 —— Dimmed Dark 主题!

云原生是一种信仰 ?

关注公众号

后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云原生实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
cgroup mount destination: unknown
Failed create pod sandbox: rpc error: code = Unknown desc = failed to start sandbox container for pod “xxx-sf-32c80-0”: Error response from daemon: cgroups: cannot find cgroup mount destination: unknown
李鹤
2023/03/06
3.5K0
cgroup mount destination: unknown
runC源码分析——cgroup
runC项目中,与cgroups相关的代码,都在目录 runc/libcontainer/cgroups/下,下面是其源码目录结构分析: 我们关注的主要内容在apply_raw.go和各个cgrou
Walton
2018/04/13
1.6K0
runC源码分析——cgroup
go.uber.org/automaxprocs 源码分析
我们知道在GMP模型中P的数量决定了并行运行的goroutine数量,runtime.GOMAXPROCS 在 Go 1.5 版本后的默认值是机器的 CPU 核数 (runtime.NumCPU),在runtime 包里有两个函数可以方便使用
golangLeetcode
2022/12/17
5970
🤔how to implement container in golang?
容器的前世今生 容器是什么 从名字上就可以很明显的看出容器就是盛放东西的实体,比如盛放饮料的杯子☕️。 [杯具] 在计算机的世界里并没有饮料,计算机世界中只有资源,比如cpu、内存、磁盘等等,而容器的作用正是盛放我们的各种计算机资源。容器是从container翻译过来的,但是其实container的另一个翻译’集装箱‘可能更能符合语义。举个例子,汽车🚗(我们的程序)从天津港(开发环境)装进集装箱箱运输到新加坡港口(生产环境),中间不会损失任何零件,而汽车🚘运输到新加坡港后落地就可以直接启动。这就是容器化的第
绝了
2021/05/31
1.2K0
PHP+Go 开发仿简书,实战高并发高可用微服务架构
在当今多元化的技术生态中,选择合适的编程语言来构建高效的应用程序至关重要。PHP 和 Go 作为两种极具特色的编程语言,各自在不同的领域展现出强大的优势。将它们结合使用,能够充分发挥两者的长处,为复杂的软件开发项目提供创新的解决方案。
瘦瘦itazs和fun
2025/01/02
2580
Containerd容器管理机制
containerd 是一个来自 Docker 的高级容器运行时,并实现了 CRI 规范。它是从 Docker 项目中分离出来,之后 containerd 被捐赠给云原生计算基金会(CNCF)为容器社区提供创建新容器解决方案的基础。
tunsuy
2023/08/19
5310
Containerd容器管理机制
大厂都在玩的容器技术到底是什么?
著名杂志《经济学人》曾经评价“没有集装箱,就没有全球化”,可以说集装箱的出现重塑了现代货运体系,实现了交通运输行业的标准化,有效降低物流运输成本,极大提升了货物转运效率。而在云原生领域,容器就相当于集装箱,它使得软件发布以及软件运行隔离实现标准化,引领了云原生基础设施的跨越式发展。从某种意义上来说,容器技术重塑了整个软件供应链。今天就和大家聊聊各个大厂都在玩的容器技术到底是什么。
慕枫技术笔记
2023/03/20
3220
大厂都在玩的容器技术到底是什么?
记一次 CGroup 小实验
之前遇到一个需求:解决 cephfs 的 csi-node 插件,在 csi-node 重启以后,ceph-fuse 进程也随之终止,导致已挂在的目录挂载点丢失。
西凉风雷
2022/11/23
4570
记一次 CGroup 小实验
第十三章 go实现分布式网络爬虫---单机版爬虫
以上是go语言中已经you封装好的爬虫库或者框架, 但我们写爬虫的目的是为了学习. 所以.....不使用框架了
用户7798898
2020/09/27
8030
第十三章   go实现分布式网络爬虫---单机版爬虫
linux Cgroup使用介绍
Control Groups提供了一种机制,可以把task以及他们的子task聚集或者分组成带有特定行为的hierarchical groups。
用户1141344
2019/02/21
5.5K0
cAdvisor源码分析
##cAdvisor监控数据分析 入口 cAdvisor 代码托管地址 代码入口: github.com/google/cadvisor/cadvisor.go API handler: githu
Walton
2018/04/13
3.8K0
cAdvisor源码分析
Cgroup泄露1
线上k8s节点创建容器时提示"no space left on device",为已知问题,参考
李鹤
2023/03/28
2870
Cgroup泄露1
【Go 语言社区】golang的bufio用于内容解析
golang提供了io.Reader,也就是读内容,可以从很多地方读,譬如: // from string.var r io.Reader = strings.NewReader(string("hello, world"))// from bytes.var r io.Reader = bytes.NewReader([]byte("hello, world!"))// from bytes buffer.var r io.Reader = bytes.NewBuffer([]byte("hello, w
李海彬
2018/03/20
1.5K0
runC源码分析——namespace
runc/libcontainer/configs/config.go中定义了container对应的Namespaces。另外对于User Namespaces,还定义了UidMappings和GidMappings for user map。 // Config defines configuration options for executing a process inside a contained environment. type Config struct { ... /
Walton
2018/04/13
1.7K0
Docker内核知识
Docker容器的本质是宿主机上的进程,通过namespace实现资源隔离,通过cgroups实现资源限制,通过写时复制机制实现高效的文件操作。
yaohong
2020/03/19
1.5K0
使用 Uber automaxprocs 正确设置 Go 程序线程数
我们知道 Go 语言没有直接对用户暴露线程的概念,而是通过 goroutine 来控制并发。不过,在 Go 程序启动时,其背后的调度器往往是多线程运行的。在 Go 语言的 GMP 调度模型中,P 决定着同时运行的 goroutine 数,我们可以通过环境变量 GOMAXPROCS 或者运行时函数 runtime.GOMAXPROCS(n) 来设置 P 的数量。默认情况下 P 的数量等于机器中可用的 CPU 核心数,不过,这也带来了一个潜在的问题,在容器运行环境下,P 的数量可能远超容器限制的 CPU 数量,从而引发性能问题。本文我们一起来看一下 Go 程序在不同运行环境中的表现,以及如何解决潜在问题。
曾高飞
2025/05/23
880
linux namespace and cgroup
提供了对UTS、IPC、mount、PID、network、User等的隔离机制。
王磊-字节跳动
2019/05/28
4.2K0
Kubelet 对 Pod 的服务质量管理
上篇文章 《Kubelet 创建 pod 工作流程》 讲解了 kubelet 如何创建 pod,各组件之间如何协作。基于上一篇文章,本文会讲解 kubelet 如何对 Pod 进行服务质量管理。
CS实验室
2021/03/22
1.1K0
Kubelet 对 Pod 的服务质量管理
Rust vs Go:常用语法对比(八)
题目来自 Golang vs. Rust: Which Programming Language To Choose in 2023?[1] 141. Iterate in sequence over
fliter
2023/09/05
3520
Rust vs Go:常用语法对比(八)
探索 Linux 命名空间和控制组:实现资源隔离与管理的双重利器
Linux 命名空间是一种隔离机制,允许将全局系统资源划分为多个独立的、相互隔离的部分,使得在不同的命名空间中运行的进程感知不到其他命名空间的存在。从而实现了对进程、网络、文件系统、IPC(进程间通信)等资源的隔离,减少了潜在的安全风险。例如,在容器中运行应用程序可以避免对主机系统的直接影响,从而提高了系统的安全性。
柠檬汁Code
2023/12/06
2.1K0
探索 Linux 命名空间和控制组:实现资源隔离与管理的双重利器
相关推荐
cgroup mount destination: unknown
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验