作者 | Kaku       责编 | 欧阳姝黎

近期有个小需求,在不重建Container的前提下修改Pod结构中的Request值,限制仅可以调小。本以为很简单的一个需求,但实际花费了一天的时间才搞完,代码改动只有几行,但是在改完测试的过程中发现很多超出预期或者认知的现象,为了搞懂为什么会这样,又重新捋了捋kubelet源码。 在这件事结束之后也进行了反思,主要是有关源码阅读的,于是把这个过程和自己的感触以及后续的一些改进方法和计划记录下来。

过程

先看下这件事的过程,我们先忽略这个需求的合理性,直接分析技术实现。

首先,kubernetes本身并不支持修改Pod的资源属性,无论Request还是Limit,可以通过修改apiserver中的校验逻辑来放开此限制;

其次,如何保证在Request改变之后容器不重启?我们知道,kubelet会为每个container都计算出一个hash值,其中用到了container的所有属性,在调用docker api进行容器创建的时候会把这个值设置到容器的Label中,后续如果kubelet检测到新计算出的hash值与在运行的容器的hash值不同,则会进行容器的原地重启操作,这也是为什么修改container的Image会出发容器原地重启的原因。很明显,如果放开Request的修改,Request值变了之后也会导致新的hash值变化从而导致容器重建,与我们的期望不符。也有办法来解决:记录container创建时的Request值,计算的时候还是使用创建时的值,此值只有在container创建时会记录,后续不再更新。

测试场景是创建了Qos类型为Guaranteed 类型的Pod,放开了kube-apiserver对Request修改的限制,kubelet保持原生不动,调小其Request值,大家可以先尝试自己思考一下会发生什么?

那么在测试的时候遇到了什么问题呢?

首先,发现放开Request修改之后,如果改了Request的值,容器重启了(这一步符合预期),但是重启次数加2(这里其实是之前的一个盲点)

接着,继续修改Request值,容器依然重启(符合预期),但是此次重启次数只加了1

最后,通过查看Pod Cgroup目录,确认修改后的Request已经在Pod级别和Container级别分别生效,但是同时存在两个Pod的目录,类似如下

# 修改之前的Pod对应的cgroup目录
/sys/fs/cgroup/cpu/kubepods/guaranteed/pod{uid}
# 修改之后的Pod对应的cgroup目录
/sys/fs/cgroup/cpu/kubepods/burstable/pod{uid}

为什么同一个Pod会存在两个cgroup目录呢?

容器重启了,重启次数应该只加1,那为什么在第一步中加了2?

你可以在继续阅读之前先自己思考一下可能的原因。

问题分析

首先看为什么会有两个cgroup目录,需要先搞清楚cgroup目录是如何创建、如何删除的。

Cgorup创建

我们采用CgroupPerQos的方式进行管理,以cpu子系统为例,层级类似如下所示

  • /sys/fs/cgroup/cpu

    • guaranteed

    • burstable

    • bestaffort

    • {containerid}

    • {containerid}

    • {containerid}

    • {containerid}

    • pod{uid}

    • pod{uid}

    • kubepods

从创建者的角度分两种:kubelet创建的、docker创建的。其中container层由docker创建,container以上的pod层、qos层和root(kubepods)都是由kubelet创建的。那docker又是怎么知道容器的cgroup parent目录是谁呢?其实是kubelet在调用docker api时传给docker的一个参数,告诉了其cgroup parent路径,可以通过执行docker inspect {containerid} | grep -i cgroup来查看每个container的cgroup parent路径。

那为什么会在两个qos目录下分别存在一个Pod目录呢?因为我们修改了Pod的Qos类型,触发了syncPod逻辑,里面会去根据Pod的qos类型进行cgroup目录判断,如果qos改变,则会把原Pod下的所有container全部杀掉,然后创建新的cgroup目录,再启动容器。这也就可以解释为什么在第一次修改Request之后Pod重启次数增加了2,因为pause容器也发生了重建。为什么要重建容器呢,因为整个pod的qos发生了变化,Pod内的所有容器需要在新的qos目录下重建其目录,但是kubelet没有去更新container的cgroup设置,而是采用重建的方式来实现。

为什么kubelet不直接去更新cgroup目录,而是重建容器呢?首先修改Request不仅影响cgroup,容器的oomscore也将受到影响,docker虽然提供了api来修改资源大小,但并没有提供相关的api去进行cgroup目录及oomscore等属性的修改,其次cgroup迁移是一个比较复杂的工作,迁移过程会出现部分历史数据丢失等问题,所以kubele直接采用重建的方式来解决这个问题。

Cgroup删除

经过分析Cgroup创建过程,重启两次的问题已经找到了答案。但为什么新的Pod cgroup目录创建出来之后,原有的目录没有被删除呢?这就需要搞清楚Pod Cgroup目录什么时候删除的,容器级别的cgroup目录是在容器被删除的时候删除的,这个很好理解,Pod级别的Cgroup目录是否也是在Pod删除时删除的呢?经过看代码发现并不是,Pod资源清理是一个异步的过程,定时监测Pod是否已经设置了deletionTimestamp属性和容器的运行状态,只有设置了此属性的Pod才有可能被清理,清理的过程中包含挂在卷、Cgroup等资源,会一并清理。因为修改Request的请求是不会去给Pod设置deletionTimestamp属性的,这就导致Pod级别的旧目录不会被删除,又因为新目录的创建,导致同时存在两个Pod级别的目录。

反思

综合看下来,这两个点都没有那么难,而且之前也做过kubelet定制开发,syncPod部分代码更是看过数次。那为什么花费了这么久的时间呢?

源码阅读的目的性

此前阅读源码的目的有几种,查问题、验证某些想法、探寻系统运行原理,还有一些人通过看源码来写blog、或者写源码分析之类的书。此前大部分场景是为了查问题、验证想法以及某些小功能的定制开发,可以聚焦到某些点或流程上,做完之后会对相关点印象比较深刻,但是相关性不大的地方就会很容易遗忘,或者说根本不会去刻意关注相关性不大的地方。

偶尔会有想法去主动阅读源码,探求系统运行原理,实现方式,写blog等,但最终发现效果很差,因为在此过程中我们的目的性并不是真正的去理解系统,缺乏针对性。而且对于在还不了解系统运行原理的情况下想通过看源码去了解其原理就是本末倒置的事情。比如接手一个新项目或者其他人的项目的时候,如果没有相关背景,而且也没有项目文档,没有代码注释的情况,直接想通过代码去了解业务和系统运行原理是一件非常痛苦的事情。

思考的必要性

无论处于什么目的去看代码,需要有自己的思考,可以假设系统由自己设计,那会设计成什么样子,代码由自己实现,会写成什么样子。在设计过程中可能会遇到一些问题,带着问题再去看代码,去验证别人是如何设计并实现的,尤其是遇到和自己预期设计不一致的地方,可以进行对比,分析那种方案好,或者他这么设计是处于什么考虑,为什么这么实现。以这样的方式看代码要比没有目的性的走马观花式的浏览代码收获更多,印象更深刻。这里推荐一本书《思考,快与慢》,解释了人的大脑是如何工作的,可以通过本书了解到思考是一个怎样的过程。

后续计划

源码还是要去读的,后面会进行一些尝试,根据上面提到的阅读方式开始进行,即 思考系统运行方式 ==》自己设计系统实现 ==》带着问题读源码(验证想法) ==》思考与总结,试运行一段时间看看效果。

生于2001年的《程序员》曾陪伴了无数开发者成长,影响了一代又一代的中国技术人。时隔20年,《新程序员》带着全球技术大师深邃思考、优秀开发者技术创造等深度内容回来了!同时将全方位为所有开发者呈现国内外核心技术生态体系全景图。扫描下方小程序码即可立即订阅!

几行代码撸了一天,源码到底该如何读?相关推荐

  1. 源码到底应该怎么读?

    1 走上阅读源码的道路 从我刚刚开始入行的时候就一直就有很多前辈们提点我,告诉我要读源码,只有阅读源码才能提高自己成为大神.但是始终没有人告诉我到底改怎么阅读源码,阅读源码到底可以怎么提高自己从哪些方 ...

  2. 年赚百万游戏主播,玩转Python后:几行代码轻松“吃鸡” 附源码

    大吉大利,准备吃鸡! 你是否玩儿了好几个月的吃鸡,依旧是落地成盒? 是否常常不得知自己如何被打.莫名其妙的挂了? 还没有吃过鸡/(ㄒoㄒ)/~~总是不明不白的就被别的玩家杀了 !!!∑(゚Д゚ノ)ノ能 ...

  3. 年赚百万游戏主播!玩转Python后:几行代码轻松“吃鸡” 附源码

    大吉大利,准备吃鸡! 你是否玩儿了好几个月的吃鸡,依旧是落地成盒? 是否常常不得知自己如何被打.莫名其妙的挂了? 还没有吃过鸡/(ㄒoㄒ)/~~总是不明不白的就被别的玩家杀了 !!!∑(゚Д゚ノ)ノ能 ...

  4. 70行代码撸一个桌面自动翻译神器(采用Markdown格式编写)

    70行代码撸一个桌面自动翻译神器 前言 工作上经常需要与外国友人邮件沟通,奈何工作电脑没有安装有道词典一类的翻译软件,结合自己的需要,自己撸一个桌面翻译神器. 基本思路:基于PySimpleGUI开发 ...

  5. 我的CSDN笔记总索引(阅读量降序,代码自动遍历生成HTML5源码)

    Python代码用"命令容器"方法os.system(),调用Linux命令行工具crul获取CSDN博文页面源码,Python内置re正则解析出博文笔记信息,按阅读量降序模块输出 ...

  6. Eatting外卖基于瑞吉外卖代码全功能优化含源码

    Eatting外卖基于瑞吉外卖代码全功能优化含源码 文章目录 项目的最终部署 源码地址 github:[源码地址](https://github.com/yangxingyue0623/Eating_ ...

  7. matlab小游戏源代码下载,3个游戏代码和164个教学源码 - 源码下载|Windows编程|源代码 - 源码中国...

    压缩包 : 5676153个游戏代码和164个教学源码.rar 列表 164个完整的Java代码.rar Java各种排序算法代码.rar 俄罗斯方块.rar 推箱子游戏.rar 贪吃蛇游戏.rar ...

  8. react相关代码库以及框架的源码解析

    持续更新中react相关库源码浅析, react ts3 项目 ???对react相关代码库以及框架的源码进行了一定的分析 ?react16.6 View contents 源码实例分析:可见runl ...

  9. DIY官网可视化工具打造低代码可视化一键生成导出源码工具

    DIY官网可视化工具 打造低代码可视化一键生成导出源码工具 设计一次打通设计师+产品经理+技术开发团队必备低代码可视化工具 从想法到原型到源码,一步到位低代码生成源码工具 立即定制DIY官网可视化工具 ...

最新文章

  1. adb 测试工作中的总结
  2. 硬件安全系列 逻辑电路基础知识介绍(三)
  3. Esxi服务器虚拟化平台搭建
  4. 录播软件开始麦克风应该打开还是关闭
  5. 浅谈专有云MQ存储空间的清理机制
  6. 放大器的传递函数_保证放大器的稳定性什么最重要?反馈电阻一定要选对!
  7. android 视频录制小例子,android 录制视频实例 VideoRecordDemo
  8. Java中各种对象的各种实例化方式
  9. 盒马申请多个“屁股脸”商标被驳回!其IP盒马先生被网友戏称:“屁股脸”...
  10. 《梦断代码》随笔第2篇
  11. LOCK - 明确地锁定一个表
  12. 工具篇:金蝶K3工具下载
  13. 商誉专题RN及H5项目总结
  14. 并发类编程—CountDownLatch(同步器)
  15. Gmail邮箱登陆问题解决方案
  16. 风清扬环保:分析乳化液破乳剂的实际应用
  17. java提高_最有效提高Java的10个计划
  18. 在正式使用计算机账务系统的银行对账功能,会计电算化账务处理系统中,银行对账的功能有哪些? 爱问知识人...
  19. 关于SAPI的两段小代码(c++)
  20. 奇怪的日常 [ 1 ]:个人微信如何实现自动回复

热门文章

  1. Unicode和ASCII的区别
  2. 计算两个日期的时间间隔 python
  3. flex 注册监听器时传值
  4. 【C++ Primer】 神秘的 sizeof(union) 、sizeof(struct) 和内存对齐技术
  5. 10大国外IT网站(转)
  6. extjs 不显示 但是不报错
  7. datatable把一个LIst的数据放入两个colum防止窜行的做法
  8. log4j的org.apache.log4j.PatternLayout
  9. WinCE OAL中的RAM定制函数
  10. [C] 从文件读取数据