诚之道 發表於 2020-2-23 15:30:00

记一次kubernetes驱逐踩坑

<p>最近在公司的线上服务器上发现了一个现象: 将某个node的kubelet短暂的停掉之后,其上的pod马上会被驱逐,这让笔者大吃一惊,印象之中,停掉kubelet后,该node会变为<code>NotReady</code>状态,随后controller-manger会经过一段时间才开始驱逐其上的pod。还有个参数专门来控制这个时间:</p>
<blockquote>
<p>--pod-eviction-timeoutThe grace period for deleting pods on failed nodes. (default 5m0s)`</p>
</blockquote>
<p>该参数默认值为5min, 也就是说当node NotReady之后,最少也得五分钟之后其上的pod才会被驱逐。但是现实情况明显不符合预期啊,这样就有点奇怪了。<br>
鉴于该问题影响巨大,笔者果断开启了debug之旅。</p>
<p>首先我们从直接原因下手,需要了解一下当node NotReady之后,controller-manager是如何判断并驱逐其上的pod的,这部分工作是由node lifecycel controller模块负责。</p>
<p>node lifecycle controller 主要负责对node生命周期进行检查,判断node是否存活,如果长时间检测不到node心跳则驱逐其上的pod。在目前的版本(v1.16)中,默认开启了<code>TaintBasedEvictions</code>, <code>TaintNodesByCondition</code>这两个feature gate,则所有node生命周期管理都是通过condition + taint的方式进行管理。其主要逻辑由三部分组成:</p>
<ol>
<li>不断地检查所有node状态,设置对应的condition</li>
<li>不断地根据node condition 设置对应的taint</li>
<li>不断地根据taint驱逐node上面的pod</li>
</ol>
<blockquote>
<p>想要详细了解node lifecycle controller工作机制的同学可以翻阅笔者上一篇文章: kubernetes中node心跳处理逻辑分析</p>
</blockquote>
<p>查看代码发现<code>--pod-eviction-timeout</code>并未起作用,原来在v1.13版本之前<code>TaintBasedEvictions</code>功能还未开启,此时node not-ready之后,controller直接判断not-ready时间是否超过了<code>--pod-eviction-timeout</code>,超过就进行删除。而v1.13,<code>TaintBasedEvictions</code>功能开启之后就不会使用了该参数,转而使用了上面描述的condition+taint方案。也就是说node NotReady之后,pod的驱逐时间完全由每个pod toleration中 tolerationSecond决定,而不是由controller-manager的参数<code>--pod-eviction-timeout</code>统一决定。这样想想也合情合理,每个pod对于故障的容忍时间不同,tolerationSecond可以更加灵活地为每个pod指定不同的驱逐时间。</p>
<p>这样说来,所有的pod都需要设置一个toleration才对,查阅相关资料后发现,社区已经有了一个<code>DefaultTolerationSeconds</code> admisson controller自动地帮助我们设置toleration,每次创建更新pod, 在请求发送到apiserver之后会自动设置5min的默认toleration。</p>
<blockquote>
<p>This admission controller sets the default forgiveness toleration for pods to tolerate the taints notready:NoExecute and unreachable:NoExecute for 5 minutes, if the pods don’t already have toleration for taints node.kubernetes.io/not-ready:NoExecute or node.alpha.kubernetes.io/unreachable:NoExecute.</p>
</blockquote>
<p>文档上显示该admission controller默认开启,但是为什么我司的环境上面没有生效,仔细看了一下文档,是因为自己使用了一个deprecated的参数:<code>--admission-control</code>,使用这个参数的话必须显式指定所有要开启的admisson controller plugin列表。该参数在v1.10被废弃,由两个新的参数<code>--enable-admission-plugins</code>和<code>--disable-admission-plugins</code>替换,这两个参数如果不指定的话会有默认值,其中<code>DefaultTolerationSeconds</code>就属于<code>--enable-admission-plugins</code>参数的默认值之一,也就是会默认开启该plugin。又是一个升级导致的坑! 正确修改了该参数之后,新创建的pod就会默认带上了toleration,<code>DefaultTolerationSeconds</code> adminssion controller plugin总算生效了。 </p>
<p>新创建的pod总算没有问题了,但是对于集群中已经存在的pod还没有toleration该怎么办? 显然社区对这种情况未做处理,<code>DefaultTolerationSeconds</code>是社区很早就开发的一个feature,但是<code>TaintBasedEvictions</code>是v1.13才默认开启,估计社区在开发时前后没有做好兼容。翻了下<code>DefaultTolerationSeconds</code>adminssion plugin源码,发现该plugin对于pod <code>create, update</code>操作都会设置toleration, 所以一般情况下, 我们升级集群的时候,总是能触发pod发生update,该toleration会自动添加上,无需过多操作,大可不必担心,心里知道有这个事情并检查一下就行。但是笔者的环境已经升级完成了,只能手动触发pod update了,思来想去,想要触发pod update又不能影响业务,给pod打label是一个比较好的方式,于是笔者写了一个脚本将集群中所有的pod都打了一个无关紧要的label, 触发uodate后就自动添加了toleration。</p>
<p>至此整个修复工作全部完成,回过头来仔细想想,kubernetes版本间升级挑战还是挺大的,兼容性问题防不胜防,很可能两个小版本间完全兼容,没任何问题,但是当一步步从低版本升级上来的时候问题就出现了,而且在兼容性测试的时候难以覆盖到每种情况,很可能需要很久问题才能暴露出来。对于这种情况,唯有掌握内部机理,熟读源码才能快速诊断,修复。</p><br><br>
来源:https://www.cnblogs.com/gaorong/p/12284888.html
頁: [1]
查看完整版本: 记一次kubernetes驱逐踩坑