k8s:pod-1
k8s:pod-1
pod生命周期
pod创建过程:
运行初始化容器(init container) > 运行主容器(main container) >容器启动后钩子(post start) 容器终止前钩子(pre stop)
容器的存活性探测(liveness probe)、就绪性探测(readiness probe)

在生命周期中会出现5种状态:
-
挂起(Pending):apiserver已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
-
运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成
-
成功(Succeeded):pod中的所有容器都已经成功终止并且不会被重启
-
失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
-
未知(Unknown):apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所导致
pod的创建过程
步骤 1: 用户提交请求
- 动作: 用户通过
kubectl、客户端库或 REST API 向 Kubernetes API Server 提交 Pod 的定义文件(通常是 YAML 或 JSON 格式)。 - 角色: API Server 是集群的网关和管理前端。
步骤 2: API Server 持久化存储
- 动作: API Server 对请求进行验证和准入控制,然后根据用户提交的信息,在 etcd 中创建一个新的 Pod 对象,并将其状态设置为
Pending。 - 角色: etcd 是 Kubernetes 的“唯一可信数据源”,所有集群状态都存储于此。
步骤 3: 事件驱动与监听机制
- 动作: API Server 中 Pod 资源的创建和更新事件会被诸如 Scheduler 和 Kubelet 这样的组件监听到。这些组件通过 Watch 机制与 API Server 建立长连接,实时获取与其相关的资源变动。
- 关键: 这是 Kubernetes 声明式 API 和控制器模式的核心,各组件通过“观察-调整-反馈”循环来驱动系统达到期望状态。
步骤 4: Scheduler 调度决策
- 动作:
- Scheduler 监测到有一个新的 Pod 被创建,且其
nodeName为空,于是开始调度流程。 - 它通过一系列预选和优选算法,从集群中选择一个最适合的节点。
- 调度器将决策结果(即目标节点名)通过
nodeName字段绑定到 Pod 对象上,并将此更新提交给 API Server。
- Scheduler 监测到有一个新的 Pod 被创建,且其
- 角色: Scheduler 是负责资源分配的“调度中心”。
步骤 5: Kubelet 创建容器
- 动作:
- 目标节点上的 Kubelet 监测到有一个 Pod 被调度到了自己所在的节点。
- Kubelet 根据 Pod 规格描述,通过 Container Runtime(如 Docker、containerd 等)来拉取镜像、创建并启动容器。
- 它还会根据配置设置网络、挂载存储卷等。
- 角色: Kubelet 是节点上的“代理”,负责管理本节点 Pod 的生命周期。
步骤 6: 状态上报与完成
- 动作:
- Kubelet 持续监控容器的状态。一旦容器启动成功,Kubelet 会将 Pod 的状态更新为
Running并上报给 API Server。如果启动失败,则上报Failed。 - API Server 接收到状态更新后,再次将 Pod 的最新状态写入 etcd。
- Kubelet 持续监控容器的状态。一旦容器启动成功,Kubelet 会将 Pod 的状态更新为
- 最终状态: 此时,Pod 的完整信息(包括其运行状态)已在 etcd 中持久化,整个创建流程结束。
流程核心特点总结
- 中心化存储:所有状态都以 Pod 对象的形式存储在 etcd 中,API Server 是唯一可对其进行操作的中心。
- 事件驱动:Scheduler 和 Kubelet 等组件不主动轮询,而是通过 Watch 机制监听 API Server 的事件,实现高效、实时的协同。
- 异步操作:用户的创建请求的返回,仅表示 API Server 已接收并存储了 Pod 的定义,并不代表 Pod 已成功运行。后续的调度和创建过程是异步进行的。
- 自我修复:如果 Pod 在运行过程中意外终止,Kubelet 会监测到这一状态变化,并根据 Pod 的
restartPolicy决定是否重启容器,并再次将最新状态上报给 API Server,进入一个新的状态更新循环。
pod的终止过程
步骤 1: 用户发起删除命令
- 动作: 用户通过
kubectl delete pod或其他客户端向 API Server 发送删除 Pod 的请求。
步骤 2 , 3: API Server 标记 Pod 为终止状态
- 动作:
- API Server 接收到删除请求后,不会立即删除 Pod 对象,而是会修改 Pod 的元数据。
- 它会在 Pod 上设置一个删除时间戳和 deletionGracePeriodSeconds(宽限期,默认30秒)。
- Pod 的状态随之变为 Terminating。此时,Pod 虽然仍在运行,但已被视为“正在死亡”的状态。
步骤 4: Kubelet 监控到终止状态并启动关闭流程
- 动作: 目标节点上的 Kubelet 通过 Watch 机制监测到其管理的 Pod 被标记为
Terminating,随即开始触发本节点的 Pod 关闭过程。
步骤 5: 从 Service 端点列表中移除
- 动作: Endpoints Controller(端点控制器)同样监控到 Pod 进入
Terminating状态。它会立即将这个 Pod 的 IP 地址从所有关联的 Service 的 Endpoints(或 EndpointSlice)列表中移除。 - 关键作用: 这一步切断了流向该 Pod 的新流量,是确保优雅终止、避免请求丢失的关键。此后,Service 的负载均衡器不会再转发新的连接到此 Pod。
步骤 6: 执行 preStop Hook
- 动作: 如果 Pod 中定义了
preStop钩子处理器,Kubelet 会同步地在 Pod 的容器内执行它。 - 目的: 这是应用在收到终止信号前,执行自定义清理逻辑(如通知注册中心、完成剩余任务、优雅关闭连接)的最后机会。
preStop钩子的执行时间包含在宽限期之内。
步骤 7: 发送 SIGTERM 信号
- 动作:
preStop钩子执行完毕后(或如果没有定义钩子),Kubelet 会向 Pod 内每个容器的主进程发送 SIGTERM 信号。 - 目的: 这是一个“优雅终止”信号,通知应用程序自行关闭。设计良好的应用会捕获此信号并开始清理工作,然后退出。
步骤 8: 宽限期结束与强制终止
- 动作:
- 情况A(成功): 如果所有容器在主进程收到
SIGTERM后,在宽限期结束前自行退出,则关闭流程顺利完成。 - 情况B(超时): 如果宽限期(例如30秒)结束后,Pod 中仍有容器在运行,Kubelet 会向这些容器发送 SIGKILL 信号,立即强制终止进程
- 情况A(成功): 如果所有容器在主进程收到
最终清理
- 动作: 无论进程是自行退出还是被强制杀死,一旦 Pod 内的所有容器都停止运行,Kubelet 就会通知 API Server,API Server 随后会彻底清理并删除 etcd 中的 Pod 记录。
- 结果: 至此,Pod 对象对于用户而言已完全消失,其生命周期正式结束。
流程核心特点总结
- 优雅终止:核心设计思想是给予应用充分的时间进行清理,而非直接暴力杀死。
- 流量切断:通过先从 Service 移除端点来防止新的请求到达正在关闭的 Pod。
- 双重信号:先
SIGTERM(友好协商),再SIGKILL(强制措施)。 - 可配置性:
terminationGracePeriodSeconds和preStop钩子让用户可以根据应用特性定制关闭行为
初始化容器
初始化容器是在 Pod 的主应用程序容器启动之前运行的一个或多个特殊容器,它们为 Pod 的部署提供了强大的前置准备和校验能力。
核心特征
- 强制性成功完成
- 每个初始化容器都必须运行直至结束。
- 如果某个初始化容器运行失败(即退出码非零),Kubernetes 会根据 Pod 的
restartPolicy策略(通常为Always或OnFailure)重启它,直到它成功运行完毕。 - 这是一个“全部或没有”的操作:所有初始化容器都必须成功,Pod 才能启动主容器。
- 顺序执行
- 初始化容器按照它们在 Pod 清单中定义的顺序逐个执行。
- 当前一个初始化容器成功退出后,下一个初始化容器才能开始启动。
- 这与主容器的并行启动模式形成鲜明对比。
主要应用场景
初始化容器与主容器共享镜像空间,这使得它们非常适合完成以下前置工作:
- 提供主容器不具备的工具或代码
- 场景:主容器镜像是为了最小化和安全而构建的,可能不包含
curl、nslookup、dig或特定脚本等调试或设置工具。 - 做法:可以使用一个包含必要工具的初始化容器镜像,来执行这些操作,而无需污染主容器镜像。
- 场景:主容器镜像是为了最小化和安全而构建的,可能不包含
- 延迟应用启动,直到依赖就绪
- 场景:主应用容器需要依赖外部服务(如数据库、API 后端、缓存服务)先正常运行。
- 做法:在初始化容器中使用一个脚本,通过循环检测(如
curl、nc)来等待依赖服务变为可连接状态。只有当依赖服务就绪后,初始化容器才会成功退出,从而允许主容器启动。
- 生成配置文件或下载数据
- 场景:主容器运行所需的配置文件需要根据环境变量或 API 查询结果动态生成。
- 做法:让初始化容器从 ConfigMap、Secret 或外部服务获取数据,然后渲染生成最终的配置文件,并存入一个共享的 Volume 中供主容器使用。
- 权限分离与安全控制
- 场景:主容器需要以非 root 用户运行以保证安全,但某些初始化步骤(如修改文件系统权限、挂载敏感目录)需要更高的权限。
- 做法:可以让初始化容器以高权限运行来完成这些设置,然后退出。随后主容器以低权限运行,从而减小了攻击面。
假设要以主容器来运行nginx,但是要求在运行nginx之前先要能够连接上mysql所在服务器
创建pod-initcontainer.yaml
[root@master ~]# vim pod-initcontainer.yamlapiVersion: v1
kind: Pod
metadata:name: pod-initcontainernamespace: dev
spec:containers:- name: main-containerimage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80initContainers:- name: test-mysqlimage: busybox:1.30command: ['sh', '-c', 'until ping 192.168.100.10 -c 2 ; do echo waiting for mysql...; sleep 2; done;']
创建pod
[root@master ~]# kubectl create ns dev
namespace/dev created[root@master ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created
查看pod
[root@master ~]# kubectl describe pod pod-initcontainer -n dev Normal Scheduled 7m59s default-scheduler Successfully assigned dev/pod-initcontainer toNormal Pulling 8m kubelet Pulling image "busybox:1.30"Normal Pulled 7m58s kubelet Successfully pulled image "busybox:1.30" in 1.Normal Created 7m58s kubelet Created container test-mysqlNormal Started 7m58s kubelet Started container test-mysqlNormal Pulling 7m56s kubelet Pulling image "nginx:1.17.1"Normal Pulled 7m47s kubelet Successfully pulled image "nginx:1.17.1" in 8.Normal Created 7m47s kubelet Created container main-containerNormal Started 7m47s kubelet Started container main-container#可以看到这里是先运行第一个初始化容器,然后再运行主容器,pod也是正常运行#现在关闭192.168.100.10模拟故障#删除pod[root@master ~]# kubectl delete -f pod-initcontainer.yaml#192.167.100.10关机,再次创建[root@master ~]# kubectl apply -f pod-initcontainer.yaml
pod/pod-initcontainer created[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Pending 0 11s
#查看会发现出问题了,原因是192.168.100.10关机,初始化过程失败导致主容器一直不能运行,实验成功
钩子函数
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
kubernetes在主容器的启动之后和停止之前提供了两个钩子函数:
钩子处理器支持3种定义动作
-
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@master ~]# vim pod-hook-exec.yamlapiVersion: v1
kind: Pod
metadata:name: pod-hook-execnamespace: dev
spec:containers:- name: main-containerimage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80lifecycle:postStart: exec: command: ["/bin/sh", "-c", "echo test111... > /usr/share/nginx/html/index.html"]preStop:exec: command: ["/usr/sbin/nginx","-s","quit"]
创建pod
[root@master ~]# kubectl apply -f pod-hook-exec.yaml
pod/pod-hook-exec created
查看pod
[root@master ~]# kubectl get pods pod-hook-exec -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hook-exec 1/1 Running 0 26s 172.16.104.28 node2 <none> <none>
访问测试
[root@master ~]# curl 172.16.104.28
test111...
容器探测
容器探测是 Kubernetes 用于检测容器应用实例是否正常工作的健康检查机制。通过定期执行诊断,确保业务的可用性。
两种核心探针类型
1. Liveness Probe(存活性探针)
- 作用:检测应用实例当前是否处于正常运行状态
- 失败后果:如果探测失败,Kubernetes 会重启容器
- 使用场景:用于恢复"进程存在但应用已死"的状态
- 应用死锁
- 内部错误导致无法响应
- 内存泄漏导致应用卡死
2. Readiness Probe(就绪性探针)
- 作用:检测应用实例当前是否可以接收请求
- 失败后果:如果探测失败,Kubernetes 会将该实例从 Service 的负载均衡中摘除,不转发流量
- 使用场景:用于处理"应用正在启动或临时不可用"的情况
- 应用启动较慢,需要时间初始化
- 依赖服务暂时不可用
- 临时高负载无法处理新请求
两种探针都支持三种探测方式:
-
Exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常
-
TCPSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常
-
HTTPGet:调用容器内Web应用的URL,如果返回的状态码在200和399之间,则认为程序正常,否则不正常
创建pod-liveness-exec.yaml(exec)
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-execnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80livenessProbe:exec:command: ["/bin/cat","/tmp/hello.txt"]
[root@master ~]# kubectl apply -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
查看
Normal Scheduled 20s default-scheduler Successfully assigned dev/pod-liveness-exec to node1Normal Pulled 20s kubelet Container image "nginx:1.17.1" already present on machineNormal Created 20s kubelet Created container nginxNormal Started 20s kubelet Started container nginxWarning Unhealthy 0s (x2 over 10s) kubelet Liveness probe failed: /bin/cat: /tmp/hello.txt: No such file or directory
#这里由于没有这个文件,导致运行失败,存活性探针检测到运行失败然后就会重启容器
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-hook-exec 1/1 Running 0 13m
pod-initcontainer 1/1 Running 0 17m
pod-liveness-exec 1/1 Running 4 (3s ago) 2m3s
#这里发现重启了4次
如果想让它恢复正常,可以修改为存在的文件,这样运行就会成功,就不会一直重启
创建pod-liveness-tcpsocket.yaml(TCPSocket)
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-tcpsocketnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80livenessProbe:tcpSocket:port: 8080
创建pod
[root@master ~]# kubectl apply -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
查看
[root@master ~]# kubectl describe pods pod-liveness-tcpsocket -n devNormal Scheduled 19s default-scheduler Successfully assigned dev/pod-liveness-tcpsocket to node1Normal Pulled 19s kubelet Container image "nginx:1.17.1" already present on machineNormal Created 19s kubelet Created container nginxNormal Started 19s kubelet Started container nginxWarning Unhealthy 9s kubelet Liveness probe failed: dial tcp 172.16.166.152:8080: connect: connection refused#这里是因为无法连接到8080端口而失败
[root@master ~]# kubectl get pods pod-liveness-tcpsocket -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 1/1 Running 4 (5s ago) 2m6s
#这里重启了4次,如果想恢复正常,则把8080端口换成80端口
创建pod-liveness-httpget.yaml(HTTPGet)
[root@master ~]# vim pod-liveness-httpget.yamlapiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet: scheme: HTTP port: 80 path: /hello
[root@master ~]# kubectl apply -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
查看
[root@master ~]# kubectl describe pod pod-liveness-httpget -n devNormal Scheduled 20s default-scheduler Successfully assigned dev/pod-liveness-httpget to node2Normal Pulled 20s kubelet Container image "nginx:1.17.1" already present on machineNormal Created 20s kubelet Created container nginxNormal Started 20s kubelet Started container nginxWarning Unhealthy 1s (x2 over 11s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404#这里返回404原因是因为没有这个网页[root@master ~]# kubectl get pod pod-liveness-httpget -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 1 (28s ago) 58s
#这里重启了一次,如果想恢复正常,则需要换成能够访问的路径
livenessProbe其他的配置
[root@master ~]# kubectl explain pod.spec.containers.livenessProbeFIELDS:exec <Object> tcpSocket <Object>httpGet <Object>initialDelaySeconds <integer> # 容器启动后等待多少秒执行第一次探测timeoutSeconds <integer> # 探测超时时间。默认1秒,最小1秒periodSeconds <integer> # 执行探测的频率。默认是10秒,最小1秒failureThreshold <integer> # 连续探测失败多少次才被认定为失败。默认是3。最小值是1successThreshold <integer> # 连续探测成功多少次才被认定为成功。默认是1
重启策略
三种重启策略
Pod 的重启策略在 .spec.restartPolicy 字段中定义,共有三种:
- Always(默认值)
- 行为:只要容器终止运行,无论其退出代码是什么,Kubernetes 都会自动重启该容器。
- 适用场景:适用于需要长期持续运行的应用,如 Web 服务器、API 服务等。
- OnFailure
- 行为:只有当容器异常终止(即退出码不为 0)时,才会自动重启。如果容器正常退出(退出码为 0),则不会重启。
- 适用场景:适用于执行一次性任务或批处理作业,但任务可能因临时故障而失败需要重试的场景。
- Never
- 行为:无论容器因何种原因终止,都不会对其进行重启。
- 适用场景:适用于确保证任务只运行一次且不允许自动重试的场景。
创建pod-restartpolicy.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-restartpolicynamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: /hellorestartPolicy: Never #运行失败不会重启
测试
[root@master ~]# kubectl apply -f pod-restartpolicy.yaml
pod/pod-restartpolicy created
#查看
[root@master ~]# kubectl describe pods pod-restartpolicy -n devNormal Scheduled 25s default-scheduler Successfully assigned dev/pod-restartpolicy to node2Normal Pulled 24s kubelet Container image "nginx:1.17.1" already present on machineNormal Created 24s kubelet Created container nginxNormal Started 24s kubelet Started container nginxWarning Unhealthy 4s (x2 over 14s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404#状态码返回404是因为没有这个网页[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Completed 0 76s
Pod调度
pod调度有4种方式:
-
自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
-
定向调度:NodeName、NodeSelector
-
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
-
污点(容忍)调度:Taints、Toleration
定向调度
核心概念
- 作用:直接将 Pod 调度到指定的 Node 节点上。
- 级别:这是一种强制性的调度约束。
- 绕过调度器:当设置了
nodeName时,Kubernetes 的默认调度器(kube-scheduler)会被绕过。它不会进行任何资源充足性检查(如 CPU、内存)、节点选择策略检查(如节点亲和性)或污点容忍度检查。 - 风险:如果指定的节点不存在,或者节点资源不足,Pod 将会运行失败,并处于
Pending状态。
创建一个pod-nodename.yaml文件
[root@master ~]# vim pod-nodename.yamlapiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1 #调度到node1节点上
查看
[root@master ~]# kubectl apply -f pod-nodename.yaml
pod/pod-nodename created[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 1/1 Running 0 18s 172.16.166.153 node1 <none> <none>
接下来,删除pod,修改nodeName的值为node3(无节点)
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node3
查看
[root@master ~]# kubectl apply -f pod-nodename.yaml
pod/pod-nodename created[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 0/1 Pending 0 35s <none> node3 <none> <none>
#确实调度到了node3,但是由于没有这个节点导致pod运行失败
NodeSelector
NodeSelector 是 Pod 规范中的一个字段,它通过为 Pod 指定一个或多个节点标签选择器,将 Pod 强制调度到拥有对应标签的节点上。它提供了一种比 nodeName 更灵活、更具声明性的调度方式。
核心概念
- 作用:根据节点的标签来选择合适的节点。
- 机制:它是一个简单的键值对匹配。Pod 的
nodeSelector中指定的键值必须与目标节点上设置的标签完全匹配,Pod 才能被调度到该节点。 - 级别:这是一种强制性的调度约束。如果没有任何节点拥有匹配的标签,Pod 将无法被调度,并处于
Pending状态。 - 经过调度器:与
nodeName不同,nodeSelector会经过kube-scheduler。调度器会检查节点的资源是否充足,并确保节点满足 Pod 的其他要求
首先为节点node1,node2添加标签
[root@master ~]# kubectl label nodes node1 tag=test1
node/node1 labeled
[root@master ~]# kubectl label nodes node2 tag=test2
node/node2 labeled
创建一个pod-nodeselector.yaml文件
apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeSelector: tag: test1
[root@master ~]# kubectl apply -f pod-nodeselector.yaml
pod/pod-nodeselector created
查看
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeselector 1/1 Running 0 46s 172.16.166.154 node1 <none> <none>
可以发现是在node1上运行,修改文件为,让pod在node2上运行
apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeSelector:tag: test2
再次运行查看
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeselector 1/1 Running 0 5s 172.16.104.31 node2 <none> <none>
#可以发现这次运行在node2上
#如果不存在这个标签,则会运行失败
修改标签为tag=test3
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeselector 0/1 Pending 0 9s <none> <none> <none> <none>
#如果没有指定的标签则会运行失败
亲和性调度
Affinity,它在NodeSelector的基础之上的进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
Affinity主要分为三类:
-
nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题
-
podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
-
podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题
关于亲和性(反亲和性)使用场景的说明:
亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。
反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性
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。关系符的使用说明:
- 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"
创建pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms:- matchExpressions: - key: tagoperator: Invalues: ["xxx","yyy"]
查看
[root@master ~]# kubectl apply -f pod-nodeaffinity-required.yaml
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-nodeaffinity-required 0/1 Pending 0 13s
#这里失败是因为没有values: ["xxx","yyy"],删除pod然后换成有的
[root@master ~]# kubectl delete -f pod-nodeaffinity-required.yaml
pod "pod-nodeaffinity-required" deletedapiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: tagoperator: Invalues: ["test1","yyy"]
再次运行查看
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeaffinity-required 1/1 Running 0 8s 172.16.166.155 node1 <none>
运行在了node1节点上
查看node1的标签
[root@master ~]# kubectl get nodes node1 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
node1 Ready <none> 7d4h v1.28.15 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,tag=test1
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节点的操作系统类型来区分
创建pod-podaffinity-target.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-targetnamespace: devlabels:tag: test
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1
[root@master ~]# kubectl apply -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created
#查看pod
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-target 1/1 Running 0 24s
创建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: tagoperator: Invalues: ["xxx","yyy"] #新pod的标签要匹配之前创建的pod标签topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl apply -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
#查看
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 0/1 Pending 0 17s
pod-podaffinity-target 1/1 Running 0 2m1s
#由于没有匹配到上一个pod的标签,因此运行失败,删除这个pod修改标签
[root@master ~]# kubectl delete -f pod-podaffinity-required.yaml
pod "pod-podaffinity-required" deleted
[root@master ~]# vim 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: tagoperator: Invalues: ["test","yyy"] #修改为testtopologyKey: kubernetes.io/hostname[root@master ~]# kubectl apply -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
#再次查看pod会发现运行成功,查看部署节点
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 1/1 Running 0 18s
pod-podaffinity-target 1/1 Running 0 3m55s[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-required 1/1 Running 0 54s 172.16.166.158 node1 <none> <none>
pod-podaffinity-target 1/1 Running 0 4m31s 172.16.166.157 node1 <none> <none>
#可以看到都是部署到node1节点
PodAntiAffinity
PodAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod跟参照pod不在一个区域中的功能
创建pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector:matchExpressions: - key: tagoperator: Invalues: ["test"]topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl apply -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created
#查看
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-required 1/1 Running 0 9m19s 172.16.166.158 node1 <none> <none>
pod-podaffinity-target 1/1 Running 0 12m 172.16.166.157 node1 <none> <none>
pod-podantiaffinity-required 1/1 Running 0 17s 172.16.104.32 node2 <none> <none>
污点和容忍
组成部分
- key: 污点的键(标签名)
- value: 污点的值(标签值)
- effect: 污点的作用效果
污点效果(effect)类型
1. PreferNoSchedule
- 作用: Kubernetes 会尽量避免将 Pod 调度到具有该污点的 Node 上
- 行为: 除非没有其他节点可调度,否则不会选择该节点
- 影响: 不影响已存在的 Pod
2. NoSchedule
- 作用: Kubernetes 不会将 Pod 调度到具有该污点的 Node 上
- 行为: 硬性限制,新 Pod 不会被调度到该节点
- 影响: 不影响当前 Node 上已存在的 Pod
3. NoExecute
- 作用: Kubernetes 不会将 Pod 调度到具有该污点的 Node 上,同时驱逐已存在的 Pod
- 行为:
- 新 Pod 不会被调度到该节点
- 节点上已存在的 Pod 会被驱逐(除非设置了容忍)
- 影响: 同时影响新 Pod 调度和已有 Pod 运行
命令:
# 设置污点
kubectl taint nodes node1 key=value:effect
# 去除污点
kubectl taint nodes node1 key:effect-
# 去除所有污点
kubectl taint nodes node1 key-
关闭node2节点进行测试,给node1节点设置污点
[root@master ~]# kubectl taint nodes node1 tag=test:PreferNoSchedule
node/node1 tainted
#创建pod
[root@master ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
pod/taint1 created[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
taint1 1/1 Running 0 39s 172.16.166.159 node1 <none> <none>
#这里由于没有其他节点可用,只能在node1运行,将node1的污点修改为NoSchedule
[root@master ~]# kubectl taint nodes node1 tag:PreferNoSchedule-
node/node1 untainted
[root@master ~]# kubectl taint nodes node1 tag=chenyu:NoSchedule
node/node1 tainted
#再次创建pod
[root@master ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
pod/taint2 created
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
taint1 1/1 Running 0 2m25s 172.16.166.159 node1 <none> <none>
taint2 0/1 Pending 0 13s <none> <none> <none> <none>
#由于pod设置了NoSchedule的,pod没有节点能够运行,于是运行失败
再次修改node1的节点,设置为NoExecute
[root@master ~]# kubectl taint nodes node1 tag:NoSchedule-
node/node1 untainted
[root@master ~]# kubectl taint nodes node1 tag=test:NoExecute
node/node1 tainted
#再次创建pod
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
taint3 0/1 Pending 0 5s <none> <none> <none> <none>
#不仅运行失败,之前两个pod也被踢出节点使用kubeadm搭建的集群,默认就会给master节点添加一个污点标记,所以pod就不会调度到master节点上.
容忍(Toleration)
添加容忍后pod就会忽略污点,不管是什么污点都能调度
创建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: "test" # 容忍的污点的valueeffect: "NoExecute" # 添加容忍的规则,这里必须和标记的污点规则相同
[root@master ~]# kubectl apply -f pod-toleration.yaml
pod/pod-toleration created
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-toleration 1/1 Running 0 22s 172.16.166.164 node1 <none> <none>
容忍的详细配置
FIELDS:key # 对应着要容忍的污点的键,空意味着匹配所有的键value # 对应着要容忍的污点的值operator # key-value的运算符,支持Equal和Exists(默认)effect # 对应污点的effect,空意味着匹配所有影响tolerationSeconds # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间
