第7章-Pod与节点
原创
本博客原创文章,转载请注明出处
- name: 原创
desc: 本博客原创文章,转载请注明出处
bgColor: '#F0DFB1'
textColor: '#1078E6'
2
3
4
# 第7章-Pod与节点
本章要点 |
---|
查看节点的标签 / 注释,为节点设置和删除标签 / 注释 |
在指定的节点上创建 Pod |
为节点设置污点,为 Pod 设置容忍污点 |
# 11. 节点标签(Labels)
默认情况下,创建的 Pod 由 master 自动调度到合适的节点上运行。
但有时候,我们需要在指定的节点上创建和运行 Pod,以满足业务需求。
# 1.11.1 查看节点标签
和 Pod 类似,在设置标签之前,我们可以通过选项--show-labels
来查看节点所具有的标签:
kubectl get nodes --show-labels
默认情况下,每个节点都具有很多的标签,例如标签kubernetes.io/os
记录了操作系统类型,标签kubernetes.io/hostname
记录了节点的主机名。而 master 节点还拥有几个专属的标签,以此突显出 master 的地位。
此外,如果你只想查看其中一个或多个节点的信息,则可以这样指定参数:
# 查看单个节点
kubectl get nodes <节点名称>
# 查看多个节点
kubectl get nodes <节点名称1> <节点名称2> <节点名称3> ...
2
3
4
5
例如:
kubectl get nodes www.k11.com
kubectl get nodes www.k11.com --show-labels
kubectl get nodes www.k11.com www.k12.com --show-labels
2
3
# 1.21.2 为节点设置标签
在第 5 章中,我们曾通过kubectl label pods
命令为 Pod 设置标签。而对于节点,我们同样可以使用以下命令为其设置标签:
kubectl label nodes <节点名称> <键>=<值>
# 多个标签之间通过 空格 来分隔
kubectl label nodes <节点名称> <键1>=<值1> <键2>=<值2>
# 通过 减号- 来删除标签
kubectl label nodes <节点名称> <键>-
2
3
4
5
6
7
例如:
# 为 worker02 节点设置标签
kubectl label nodes www.k12.com abc=123 xyz=456
2
成功设置标签,多个标签之间通过空格分隔。
删除标签:
# 删除 worker02 上的 abc 标签
kubectl label nodes www.k12.com abc-
2
通过减号-
可以很轻松地删除某个标签。
# 1.2.11.2.1 特殊标签-节点角色
master 节点上存在这样一个标签node-role.kubernetes.io/control-plane=
,这个标签的键名是node-role.kubernetes.io/control-plane
,但值却为空?而在键名中,斜杠后面的control-plane
又刚好是节点信息 “ROLES” 这一列的内容。
其他工作节点没有这个标签,所以 “ROLES” 这一列的内容为<none>
(空)。
其他工作节点也同样可以设置这个标签,我们分别为其设置一个只有键node-role.kubernetes.io/<名称>
,没有值的标签。
# 为 worker01 和 worker02 节点设置角色标签
kubectl label nodes www.k11.com node-role.kubernetes.io/worker1=
kubectl label nodes www.k12.com node-role.kubernetes.io/worker2=
2
3
可以看到,在为节点设置了一个只有键node-role.kubernetes.io/<名称>
没有值的标签之后,节点信息 “ROLES” 那一列的内容变为了斜杠/
后面的名称。
能否删除这个标签呢?可以,同样是使用 “键名 + 减号-
” 的格式来删除这些标签。
# 删除 master 节点的角色标签
kubectl label nodes www.k10.com node-role.kubernetes.io/control-plane-
2
删除标签node-role.kubernetes.io/<名称>
之后,节点信息 “ROLES” 这一列的内容也会变为空。
该标签不需要参数值,所以你留空,或提供一个空字符串都可以。
# 不加引号
kubectl label nodes www.k10.com node-role.kubernetes.io/control-plane=
# 加引号
kubectl label nodes www.k10.com node-role.kubernetes.io/control-plane=""
2
3
4
5
但就算你真的为该标签提供了一个值,那也不会有任何影响。
# 22. 手动指定Pod运行位置
看了那么久,节点的标签和 Pod 有什么关系?
# 2.12.1 选择节点
下面我们通过spec.nodeSelector
字段,让 Pod 在指定的节点上运行。创建以下 yaml 文件:
# pod-node.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node
spec:
nodeSelector:
xyz: "456"
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-node
2
3
4
5
6
7
8
9
10
11
12
看到xyz: "456"
你可能会觉得很眼熟,这正是之前为 worker02 节点设置的xyz=456
标签。
- 标签的值一般是不用加引号的,但由于这里的
456
是纯数字,所以要加上引号,将其视为一个字符串
当这个 Pod 被创建时,它会寻找带有xyz=456
标签的节点,然后到该节点上运行。
kubectl apply -f pod-node.yaml
从图中可以看到,在创建 Pod 之后,它确实被调度到 worker02 节点上运行了。
之前介绍过,标签kubernetes.io/hostname
记录了节点的主机名。再创建一个 Pod,这次通过该标签来指定 Pod 的运行位置:
# pod-node-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-2
spec:
nodeSelector:
kubernetes.io/hostname: www.k12.com
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-node-2
2
3
4
5
6
7
8
9
10
11
12
创建这个 Pod。
kubectl apply -f pod-node-2.yaml
pod-node-2
依然运行在 worker02 节点上。
之前还介绍过一个特殊的标签-节点角色,这个标签的值一般为空,你可以这样写:
- 值的位置空着,只写标签的键名
- 提供一个空字符
# pod-node-3.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-3
spec:
nodeSelector:
node-role.kubernetes.io/worker2: # 这里只设置了键, 值可以空着不写(分号一定要写)
node-role.kubernetes.io/worker2: "" # 这里提供了一个空字符串
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-node-3
2
3
4
5
6
7
8
9
10
11
12
13
两种写法选一种即可,我这里演示第一种:空值。
创建 Pod 之后,这个 Pod 顺利运行了,并位于 worker02 节点上。
# 2.22.2 多个标签选择条件
为两个工作节点设置标签,待会要用到。
kubectl label nodes www.k11.com type=worker status=opening
kubectl label nodes www.k12.com type=worker status=closed
2
创建以下 yaml 文件。
# pod-working.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-working
spec:
nodeSelector:
type: worker
status: opening
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-working
2
3
4
5
6
7
8
9
10
11
12
13
这个 Pod 将被调度到指定的节点上运行,这个节点必须同时具有type=worker
和status=opening
两个标签。这些标签是 “且” 的关系,而满足这个条件的只有 worker01 节点。
# 创建这个 Pod
kubectl apply -f pod-working.yaml
2
和预期中想的一样,这个 Pod 被调度到了 worker01 节点上运行。
# 2.32.3 不满足条件的情况
和上一个 Pod 类似,但这次我将status: opening
故意写成了status: open
(少了 “ing”)。
# pod-notfound.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-notfound
spec:
nodeSelector:
type: worker
status: open
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-notfound
2
3
4
5
6
7
8
9
10
11
12
13
节点必须同时拥有type=worker
和status=open
两个标签,这个 Pod 才能在其上运行。
但此时没有节点满足这个条件,如果创建这个 Pod 会发生什么?
kubectl apply -f pod-notfound.yaml
可以看到 Pod 的状态一直停留在 “Pending”(等待中)。由于没有合适的节点,所以这个 Pod 会一直处于等待状态,直到出现符合条件的节点。
如果后续出现了合适的节点,则 Pod 的状态会转变为 “Running”,否则它将一直等待。
# 删除 worker02 原有的 status 标签
kubectl label nodes www.k12.com status-
# 为 worker02 设置新的 status 标签
kubectl label nodes www.k12.com status=open
2
3
4
5
标签键名不能重复使用,所以要删除原有标签,才能设置一个新的标签值。
# 2.42.4 多个满足条件的情况下
如果有多个节点都满足条件,例如 worker01 和 worker02 都拥有标签type=worker
,这时候如果我这样写:
...
spec:
nodeSelector:
type: worker
...
2
3
4
5
两个节点都满足条件,这时候 k8s 就会回到最初的起点......通过算法智能调度 Pod,你可以理解为随机挑选一个节点。
但 k8s 通常会选择最空闲的那个节点,因为要平均每台机器上的 Pod 数量,以实现负载均衡。
所以,只有当集群内拥有大量节点时,nodeSelector
才能发挥出最大的效果。
# 33. 节点注释(Annotations)
注释有点类似于标签,都可以为一个集群资源对象标注信息。
# 3.13.1 查看节点注释
可以通过describe
列出节点的详细信息,在详细信息中可以找到节点的注释。
kubectl describe nodes
# 只列出一个节点的详细信息
kubectl describe nodes www.k10.com
# 同时列出多个节点的详细信息,通过 空格 来分隔节点名称
kubectl describe nodes www.k11.com www.k12.com
2
3
4
5
6
7
由于节点的详细信息过多,所以此处通过head
命令截取前20
行的内容。
kubectl describe nodes www.k11.com | head -20
可以看到,注释记录了 worker01 节点的一些基本信息,例如该节点使用的容器运行时是 containerd,IP 地址是多少等等。
而在注释信息的上方,还列出了节点的所有标签。这些标签一行一个,可读性更高。
# 3.23.2 为节点设置注释
使用annotate
为集群资源设置注释信息,语法和label
是一样的。
kubectl annotate nodes <节点名称> <键>=<值>
# 多个注释信息通过 空格 来分隔
kubectl annotate nodes <节点名称> <键1>=<值1> <键2>=<值2> ...
# 通过 减号- 删除某个注释信息
kubectl annotate nodes <节点名称> <键>-
2
3
4
5
6
7
为 worker01 节点设置两个注释信息hostname=www.k11.com
和node.name=worker01
。
kubectl annotate nodes www.k11.com hostname=www.k11.com node.name=worker01
注释设置成功。
删除 worker01 的注释信息hostname
。
kubectl annotate nodes www.k11.com hostname-
删除成功,已经看不到刚刚的那条注释了。
# 3.33.3 标签和注释的区别
# (1)标签
标签可以用于标记某个集群资源,例如:
# 为一个 Pod 设置标签
kubectl label pods <Pod名称> <键>=<值>
# 为一个节点设置标签
kubectl label nodes <节点名称> <键>=<值>
2
3
4
5
然后,你可以通过get
或describe
查看这些标签:
kubectl get pods --show-labels
kubectl get nodes --show-labels
2
又或者,你可以针对这些标签,进行某些操作,例如:
# 列出具有标签 type=worker 的多个节点
kubectl get nodes -l type=worker
# 同时删除具有标签 abc=123 的多个 Pod
kubectl delete pods -l abc=123
2
3
4
5
甚至,你可以通过spec.nodeSelector
字段来指定 Pod 的运行位置。
...
spec:
nodeSelector:
status: opening
...
2
3
4
5
总的来讲,你可以设置标签、查看标签、针对标签做一些操作。
# (2)注释
你可以对一个集群资源设置注释、查看其注释,例如:
# 为一个 Pod 设置注释
kubectl annotate pods <Pod名称> <键>=<值>
# 查看 Pod 详细信息并在其中寻找注释
kubectl describe pods <Pod名称>
2
3
4
5
但注释也就仅限于查看了,你无法针对注释做一些特殊的操作。
我们来看看编程语言中的注释 (opens new window):
- 注释就是对代码的解释和说明,其目的是让人们能够更加轻松地了解代码。
- 注释是编写程序时,写程序的人给一个语句、程序段、函数等的解释或提示,能提高程序代码的可读性。
- 注释只是为了提高可读性,不会被计算机编译。
k8s 中的注释和编程语言一样,只是用来看的,不会影响集群的运行。
笔记
k8s 中的注释:
- 注释就是对集群资源的解释和说明,其目的是让人们能够更加轻松地了解资源信息。
- 注释是创建集群资源时,创建者给一个资源对象的名称、环境、作用、使用方法等的解释或提示,能提高集群的可读性、可维护性。
- 注释只是为了提高可读性,不会被集群作用于任何方面。
# 44. 污点与容忍污点
举个例子,如果一栋房子存在污点(比如是个凶宅),一般买房的人都不会选择这栋房子。但是如果某个人可以容忍这个污点(接受凶宅),那他就可以买下这栋房子并且住进去。
在 k8s 中,如果一个节点存在污点,那么 Pod 就不会被调度到上面运行。但如果这个 Pod 可以容忍污点,那么它就可以在这个节点上运行。
什么?你说把污点藏起来,然后骗 Pod 进来?...... k8s 没那种东西。
# 4.14.1 污点(taint)
# 4.1.14.1.1 查看污点
和注释的查看方式一样,都需要通过describe
列出节点详情,然后在其中寻找污点信息。
# 列出污点
kubectl describe nodes | grep Taint
# 列出节点角色 和 污点
kubectl describe nodes | grep -E '(Roles|Taints)'
2
3
4
5
可以看到,master 节点拥有一个默认的污点node-role.kubernetes.io/control-plane:NoSchedule
,而其他两个工作节点的污点默认为<none>
(空,没有污点)。
由于 master 是控制节点,所以不应该在其上运行业务 Pod。通过为 master 设置污点,Pod 将会避开它,然后被调度到其他合适的工作节点上运行。
# 4.1.24.1.2 为节点设置污点
和标签 / 注释一样,污点也采用 “键值对” 的形式,但是污点的键值对具有一个额外属性。
kubectl taint nodes <节点名称> <键>=<值>:<效果>
# 同时设置多个污点,通过 空格 来分隔
kubectl taint nodes <节点名称> <键1>=<值1>:<效果1> <键2>=<值2>:<效果2> ...
2
3
4
在这条命令当中,你肯定注意到了 “效果” 这个东西,效果不能随意指定,它有三个可选的值:
NoSchedule
:后续新创建的 Pod 不会被调度到这个节点,该节点上现有的 Pod 继续运行。NoExecute
:和 NoSchedule 类似,但是会影响节点上现有的 Pod。如果这些 Pod 可以容忍污点,则可以继续运行;如果这些 Pod 不能容忍污点,则会被驱逐到其他节点上运行。PreferNoSchedule
:是 NoSchedule 的降级版本,控制平面在调度 Pod 的时候,会尽量避开这个节点,尽量不让 Pod 在该节点上运行。
在这三个可选值当中,最经常使用的是NoSchedule
,因为你给一个节点设置污点,当然是不希望后续创建的 Pod 在其上运行啦。
运行以下命令,为 worker02 节点设置污点personality=bad
,效果是NoSchedule
(不允许调度)。
kubectl taint nodes www.k12.com personality=bad:NoSchedule
污点设置成功。
在和 master 的污点进行比较时,你会发现 master 的污点格式是<键>:<效果>
,和默认的角色标签一样,都是没有参数值的。
创建三个 Pod 测试下污点的效果:
kubectl run pod1 --image nginx --image-pull-policy=IfNotPresent
kubectl run pod2 --image nginx --image-pull-policy=IfNotPresent
kubectl run pod3 --image nginx --image-pull-policy=IfNotPresent
2
3
一般来讲,master 会平均地调度 Pod 到每个节点上。
但此时 worker02 拥有污点,所以创建的 Pod 全部跑到 worker01 上面去了。
# 4.1.34.1.3 删除污点
和标签 / 注释一样,可以通过减号-
来删除污点。
kubectl taint nodes <节点名称> <键>-
删除刚刚为 worker02 设置的污点。
kubectl taint nodes www.k12.com personality-
删除成功。
# 4.24.2 为Pod设置容忍污点(tolerations)
# 示例-1
再为 worker02 设置一个污点,但这次我们不设置 “值”,只有键和效果:
kubectl taint nodes www.k12.com notgood:NoSchedule
设置成功。
创建以下 yaml 文件:
# pod-toler.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-toler
spec:
nodeSelector:
kubernetes.io/hostname: www.k12.com
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-toler
2
3
4
5
6
7
8
9
10
11
12
以上 Pod 通过nodeSelector
字段指定运行节点为 www.k12.com。但此时该节点具有污点,当创建 Pod 之后,这个 Pod 能否正常运行?
kubectl apply -f pod-toler.yaml
可想而知,虽然标签匹配,但是这个 Pod 无法容忍污点,状态会一直停留在 “Pending”(等待),直到出现一个合适的节点。
接下来,修改刚刚的 yaml 文件,在其中添加spec.tolerations
字段:
# pod-toler.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-toler
spec:
nodeSelector:
kubernetes.io/hostname: www.k12.com
tolerations:
- key: notgood
effect: NoSchedule
operator: Exists
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-toler
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在字段spec.tolerations
中:
key
是要容忍污点的对应 “键”effect
是这个 “键” 的对应 “效果”- 这里
operator
被设置为Exists
,意思是可以匹配任何 “值”,包括空值
修改文件之后,原先创建的 Pod 不用删除,可以使用apply
重新应用文件,更新 Pod 属性和状态:
kubectl apply -f pod-toler.yaml
在重新应用文件之后,Pod 的属性得到了更新。现在它可以容忍污点,于是成功地在 worker02 节点上运行了。
删除这个 Pod:
kubectl delete -f pod-toler.yaml
注意
即使一个 Pod 可以容忍污点,但其不一定会在污点主机上运行,因为运行位置是由 master 随机调度的。
如果你想让 Pod 百分之百运行在污点主机上,那么除了配置容忍污点之外,还需要通过nodeSelector
指定其运行位置。
# 示例-2
一个节点可以同时拥有多个污点。如果 Pod 想要在该节点上运行,就必须容忍其所有污点。
为 worker02 设置第二个污点,这次具有参数值:
kubectl taint nodes www.k12.com personality=bad:NoSchedule
设置成功,此时 worker02 同时具有两个污点。
创建以下 yaml 文件:
# pod-bad.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-bad
spec:
nodeSelector:
kubernetes.io/hostname: www.k12.com
tolerations:
- key: "notgood"
effect: "NoSchedule"
operator: "Exists"
- key: "personality"
effect: "NoSchedule"
operator: "Equal"
value: "bad"
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-bad
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
之前你可能就已经注意到了,spec.tolerations
是一个数组,意味着它可以同时容忍多个污点。
- 第一个容忍的污点是
notgood:NoSchedule
,这和上一个示例相同,不在多讲。 - 第二个容忍的污点是
personality=bad:NoSchedule
。在这里operator
被设置为了Equal
,意思是存在参数值,并且value
字段需要和污点中的值相等。
另外,这一次tolerations
中的值都使用引号"
包裹起来了,表示这是一个字符串,官方文档中推荐这样做。
创建这个 Pod:
kubectl apply -f pod-bad.yaml
Pod 成功容忍了两个污点,并运行在了 worker02 节点上。
# operator与空value
字段spec.tolerations.operator
有两个可选的值:
operator: Exists
:没有value
的情况下使用,可以匹配任何值,包括空值operator: Equal
:有value
的情况下使用,其值必须和污点中的值相同
在上面的示例中,这两个operator
可选值我们都演示过。例如:
# 没有value的情况下
...
tolerations:
- key: "abc"
effect: <对应效果>
operator: "Exists"
2
3
4
5
6
还有:
# 有value的情况下
...
tolerations:
- key: "xyz"
effect: <对应效果>
operator: "Equal"
value: 456
2
3
4
5
6
7
那如果,我即没有污点值(为空),又想将operator
设置为Equal
怎么办(叛逆)?很简单,只需要提供一个空字符串即可:
# Equal 并且无 value 的情况下
...
tolerations:
- key: "jkl"
effect: <对应效果>
operator: "Equal"
value: ""
2
3
4
5
6
7
有关于污点的更多信息,你可以查阅官方文档。
最后,记得删除 worker02 节点上的所有污点,以免影响后续实验。
kubectl taint node www.k12.com notgood-
kubectl taint node www.k12.com personality-
2
# 55. 小结
本章学习了标签和注释的使用方法,并对它们做了一个区分。然后在 yaml 文件中为 Pod 指定了节点标签,以将 Pod 调度到特定的节点上运行。最后介绍了节点的污点,以及 Pod 的容忍污点。
通过本章的学习,并结合前面所学的内容,你应该掌握以下技能:
- 为节点或 Pod 设置、删除标签
- 为节点或 Pod 设置、删除注释
- 针对特定标签,进行批量的资源操作
- 调度 Pod 到指定的节点上运行
- 为节点设置、删除污点
- 使 Pod 容忍污点
如果你的状态不错,可以去练练手(练习题-5)。