一、Pod的生命周期
建立pod
1. 提交 Pod 定义
- 用户或控制器提交请求:用户通过
kubectl apply
命令或 Kubernetes 控制器(如 Deployment 控制器)提交包含 Pod 定义的 YAML 或 JSON 文件到 Kubernetes API 服务器。 - Pod Spec 定义:Pod 包含详细的定义,如容器镜像、资源请求、环境变量、卷挂载等。
2. API 服务器处理
- 验证和持久化:API 服务器接收到请求后,会验证请求格式和内容。通过验证的请求会被存储到 etcd(Kubernetes 的分布式数据库)中。
- 对象创建:一旦存储在 etcd 中,Pod 就被正式创建,标记为 Pending 状态。
3. 调度
- Kube-Scheduler 调度决定:Kubernetes 调度器监控未调度的 Pod,选择适合的节点来运行 Pod。
- 节点选择:调度器根据资源需求、节点可用容量、亲和性/反亲和性、污点和容忍度等来选择节点。
4. 节点准备
- Kubelet 执行动作:一旦 Pod 被分配到节点,节点上的 kubelet(节点代理)会获取 Pod 规范,负责在节点上进行 Pod 的管理。
- 下载容器镜像:如果指定的容器镜像尚未存在于节点上,kubelet 使用容器运行时(如 Docker、containerd)来下载镜像。
5. 容器初始化
- 执行 Init Containers(如果有):在应用容器启动之前,所有 Init 容器按顺序执行,如果任何一个失败,整个 Pod 都不能启动。
- 配置网络和存储:kubelet 设置网络配置(如分配 IP 地址),并准备持久化存储卷(如 PVC)以供 Pod 使用。
6. 启动应用容器
- 运行主容器:完成初始化之后,kubelet 启动 Pod 的应用容器。
- 健康检查和监控:kubelet 定期检查 Pod 的健康状态和生命周期事件,并更新状态。
7. 持续管理
- 监控和修复:kubelet 控制循环会不停检查 Pod 状态,如果容器崩溃(在配置重启策略时),kubelet 会尝试重新启动它。
- 日志记录和收集:容器的日志可以被集群的日志系统(如 Fluentd)收集和存储,以便日后分析。
Pod结束
pod结束运行的过程是一个优雅的终止流程,确保容器有机会完成当前工作并释放资源。
-
Pod running:
- Pod 正在运行中,所有容器都处于正常工作状态。
-
Terminating:
- 当你删除 Pod 时,Pod 进入 Terminating 状态。此时,Kubernetes 开始执行优雅的终止流程。
-
preStop hook:
- 如果 Pod 配置了
preStop
钩子,Kubernetes 会在发送终止信号之前执行这个钩子。preStop
钩子允许你在容器终止前执行一些清理操作或其他必要的任务。
- 如果 Pod 配置了
-
SIGTERM:
- Kubernetes 向 Pod 中的每个容器发送
SIGTERM
信号。SIGTERM
信号通知容器应该开始优雅地关闭,完成当前处理的请求并释放资源。
- Kubernetes 向 Pod 中的每个容器发送
-
terminationGracePeriodSeconds:
- Kubernetes 等待一段时间,称为
terminationGracePeriodSeconds
,默认值为 30 秒。在这段时间内,容器有机会完成优雅关闭。如果容器在这段时间内没有终止,Kubernetes 将强制终止它。
- Kubernetes 等待一段时间,称为
-
SIGKILL:
- 如果容器在
terminationGracePeriodSeconds
内没有终止,Kubernetes 会发送SIGKILL
信号。SIGKILL
信号会立即终止容器,不再等待优雅关闭。
- 如果容器在
-
Deleted:
- 当所有容器都终止后,Pod 资源会从 Kubernetes 集群中删除,Pod 进入 Deleted 状态。
Pod的探针
在 Kubernetes 中,探针(Probe)用于检测 Pod 中容器的健康状况和运行状态。探针有三种类型:存活探针(Liveness Probe)、就绪探针(Readiness Probe) 和 启动探针(Startup Probe)。每种探针都有不同的用途和配置方式。
1. 存活探针(Liveness Probe)
- 用途:检测容器是否处于健康状态。如果存活探针失败,Kubernetes 会重启容器。
- 配置:可以通过 HTTP 请求、TCP 连接或执行命令来配置。
2. 就绪探针(Readiness Probe)
- 用途:检测容器是否已经准备好接受流量。如果就绪探针失败,Kubernetes 会将 Pod 从服务的端点列表中移除。
- 配置:可以通过 HTTP 请求、TCP 连接或执行命令来配置。
3. 启动探针(Startup Probe)
- 用途:检测容器是否已经成功启动。如果启动探针失败,Kubernetes 会重启容器。启动探针只在容器启动期间运行,一旦成功,其他探针(如存活探针和就绪探针)将接管。
- 配置:可以通过 HTTP 请求、TCP 连接或执行命令来配置。
探针一般是配置在yaml文件中。
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: example-container
image: example-image
livenessProbe:
httpGet:
path: /healthz 路径,应用程序会暴露一个健康检查的URL
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
pod的钩子
在 Kubernetes 中,Pod 的生命周期钩子(Lifecycle Hooks)允许你在容器的生命周期的特定事件发生时执行自定义的操作。主要有两种类型的生命周期钩子:PostStart 和 PreStop。这些钩子可以帮助你对容器的启动和终止进行更精细的控制。
1. PostStart 钩子
- 用途:在容器启动后立即执行一些初始化任务。这是在容器的入口点执行之前调用的。
- 用例:可以用于初始化应用程序状态、记录启动日志或执行其他需要在应用启动前完成的操作。
2. PreStop 钩子
- 用途:在 Kubernetes 终止容器之前执行一些清理或收尾工作。
- 用例:用于完成与外部系统的正常关机、注销服务注册和状态保存等任务,以确保优雅停止。
运行机制
- PostStart:在容器创建完毕并且所有 init 容器运行后立即调用。
- PreStop:在 Kubernetes 向容器发送 SIGTERM 信号之前调用,给予容器机会进行收尾工作。
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: example-container
image: example-image
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart hook > /var/log/postStart.txt"]
preStop:
exec:
command: ["/bin/sh", "-c", "echo Goodbye from the preStop hook"]
二、使用yaml文件去启动pod
普通yaml文件启动pod
应用yaml文件来应用并启动pod。
kubectl apply -f pod-example.yaml
关闭pod
kubectl delete -f pod-example.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: harbor.hiuiu.com/nginx/nginx:1.21.5
ports:
- containerPort: 80
在这个YAML文件中:
-
apiVersion: v1
定义了API的版本。 -
kind: Pod
指明这个文件描述的是一个Pod。 -
metadata
包含了Pod的名字和标签,通过标签可以实现对Pod的分组和查询。 -
spec
部分描述了Pod内部的状态:containers
列表定义了Pod中运行的一个或多个容器,这里使用了一个基于nginx:latest
镜像的容器。ports
定义了哪些端口需要暴露给集群中的其他组件。
注意:
这里面还有个镜像拉取原则。
imagePullPolicy
是一个用于控制在 Kubernetes 中如何拉取容器镜像的策略。它有三个选项:
- Always:每次启动容器时,总是从镜像仓库拉取最新的镜像。
- Never:从不从镜像仓库拉取镜像。如果镜像已经存在于本地,则尝试启动容器;如果不存在,则启动失败。
- IfNotPresent:只有当本地没有该镜像时才从镜像仓库拉取镜像。
默认镜像拉取策略设置规则:
-
如果省略了
imagePullPolicy
字段,并且容器镜像的标签是:latest
:imagePullPolicy
会自动设置为Always
,以确保总是使用最新的镜像版本。
-
如果省略了
imagePullPolicy
字段,并且没有为容器镜像指定任何标签:imagePullPolicy
也会自动设置为Always
。在这种情况下,未指定标签通常被理解为使用latest
标签。
-
如果省略了
imagePullPolicy
字段,并且为容器镜像指定了非:latest
的标签:imagePullPolicy
会自动设置为IfNotPresent
,从而在本地不存在镜像的情况下才拉取镜像。
利用namespace进行资源限制生成pod
创建一个命名空间
kubectl create ns (namespace) test
切换当前默认的namespace
kubectl config set-context --current --namespace=test
查看当前namespace
kubectl config view
对命名空间的资源限制
查看命名空间的资源限制
kubectl get resourcequota -n test -o wide
apiVersion: v1
kind: ResourceQuota
metadata:
name: example-quota
namespace: test
spec:
hard:
requests.cpu: "2"
requests.memory: "1Gi"
limits.cpu: "4"
limits.memory: "4Gi"
pods: "10"
ResourceQuota
是用来限制一个特定命名空间(在这里是 test
命名空间)中资源使用的对象。它可以帮助你在繁忙的环境中限制资源滥用。该配置的含义如下:
requests.cpu: "2"
:在这个命名空间中,所有 Pod 合起来可以请求的 CPU 资源最大值为 2 个 CPU。requests.memory: "1Gi"
:在这个命名空间中,所有 Pod 合起来可以请求的内存资源最大值为 1 GiB。limits.cpu: "4"
:在这个命名空间中,所有 Pod 合起来可以限制使用的 CPU 资源最大值为 4 个 CPU。limits.memory: "4Gi"
:在这个命名空间中,所有 Pod 合起来可以限制使用的内存资源最大值为 4 GiB。pods: "10"
:在这个命名空间中,最多可以存在 10 个 Pod。
如果命名空间受限制了,其后的每一个pod都要添加资源的限制内容,否则无法成功启动。
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: test
spec:
containers:
- name: nginx
image: harbor.hiuiu.com/nginx/nginx:1.21.5
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 8
当然,平常的pod也能限制资源,并非要在namespace下;但是有限制的namespace里的pod必须受限制才行。
下图删除相对应的命名空间,pod也会被删除。
副本控制器
在Kubernetes中,ReplicaSet(复制集)是用于确保在任何时候都有指定数量的Pod副本在运行的控制器。ReplicaSet的主要功能是维持Pod的稳定。
功能与作用
- 保持Pod数量一致:ReplicaSet会监控其管理的Pod的状态,并自动启动新的副本或删除多余的副本,以保持系统在用户定义的副本数量。
- 自动重启与故障恢复:如果有Pod意外终止或者节点故障导致Pod非正常消失,ReplicaSet会自动创建新的Pod来取代它。
- 滚动更新(与Deployment结合使用) :虽然ReplicaSet本身不直接支持滚动更新,但在与Deployment资源结合使用时,ReplicaSet可以参与到应用的滚动更新过程中。
相关命令
通常通过一个YAML配置文件来创建ReplicaSet:
kubectl apply -f replicaset.yaml
查看当前命名空间中的所有ReplicaSet:
kubectl get replicasets
查看特定ReplicaSet的详细信息:
kubectl describe replicaset <replicaset-name>
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-replicaset
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: mycontainer
image: harbor.hiuiu.com/nginx/nginx:1.21.5
ports:
- containerPort: 80
如果需要动态扩容怎么办?
只需要修改spec:
replicas: 5
然后重新执行yaml文件即可。注意:如果删除pod,repolicaset会自动创建该pod。
调度pod
在 Kubernetes 中,nodeName 和 nodeSelector 是用来指定 Pod 在某个节点上调度的两种方式。主要解决的是下面的问题如果需要将pod固定生成在某个节点上,该如何实现?
nodeName
- 定义:
nodeName
是一个直接指定 Pod 需要调度到的节点名称。 - 用法:当你在 Pod 规范中设置
nodeName
时,Kubernetes 调度器会跳过这个 Pod,而是将它直接指派到指定的节点上。因为没有调度的过程,这意味着如果指定的节点不可用或者不具备运行此 Pod 的条件,会导致 Pod 无法正常运行。
nodeSelector
- 定义:
nodeSelector
是一种使用键值对的方式来限制 Pod 可以调度到哪些节点上。节点需要带有相应的标签才能被选中。 - 用法:
nodeSelector
提供了一种简单的手段,通过基于标签的选择来指导调度器进行 Pod 指派。节点需要提前打上与nodeSelector
匹配的标签。
实现方式
使用nodename
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-replicaset
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: mycontainer
image: harbor.hiuiu.com/nginx/nginx:1.21.5
ports:
- containerPort: 80
nodeName: slave1
#指定节点名字。
使用nodeSelector
使用这个必须要先在节点打上与之相匹配的标签。
给node打标签。
查看pod标签
kubectl get pod --show-labels
给pod打标签
kubectl label pod pod-name version=v1
查看节点的标签
kubectl get node --show-labels
给节点追加标签
kubectl label node slave1 nodename=one
给节点删除标签
kubectl label node slave1 nodename -
-L 后面跟随的标签键将作为附加列展示在 kubectl get 命令的输出中。
kubectl get pods -L或--label-columns
-l 后面跟随的标签键值对用来过滤出拥有特定标签的资源。
kubectl get pods -l app=myapp
-l 或 --selector
具体操作
给节点1加上标签
kubectl label node slave1 nodenum=first
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-replicaset
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: mycontainer
image: harbor.hiuiu.com/nginx/nginx:1.21.5
ports:
- containerPort: 80
nodeSelector:
nodenum: first
注意:pod可能会出现Evicted状态。
在Kubernetes中,当节点资源紧张时,Kubelet可能会驱逐节点上的一些Pods以释放资源。当这种情况发生时,Pod的状态会被设置为"Evicted"。
利用亲和性调度pod
在 Kubernetes 中,Pod 亲和性(Affinity)是一种机制,用于影响 Pod 调度到节点上的策略。Pod 的亲和性规则可以帮助你控制哪些 Pod 应该一起或不应该一起调度到同一个节点上,从而提高应用程序的性能、可靠性以及资源利用效率。
Kubernetes 提供了两种主要的亲和性类型:
-
节点亲和性(Node Affinity) :
-
类似于
nodeSelector
,但更灵活。 -
允许你基于节点的标签,将 Pod 调度到满足特定条件的节点上。
-
分为 “requiredDuringSchedulingIgnoredDuringExecution” 和 “preferredDuringSchedulingIgnoredDuringExecution” 两种类型:
requiredDuringSchedulingIgnoredDuringExecution
:要求调度器严格满足节点亲和性规则。硬亲和。preferredDuringSchedulingIgnoredDuringExecution
:表示更倾向于满足节点亲和性规则,但不是非强制性的。软亲和
-
-
Pod 亲和性和反亲和性(Pod Affinity and Anti-Affinity) :
- 允许你基于其他 Pod 的分布,控制 Pod 的调度。
- 提供类似的 “required” 和 “preferred” 种类的规则。
- Pod Affinity:要求调度器将新的 Pod 安排在某些具有特定标签的 Pod 运行的节点上。
- Pod Anti-Affinity:要求调度器将新的 Pod 安排在没有特定标签的 Pod 运行的节点上。
-
实际操作
node的亲和性
kubectl explain pods.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution
调度器将倾向于将pods调度到满足该字段指定的亲和性表达式的节点上,但它也可能选择违背一个或多个表达式的节点。最受青睐的节点是具有最大权重和的节点,即对于每个满足所有调度要求(资源请求、调度期间的亲和性表达式等)的节点,通过迭代该字段的元素计算和,如果节点匹配相应的匹配表达式,则为和添加“权重”,具有最高权重的节点是最受青睐的。---- 是软性偏好,表示调度器会尽量但不强求将Pod调度到满足特定条件的节点上。
kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution
如果调度时不满足该字段指定的亲和性要求,pod将不会被调度到该节点上。如果该字段指定的亲和性需求在pod执行期间的某个点停止满足(例如由于更新),系统可能会也可能不会尝试最终将pod从其节点中移除。---- 是硬性要求,表示Pod必须被调度到满足特定条件的节点上
硬限制
kubectl explain ReplicaSet.spec.template.spec.affinity.nodeAffinity
查询如何配置
给节点打上标签
kubectl label node slave1 zone=slave
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-replicaset
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: mycontainer
image: harbor.hiuiu.com/nginx/nginx:1.21.5
ports:
- containerPort: 80
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- slave
现在删除node的标签后,再观察pod的情况。
发现无法调度pod。
软限制
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-replicaset
spec:
replicas: 10
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: mycontainer
image: harbor.hiuiu.com/nginx/nginx:1.21.5
ports:
- containerPort: 80
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: zone
operator: In
values:
- slave
现在,我们在node1节点上打上labels,重新分配pod再观察。
现在看,大多数的pod会在slave1上,可能因为资源不足等的情况,分配到节点2上去,并不是硬限制。
pod的亲和性
应用场景
-
性能优化:
- 将需要频繁通信的 Pods 放在同一个节点上,以减少网络延迟,提高性能。
-
高可用性:
- 使用反亲和性策略确保应用的各个副本分布在不同节点上,以防止单点故障。
-
资源利用:
- 将不同类型的工作负载(如 CPU 密集型和 I/O 密集型)放在一起,优化资源利用。
-
区域部署:
- 根据区域或机架信息将 Pods 部署在特定位置,以满足拓扑结构需求。
-
合规性:
- 遵循数据政策,确保特定应用被限制在特定节点或区域。
pod亲和性
先创建一个pod
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app2: myapp2
tier: frontend
spec:
containers:
- name: myapp
image: harbor.hiuiu.com/nginx/nginx:1.21.5
利用pod亲和性创建另一个pod
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: mysql
image: harbor.hiuiu.com/nginx/nginx:1.21.5
imagePullPolicy: IfNotPresent
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app2
operator: In
values: ["myapp2"]
topologyKey: kubernetes.io/hostname
创建成功。
反亲和性
跟亲和性一样配置,意思是不允许同时存在满足条件的pods上面。
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: mysql
image: harbor.hiuiu.com/nginx/nginx:1.21.5
imagePullPolicy: IfNotPresent
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app2
operator: In
values: ["myapp2"]
topologyKey: kubernetes.io/hostname
注意
上述文件中有一个topologyKey: kubernetes.io/hostname,其作用是用来限制 Kubernetes 调度规则在节点级别生效。具体来说:
- 亲和性:Pods 更倾向于被调度到同一个节点上。
- 反亲和性:Pods 被避免调度到已经有特定标签的 Pods 的同一个节点上。
Pod的重启策略
在 Kubernetes 中,Pod 的重启策略通过 restartPolicy
字段设置,常用于定义容器在失败时的重启行为。restartPolicy
可以有以下三种策略:
-
Always:
- 默认重启策略。
- 无论容器运行结果如何(成功或失败),总是会重启容器。
- 常用于长期运行的服务,因为可以确保服务总是处于运行状态。
-
OnFailure:
- 仅在容器因错误退出(退出码非零)时才重启容器。
- 适用于批处理作业等场景,当容器成功完成后(退出码为零)不会重启。
-
Never:
- 容器无论以任何退出状态(成功或失败)退出,都不会被重启。
- 适用于希望容器仅运行一次并且不再重启的情况。
需要注意的是,这些重启策略适用于 Pod 中的单个容器,并且对于通过其他控制器(如 Deployment、StatefulSet)管理的 Pod,一般使用 Always
进行管理,而 Pod 级别的重启通常由这些控制器处理。