4.Pod详解
4.Pod详解
文章目录
- 4.Pod详解
- 4.1 Pod介绍
- 4.1.1 Pod结构
- 4.1.2 Pod定义
- 4.1.3 在kubernetes中基本所有资源的一级属性都是一样的,主要包含5部分:
- 4.1.4 在上面的属性中,spec是接下来研究的重点,继续看下它的常见子属性:
- 4.2 Pod配置
- 4.2.1 基本配置
- 4.2.2 镜像拉取
- 4.2.3 添加标签
- 4.2.4 镜像拉取策略 imagePullPolicy
- 4.2.5 启动命令
- 4.2.6 执行命令 - command
- 4.2.7 环境变量
- 4.2.8 端口设置
- 4.2.9 资源配额
- 4.3 Pod生命周期
- 4.3.1 创建和终止
- 4.3.2 初始化容器
- 4.3.3 钩子函数
- 4.3.4 容器探测
- 4.4.5 查看livenessProbe的子属性
- 4.3.5 重启策略
- 4.4 Pod调度
- 4.4.1 定向调度
- 4.4.2 亲和性调度
- 4.4.3 污点和容忍
4.1 Pod介绍
4.1.1 Pod结构

每个Pod中都可以包含一个或者多个容器,这些容器可以分为两类:
-
用户程序所在的容器,数量可多可少
-
Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:
- 可以以它为依据,评估整个Pod的健康状态
- 可以在根容器上设置Ip地址,其它容器都此Ip(Pod IP),以实现Pod内部的网路通信
这里是Pod内部的通讯,Pod的之间的通讯采用虚拟二层网络技术来实现,我们当前环境用的是Flannel
4.1.2 Pod定义
下面是Pod的资源清单:
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,资源类型,例如 Pod
metadata: #必选,元数据name: string #必选,Pod名称namespace: string #Pod所属的命名空间,默认为"default"labels: #自定义标签列表- name: string
spec: #必选,Pod中容器的详细定义containers: #必选,Pod中容器列表- name: string #必选,容器名称image: string #必选,容器的镜像名称imagePullPolicy: [ Always|Never|IfNotPresent ] #获取镜像的策略 command: [string] #容器的启动命令列表,如不指定,使用打包时使用的启动命令args: [string] #容器的启动命令参数列表workingDir: string #容器的工作目录volumeMounts: #挂载到容器内部的存储卷配置- name: string #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符readOnly: boolean #是否为只读模式ports: #需要暴露的端口库号列表- name: string #端口的名称containerPort: int #容器需要监听的端口号hostPort: int #容器所在主机需要监听的端口号,默认与Container相同protocol: string #端口协议,支持TCP和UDP,默认TCPenv: #容器运行前需设置的环境变量列表- name: string #环境变量名称value: string #环境变量的值resources: #资源限制和请求的设置limits: #资源限制的设置cpu: string #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数requests: #资源请求的设置cpu: string #Cpu请求,容器启动的初始可用数量memory: string #内存请求,容器启动的初始可用数量lifecycle: #生命周期钩子postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器exec: #对Pod容器内检查方式设置为exec方式command: [string] #exec方式需要制定的命令或脚本httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、portpath: stringport: numberhost: stringscheme: stringHttpHeaders:- name: stringvalue: stringtcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式port: numberinitialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次successThreshold: 0failureThreshold: 0securityContext:privileged: falserestartPolicy: [Always | Never | OnFailure] #Pod的重启策略nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定- name: stringhostNetwork: false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络volumes: #在该pod上定义共享存储卷列表- name: string #共享存储卷名称 (volumes类型有很多种)emptyDir: {} #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值hostPath: string #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录path: string #Pod所在宿主机的目录,将被用于同期中mount的目录secret: #类型为secret的存储卷,挂载集群与定义的secret对象到容器内部scretname: string items: - key: stringpath: stringconfigMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部name: stringitems:- key: stringpath: string
#小提示:
# 在这里,可通过一个命令来查看每种资源的可配置项
# kubectl explain 资源类型 查看某种资源可以配置的一级属性
# kubectl explain 资源类型.属性 查看属性的子属性
[root@k8s-master01 ~]# kubectl explain pod
KIND: Pod
VERSION: v1
FIELDS:apiVersion <string>kind <string>metadata <Object>spec <Object>status <Object>[root@k8s-master01 ~]# kubectl explain pod.metadata
KIND: Pod
VERSION: v1
RESOURCE: metadata <Object>
FIELDS:annotations <map[string]string>clusterName <string>creationTimestamp <string>deletionGracePeriodSeconds <integer>deletionTimestamp <string>finalizers <[]string>generateName <string>generation <integer>labels <map[string]string>managedFields <[]Object>name <string>namespace <string>ownerReferences <[]Object>resourceVersion <string>selfLink <string>uid <string>
4.1.3 在kubernetes中基本所有资源的一级属性都是一样的,主要包含5部分:
- apiVersion 版本,由kubernetes内部定义,版本号必须可以用 kubectl api-versions 查询到
- kind 类型,由kubernetes内部定义,版本号必须可以用 kubectl api-resources 查询到
- metadata
- 元数据,主要是资源标识和说明,常用的有name、namespace、labels等
- spec 描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述
- status 状态信息,里面的内容不需要定义,由kubernetes自动生成
4.1.4 在上面的属性中,spec是接下来研究的重点,继续看下它的常见子属性:
- containers <[]Object> 容器列表,用于定义容器的详细信息
- nodeName 根据nodeName的值将pod调度到指定的Node节点上
- nodeSelector 根据节点选择器中定义的信息选择将该Pod调度到包含这些label的Node 上
- hostNetwork 是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
- volumes <[]Object> 存储卷,用于定义Pod上面挂在的存储信息
- restartPolicy 重启策略,表示Pod在遇到故障的时候的处理策略(出现故障,是重启,不重启,还是在干掉在重启)
4.2 Pod配置
pod.spec.containers属性
<[]Object> 是个列表容器拉取策略:
Always, Never, IfNotPresent
一直拉取,绝不拉,没有的时候拉,有的时候不拉取镜像[root@k8s-master01 ~]# kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 数组,代表可以有多个容器
FIELDS:name <string> # 容器名称image <string> # 容器需要的镜像地址imagePullPolicy <string> # 镜像拉取策略 command <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令args <[]string> # 容器的启动命令需要的参数列表env <[]Object> # 容器环境变量的配置ports <[]Object> # 容器需要暴露的端口号列表resources <Object> # 资源限制和资源请求的设置(能用多少资源)
4.2.1 基本配置
[root@k8s-master inventory]# cat nginx.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: devlabels:dev: test
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30
定义了一个比较简单Pod的配置,里面有两个容器:nginx:用1.17.1版本的nginx镜像创建,(nginx是一个轻量级web容器)
busybox:用1.30版本的busybox镜像创建,(busybox是一个小巧的linux命令集合)# 创建Pod
[root@k8s-master inventory]# kubectl apply -f nginx.yaml
namespace/dev created
pod/nginx created# 查看Pod状况
# READY 1/2 : 表示当前Pod中有2个容器,其中1个准备就绪,1个未就绪
# RESTARTS : 重启次数,因为有1个容器故障了,Pod一直在重启试图恢复它
[root@k8s-master inventory]# kubectl get -f nginx.yaml
NAME STATUS AGE
namespace/dev Active 75sNAME READY STATUS RESTARTS AGE
pod/nginx 1/2 CrashLoopBackOff 3 (36s ago) 75s
# 可以通过describe查看内部的详情
# 此时已经运行起来了一个基本的Pod,虽然它暂时有问题
- 修改之后,busybox没有内置命令,一执行就退出了
[root@k8s-master inventory]# kubectl delete -f nginx.yaml
namespace "dev" deleted
pod "nginx" deleted[root@k8s-master inventory]# cat nginx.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: devlabels:dev: test
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30command: ["/bin/sleep","6000"]
[root@k8s-master inventory]# kubectl get -f nginx.yaml
NAME STATUS AGE
namespace/dev Active 30sNAME READY STATUS RESTARTS AGE
pod/nginx 2/2 Running 0 30s
4.2.2 镜像拉取
[root@k8s-master inventory]# cat pod-httpd.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: pod-httpdnamespace: devlabels:dev: test
spec:containers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent- name: busyboximage: busybox:latestcommand: ["/bin/sleep","6000"]
namespace/dev unchanged
pod/pod-httpd created
[root@k8s-master inventory]# kubectl get -f pod-httpd.yaml
NAME STATUS AGE
namespace/dev Active 25mNAME READY STATUS RESTARTS AGE
pod/pod-httpd 2/2 Running 0 16s# 查看Pod详情
# 此时明显可以看到httpd镜像有一步Pulling image "httpd:latest"的过程
[root@k8s-master inventory]# kubectl describe -f pod-httpd.yaml
.........
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 70s default-scheduler Successfully assigned dev/pod-httpd to k8s-node2Normal Pulled 70s kubelet Container image "httpd:latest" already present on machineNormal Created 69s kubelet Created container httpdNormal Started 69s kubelet Started container httpdNormal Pulling 69s kubelet Pulling image "busybox:latest"Normal Pulled 67s kubelet Successfully pulled image "busybox:latest" in 2.613062239s (2.613072529s including waiting)Normal Created 67s kubelet Created container busyboxNormal Started 66s kubelet Started container busybox
4.2.3 添加标签
[root@k8s-master inventory]# cat pod-httpd.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: pod-httpdnamespace: devlabels:dev: test
spec:containers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent- name: busyboximage: busybox:latestcommand: ["/bin/sleep","6000"]
[root@k8s-master inventory]# kubectl apply -f pod-httpd.yaml
namespace/dev unchanged
pod/pod-httpd created# 查看标签:
[root@k8s-master inventory]# kubectl get -f pod-httpd.yaml --show-labels
NAME STATUS AGE LABELS
namespace/dev Active 20h kubernetes.io/metadata.name=devNAME READY STATUS RESTARTS AGE LABELS
pod/pod-httpd 2/2 Running 12 (10m ago) 20h dev=test
4.2.4 镜像拉取策略 imagePullPolicy
-
用于设置镜像拉取策略,kubernetes支持配置三种拉取策略:
- Always:总是从远程仓库拉取镜像(一直远程下载)
- IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就本地 本地没就远程下载)
- Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错 (一直使用本地)
-
默认值说明:
- 如果镜像tag为具体版本号, 默认策略是:IfNotPresent
- 如果镜像tag为:latest(最终版本) ,默认策略是always
4.2.5 启动命令
- busybox并不是一个程序,而是类似于一个工具类的集合,kubernetes集群启动管理后,它会自动关闭。解决方法就是让其一直在运行,这就用到了command配置
[root@k8s-master inventory]# cat pod-command.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: pod-pullimagenamespace: devlabels:app: httpdlab
spec:containers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent- name: busyboximage: busybox:latestcommand: ["/bin/sh","-c","while true;do /bin/echo $(date +%T) > /tmp/hello.txt; sleep 3; done;"]# 查看Pod状态
# 此时发现两个pod都正常运行了
[root@k8s-master inventory]# kubectl apply -f pod-command.yaml
namespace/dev created
pod/pod-pullimage created
[root@k8s-master inventory]# kubectl get -f pod-command.yaml
NAME STATUS AGE
namespace/dev Active 6sNAME READY STATUS RESTARTS AGE
pod/pod-pullimage 2/2 Running 0 6s
[root@k8s-master inventory]# kubectl get -f pod-command.yaml --show-labels
NAME STATUS AGE LABELS
namespace/dev Active 9s kubernetes.io/metadata.name=devNAME READY STATUS RESTARTS AGE LABELS
pod/pod-pullimage 2/2 Running 0 9s app=httpdlab# 进入pod中的busybox容器,查看文件内容
# 补充一个命令: kubectl exec pod名称 -n 命名空间 -it -c 容器名称 /bin/sh 在容器内部执行命令
# 使用这个命令就可以进入某个容器的内部,然后进行相关操作了
# 比如,可以查看txt文件的内容
[root@k8s-master inventory]# kubectl exec pod-pullimage -itn dev -c busybox -- /bin/sh
/ #
/ # tail -f /tmp/hello.txt
09:31:08
^C
/ # tail -f /tmp/hello.txt
09:31:17
[root@k8s-master inventory]# kubectl exec pod-pullimage -itn dev -c httpd -- /bin/bash
root@pod-pullimage:/usr/local/apache2#
root@pod-pullimage:/usr/local/apache2# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
4.2.6 执行命令 - command
- 用于在pod中的容器初始化完毕之后运行一个命令
"/bin/sh","-c", 使用sh执行命令while true;do /bin/echo $(date +%T) > /tmp/hello.txt; sleep 3; done; 每隔3秒向文件/tmp/hello.txt 文件中写入当前时间
- 特别说明
通过上面发现command已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个args选项,用于传递参数呢?这其实跟docker有点关系,kubernetes中的command、args两项其实是实现覆盖Dockerfile中ENTRYPOINT的功能。1 如果command和args均没有写,那么用Dockerfile的配置。2 如果command写了,但args没有写,那么Dockerfile默认的配置会被忽略,执行输入的command3 如果command没写,但args写了,那么Dockerfile中配置的ENTRYPOINT的命令会被执行,使用当前args的参数4 如果command和args都写了,那么Dockerfile的配置被忽略,执行command并追加上args参数command取代ENTRYPOINT功能
args取代cmd功能docker下entrypoint和cmd的区别是:
1、CMD指令运行一个可执行的文件并提供参数,可以为ENTRYPOINT指定参数;
2、ENTRYPOINT指令本身也可以包含参数,变动的参数不会被覆盖。
4.2.7 环境变量
[root@k8s-master inventory]# vi pod-env.yaml
[root@k8s-master inventory]# cat pod-env.yaml
apiVersion: v1
kind: Namespace
metadata: name: dev---apiVersion: v1
kind: pod
metadata:name: pod-envnamespace: devlabels:app: httpdlab
spec;containers:- name: busyboximage: busybox: latestimagePullPolicy: IfNotPresentcommand: ["/bin/sh", "-c","while true;do /bin/echo $(date +%T) >> /tem/hello.txt;sleep 3; done;"]env:- name: "username"value: "mushuang"- name: "password"value: "run123123"- name: "age"value: "20"
[root@k8s-master inventory]# kubectl apply -f pod-env.yaml
namespace/dev unchanged
pod/pod-env createdenv # 设置环境变量列表
- env,环境变量,用于在pod中的容器设置环境变量。
[root@k8s-master inventory]# kubectl get -f pod-env.yaml
NAME STATUS AGE
namespace/dev Active 21hNAME READY STATUS RESTARTS AGE
pod/pod-env 1/1 Running 0 62s# 进入容器,输出环境变量
[root@k8s-master inventory]# kubectl exec pod-env -itn dev -c busybox -- /bin/sh
/ #
/ # echo $username
mushuang
/ # echo $age
20
/ # echo $password
run123123
/ #
4.2.8 端口设置
- containers的ports选项,ports支持的子选项
[root@k8s-master inventory]# kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:name <string> # 端口名称,如果指定,必须保证name在pod中是唯一的 containerPort<integer> # 容器要监听的端口(0<x<65536)hostPort <integer> # 容器要在主机上公开的端口,如果设置,主机上只能运行容器的一个副本(一般省略) hostIP <string> # 要将外部端口绑定到的主机IP(一般省略)protocol <string> # 端口协议。必须是UDP、TCP或SCTP。默认为“TCP”。
[root@k8s-master inventory]# cat pod-ports.yaml
apiVersion: v1
kind: Namespace
metadata: name: dev---apiVersion: v1
kind: Pod
metadata: name: pod-pullimagenamespace: devlabels:app: httpdlab
spec:containers: - name: httpdimage: httpd:latestimagePullPolicy: IfNotPresentports: # 设置容器暴露的端口列表- name: httpd-portcontainerPort: 80protocol: TCP
[root@k8s-master inventory]# kubectl apply -f pod-ports.yaml
namespace/dev created
pod/pod-pullimage created
[root@k8s-master inventory]# kubectl get -f pod-ports.yaml
NAME STATUS AGE
namespace/dev Active 23mNAME READY STATUS RESTARTS AGE
pod/pod-pullimage 1/1 Running 0 23m# 在下面可以明显看到配置信息
[root@k8s-master inventory]# kubectl get -f pod-ports.yaml -o yamlspec:containers:- image: httpd:latestimagePullPolicy: IfNotPresentname: httpdports:- containerPort: 80name: httpd-portprotocol: TCPresources: {}
- 访问容器中的程序需要使用的是
Podip:containerPort
4.2.9 资源配额
-
容器中的程序要运行,肯定是要占用一定资源的,比如cpu和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes提供了对内存和cpu的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:
- limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启(限制最大使用多少,超过了,容器会进行重启)
- requests :用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动(限制最小使用多少)
-
可以通过上面两个选项设置资源的上下限
[root@k8s-master inventory]# cat pod-resources.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata: name: httpd-resourcesnamespace: dev
spec:containers:- name: apacheimage: httpd:latestresources: # 资源配额limits: # 限制资源(上限,最大)cpu: "2" # CPU限制,单位是core数memory: "10Gi" # 内存限制requests: # 请求资源(下限,最少)cpu: "1" # CPU限制,单位是core数memory: "10Mi" # 内存限制
[root@k8s-master inventory]# kubectl apply -f pod-resources.yaml
namespace/dev unchanged
pod/httpd-resources created
- 对cpu和memory的单位
- cpu:core数,可以为整数或小数
- memory: 内存大小,可以使用Gi、Mi、G、M等形式
[root@k8s-master inventory]# kubectl get -f pod-resources.yaml
NAME STATUS AGE
namespace/dev Active 11mNAME READY STATUS RESTARTS AGE
pod/httpd-resources 1/1 Running 0 9m58s
[root@k8s-master inventory]# kubectl delete -f pod-resources.yaml
namespace "dev" deleted
pod "httpd-resources" deleted
[root@k8s-master inventory]# vi pod-resources.yaml
[root@k8s-master inventory]# kubectl apply -f pod-resources.yaml
namespace/dev created
pod/httpd-resources created
[root@k8s-master inventory]# kubectl get -f pod-resources.yaml
NAME STATUS AGE
namespace/dev Active 9sNAME READY STATUS RESTARTS AGE
pod/httpd-resources 0/1 Pending 0 9s
[root@k8s-master inventory]# kubectl describe -f pod-resources.yaml
........Warning FailedScheduling 33s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Insufficient memory. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod..####(Insufficient memory:内存不足)68s default-scheduler 0/3节点可用:1个(s)节点有不可容忍的污染{node- roles .kubernetes。io/control-plane:}, 3内存不足。抢占:0/3节点可用:1抢占对调度没有帮助,2传入pod没有发现抢占受害者。
4.3 Pod生命周期
我们一般将pod对象从创建至终的这段时间范围称为pod的生命周期,它主要包含下面的过程:
-
pod创建过程
-
运行初始化容器(init container)过程
-
运行主容器(main container)
- 容器启动后钩子(post start)、容器终止前钩子(pre stop)
- 容器的存活性探测(liveness probe)、就绪性探测(readiness probe)
-
pod终止过程
-

-
在整个生命周期中,Pod会出现5种状态(相位),分别如下:
- 挂起(Pending):apiserver已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
- 运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成
- 成功(Succeeded):pod中的所有容器都已经成功终止并且不会被重启
- 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
- 未知(Unknown):apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所导致
4.3.1 创建和终止
pod的创建过程
- 用户通过kubectl或其他api客户端提交需要创建的pod信息给apiServer
- apiServer开始生成pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端
- apiServer开始反映etcd中的pod对象的变化,其它组件使用watch机制来跟踪检查apiServer上的变动
- scheduler发现有新的pod对象要创建,开始为Pod分配主机并将结果信息更新至apiServer
- node节点上的kubelet发现有pod调度过来,尝试调用docker启动容器,并将结果回送至apiServer
- apiServer将接收到的pod状态信息存入etcd中

pod的终止过程
1. 用户向apiServer发送删除pod对象的命令
2. apiServcer中的pod对象信息会随着时间的推移而更新,在宽限期内(默认30s),pod被视为dead
3. 将pod标记为terminating状态
4. kubelet在监控到pod对象转为terminating状态的同时启动pod关闭过程
5. 端点控制器监控到pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除
6. 如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式启动执行
7. pod对象中的容器进程收到停止信号
8. 宽限期结束后,若pod中还存在仍在运行的进程,那么pod对象会收到立即终止的信号
9. kubelet请求apiServer将此pod资源的宽限期设置为0从而完成删除操作,此时pod对于用户已不可见
4.3.2 初始化容器
-
初始化容器是在pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:
-
- 初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么kubernetes需要重启它直到成功完成
-
- 初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行
-
-
初始化容器有很多的应用场景,下面列出的是最常见的几个:
- 提供主容器镜像中不具备的工具程序或自定义代码
- 初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足
接下来做一个案例,模拟下面这个需求:
假设要以主容器来运行nginx,但是要求在运行nginx之前先要能够连接上mysql和redis所在服务器
为了简化测试,事先规定好mysql(192.168.207.13)和redis(192.168.207.14)服务器的地址
[root@k8s-master inventory]# vi pod-initcon.yaml
[root@k8s-master inventory]# cat pod-initcon.yaml
apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: pod-initconnamespace: dev
spec:containers;- name: main-conimage: nginx:latestports:- name: nginx-portcontainerPort: 80initContainers:- name: test-mysqlimage: busybox:latestcommand: ['sh', '-c', 'until ping 192.168.232.160 -c 1 ; do echo waiting for mysql...; sleep 2; done;']- name: test-redisimage: busybox:latestcommand: ['sh', '-c', 'until ping 192.168.232.160 -c 1 ; do echo waiting for mysql...; sleep 2; done;']command: ['sh', '-c', 'until ping 192.168.232.160 -c 1 ; do echo waiting for mysql...; sleep 2; done;']# 条件不满足,ping不通,做后面的事情。# 通了,条件满足,然后退出
# 创建pod
[root@k8s-master inventory]# kubectl apply -f pod-initcon.yaml
namespace/dev created
pod/pod-initcon created# 查看pod状态
[root@k8s-master inventory]# kubectl get -f pod-initcon.yaml
NAME STATUS AGE
namespace/dev Active 21sNAME READY STATUS RESTARTS AGE
pod/pod-initcon 0/1 Init:0/2 0 21s# 发现pod卡在启动第一个初始化容器过程中,后面的容器不会运行
[root@k8s-master inventory]# kubectl describe pods pod-initcon -n dev
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 31s default-scheduler Successfully assigned dev/pod-initcon to k8s-node2Normal Pulling 31s kubelet Pulling image "busybox:latest"Normal Pulled 26s kubelet Successfully pulled image "busybox:latest" in 4.252818682s (4.252828991s including waiting)Normal Created 26s kubelet Created container test-mysqlNormal Started 26s kubelet Started container test-mysql# 接下来新开一个shell,为当前服务器新增两个ip,观察pod的变化
[root@k8s-master ~]# ip addr add 192.168.207.13/24 dev ens160
[root@k8s-master ~]# ip addr add 192.168.207.14/24 dev ens160# 动态查看pod
[root@k8s-master inventory]# kubectl get pods pod-initcon -n dev -w
NAME READY STATUS RESTARTS AGE
pod-initcon 0/1 Init:0/2 0 2s
pod-initcon 0/1 Init:0/2 0 8s
pod-initcon 0/1 Init:1/2 0 20s
pod-initcon 0/1 PodInitializing 0 24s
pod-initcon 1/1 Running 0 27s
^C[root@k8s-master inventory]# # 查看pod状态
[root@k8s-master inventory]kubectl get -f pod-initcon.yaml
NAME STATUS AGE
namespace/dev Active 83sNAME READY STATUS RESTARTS AGE
pod/pod-initcon 1/1 Running 0 83s
4.3.3 钩子函数
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
kubernetes在主容器的启动之后和停止之前提供了两个钩子函数:
- post start:容器创建之后执行,如果失败了会重启容器
- pre stop :容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
钩子处理器支持使用下面三种方式定义动作:
- Exec命令:在容器内执行一次命令
……
lifecycle:
postStart: exec:command:- cat- /tmp/healthy
……
-
TCPSocket:在当前容器尝试访问指定的socket
…… lifecycle:postStart:tcpSocket:port: 8080 …… -
HTTPGet:在当前容器中向某url发起http请求
…… lifecycle:postStart:httpGet:path: / #URI地址port: 80 #端口号host: 192.168.5.3 #主机地址scheme: HTTP #支持的协议,http或者https ……
接下来,以exec方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:
[root@k8s-master inventory]# vi pod-exec.yaml
[root@k8s-master inventory]# cat pod-exec.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-execnamespace:
spec:containers:- name: main-conimage: nginx:latestports:- name: nginx-portcontainerPort: 80lifecycle:postStart:exec: # 在容器启动的时候执行一个命令,修改掉nginx的默认首页内容command: ["/bin/sh", "-c", "echo 'hello world' > /usr/share/nginx/html/index.html"]preStop:exec: # 在容器停止之前停止nginx服务command: ["/usr/sbin/nginx","-s","quit"]
[root@k8s-master inventory]# kubectl get -f pod-exec.yaml
NAME READY STATUS RESTARTS AGE
pod-exec 1/1 Running 0 26m[root@k8s-master inventory]# kubectl get -f pod-exec.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-exec 1/1 Running 0 26m 10.244.2.25 k8s-node2 <none> <none>
[root@k8s-master inventory]# curl 10.244.2.25
hello world[root@k8s-master inventory]# kubectl exec pod-exec -itn dev -c main-con -- /bin/bash
root@pod-exec:/# cd /usr/share/nginx/html/
root@pod-exec:/usr/share/nginx/html# ls
50x.html index.html
root@pod-exec:/usr/share/nginx/html# cat index.html
hello world
root@pod-exec:/usr/share/nginx/html#
4.3.4 容器探测
容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么kubernetes就会把该问题实例" 摘除 ",不承担业务流量。kubernetes提供了两种探针来实现容器探测,分别是:
- liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器
- readiness probes:就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,k8s不会转发流量
livenessProbe 决定是否重启容器,readinessProbe 决定是否将请求转发给容器。
上面两种探针目前均支持三种探测方式:
-
Exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常
…… livenessProbe:exec:command:- cat- /tmp/healthy …… -
TCPSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常
…… livenessProbe:tcpSocket:port: 8080 …… -
HTTPGet:调用容器内Web应用的URL,如果返回的状态码在200和399之间,则认为程序正常,否则不正常
…… livenessProbe:httpGet:path: / #URI地址port: 80 #端口号host: 127.0.0.1 #主机地址scheme: HTTP #支持的协议,http或者https ……
下面以liveness probes为例,做几个演示:
Exec
[root@k8s-master inventory]# cat pod-liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata: name: pod-liveness-execnamespace: dev
spec:containers:- name: nginximage: nginx:latestports:- name: nginx-portcontainerPort: 80livenessProbe:exec:command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令
[root@k8s-master inventory]# kubectl apply -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
[root@k8s-master inventory]# kubectl get -f pod-liveness-exec.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 1/1 Running 3 (35s ago) 2m5s[root@k8s-master inventory]# kubectl describe pods pod-liveness-exec -n dev
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 99s default-scheduler Successfully assigned dev/pod-liveness-exec to k8s-node1Normal Pulled 96s kubelet Successfully pulled image "nginx:latest" in 2.822924352s (2.822938262s including waiting)Normal Pulled 65s kubelet Successfully pulled image "nginx:latest" in 3.407035458s (3.407042031s including waiting)Normal Killing 39s (x2 over 69s) kubelet Container nginx failed liveness probe, will be restartedNormal Pulling 38s (x3 over 98s) kubelet Pulling image "nginx:latest"Normal Created 35s (x3 over 96s) kubelet Created container nginxNormal Started 35s (x3 over 95s) kubelet Started container nginxNormal Pulled 35s kubelet Successfully pulled image "nginx:latest" in 3.140911903s (3.14092833s including waiting)Warning Unhealthy 29s (x7 over 89s) kubelet Liveness probe failed: /bin/cat: /tmp/hello.txt: No such file or directory# 观察上面的信息就会发现nginx容器启动之后就进行了健康检查
# 检查失败之后,容器被kill掉,然后尝试进行重启
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master inventory]# kubectl get -f pod-liveness-exec.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 0/1 CrashLoopBackOff 4 (6s ago) 2m36s# 当然接下来,可以修改成一个存在的文件,比如/tmp/hello.txt,再试,结果就正常了......
[root@k8s-master inventory]# kubectl exec pod-liveness-exec -itn dev -c nginx -- /bin/bash
root@pod-liveness-exec:/# echo '123456' > tmp/hello.txt
root@pod-liveness-exec:/# cat /tmp/hello.txt
123456
root@pod-liveness-exec:/# exit
exit
[root@k8s-master inventory]# kubectl get -f pod-liveness-exec.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 1/1 Running 2 (2m10s ago) 3m11s
TCPSocket
[root@k8s-master inventory]# vi pod-liveness-tcpsocket.yaml
[root@k8s-master inventory]# cat pod-liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-tcpsocketnamespace: dev
spec: containers:- name: nginximage: nginx:latestports:- name: nginx-portcontainerPort: 80livenessProbe:tcpSocket:port: 8080 # 尝试访问8080端口
[root@k8s-master inventory]# kubectl apply -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
[root@k8s-master inventory]# kubectl describe -f pod-liveness-tcpsocket.yaml
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 13s default-scheduler Successfully assigned dev/pod-liveness-tcpsocket to k8s-node1Normal Pulling 12s kubelet Pulling image "nginx:latest"Normal Pulled 8s kubelet Successfully pulled image "nginx:latest" in 3.669903843s (3.669924445s including waiting)Normal Created 8s kubelet Created container nginxNormal Started 8s kubelet Started container nginxWarning Unhealthy 2s kubelet Liveness probe failed: dial tcp 10.244.1.26:8080: connect: connection refusedfailed: dial tcp 10.244.1.26:8080# 观察上面的信息,发现尝试访问8080端口,但是失败了
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master inventory]# kubectl get -f pod-liveness-tcpsocket.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 0/1 CrashLoopBackOff 4 (36s ago) 3m7s# 当然接下来,可以修改成一个可以访问的端口,比如80,再试,结果就正常了......
[root@k8s-master inventory]# cat pod-liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-tcpsocketnamespace: dev
spec: containers:- name: nginximage: nginx:latestports:- name: nginx-portcontainerPort: 80livenessProbe:tcpSocket:port: 80
[root@k8s-master inventory]# kubectl get -f pod-liveness-tcpsocket.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 1/1 Running 0 44s
HTTPGet
[root@k8s-master inventory]# vi pod-httpget.yaml
[root@k8s-master inventory]# cat pod-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: dev
spec: containers:- name: nginximage: nginx:latestports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet: # 其实就是访问http://127.0.0.1:80/hello scheme: HTTP #支持的协议,http或者httpsport: 80 #端口号path: /hello #URI地址
[root@k8s-master inventory]# kubectl apply -f pod-httpget.yaml
pod/pod-liveness-httpget created
[root@k8s-master inventory]# kubectl describe -f pod-httpget.yaml
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 14s default-scheduler Successfully assigned dev/pod-liveness-httpget to k8s-node2Normal Pulling 14s kubelet Pulling image "nginx:latest"Normal Pulled 10s kubelet Successfully pulled image "nginx:latest" in 3.608349739s (3.608355865s including waiting)Normal Created 10s kubelet Created container nginxNormal Started 10s kubelet Started container nginxWarning Unhealthy 4s kubelet Liveness probe failed: HTTP probe failed with statuscode: 404# 观察上面信息,尝试访问路径,但是未找到,出现404错误
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master inventory]# kubectl get -f pod-httpget.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 0/1 CrashLoopBackOff 4 (32s ago) 3m2s# 当然接下来,可以修改成一个可以访问的路径path,比如/,再试,结果就正常了......
[root@k8s-master inventory]# vi pod-httpget.yaml
[root@k8s-master inventory]# cat pod-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: dev
spec: containers:- name: nginximage: nginx:latestports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: /
[root@k8s-master inventory]# kubectl get -f pod-httpget.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 0 56s
[root@k8s-master inventory]# kubectl describe -f pod-httpget.yaml
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 78s default-scheduler Successfully assigned dev/pod-liveness-httpget to k8s-node2Normal Pulling 78s kubelet Pulling image "nginx:latest"Normal Pulled 75s kubelet Successfully pulled image "nginx:latest" in 3.21366977s (3.213685373s including waiting)Normal Created 75s kubelet Created container nginxNormal Started 74s kubelet Started container nginx
4.4.5 查看livenessProbe的子属性
[root@k8s-master inventory]# kubectl describe -f pod-httpget.yaml
FIELDS:exec <Object> tcpSocket <Object>httpGet <Object>initialDelaySeconds <integer> # 容器启动后等待多少秒执行第一次探测timeoutSeconds <integer> # 探测超时时间。默认1秒,最小1秒periodSeconds <integer> # 执行探测的频率。默认是10秒,最小1秒failureThreshold <integer> # 连续探测失败多少次才被认定为失败。默认是3。最小值是1successThreshold <integer> # 连续探测成功多少次才被认定为成功。默认是1
[root@k8s-master inventory]# vi pod-liveness-httpget.yaml
[root@k8s-master inventory]# cat pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: dev
spec:containers:- name: nginximage: nginx:latestports: - name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: / initialDelaySeconds: 30 # 容器启动后30s开始探测timeoutSeconds: 5 # 探测超时时间为5s
[root@k8s-master inventory]# kubectl apply -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
[root@k8s-master inventory]# kubectl get -f pod-liveness-httpget.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 0 19s
[root@k8s-master inventory]# kubectl describe -f pod-liveness-httpget.yaml
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 32s default-scheduler Successfully assigned dev/pod-liveness-httpget to k8s-node2 # 成功分配dev/pod- alive -httpget到k8s-node2Normal Pulling 32s kubelet Pulling image "nginx:latest" # 将图像“nginx:最新的“Normal Pulled 29s kubelet Successfully pulled image "nginx:latest" in 2.661612093s (2.661632391s including waiting) # 在2.661612093s中成功拉出“nginx:latest”图像Normal Created 29s kubelet Created container nginx # 创建容器nginxNormal Started 29s kubelet Started container nginx # 启动容器nginx
4.3.5 重启策略
在上一节中,一旦容器探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由pod的重启策略决定的,pod的重启策略有 3 种,分别如下:
- Always :容器失效时,自动重启该容器,这也是默认值。
- OnFailure : 容器终止运行且退出码不为0时重启
- Never : 不论状态为何,都不重启该容器
重启策略适用于pod对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大延迟时长。
创建pod-restartpolicy.yaml:
[root@k8s-master inventory]# vi pod-restartPolicy.yaml
[root@k8s-master inventory]# cat pod-restartPolicy.yaml
apiVersion: v1
kind: Pod
metadata: name: pod-restartpolicynamespace: dev
spec: containers:- name: nginximage: nginx:latestports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: /hello # 访问不存在restartPolicy: Never # 设置重启策略为never 不重启
# 创建Pod
[root@k8s-master inventory]# kubectl apply -f pod-restartPolicy.yaml
pod/pod-restartpolicy created# 查看Pod详情,发现nginx容器失败
[root@k8s-master inventory]# kubectl describe -f pod-restartPolicy.yaml
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 53s default-scheduler Successfully assigned dev/pod-restartpolicy to k8s-node2Normal Pulling 53s kubelet Pulling image "nginx:latest"Normal Pulled 50s kubelet Successfully pulled image "nginx:latest" in 2.539945326s (2.539955192s including waiting)Normal Created 50s kubelet Created container nginxNormal Started 50s kubelet Started container nginxWarning Unhealthy 23s (x3 over 43s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404Normal Killing 23s kubelet Stopping container nginx
# 活动探测失败:HTTP探测失败,状态码为404# 多等一会,再观察pod的重启次数,发现一直是0,并未重启
[root@k8s-master inventory]# kubectl get -f pod-restartPolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Completed 0 66s
[root@k8s-master inventory]# kubectl get -f pod-restartPolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Completed 0 70s
[root@k8s-master inventory]# kubectl get -f pod-restartPolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Completed 0 72s
[root@k8s-master inventory]# kubectl get -f pod-restartPolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Completed 0 73s
4.4 Pod调度
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式:
-
自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
-
定向调度:NodeName、NodeSelector
-
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
-
污点(容忍)调度:Taints、Toleration
[root@k8s-master inventory]# kubectl describe node k8s-master Taints: node-role.kubernetes.io/control-plane:NoSchedule
4.4.1 定向调度
定向调度,指的是利用在pod上声明nodeName或者nodeSelector,以此将Pod调度到期望的node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过pod运行失败而已。
NodeName
NodeName用于强制约束将Pod调度到指定的Name的Node节点上。这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。
[root@k8s-master inventory]# vi pod-NodeName.yaml
[root@k8s-master inventory]# cat pod-NodeName.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: devlabels:app: httpdlab
spec: nodeName: k8s-node1containers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent- name: busyboximage: busybox:latestcommand: ["/bin/sleep","6000"]
[root@k8s-master inventory]# kubectl apply -f pod-NodeName.yaml
pod/pod-nodename created
[root@k8s-master inventory]# kubectl get -f pod-NodeName.yaml
NAME READY STATUS RESTARTS AGE
pod-nodename 2/2 Running 0 9s
[root@k8s-master inventory]# kubectl get -f pod-NodeName.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 2/2 Running 0 18s 10.244.1.29 k8s-node1 <none> <none>
[root@k8s-master inventory]# kubectl delete -f pod-NodeName.yaml
pod "pod-nodename" deleted
[root@k8s-master inventory]# clear
[root@k8s-master inventory]# vi pod-NodeName.yaml
[root@k8s-master inventory]# cat pod-NodeName.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: devlabels:app: httpdlab
spec: nodeName: k8s-node2 # 修改nodeName的值为node2containers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent- name: busyboximage: busybox:latestcommand: ["/bin/sleep","6000"]
[root@k8s-master inventory]# kubectl apply -f pod-NodeName.yaml
pod/pod-nodename created
[root@k8s-master inventory]# kubectl get -f pod-NodeName.yaml
NAME READY STATUS RESTARTS AGE
pod-nodename 2/2 Running 0 35s#再次查看,发现已经向Node2节点调度
[root@k8s-master inventory]# kubectl get -f pod-NodeName.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 2/2 Running 0 44s 10.244.2.35 k8s-node2 <none> <none>
[root@k8s-master inventory]# kubectl delete -f pod-NodeName.yaml
pod "pod-nodename" deleted
# 接下来,删除pod,修改nodeName的值为node3(并没有node3节点)
[root@k8s-master inventory]# vi pod-NodeName.yaml
[root@k8s-master inventory]# cat pod-NodeName.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: devlabels:app: httpdlab
spec: nodeName: k8s-node3containers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent- name: busyboximage: busybox:latestcommand: ["/bin/sleep","6000"]
[root@k8s-master inventory]# kubectl apply -f pod-NodeName.yaml
pod/pod-nodename created#再次查看,发现已经向Node3节点调度,但是由于不存在node3节点,所以pod无法正常运行
[root@k8s-master inventory]# kubectl get -f pod-NodeName.yaml
NAME READY STATUS RESTARTS AGE
pod-nodename 0/2 Pending 0 8s
[root@k8s-master inventory]# kubectl get -f pod-NodeName.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 0/2 Pending 0 14s <none> k8s-node3 <none> <none>
NodeSelector
NodeSelector用于将pod调度到添加了指定标签的node节点上。它是通过kubernetes的label-selector机制实现的,也就是说,在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束。
# 首先分别为node节点添加标签
[root@k8s-master inventory]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane 98d v1.27.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-node1 Ready <none> 98d v1.27.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2 Ready <none> 98d v1.27.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
[root@k8s-master inventory]# kubectl label nodes k8s-node1 env=test
node/k8s-node1 labeled
[root@k8s-master inventory]# kubectl label nodes k8s-node2 env=prod
node/k8s-node2 labeled
[root@k8s-master inventory]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane 98d v1.27.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-node1 Ready <none> 98d v1.27.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=test,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2 Ready <none> 98d v1.27.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=prod,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
- 创建一个pod-nodeselector.yaml文件,并使用它创建Pod
[root@k8s-master inventory]# vi pod-nodeselector.yaml
[root@k8s-master inventory]# cat pod-nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: devlabels:app: httpdlab
spec: nodeSelector:env: prod # 指定调度到具有env=prod标签的节点上containers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent
[root@k8s-master inventory]# kubectl apply -f pod-nodeselector.yaml
pod/pod-nodename created
[root@k8s-master inventory]# kubectl get -f pod-nodeselector.yaml
NAME READY STATUS RESTARTS AGE
pod-nodename 1/1 Running 0 10s#查看Pod调度到NODE属性,确实是调度到了node2节点上
[root@k8s-master inventory]# kubectl get -f pod-nodeselector.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 1/1 Running 0 14s 10.244.2.36 k8s-node2 <none> <none># 接下来,删除pod,修改nodeSelector的值为env: mushuang(不存在打有此标签的节点)
[root@k8s-master inventory]# vi pod-nodeselector.yaml
[root@k8s-master inventory]# cat pod-nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: devlabels:app: httpdlab
spec: nodeSelector:env: mushuangcontainers:- name: httpdimage: httpd:latestimagePullPolicy: IfNotPresent#再次查看,发现pod无法正常运行,Node的值为none
[root@k8s-master inventory]# kubectl apply -f pod-nodeselector.yaml
pod/pod-nodename created# 查看详情,发现node selector匹配失败的提示
[root@k8s-master inventory]# kubectl get -f pod-nodeselector.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 0/1 Pending 0 17s <none> <none> <none> <none>
4.4.2 亲和性调度
上一节,介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用Node列表也不行,这就限制了它的使用场景。
基于上面的问题,kubernetes还提供了一种亲和性调度(Affinity)。它在NodeSelector的基础之上的进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
Affinity主要分为三类:
- nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题
- podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
- podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题
关于亲和性(反亲和性)使用场景的说明:
亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。
反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。
NodeAffinity
首先来看一下NodeAffinity的可配置项:
pod.spec.affinity.nodeAffinityrequiredDuringSchedulingIgnoredDuringExecution Node节点必须满足指定的所有规则才可以,相当于硬限制nodeSelectorTerms 节点选择列表matchFields 按节点字段列出的节点选择器要求列表matchExpressions 按节点标签列出的节点选择器要求列表(推荐)key 键values 值operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, LtpreferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)preference 一个节点选择器项,与相应的权重相关联matchFields 按节点字段列出的节点选择器要求列表matchExpressions 按节点标签列出的节点选择器要求列表(推荐)key 键values 值operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Ltweight 倾向权重,在范围1-100。# 硬限制requiredDuringSchedulingIgnoredDuringExecution:必须调度到那个节点上去
# 软限制preferredDuringSchedulingIgnoredDuringExecution:倾向于调度到那个节点,也可在别的节点上去# 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
# 存在 不存在 在 不在 大于 小于关系符的使用说明:- matchExpressions:- key: nodeenv # 匹配存在标签的key为nodeenv的节点operator: Exists- key: nodeenv # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点operator: Invalues: ["xxx","yyy"]- key: nodeenv # 匹配标签的key为nodeenv,且value大于"xxx"的节点operator: Gtvalues: "xxx"
接下来首先演示一下requiredDuringSchedulingIgnoredDuringExecution ,
[root@k8s-master inventory]# vi pod-test.yaml
[root@k8s-master inventory]# cat pod-test.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: httpdimage: httpd:latestaffinity: #亲和性设置nodeAffinity: #设置node亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制nodeSelectorTerms:- matchExpressions:- key: envoperator: Invalues: ["abc","123"] # 匹配env的值在["abc","123"]中的标签[root@k8s-master inventory]# kubectl apply -f pod-test.yaml
pod/pod-nodeaffinity-required created# 查看pod状态 (运行失败)
[root@k8s-master inventory]# kubectl get -f pod-test.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeaffinity-required 0/1 Pending 0 45s <none> <none> <none> <none># 查看Pod的详情
# 发现调度失败,提示node选择失败
[root@k8s-master inventory]# kubectl describe -f pod-test.yaml
Events:Type Reason Age From Message---- ------ ---- ---- -------Warning FailedScheduling 55s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 node(s) didn't match Pod's node affinity/selector. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling..
[root@k8s-master inventory]# kubectl delete -f pod-test.yaml
pod "pod-nodeaffinity-required" deleted
#31s default-scheduler 0/3节点可用:1个(s)节点有不可容忍的污染{node- roles .kubernetes。io/control-plane:}, 3个节点不匹配Pod的节点亲和性/选择器。preemption: 0/3节点可用:3节点抢占对调度没有帮助。
[root@k8s-master inventory]# kubectl delete -f pod-test.yaml
pod "pod-nodeaffinity-required" deleted# 修改文件,将values: ["abc","123"]------> ["prod","123"]
[root@k8s-master inventory]# vi pod-test.yaml
[root@k8s-master inventory]# cat pod-test.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: httpdimage: httpd:latestaffinity: nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: envoperator: Invalues: ["prod","123"][root@k8s-master inventory]# kubectl apply -f pod-test.yaml
pod/pod-nodeaffinity-required created# 此时查看,发现调度成功,已经将pod调度到了node2上
[root@k8s-master inventory]# kubectl get -f pod-test.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeaffinity-required 1/1 Running 0 65s 10.244.2.37 k8s-node2 <none> <none>
接下来再演示一下prequiredDuringSchedulingIgnoredDuringExecution ,
创建pod-nodeaffinity-preferred.yaml
[root@k8s-master inventory]# vi pod-nodeaffinity-preferred.yaml
[root@k8s-master inventory]# cat pod-nodeaffinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-preferrednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: #亲和性设置 nodeAffinity: #设置node亲和性preferredDuringSchedulingIgnoredDuringExecution: # 软限制- weight: 1preference:matchExpressions: - key: envoperator: Invalues: ["xxx","yyy"] # 匹配env的值在["xxx","yyy"]中的标签(当前环境没有)
[root@k8s-master inventory]# kubectl apply -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created
[root@k8s-master inventory]# kubectl get -f pod-nodeaffinity-preferred.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeaffinity-preferred 1/1 Running 0 58s 10.244.1.30 k8s-node1 <none> <none>
NodeAffinity规则设置的注意事项:1 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上2 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可3 如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的才能匹配成功4 如果一个pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的节点亲和性需求,则系统将忽略此变化
PodAffinity
PodAffinity主要实现以运行的Pod为参照,实现让新创建的Pod跟参照pod在一个区域的功能。
首先来看一下PodAffinity的可配置项:
pod.spec.affinity.podAffinityrequiredDuringSchedulingIgnoredDuringExecution 硬限制namespaces 指定参照pod的namespacetopologyKey 指定调度作用域labelSelector 标签选择器matchExpressions 按节点标签列出的节点选择器要求列表(推荐)key 键values 值operator 关系符 支持In, NotIn, Exists, DoesNotExist.matchLabels 指多个matchExpressions映射的内容preferredDuringSchedulingIgnoredDuringExecution 软限制podAffinityTerm 选项namespaces topologyKeylabelSelectormatchExpressions key 键values 值operatormatchLabels weight 倾向权重,在范围1-100
topologyKey用于指定调度时作用域,例如:如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分
接下来,演示下requiredDuringSchedulingIgnoredDuringExecution,
[root@k8s-master inventory]# vi pod-podaffinity-target.yaml
[root@k8s-master inventory]# cat pod-podaffinity-target.yaml
apiVersion: v1
kind: Pod
metadata: name: pod-podaffinity-targetnamespace: devlabels:env: prod
spec:containers:- name: nginximage: nginx:1.17.1nodeName: k8s-node1 # 将目标pod名确指定到node1上
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-target.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-target 1/1 Running 0 14s 10.244.1.31 k8s-node1 <none> <none>
- 创建pod-podaffinity-required.yaml
- 新Pod必须要与拥有标签env=xxx或env=yyy的pod在同一Node上,显然现在没有这样pod,接下来,运行测试一下。
[root@k8s-master inventory]# vi pod-podaffinity-required.yaml
[root@k8s-master inventory]# cat pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affitiny: #亲和性设置podAffinity: #设置pod亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签- key: envoperator: Invalues: ["xxx","yyy"]topologykey: kubernetes.io/hostname[root@k8s-master inventory]# kubectl apply -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-required.yaml
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 0/1 Pending 0 11s
[root@k8s-master inventory]# kubectl describe -f pod-podaffinity-required.yaml
Events:Type Reason Age From Message---- ------ ---- ---- -------Warning FailedScheduling 14s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 node(s) didn't match pod affinity rules. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling..
[root@k8s-master inventory]# kubectl delete -f pod-podaffinity-required.yaml
pod "pod-podaffinity-required" deleted
[root@k8s-master inventory]# vi pod-podaffinity-required.yaml
[root@k8s-master inventory]# cat pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: envoperator: Invalues: ["prod","yyy"]topologyKey: kubernetes.io/hostname
[root@k8s-master inventory]# kubectl apply -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-required.yaml
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 1/1 Running 0 12s
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-required 1/1 Running 0 18s 10.244.1.33 k8s-node1 <none> <none>
关于PodAffinity的 preferredDuringSchedulingIgnoredDuringExecution
[root@k8s-master inventory]# cat pod-podaffinity-target2.yaml
apiVersion: v1
kind: Pod
metadata: name: pod-podaffinity-target2namespace: devlabels:env: test
spec:containers:- name: nginximage: nginx:1.17.1nodeName: k8s-node2
[root@k8s-master inventory]# kubectl apply -f pod-podaffinity-target2.yaml
pod/pod-podaffinity-target2 created
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-target2.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-target2 1/1 Running 0 105s 10.244.2.38 k8s-node2 <none> <none>
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-target.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-target 1/1 Running 0 3d18h 10.244.1.32 k8s-node1 <none> <none>
[root@k8s-master inventory]# vi pod-podaffinity-required.yaml# 使用pod调度权重方式选择合适的pod调度到相同的节点。
[root@k8s-master inventory]# cat pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:podAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 60podAffinityTerm:labelSelector: matchExpressions:- key: envoperator: Invalues: ["prod"]topologyKey: kubernetes.io/hostname- weight: 30podAffinityTerm:labelSelector:matchExpressions:- key: envoperator: Invalues: ["test"] topologyKey: kubernetes.io/hostname
[root@k8s-master inventory]# kubectl apply -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-required 1/1 Running 0 12s 10.244.1.35 k8s-node1 <none> <none>
[root@k8s-master inventory]# kubectl get pod -n dev -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
pod-podaffinity-required 1/1 Running 0 9m3s 10.244.1.35 k8s-node1 <none> <none> <none>
pod-podaffinity-target 1/1 Running 0 3d18h 10.244.1.32 k8s-node1 <none> <none> env=prod
pod-podaffinity-target2 1/1 Running 0 12m 10.244.2.38 k8s-node2 <none> <none> env=test
PodAntiAffinity
PodAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod跟参照pod不在一个区域中的功能。
它的配置方式和选项跟PodAffinty是一样的,这里不再做详细解释,直接做一个测试案例。
1)继续使用上个案例中目标pod
[root@k8s-master inventory]# kubectl get -f pod-podaffinity-target.yaml -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
pod-podaffinity-target 1/1 Running 0 42s 10.244.1.36 k8s-node1 <none> <none> env=prod
2)创建pod-podantiaffinity-required.yaml,内容如下:
[root@k8s-master inventory]# cat pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: #亲和性设置podAntiAffinity: #设置pod亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配podenv的值在["prod"]中的标签- key: envoperator: Invalues: ["prod"]topologyKey: kubernetes.io/hostname
- 上面配置表达的意思是:新Pod必须要与拥有标签env=prod的pod不在同一Node上,运行测试一下
[root@k8s-master inventory]# kubectl get -f pod-podantiaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podantiaffinity-required 1/1 Running 0 17s 10.244.2.39 k8s-node2 <none> <none>
4.4.3 污点和容忍
污点(Taints)
前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来决定是否允许Pod调度过来。
Node被设置上污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。
污点的格式为:key=value:effect, key和value是污点的标签,effect描述污点的作用,支持如下三个选项:
- PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可调度
- NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但不会影响当前Node上已存在的Pod
- NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已存在的Pod驱离

# 先运行几个pod
[root@k8s-master inventory]# kubectl run pod1 --image httpd
pod/pod1 created
[root@k8s-master inventory]# kubectl run pod2 --image httpd
pod/pod2 created
[root@k8s-master inventory]# kubectl run pod3 --image httpd
pod/pod3 created
[root@k8s-master inventory]# kubectl run pod4 --image httpd
pod/pod4 created
[root@k8s-master inventory]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 30s 10.244.2.40 k8s-node2 <none> <none>
pod2 1/1 Running 0 26s 10.244.1.37 k8s-node1 <none> <none>
pod3 1/1 Running 0 23s 10.244.2.41 k8s-node2 <none> <none>
pod4 1/1 Running 0 20s 10.244.1.38 k8s-node1 <none> <none>
使用kubectl设置和去除污点的命令示例如下:
# 设置污点
kubectl taint nodes node1 key=value:effect# 去除污点
kubectl taint nodes node1 key:effect-# 去除所有污点
kubectl taint nodes node1 key-
接下来,演示下污点的效果:
# 为k8s-node1设置污点(PreferNoSchedule,尽可能不调度)
[root@k8s-master inventory]# kubectl taint nodes k8s-node1 tag=zlbb:PreferNoSchedule
node/k8s-node1 tainted
[root@k8s-master inventory]# kubectl describe node k8s-node1|grep -i taint
Taints: tag=zlbb:PreferNoSchedule
[root@k8s-master inventory]# kubectl run pod5 --image httpd
pod/pod5 created
[root@k8s-master inventory]# kubectl run pod6 --image httpd
pod/pod6 created
[root@k8s-master inventory]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 25m 10.244.2.40 k8s-node2 <none> <none>
pod2 1/1 Running 0 25m 10.244.1.37 k8s-node1 <none> <none>
pod3 1/1 Running 0 25m 10.244.2.41 k8s-node2 <none> <none>
pod4 1/1 Running 0 25m 10.244.1.38 k8s-node1 <none> <none>
pod5 1/1 Running 0 9s 10.244.2.42 k8s-node2 <none> <none>
pod6 0/1 ContainerCreating 0 5s <none> k8s-node2 <none> <none>
[root@k8s-master inventory]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 25m 10.244.2.40 k8s-node2 <none> <none>
pod2 1/1 Running 0 25m 10.244.1.37 k8s-node1 <none> <none>
pod3 1/1 Running 0 25m 10.244.2.41 k8s-node2 <none> <none>
pod4 1/1 Running 0 25m 10.244.1.38 k8s-node1 <none> <none>
pod5 1/1 Running 0 12s 10.244.2.42 k8s-node2 <none> <none>
pod6 1/1 Running 0 8s 10.244.2.43 k8s-node2 <none> <none># 为node1设置污点(取消PreferNoSchedule,设置NoSchedule)
[root@k8s-master inventory]# kubectl taint nodes k8s-node1 tag=zlbb:PreferNoSchedule-
node/k8s-node1 untainted
[root@k8s-master inventory]# kubectl taint nodes k8s-node1 tag=zlbb:NoSchedule
node/k8s-node1 tainted
[root@k8s-master inventory]# kubectl describe node k8s-node1|grep -i taint
Taints: tag=zlbb:NoSchedule
[root@k8s-master inventory]# kubectl run pod7 --image httpd
pod/pod7 created
[root@k8s-master inventory]# kubectl run pod8 --image httpd
pod/pod8 created<none>
[root@k8s-master inventory]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 29m 10.244.2.40 k8s-node2 <none> <none>
pod2 1/1 Running 0 29m 10.244.1.37 k8s-node1 <none> <none>
pod3 1/1 Running 0 29m 10.244.2.41 k8s-node2 <none> <none>
pod4 1/1 Running 0 28m 10.244.1.38 k8s-node1 <none> <none>
pod5 1/1 Running 0 3m41s 10.244.2.42 k8s-node2 <none> <none>
pod6 1/1 Running 0 3m37s 10.244.2.43 k8s-node2 <none> <none>
pod7 1/1 Running 0 36s 10.244.2.45 k8s-node2 <none> <none>
pod8 1/1 Running 0 62s 10.244.2.44 k8s-node2 <none> <none># 取消node1 NoSchedule
[root@k8s-master inventory]# kubectl taint nodes k8s-node1 tag=zlbb:NoSchedule-
node/k8s-node1 untainted
[root@k8s-master inventory]# kubectl describe node k8s-node1|grep -i taint
Taints: <none># 为node2设置污点(设置NoExecute)
[root@k8s-master inventory]# kubectl taint nodes k8s-node2 tag=zlbb:NoExecute
node/k8s-node2 tainted
[root@k8s-master inventory]# kubectl describe node k8s-node2|grep -i taint
Taints: tag=zlbb:NoExecute# 会自动将node2上的pod去除
[root@k8s-master inventory]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod2 1/1 Running 0 31m 10.244.1.37 k8s-node1 <none> <none>
pod4 1/1 Running 0 31m 10.244.1.38 k8s-node1 <none> <none>
小提示:使用kubeadm搭建的集群,默认就会给master节点添加一个污点标记,所以pod就不会调度到master节点上.
容忍(Toleration)
上面介绍了污点的作用,我们可以在node上添加污点用于拒绝pod调度上来,但是如果就是想将一个pod调度到一个有污点的node上去,这时候应该怎么做呢?这就要使用到容忍。

污点就是拒绝,容忍就是忽略,Node通过污点拒绝pod调度上去,Pod通过容忍忽略拒绝
下面先通过一个案例看下效果:
- 上一小节,已经在node1节点上打上了
NoExecute的污点,此时pod是调度不上去的 - 本小节,可以通过给pod添加容忍,然后将其调度上去
创建pod-toleration.yaml,内容如下
[root@k8s-master inventory]# kubectl describe node k8s-node1|grep -i taint
Taints: <none>
[root@k8s-master inventory]# kubectl describe node k8s-node2|grep -i taint
Taints: tag=zlbb:NoExecute
[root@k8s-master inventory]# vi pod-toleration.yaml
[root@k8s-master inventory]# cat pod-toleration.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1tolerations: # 添加容忍- key: "tag" # 要容忍的污点的keyoperator: "Equal" # 操作符value: "zlbb" # 容忍的污点的valueeffect: "NoExecute" # 添加容忍的规则,这里必须和标记的污点规则相同
[root@k8s-master inventory]# kubectl apply -f pod-toleration.yaml
pod/pod-toleration created# 添加容忍之后可以在node2上运行
[root@k8s-master inventory]# kubectl get -f pod-toleration.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-toleration 1/1 Running 0 19s 10.244.2.46 k8s-node2 <none> <none>
下面看一下容忍的详细配置:
[root@k8s-master01 ~]# kubectl explain pod.spec.tolerations
......
FIELDS:key # 对应着要容忍的污点的键,空意味着匹配所有的键value # 对应着要容忍的污点的值operator # key-value的运算符,支持Equal和Exists(默认)effect # 对应污点的effect,空意味着匹配所有影响tolerationSeconds # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间
相关文章:
4.Pod详解
4.Pod详解 文章目录 4.Pod详解4.1 Pod介绍4.1.1 Pod结构4.1.2 Pod定义4.1.3 在kubernetes中基本所有资源的一级属性都是一样的,主要包含5部分:4.1.4 在上面的属性中,spec是接下来研究的重点,继续看下它的常见子属性: 4.2 Pod配置4…...
OCR技术狂潮:揭秘最新发展现状,引爆未来智能时代
OCR(Optical Character Recognition,光学字符识别)技术自20世纪以来经历了长足的发展,随着计算机视觉、人工智能和深度学习等领域的进步,OCR技术在准确性、速度和适用范围上都取得了显著的进展。以下是OCR技术发展的现…...
【hcie-cloud】【3】华为云Stack规划设计之华为云Stack交付综述【上】
文章目录 前言华为云Stack交付综述交付流程华为云Stack交付流程华为云Stack安装部署流程 交付工具链华为云Stack交付工具链eDesigner - 让解决方案销售更智能eDesigner配置页面 - 基本信息eDesigner配置页面 - 服务及组网配置eDesigner配置页面 - 弹性云服务器/ECSeDesigner配置…...
Spring Ioc 容器启动流程
Spring容器的启动流程 本文基于 Spring 5.3.23 基于XML文件 public void test() {ApplicationContext applicationContext new ClassPathXmlApplicationContext("applicationContext.xml");User user applicationContext.getBean("user", User.class)…...
【714. 买卖股票的最佳时机含手续费】
目录 一、题目解析二、算法原理三、代码实现 一、题目解析 二、算法原理 三、代码实现 class Solution { public:int maxProfit(vector<int>& prices, int fee) {int nprices.size();vector<vector<int>> dp(n,vector<int>(2));dp[0][0]-prices[0…...
JS前端实现身份证号码合法性校验(校验码校验)
在做项目过程中针对自然人数据提交到后端前一般是要进行身份证的合法性校验,当身份证号输入错误以便给于用户友好的提示(也可以根据身份证号同时校验表单中性别和出生日期等),验证主要是防止无效数据入库。本文在前端使用JavaScript实现15/18位身份证的合…...
操作系统 day09(线程)
线程 为什么引入线程 在没引入进程之前,系统中的各个程序只能串行的执行,比如:只能先听歌,再聊QQ。引入进程之后,各个程序可以并发执行,比如:一边听歌,一边聊QQ。但是现在QQ可以一…...
单通道低压 H 桥电机驱动芯片AT9110H 兼容L9110 马达驱动芯片
H桥直流电机驱动电路是一种用于控制直流电机运转的电路,其主要特点是可以实现正反转控制,控制电机转速和方向,同时也具有过流保护功能。 H桥电路由四个功率晶体管和一些辅助电路组成,其中两个晶体管用于控制电机正转,…...
18. 深度学习 - 从零理解神经网络
文章目录 本文目标预测趋势与关系波士顿房价预测 Hi, 你好。我是茶桁。 我们终于又开启新的篇章了,从今天这节课开始,我们会花几节课来理解一下深度学习的相关知识,了解神经网络,多层神经网络相关知识。并且,我们会尝…...
Pycharm加载项目时异常,看不到自己的项目文件
最近看到一个朋友问,他把项目导入pycharm为什么项目里的包不在项目里显示,只在projects file里显示?问题截图如下: Project里看不到自己的项目文件 只能在Project Files里看到自己的项目文件 问题解答 我也是偶然发现的这个方案…...
目标检测YOLO实战应用案例100讲-基于无人机的轻量化目标检测系统设计(续)
目录 3.2 深度神经网络处理器设计 3.2.1 卷积神经网络处理器设计思路...
大文件传输小知识 | UDP和TCP哪个传输速度快?
在网络世界中,好像有两位“传输巨头”常常被提起:UDP和TCP。它们分别代表着用户数据报协议和传输控制协议。那么它们是什么?它们有什么区别?它们在传输大文件时的速度又如何?本文将深度解析这些问题,帮助企…...
【tgcalls】Instance接口的实例类的创建
tg 里有多个版本,因此设计了版本管理的map,每次可以选择一个版本进行实例创建这样,每个客户端就可以定制开发了。tg使用了c++20创建是要传递一个描述者,里面是上下文信息 G:\CDN\P2P-DEV\tdesktop-offical\Telegram\ThirdParty\tgcalls\tgcalls\Instance.cpp可以看到竟然是…...
【java:牛客每日三十题总结-3】
java:牛客每日三十题总结 总结如下 总结如下 集合相关知识点 Collection主要的子接口: List:可以存放重复内容 Set:不能存放重复内容,所有重复的内容靠hashCode()和equals()两个方法区分 Queue:队列接口 SortedSet:可以对集合中的数据进行排序 Map没有继承Collection接口&…...
区块链多链数字钱包开发
随着区块链技术的不断发展,多链数字钱包的开发逐渐成为热门领域。多链数字钱包是一种可以支持多种区块链网络的数字钱包,用户可以使用它来存储、管理和转移不同的数字资产。本文将探讨多链数字钱包的开发背景、市场需求、技术实现和未来趋势等方面。 一、…...
hive-行转列
xx...
【赠书第2期】嵌入式虚拟化技术与应用
文章目录 前言 1 背景概述 2 专家推荐 3 本书适合谁? 4 内容简介 5 书籍目录 6 权威作者团队 7 粉丝福利 前言 随着物联网设备的爆炸式增长和万物互联应用的快速发展,虚拟化技术在嵌入式系统上受到了业界越来越多的关注、重视和实际应用。嵌入式…...
如何写一篇吊炸天的竞品分析
这段时间,除了撩妹之外,最多的就是竞品分析了。最近很多临近毕业的同学也在四处应聘产品岗,而一份不错的竞品分析一定能为你的求职加分不少。于是,有着菩萨心肠天使面孔魔鬼身材的我,就来教大家怎么做一份完整的竞品分…...
校园安防监控系统升级改造方案:如何实现设备利旧上云与AI视频识别感知?
一、背景与需求分析 随着现代安防监控科技的兴起和在各行各业的广泛应用,监控摄像头成为众所周知的产品,也为人类的工作生活提供了很大的便利。由于科技的发达,监控摄像头的升级换代也日益频繁。每年都有不计其数的摄像头被拆掉闲置…...
刷题笔记day15-二叉树层序遍历
层序遍历 /*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/import ("container/list" )func levelOrder(root *TreeNode) [][]int {// 思路1:此处肯定要使用队列result : …...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
