1. 故事背景

目前我只有一个k8s集群,测试和生产环境都在这个集群里。测试环境是未经测试的版本,有可能会因为BUG而拉垮机器。为了避免这个风险,我有两个选择:

  • 建立两个集群,把生产和测试分离

  • 同一个集群,但把测试的服务调度到指定的节点上。

基于成本,我选择了第二个方案。

2. 提前准备

  • 本次试验是在CentOS 7.4 64位系统
  • 拥有Kubernetes集群

3. Taints and Tolerations

官方文档参考

k8s通过Taints/Tolerations机制,可以让那些心胸宽广(容忍)的Pod才可以接受(调度到)这个有缺陷(瑕疵)的节点上。

Node(节点)被打上Taint(瑕疵)后,因为Pod追求“完美”,正常的Pod是不会被调度至有瑕疵的节点(Master节点就是利用了这个特性)。如果Pod比较大度,可以Toleration(容忍)这些Taint(瑕疵),那么是可以调度到这个节点的。

根据背景,我需要在测试节点上,打上Taint(瑕疵):

# 简易语法说明,详细看官方文档,NoSchedule是effect的一种
kubectl taint nodes {node-name} {key}={value}:NoSchedule
# 我的实践,test=true表达这个节点是测试环境的节点
kubectl taint nodes k8s-test test=true:NoSchedule

执行之后,不会有新的Pod调度至此,不过原来就在此节点的Pod不会被驱逐。

“瑕疵”有了,测试环境的Pod也需要“容忍”TA才可以调度至此:

# 因为我用的Deployment,我以此为例,如果你直接用Pod,也是一样的
# 在节点spec.template.spec下,新增tolerations
tolerations:
  - key: "test"
    operator: "Exists"
    effect: "NoSchedule"

上面的例子表达可以容忍key为”test”(只需要存在即可,不需要判断value),并且effect为”NoSchedule”的节点。

至此,我的测试Pod便可以调度至这个有瑕疵的节点上了。

4. NodeSelector

官方参考文档

上一节完成后,看起来已经完成我的需求了,实际上并没有。上一节完成的需求仅仅是测试Pod可以在测试节点上调度,但测试Pod并非100%调度至测试节点上。(我虽然容忍你,但我不一定选你)

为了完成目的,我们可以这样做:

# 首先给节点打标签label(同样表示测试环境)
kubectl label nodes k8s-test test=true

# 在Deployment描述文件里,节点spec.template.spec下,新增nodeSelector
nodeSelector:
  test: "true"

通过NodeSelector让Pod选择符合条件的节点,就可以满足我的需求了。

5. 总结

  • 首先,节点通过新增Taint(瑕疵)来拒绝Pod调度
  • 其次,Pod增加Toleration表示容忍这个瑕疵,拥有了调度至此的条件
  • 最后,Pod通过nodeSelector,告诉k8s一生只爱TA,不愿意调度去其他节点

🙂联姻成功,恭喜💐

6. 题外话

在Taint的实践上,我遇过一个问题。上面的实践中,effect使用了NoSchedule,我也说明了,原来在此节点上的Pod不会被驱逐。

在我一开始实践时,使用的是NoExecute,这个effect则会驱逐不该在这里生存的Pod。

对于业务Pod没关系,而那些系统类、工具辅助类的Pod则有影响,譬如:flannel、traefik等,会导致网络不正常,还有些收集日志的Pod等等,他们存在的形式大多是daemonset。

最后我把Taint删掉,等Daemonset类的Pod同步完成后,再改为NoSchedule,就一切正常了。

删除的例子:

# 简易说明,注意后面的减号
kubectl taint nodes {node-name} {key}:{effect}-
# 实例
kubectl taint nodes k8s-test test:NoExecute-