🐇明明跟你说过:个人主页
🏅个人专栏:《Kubernetes航线图:从船长到K8s掌舵者》 🏅
🔖行路有良友,便是天堂🔖
在上一章节中,我们详细探讨了Pod的亲和性,本期将作为上期文章的续篇,详细介绍 Pod污点、容忍度、生命周期等
三、污点与容忍度
1、污点与容忍度的基本概念
在Kubernetes(K8s)中,Pod的污点(Taints)和容忍度(Tolerations)是用于实现更精细化的节点调度策略的重要概念。
污点:污点是一种附加到节点上的属性,用于表示该节点具有某种不希望Pod调度过来的特性或条件。通过给节点设置污点,管理员可以控制哪些Pod可以被调度到该节点上。每个污点都由三个部分组成:key、value和effect。其中,key和value是用户自定义的标识污点的标签,而effect则描述了污点对Pod调度的影响的方式。
容忍度:容忍度是定义在Pod规格中的一种属性,它允许Pod调度到带有与之匹配的污点的节点上。每个容忍度也由三个部分组成:key、operator和value(有时也可以省略value)。Pod只有在其容忍度与节点的污点相匹配时,才能被调度到该节点上。通过这种方式,管理员可以确保只有满足特定条件的Pod才能被调度到带有污点的节点上
污点和容忍度的配合使用,使得Kubernetes能够实现更灵活、更细粒度的节点调度控制,以满足各种复杂的部署需求。例如,管理员可以使用污点和容忍度来确保某些关键服务只运行在具有特定硬件或软件配置的节点上,或者将某些资源密集型的Pod分散到不同的节点上以避免资源争用。
2、污点的类型与设置方法
在Kubernetes(K8s)中,Pod的污点(Taints)是用来表示节点不希望或者不能运行某些Pod的一种属性。污点可以用来实现更精细化的Pod调度控制。污点主要由三个部分组成:key、value和effect。
污点的类型(effect)主要有以下几种:
- NoSchedule:表示Kubernetes不会将新的Pod调度到带有此污点的节点上,但已经存在的Pod不会被驱逐。
- PreferNoSchedule:表示Kubernetes尽量不会将新的Pod调度到带有此污点的节点上,但不是强制性的。
- NoExecute:表示Kubernetes不仅不会将新的Pod调度到带有此污点的节点上,而且还会驱逐已经存在的、不能容忍此污点的Pod。
设置污点的方法主要是使用kubectl命令行工具。例如:
kubectl taint nodes <node-name> key1=value1:NoSchedule
上面的例子中,<node-name>是想要设置污点的节点的名称,key1=value1是自定义的污点特征,NoSchedule是污点的effect。
如果想设置多个污点,可以重复使用kubectl taint命令,或者在一次命令中列出多个污点,例如:
kubectl taint nodes <node-name> key1=value1:NoSchedule key2=value2:PreferNoSchedule
通过合理设置污点和容忍度,可以确保Pod被调度到满足其特定需求的节点上
3、容忍度的配置与应用
Pod的容忍度(Tolerations)是Pod的一个属性,用于表示该Pod可以容忍(即接受调度到)带有特定污点(Taints)的节点上。
容忍度的配置通常包括以下几个字段:
- key:这是污点的key,用于匹配Pod可以容忍的污点。
- operator:用于比较污点的value和Pod容忍度的value。常见的操作符有Exists和Equal。如果未指定operator,则默认为Equal。
- value:这是污点的value,用于与Pod容忍度的value进行比较。当operator为Exists时,value字段可以省略。
- effect:这是污点的effect,表示Pod可以容忍的调度规则。Pod必须能够容忍节点上的所有污点才能被调度到该节点上。
下面是一个示例:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key2"
operator: "Exists"
effect: "NoExecute"
在这个例子中,Pod配置了两个容忍度:
- 第一个容忍度表示该Pod可以容忍key为"key1",value为"value1",effect为"NoSchedule"的污点;
- 第二个容忍度表示该Pod可以容忍存在key为"key2"的任意value和effect为"NoExecute"的污点。
应用容忍度时,Kubernetes调度器会检查每个Pod的容忍度和集群中节点的污点。只有当Pod的容忍度与节点的污点完全匹配时,Pod才会被调度到该节点上。如果节点上有多个污点,Pod必须能够容忍所有这些污点才能被调度到该节点。
4、污点与容忍度在资源隔离与调度中的应用
假设我们管理一个包含多个节点的Kubernetes集群,并且集群中有一些节点配备了特殊的GPU硬件,这些节点主要用于运行深度学习训练任务。为了确保这些GPU资源不被其他非关键任务占用,我们可以使用污点和容忍度来实现资源隔离和调度控制。
首先,可以给那些配备了GPU的节点打上污点。例如,可以使用以下命令给节点node-with-gpu设置一个污点:
kubectl taint nodes node-with-gpu gpu=true:NoSchedule
这个命令给node-with-gpu节点添加了一个key为gpu、value为true、effect为NoSchedule的污点。这意味着默认情况下,Kubernetes不会将新的Pod调度到这个节点上,除非这些Pod具有能够容忍这个污点的容忍度。
接着,对于需要运行在GPU节点上的深度学习训练Pod,我们可以在它们的Pod定义中添加相应的容忍度配置。例如:
apiVersion: v1
kind: Pod
metadata:
name: deep-learning-pod
spec:
containers:
- name: deep-learning-container
image: deep-learning-image
# 其他容器配置...
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
在这个Pod定义中,我们添加了一个容忍度,它匹配了我们之前给node-with-gpu节点设置的污点。这意味着这个Pod可以被调度到带有这个污点的节点上。
现在,当创建这个deep-learning-pod时,Kubernetes调度器会检查它的容忍度,并找到匹配的污点。由于deep-learning-pod具有能够容忍gpu=true:NoSchedule污点的容忍度,因此它会被调度到node-with-gpu节点上。其他没有这个容忍度的Pod则不会被调度到这个节点上,从而实现了GPU资源的隔离。
四、Pod生命周期
1、Pod的生命周期概述
1. Pod创建过程
- 定义:用户或自动化工具通过编写Pod YAML配置文件或使用Kubernetes API直接创建Pod资源对象。
- 验证:Kubernetes API Server接收到Pod定义后,对其进行语法和语义验证。
- 调度:通过Kubernetes调度器(Scheduler),根据Pod的资源需求、节点选择器、亲和性和反亲和性规则等,将Pod分配到集群中的一个合适节点。
- 绑定:调度器将Pod与选定节点绑定,更新Pod的元数据以记录节点分配信息。
2. 初始化容器(Init Containers)执行
- 启动:如果Pod定义中包含初始化容器(Init Containers),这些容器会在主容器启动前按顺序逐一运行。
- 等待:每个Init Container必须成功退出(退出状态码为0)后,下一个Init Container才会启动。它们用于执行预启动任务,如设置应用环境、加载数据、配置安全凭据等。
- 完成:所有Init Containers执行完毕且成功退出后,主容器启动的条件就绪。
3. 主容器(Main Containers)启动与运行
- 拉取镜像:节点上的kubelet负责下载Pod中每个容器所需的镜像。
- 容器创建:kubelet基于镜像创建容器,并为容器配置网络、存储卷等资源。
- 启动:容器按定义顺序启动(如果有指定启动顺序的话)。此时,Pod状态变为Running。
- 健康检查:kubelet使用容器探针(Liveness Probe、Readiness Probe)监控容器的健康状况。探针的结果影响Pod及服务的可用性判断以及容器是否需要重启。
4. Pod状态变更与维护
- 状态监控:kubelet持续监控Pod中所有容器的状态,并根据容器的退出代码、重启策略等信息更新Pod的整体状态。
- 重启策略:根据定义的重启策略(Always、OnFailure、Never),kubelet决定是否自动重启失败的容器。对于NoExecute类型的节点污点,kubelet可能需要根据Pod的容忍度决定是否将Pod从受影响节点驱逐。
- 清理:当Pod因各种原因(如用户删除、部署更新、节点故障等)需要终止时,kubelet负责优雅地停止容器,清理资源(如释放IP地址、解除存储卷挂载等),并将Pod状态更新为Terminating。
5. Pod最终状态
- 成功:如果所有容器均正常退出(退出状态码为0),且重启策略未触发重启,Pod进入Succeeded状态,表示其任务已完成。
- 失败:若容器以非零退出状态结束,且重启策略不允许或已超过最大重试次数,Pod进入Failed状态,表示其运行失败。
- 未知状态:在某些异常情况下,如节点失联,Pod可能会进入Unknown状态,表示Kubernetes无法确定其确切状态
2、Init Container的作用与配置
Init Container 主要用于在启动应用容器(App Container)之前执行一些初始化任务。这些任务可以包括配置文件的准备、数据的预处理、网络设置等。
Init Container与应用容器在本质上是一样的,但它们有一些关键的区别。首先,Init Container不支持lifecycle、livenessProbe、readinessProbe和startupProbe,因为这些探针必须在Pod就绪之前运行完成,而Init Container的任务仅运行一次并结束。这意味着Init Container必须在成功执行完毕后,系统才能继续执行下一个容器。
如果Pod中的Init Container启动失败,Kubernetes会不断重启该Pod,直到Init Container成功为止。但是,如果Pod对应的restartPolicy设置为Never,那么Pod在Init Container失败后不会重新启动。
在配置Init Container时,可以定义多个Init Container,它们会按照定义的顺序依次执行。每个Init Container都必须成功完成执行后,下一个才会开始。一旦所有的Init Container都成功完成,Pod内的应用容器才会并行启动。
Init Container对于访问一些应用容器不能访问的资源,比如Secret,有特殊的权限。这使得Init Container可以用来判断环境是否已经满足运行Pod应用前所需要的条件。例如,如果一个应用需要部署到Tomcat环境,Init Container可以用来检查Tomcat环境是否已经准备好。
在Kubernetes中,Init Container的配置是在Pod的YAML文件中定义的。下面是一个简单的示例,展示了如何配置Init Container。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: myapp:latest
ports:
- containerPort: 8080
# 其他容器配置...
initContainers:
- name: init-mydb
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: work-volume
mountPath: /work/
- name: init-myservice
image: busybox
command: ['sh', '-c', 'echo Hello from the init container;']
volumeMounts:
- name: work-volume
mountPath: /work2/
volumes:
- name: work-volume
emptyDir: {}
在这个示例中,Pod myapp-pod 包含一个应用容器 myapp-container 和两个Init Container:init-mydb 和 init-myservice。
init-mydb Init Container 使用 busybox 镜像,并运行一个shell命令来等待 myservice 的DNS解析成功。它通过 nslookup 命令不断尝试解析 myservice,直到成功为止。这通常用于等待数据库或其他服务变得可用。它还定义了两个环境变量 MY_POD_NAME 和 MY_POD_NAMESPACE,它们分别通过 fieldRef 引用Pod的元数据字段。最后,它挂载了一个名为 work-volume 的卷到 /work/ 路径。
init-myservice Init Container 也是一个简单的 busybox 容器,它只打印一条消息到控制台。它同样挂载了 work-volume 卷到 /work2/ 路径。
Pod还定义了一个名为 work-volume 的卷,类型为 emptyDir,用于在容器之间共享数据。
Init Container按照它们在Pod定义中的顺序依次执行。在这个例子中,init-mydb 会在 init-myservice 之前执行。只有当所有的Init Container都成功执行完成后,myapp-container 应用容器才会启动。
3、PostStart与PreStop钩子的使用
PostStart 和 PreStop 是 Kubernetes 中针对 Pod 中容器定义的生命周期钩子(lifecycle hooks),它们允许在容器的特定阶段执行自定义操作。
PostStart 钩子
- 作用: PostStart 钩子在容器启动后立即执行。请注意,虽然它会在容器启动后很快触发,但并不能保证其在容器主进程(由 ENTRYPOINT 和 CMD 指定)之前运行。它是异步执行的,这意味着即使 PostStart 钩子脚本或命令还在执行,容器的主进程也可能已经开始。
使用情景和案例:
- 数据库连接:在容器启动后,通过 PostStart 钩子来建立数据库连接,确保应用程序在启动时能够正常访问数据库。
- 文件下载:容器启动后,利用 PostStart 钩子从外部源下载必要的文件或配置,并将其放置在容器内部指定位置,供应用程序使用。
- 启动后台任务:在容器启动后,通过 PostStart 钩子启动或触发后台任务,如数据同步、日志收集代理的初始化、缓存预热等。
- 环境准备:执行一些必要的环境设置操作,如设置环境变量、创建目录结构、调整权限等。
- 配置方式: 在 Pod 定义的 containers 部分中,为某个容器添加 lifecycle 配置项,其中包含 postStart 字段。postStart 字段接受一个 Handler 类型,可以是 Exec(执行命令)、HTTPGet(发送 HTTP GET 请求)或 TCPSocket(打开 TCP 连接)。最常见的做法是使用 Exec 来执行一个命令,
示例:
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: my-container
image: my-image:latest
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'Container started'; setup_database_connection"]
PreStop 钩子
- 作用: PreStop 钩子在容器被终止之前执行。当 Kubernetes 决定要停止一个容器时(例如由于缩容、滚动更新、Pod 删除等原因),它会先触发 PreStop 钩子。PreStop 钩子的执行是同步的,即容器的终止过程会等待 PreStop 钩子命令完成。这提供了优雅关闭的机会,确保在容器退出前完成必要的清理工作。
使用情景和案例:
- 优雅关闭:通知应用程序开始优雅关闭流程,如保存状态、结束当前请求、关闭连接等,避免数据丢失或服务中断。
- 资源释放:释放外部资源,如解除数据库连接、清理临时文件、注销服务发现注册等。
- 日志记录:记录容器停止事件,或者将最后时刻的状态信息发送到监控系统。
- 通知系统:向其他服务或系统发送容器即将终止的通知,以便它们可以做出相应调整。
💕💕💕每一次的分享都是一次成长的旅程,感谢您的陪伴和关注。希望这些关于Kubernetes的文章能陪伴您走过技术的一段旅程,共同见证成长和进步!😺😺😺
🧨🧨🧨让我们一起在技术的海洋中探索前行,共同书写美好的未来!!!