K8s官方文档:https://kubernetes.io/
中文官方文档: https://kubernetes.io/zh/
K8s Github地址:https://github.com/kubernetes/
本文基于官方文档及实践操作,系统梳理了 Kubernetes 中 Pod 的定义、创建、调度、生命周期、健康探测、命名空间、标签、污点与容忍度等核心知识,适合作为入门及进阶参考。
一、Pod 介绍
1.1 什么是 Pod
pod是什么?
官方文档:https://kubernetes.io/docs/concepts/workloads/pods/
Pod,在英文中是豆荚的意思,在这里,容器就像是豆子,Pod就是豆荚。所以,Pod是存放容器的容器,或者说是一个容器组 ,Pod是对容器的二次封装。就像同一个豆荚中的豆子共享豆荚的养分一样,Pod 内的容器共享存储、网络等资源,。相当于一个共享网络和存储的逻辑主机,内部封装了一个或多个紧密协作的应用容器

- Pod 是 Kubernetes 中的最小调度单元,K8s 通过定义 Pod 资源,在 Pod 内运行一个或多个容器。
- 容器需要指定镜像,用于运行具体服务。
- Pod 内的容器共享存储、网络等资源 ,可以理解为:
- 将整个 Pod 看作一台逻辑主机(虚拟机)
- 每个容器相当于运行在该虚拟机上的进程
- Pod 会被调度到集群的工作节点上运行,具体调度由
scheduler组件决定。
关于Pod,容器和主机的关系:Pod 通过 Scheduler 的调度,与物理主机(Node)建立绑定关系。绑定成功后,该主机上的 kubelet 才在 Pod 内部创建具体的容器。 容器是 Pod 被调度后的产物
Pod 的作用:充当逻辑主机的角色。例如,要部署 Tomcat 应用,可以在 Pod 中定义 Tomcat 容器,Pod 就是这个应用的"主机"。
多容器 Pod :同一个 Pod 中可以运行多个容器,它们会被自动调度到同一个 Node 上,共享资源、网络环境,且同时被调度。仅当容器需要紧密协作时才使用多容器模式。
1.2 Pod 网络和存储
- 网络 :
- 每个 Pod 被分配一个唯一的 IP 地址(由网络插件如 Calico、Flannel、Weave 等分配)。
- Pod 内的容器共享网络名称空间 (包括 IP 和端口),容器之间可用
localhost互相通信。 - 不同节点的 Pod 可通过网络插件互访。
- 存储 :
- 创建 Pod 时可指定存储卷(Volume)。
- Pod 内所有容器均可访问共享卷,实现数据共享。
- 若挂载持久化数据卷,Pod 重启后数据依然存在。
1.3 使用命令创建 Pod(kubectl run)
kubectl run这个命令可以在任意能连接到apiserver的节点使用(node节点也可以使用),并且通常Pod只会被调度到工作节点上,不会跑到 master 节点上
bash
# 在 worker 节点上导入 nginx 镜像
[root@hd2 ~]# docker load -i nginx.tar.gz
[root@hd3 ~]# docker load -i nginx.tar.gz
# 在 master 节点上创建 Pod
[root@hd1 ~]# kubectl run nginx1 --image="docker.io/library/nginx:latest" --image-pull-policy="IfNotPresent"
# 查看 Pod
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx1 0/1 ContainerCreating 0 11s
#更详细输出,可以看到pod在哪个服务器上运行
[root@hd1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx1 1/1 Running 0 49s 10.244.169.65 hd3 <none> <none>
#删除对应pod
[root@hd1 ~]# kubectl delete pod nginx1
pod "nginx1" deleted
#查看已经删除
[root@hd1 ~]# kubectl get pods
No resources found in default namespace.
注意 :
kubectl run方式不常用,生产环境推荐使用 YAML 文件。
1.4 通过 YAML 文件创建 Pod
示例:创建 Tomcat Pod
导入镜像 (使用 containerd 的 ctr 命令):
bash
# 将 tomcat.tar.gz 上传到 hd2 和 hd3 节点,手动导入
# 假如文档容器运行时是Docker
[root@hd2 ~]# docker load -i tomcat.tar.gz
[root@hd3 ~]# docker load -i tomcat.tar.gz
# 假如文档容器运行时是containerd
[root@hd2 ~]# ctr -n k8s.io images import tomcat.tar.gz
[root@hd3 ~]# ctr -n k8s.io images import tomcat.tar.gz
编写 YAML 文件 pod-tomcat.yaml:
yaml
apiVersion: v1 # 指定 Kubernetes API 的版本,v1 是核心 API 组,支持 Pod、Service 等基础资源
kind: Pod # 声明资源类型为 Pod,即 Kubernetes 中最小的可部署工作负载单元
metadata: # 元数据部分,用于定义该 Pod 的唯一标识和附加信息
name: tomcat-test # 定义 Pod 的名称,在同一个命名空间下必须唯一
namespace: default # 指定该 Pod 所属的命名空间,默认为 default,不填写时也默认为此值。命名空间相当于于存放名称的文件夹
labels: # 定义标签(Label)键值对,用于后续的服务发现、筛选或分组管理
app: tomcat # 设置标签 app=tomcat,常用于标识该 Pod 属于哪个应用
spec: # 期望状态(Spec)部分,定义 Pod 的具体运行规格,即"期望 Pod 达到的状态"
containers: # 定义 Pod 中运行的容器列表(Pod 支持多个容器共享网络和存储)
- name: tomcat-java # 定义第一个容器的名称为 tomcat-java,便于 kubectl 或 API 操作时区分
ports: # 定义容器需要暴露的端口列表。此处仅作为声明,便于阅读,不强制影响流量转发
- containerPort: 8080 # 指定容器监听的端口号为 8080(Tomcat 默认端口),实际访问需配合 Service
image: docker.io/library/tomcat:8.5-jre8-alpine # 指定容器所使用的镜像地址(官方 Tomcat 镜像,基于 Alpine Linux,轻量化)
imagePullPolicy: IfNotPresent # 镜像拉取策略:如果本地不存在该镜像则拉取,若已存在则直接使用本地缓存
应用并查看:
bash
#将 pod-tomcat.yaml 文件中定义的资源(Pod)创建到 Kubernetes 集群中
[root@hd11 ~]# kubectl apply -f pod-tomcat.yaml
#查看是否创建成功
[root@hd11 ~]# kubectl get pods -o wide
NAME READY STATUS IP NODE
tomcat-test 1/1 Running 10.244.121.45 hd2
1.5 编写 YAML 文件的技巧
-
查询官方文档(不推荐) 。
来到官网中文站,进入文档,搜索Pod:
往下翻,就能找到最简单的示例

但是官方文档信息量太大、不够直观,所以仅推荐在学习相关概念时阅读官方文档,学习yaml文件编写并不推荐
-
使用
kubectl run --dry-run(干跑)生成 YAML 模板(推荐):
命令格式:kubectl create <资源类型> <资源名> --image=<镜像名> --dry-run=client -o yaml >
示例
bash
#输出 Pod 模板
[root@hd1 ~]# kubectl run my-pod --image=nginx:latest --dry-run=client -o yaml
#配合重定向,直接生成模板文件
[root@hd1 ~]# kubectl run my-pod --image=nginx:latest --dry-run=client -o yaml > my-pod.yaml
#生成 Deployment 模板
[root@hd1 ~]# kubectl create deployment my-deployment --image=nginx:latest --dry-run=client -o yaml > my-deployment.yaml
#生成 Service (NodePort类型) 模板
[root@hd1 ~]# kubectl create service nodeport my-service --tcp=80:80 --node-port=30001 --dry-run=client -o yaml > my-service.yaml
#生成 Service (ClusterIP类型) 模板
[root@hd1 ~]# kubectl create service clusterip my-service --tcp=80:8080 --dry-run=client -o yaml > my-service.yaml
这条命令可以直接输出模板内容,配合重定向,可以直接生成模板文件
- 用
kubectl explain查字段(推荐)
此外,还可使用 kubectl explain <对象> 辅助编写。kubectl explain 就像是Kubernetes 内置的"API 字典",它会告诉你资源层级都有哪些字段,方便你进行参照编写yaml文件
通过kubect explain查看定义Pod资源包含哪些字段
bash
# 查看 Pod 的顶层字段
[root@hd11 ~]# kubectl explain pod
#可以看到,显示出了所有的pod层级的字段以及字段限制。
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
#FIELDS下就是启动一个Pod需要的字段
FIELDS:
apiVersion <string>
......
kind <string>
......
metadata <Object>
......
spec <Object>
......
status <Object>
......
还可以继续进一步查看pod中对应字段如何定义
bash
#查看pod的spec下有哪些字段
[root@hd11 ~]# kubectl explain pod.spec
KIND: Pod
VERSION: v1
RESOURCE: spec <Object>
containers <[]Object> -requird- #带[]意味着后面跟着列表,requird代表必须
#Pod的spec字段是用来描述Pod的
#还可以继续进一步查看具体某个字段的说明
[root@hd11 ~]# kubectl explain pod.spec.containers
......
labels <map[string]string>
......
managedFields <[]Object>
......
- 查看已存在 Pod 的 YAML 资源清单 :
获取已存在的 Pod 的完整定义,并以 YAML 格式输出显示:
bash
[root@hd1 ~]# kubectl get pod nginx1 -o yaml
事实上,本命令通常用于排查错误
- 使用 IDE 插件(VS Code,强烈推荐)
安装 Kubernetes 扩展 后,在 VS Code 中新建 YAML 文件,输入资源类型(如 deployment),编辑器会自动提示并补全 YAML 模板
打开vscode,安装这两个插件即可

创建一个yaml文件

会弹出来这两个警告,意思是我们没有安装k8s命令行支持,这里直接点击install dependencies 让它自动下载即可

下载完毕,就可以实验了
输入一个p,可以看到它直接就跳出一堆选项。我们选择k8spod

直接生成了一个完整的pod模板,非常便捷。

此外,如果你所在公司的业务深度依赖阿里云,VsCode中还可以下载CloudApps插件,支持以下功能:
- 一键连接:通过 SSH 直接连接到 Kubernetes Pod
- 智能搜索:支持按 Pod 名称、IP 地址、应用名称快速搜索
- 远程开发:集成 VS Code Remote-SSH,支持远程工作区开发
- 安全连接:自动生成和管理 SSH 密钥对
可以极大提升工作效率
1.6 Pod 资源清单关键字段解释
绝大多数情况下,yaml文件不需要我们从头开始编写,而是通过模板/已有文件进行编写
| 字段 | 说明 |
|---|---|
apiVersion |
API 版本,Pod 为 v1 |
kind |
资源类型,此处为 Pod |
metadata.name |
Pod 名称 |
metadata.namespace |
所属命名空间(默认为 default) |
metadata.labels |
标签,用于分组管理 |
spec.containers[].name |
容器名称(必填) |
spec.containers[].image |
容器镜像 |
spec.containers[].ports[].containerPort |
容器暴露的端口(建议填写) |
spec.containers[].imagePullPolicy |
镜像拉取策略: - Always:总是拉取 - Never:从不拉取 - IfNotPresent:本地不存在则拉取(默认) |
YAML 文件 pod-first.yaml:
yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
namespace: default
labels:
app: tomcat-pod-first
spec:
containers:
- name: tomcat-first
ports:
- containerPort: 8080
image: tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
应用并查看:
bash
#将 pod-tomcat.yaml 文件中定义的资源(Pod)创建到 Kubernetes 集群中
[root@hd11 ~]# kubectl apply -f pod-tomcat.yaml
#查看是否创建成功
[root@hd11 ~]# kubectl get pods -o wide
NAME READY STATUS IP NODE
tomcat-test 1/1 Running 10.244.121.45 hd2
将 Pod 端口暴露到集群外(临时调试):
bash
#0.0.0.0指代任意一个地址
[root@hd11 ~]# kubectl port-forward --address 0.0.0.0 tomcat-test 8888:8080
此时可通过 http://<任意节点IP>:8888 访问 Tomcat。
自主式 Pod 的问题:若手动删除该 Pod,它不会被自动重建(因为没有控制器管理)。
bash
[root@hd11 ~]# kubectl delete pods tomcat-test
[root@hd11 ~]# kubectl get pods # 结果为 empty,说明 Pod 已彻底删除
节点故障处理 :若节点 hd2 有问题,可禁止调度(cordon)或恢复:
bash
[root@hd1 ~]# kubectl cordon hd2 # 标记为不可调度
[root@hd1 ~]# kubectl uncordon hd2 # 恢复调度
查看 Pod 日志:
bash
kubectl logs pod-first # 当前启动日志
kubectl logs -p pod-first # 上一次启动日志(--previous)。生产环境常用
kubectl logs pod-first -c tomcat-first # 查看指定容器日志。-c指定容器。如果不指定,就是看第一个容器的日志
进入容器:
bash
#注意,这里指定的是pod名。不指定哪个容器,默认进入第一个容器
kubectl exec -it pod-first -- /bin/bash
# 若 Pod 中有多个容器,指定 -c 容器名
kubectl exec -it pod-first -c tomcat-first -- /bin/bash
二、命名空间(Namespace)
2.1 什么是命名空间
- 命名空间是 Kubernetes 集群级别的资源 ,用于将同一个物理集群划分为多个虚拟集群。
- 适合多团队、多项目、多环境(如
test、dev、prod)的场景。 - 对于小规模集群(几十个节点以内),通常不需要额外创建命名空间。
2.2 默认命名空间
default:未指定命名空间的资源默认放置于此。kube-system:存放 Kubernetes 系统组件(如 API Server、etcd、Controller Manager 等)。
kubectl get pod <-n 命名空间> 查看不同命名空间的pod(不指定就是default命名空间)
kubectl get ns :查看命名空间
kubectl create ns <名称>:创建命名空间
常用操作:
bash
# 查看所有命名空间
[root@hd11 ~]# kubectl get namespace
# 创建命名空间
[root@hd11 ~]# kubectl create ns test
# 切换当前上下文到指定命名空间(后续 kubectl 操作默认使用该 ns)
[root@hd11 ~]# kubectl config set-context --current --namespace=test
# 查看哪些资源属于命名空间级别
[root@hd11 ~]# kubectl api-resources --namespaced=true
2.3 命名空间资源限额(ResourceQuota)
可对命名空间设置资源总量限制,防止某个命名空间过度消耗集群资源。
示例:对 test 命名空间设置 CPU 和内存限额
bash
[root@hd1~]# vim namespace-quota.yaml
apiVersion: v1
kind: ResourceQuota # 资源类型为 ResourceQuota(资源配额)
metadata:
name: mem-cpu-quota #标识该任务,name是操作该资源的唯一入口
namespace: test #作用的命名空间
spec:
hard: # "硬限制":一旦达到上限,新的 Pod 将无法创建
requests.cpu: "2" #至少有 2 核空闲 CPU
requests.memory: 2Gi #至少有 2GiB 空闲内存
limits.cpu: "4" #Pod 最多只能使用 4 核 CPU
limits.memory: 4Gi #Pod 最多只能使用 4GiB 内存
#应用文件。因为我们目前在命名空间test,所以本文件的限制对test这个命名空间生效
[root@hd1 ~]# kubectl apply -f namespace-quota.paml
含义:
- 所有容器的
requests.cpu总和 ≤ 2 CPU - 所有容器的
requests.memory总和 ≤ 2 GiB - 所有容器的
limits.cpu总和 ≤ 4 CPU - 所有容器的
limits.memory总和 ≤ 4 GiB
创建超限的 Pod 会失败。
示例:创建 pod-test.yaml(同样位于 test 命名空间)
bash
[root@hd1 ~]# vim pod-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: mynginx1 #pod的名字
namespace: test
labels: #指定标签
app: mynginx
spec:
containers: #容器名
- name: mycontainers
image: docker.io/library/nginx:latest #如果没有指定拉取策略,不论本地是否存在对应镜像,都会默认从网上拉取,可能会出现imagepullbackoff的错误
resources: #进行资源分配的配置
limits:
cpu: "3"
memory: 500Mi
requests:
cpu: "3"
memory: 200Mi
ports:
- containerPort: 8080
name: http
该 Pod 的 CPU 请求(3)超出命名空间总限额(2), Pod 将部署失败。
bash
#应用yaml文件,会报错,表示资源不足
[root@hd1 ~]# kubectl apply -f pod-test.yaml
Error from server (Forbidden): error when creating "pod-test.yaml": pods "mynginx" is forbidden: exceeded quota: mem-cpu-quota, requested: requests.cpu=3, used: requests.cpu=0, limited: requests.cpu=2
#重新修改文件中cpu数量为2,pod状态为pending(表示悬停/等待状态),说明容器并没有被创建。
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
mynginx1 0/1 Pending 0 19s
#此时查看日志是没有输出的
[root@hd11 ~]# kubectl logs mynginx1
#此时可以通过kubectl describe查看详细信息
[root@hd11 ~]# kubectl describe pod mynginx1
Insufficient cpu. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod.
#这个报错告诉我们没有节点满足pod对cpu个数的要求
# 删除yaml文件和pod。通过kubectl命令删除yaml文件可以一并清除对应的资源限制
[root@hd11 ~]# kubectl delete -f pod-test.yaml
[root@hd11 ~]# kubectl delete pod mynginx1
#切换回我们的默认命名空间
[root@hd11~]# kubectl config set-context --current --namespace=default
三、标签(Label)
3.1 什么是标签
- 标签是一组 key/value,关联到资源对象(如 Pod)上。
- 用于标识对象的特殊属性(如版本、服务类型、环境等),便于分组管理 和筛选。
- 一个对象可拥有多个标签,但 key 必须唯一。
- 除了pod之外,绝大多数 K8s 资源均可打标签,包括但不限于:
- Pod
- Node
- Service
- Deployment
- ReplicaSet
- Namespace
- PersistentVolume
- ConfigMap
- Secret
- Ingress
- 甚至自定义资源(CRD)
3.2 给 Pod 打标签
kubectl label pods <pod名称> <标签键>=<标签值>:给pod打标签(给node等资源打标签也是同样的语法)
bash
# 给已存在的 pod-first 打标签: release=v1
[root@hd11 ~]# kubectl label pods pod-first release=v1
# 查看 Pod 的标签
[root@hd11 ~]# kubectl get pods pod-first --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-first 1/1 Running 1 21h release=v1
# 取消标签(标签键后加 -)
[root@hd1 ~]# kubectl label pods pod-first release-
3.3 查看和筛选标签
kubectl get pod pod名 --show-labels 查看该 Pod 的所有标签
kubectl get pods --show-labels 查看所有 Pod 的标签
kubectl get pods -l app=nginx 筛选出具有 app=nginx 标签的 Pod
kubectl get pods -L app,env 显示标签包含 app 和 env 的 Pod 并额外展示 app 和 env 的值
bash
# 查看所有 Pod 的标签
[root@hd11 ~]# kubectl get pods --show-labels
# 查看指定 Pod 的标签
[root@hd11 ~]# kubectl get pods pod-first --show-labels
# 列出拥有 release 标签的 Pod(不显示标签值)
[root@hd11 ~]# kubectl get pods -l release
# 列出 release=v1 的 Pod
[root@hd11 ~]# kubectl get pods -l release=v1
# 列出 release 标签并显示对应的值(-L)
[root@hd11 ~]# kubectl get pods -L release
# 查看所有命名空间下所有 Pod 的标签。--all-namespaces 指定了所有的命名空间
[root@hd11 ~]# kubectl get pods --all-namespaces --show-labels
#也可以写成这样
[root@hd11 ~]# kubectl get pods -A --show-labels
# 组合使用
[root@hd11 ~]# kubectl get pods -l release=v1 -L release
NAME READY STATUS RESTARTS AGE RELEASE
pod-first 1/1 Running 0 13h v1
#删除当前命名空间下所有的pod
[root@hd1 ~]# kubectl delete pod --all
#删除命名空间test的所有pod
[root@hd1 ~]# kubectl delete pod --all -n test
四、Pod 的调度
4.1 nodeName ------ 指定调度到具体节点
我们在创建pod资源的时候,pod默认会提供schduler实现调度到某个节点上
直接在 Pod 的 spec 中指定 nodeName(节点主机名),Pod 将被调度到该节点(跳过调度器)。
bash
[root@hd11 ~]# vim pod-node.yaml
apiVersion: v1
kind: Pod
metadata:
name: demopod1
namespace: default
labels:
app: myapp
env: dev
spec:
nodeName: hd2 #指定主机名,将固定在这个主机部署pod
containers: #部署两个容器
- name: tomcat-pod-java
ports:
- containerPort: 8080
image: docker.io/library/tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
- name: busybox
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
command: #入口命令。对于没有常驻进程的镜像,在启动容器时如果不指定入口命令,容器启动后会立即退出
- "/bin/sh"
- "-c"
- "sleep 3600"
应用后 Pod 必定运行在 hd2 节点上。
bash
#应用yaml文件
[root@hd1 ~]# kubectl apply -f pod-node.yaml
pod/demopod1 created
#查看确认pod调度到哪个节点
[root@hd1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demopod1 2/2 Running 0 30s 10.244.59.129 hd2 <none> <none>
4.2 nodeSelector ------ 根据节点标签调度
通过给节点打标签,然后在 Pod 中指定 nodeSelector,让 Pod 调度到具有特定标签的节点上。
步骤:
- 给节点打标签:
bash
[root@hd11 ~]# kubectl label nodes hd3 disk=ceph
- 编写YAML文件
pod-1.yaml,指定nodeSelector:
yaml
apiVersion: v1
kind: Pod
metadata:
name: pod3
namespace: default
labels:
app: myapp
env: dev
spec:
nodeSelector: #指定标签的节点
disk: ceph
containers:
- name: tomcat-pod-java
ports:
- containerPort: 8080
image: docker.io/library/tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
应用后 Pod 将调度到拥有 disk=ceph 标签的节点(此处为 hd3)。
此外,如果没有符合条件的节点,pod会处于pending等待状态
bash
#应用
[root@hd1 ~]# kubectl apply -f pod-1.yaml
pod/pod3 created
#查看pod运行在哪里
[root@hd1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod3 1/1 Running 0 58s 10.244.169.66 hd3 <none> <none>
4.3 节点亲和性(Node Affinity)
节点亲和性比 nodeSelector 更灵活,支持**硬性(required)和软性(preferred)**要求。
字段路径:spec.affinity.nodeAffinity
bash
#查看亲和性
[root@hd11 ~]# kubectl explain pods.spec.affinity
FIELDS:
nodeAffinity <Object>
podAffinity <Object>
podAntiAffinity <Object>
#查看node亲和性,
[root@hd11 ~]# kubectl explain pods.spec.affinity.nodeAffinity
FIELDS:
preferredDuringSchedulingIgnoredDuringExecution <[]Object>
requiredDuringSchedulingIgnoredDuringExecution <Object>
requiredDuringSchedulingIgnoredDuringExecution:硬亲和,必须满足,否则 Pod 无法调度。preferredDuringSchedulingIgnoredDuringExecution:软亲和,尽可能满足,不满足也可调度。
bash
#查看硬亲和的属性
[root@hd11 ~]# kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution
FIELDS:
nodeSelectorTerms <[]Object> -required-
Required. A list of node selector terms. The terms are ORed.
#可以看到必须属性nodeSelectorTerms,继续查看这个属性的字段
[root@hd1 ~]# kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms
FIELDS:
matchExpressions <[]Object> #用于匹配表达式
A list of node selector requirements by node's labels.
matchFields <[]Object> #用于匹配字段
A list of node selector requirements by node's fields.
#查看软亲和的属性
[root@hd11 ~]# kubectl explain pods.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution
FIELDS:
preference <Object> -required-
A node selector term, associated with the corresponding weight.
weight <integer> -required-
Weight associated with matching the corresponding nodeSelectorTerm, in the
range 1-100.
4.3.1 硬亲和示例
使用requiredDuringSchedulingIgnoredDuringExecution硬亲和性
提前把myapp-blue-v1.tar.gz上传到hd3和hd2上,手动解压
bash
[root@hd11 ~]# vim pod-nodeaffinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo
namespace: default
labels:
app: myapp #归属于myapp这个应用
tier: frontend # 层级:前端
spec:
containers:
- name: myapp
image: docker.io/janakiramm/myapp:v1
imagePullPolicy: IfNotPresent
affinity: #亲和性
nodeAffinity: #节点亲和
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms: #节点亲和
- matchExpressions: #标签选择器
- key: zone #下面的意思是zone这个键的值要在values的范围之内
operator: In
values:
- foo
- bar
说明 :要求节点标签 zone 的值为 foo 或 bar。若没有节点满足,Pod 将处于 Pending 状态。
测试:
bash
# 此时无节点满足,Pod 为 Pending。硬亲和是必须要满足的条件,不满足就不会正常创建pod
[root@hd11 ~]# kubectl apply -f pod-nodeaffinity-demo.yaml
[root@hd1 ~]# kubectl get pod -o wide | grep pod-node
pod-node-affinity-demo 0/1 Pending 0 36s <none> <none> <none> <none>
# 给 hd2 打上 zone=foo 标签
[root@hd1 ~]# kubectl label node hd2 zone=foo
node/hd2 labeled
# 再次查看,Pod 被调度到 hd2
[root@hd1 ~]# kubectl get pod -o wide | grep pod-node
pod-node-affinity-demo 1/1 Running 0 55s 10.244.59.133 hd2 <none> <none>
4.3.2 软亲和示例
pod-nodeaffinity-demo2.yaml:
yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo2
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: docker.io/janakiramm/myapp:v1
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: zone1
operator: In
values:
- foo1
- bar1
weight: 60
说明 :软亲和,权重 60,调度器会尽量满足,但若无节点满足,Pod 仍会被调度到其他节点。
(如果被赋予了权重不同的两个规则,会优先满足权重更高的,比如权重60大于权重20的)
bash
#应用
[root@hd11 ~]# kubectl apply -f pod-nodeaffinity-demo2.yaml
#查看部署到哪个节点了
[root@hd1 ~]# kubectl get pods -o wide |grep demo2
pod-node-affinity-demo2 0/1 ContainerCreating 0 11s <none> hd3 <none> <none>
Node节点亲和性针对的是pod和node的关系,Pod调度到node节点的时候匹配的条件
4.4 Pod 亲和性(Pod Affinity)与反亲和性(Pod Anti-Affinity)
Pod 亲和性 :让新 Pod 与已有 Pod 部署在"同一位置"(如相同节点、相同机架等),用于解决 "高耦合、低延迟" 的需求
Pod 反亲和性 :让新 Pod 与已有 Pod 部署在不同位置,实现高可用或隔离,防止单点故障。
bash
#查看pod亲和性
[root@hd11 ~]# kubectl explain pods.spec.affinity.podAffinity
FIELDS:
preferredDuringSchedulingIgnoredDuringExecution <[]Object>
requiredDuringSchedulingIgnoredDuringExecution <[]Object>
requiredDuringSchedulingIgnoredDuringExecution: 硬亲和性
preferredDuringSchedulingIgnoredDuringExecution:软亲和性
#查看配置pod亲和性需要的字段
[root@hd11 ~]# kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
FIELDS:
labelSelector <Object>
namespaces <[]string>
topologyKey <string> -required- #这个是必须的
位置的定义由 topologyKey 决定(如 kubernetes.io/hostname 表示节点级)。
topologyKey是节点标签的"引用指针"或"查询索引",并且只查 键 不认 值 ,找到拥有这个键的所有节点后,调度器会把键的值相同的节点划为同一个"拓扑域"(比如 zone=beijing 的节点们是一个域,zone=shanghai 的是另一个域)
常见 topologyKey:
kubernetes.io/hostname 节点级别:同一台物理机或虚拟机
topology.kubernetes.io/zone 可用区级别:同一个云可用区
topology.kubernetes.io/region 地域级别:同一个云地域
4.4.1 Pod 硬亲和示例
创建两个 Pod:pod-first 作为基准,pod-second 必须与 pod-first 在同一节点。
pod-required-affinity-demo.yaml:
bash
#删掉当前命名空间的所有的pod,排除干扰
[root@hd1 ~]# kubectl delete pods --all
[root@hd1 ~]# vim pod-required-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app2: myapp2 #关键标签
tier: frontend
spec:
containers:
- name: myapp
image: docker.io/janakiramm/myapp:v1
imagePullPolicy: IfNotPresent
#创建第二个yaml文件
[root@hd1 ~]# vim pod-required-affinity-demo2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: #硬亲和
- labelSelector: #标签选择器
matchExpressions:
- key: app2 #选择带有标签带 app2 且值为 myapp2 的 Pod
operator: In
values:
- myapp2
topologyKey: kubernetes.io/hostname #拓扑域:按节点主机名分组。即要求当前 Pod 必须与匹配到的 Pod 位于同一节点主机上
应用后,pod-second 会与 pod-first 调度到同一节点(因为 topologyKey 为 hostname):
bash
#应用
[root@hd1 ~]# kubectl apply -f pod-required-affinity-demo.yaml
pod/pod-first created
[root@hd1 ~]# kubectl apply -f pod-required-affinity-demo2.yaml
pod/pod-second created
#可以看到,两个部署到了第同一个主机上
[root@hd1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first 1/1 Running 0 38s 10.244.59.134 hd2 <none> <none>
pod-second 1/1 Running 0 33s 10.244.59.135 hd2 <none> <none>
#第一个pod调度到哪,第二个pod也调度到哪,这就是pod亲和性
#删除两个容器
[root@hd1 ~]# kubectl delete -f pod-required-affinity-demo.yaml
[root@hd1 ~]# kubectl delete -f pod-required-affinity-demo2.yaml
#注意:topologyKey用来查找节点上的一个标签键
[root@hd1 ~]# kubectl get nodes --show-labels
kubectl delete -f :删除yaml文件创建的资源,但不会删除yaml文件
4.4.2 Pod 硬反亲和示例
bash
[root@hd1 ~]# vim pod-required-anti-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app1: myapp1
tier: frontend
spec:
containers:
- name: myapp
image: docker.io/janakiramm/myapp:v1
imagePullPolicy: IfNotPresent
[root@hd1 ~]# vim pod-required-anti-affinity-demo2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAntiAffinity: #指定是反亲和性
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions: #带有 app1 标签且值为 myapp1 的 Pod为不亲和对象
- key: app1
operator: In
values:
- myapp1
topologyKey: kubernetes.io/hostname
应用后,pod-second 会调度到与 pod-first 不同的节点上(若集群有多个节点)。
bash
#依次应用
[root@hd1 ~]# kubectl apply -f pod-required-anti-affinity-demo.yaml
pod/pod-first created
[root@hd1 ~]# kubectl apply -f pod-required-anti-affinity-demo2.yaml
pod/pod-second created
#查看二者都部署到哪里
[root@hd1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first 1/1 Running 0 45s 10.244.59.136 hd2 <none> <none>
pod-second 1/1 Running 0 41s 10.244.169.68 hd3 <none> <none>
#可以看到,他们被部署到了不同的主机上。
若 topologyKey 为 zone ,且多个节点同属一个 zone,则反亲和将找不到满足条件的节点,Pod 会处于 Pending。
bash
#先删除掉之前部署的两个pod
[root@hd1 ~]# kubectl delete -f pod-required-anti-affinity-demo.yaml
pod "pod-first" deleted
[root@hd1 ~]# kubectl delete -f pod-required-anti-affinity-demo2.yaml
pod "pod-second" deleted
#换一个topologykey
#给hd3打zone=foo的标签
[root@hd11 ~]# kubectl label nodes hd3 zone=foo
#给hd2也打上zone=foo的标签,因为之前hd2已经打了zone的标签,所以加上--overwrite强制覆盖这个标签值
[root@hd11 ~]# kubectl label nodes hd2 zone=foo --overwrite
#查看节点的标签,确认二者打上了相同的标签
[root@hd1 ~]# kubectl get node --show-labels
[root@hd1 ~]# vim pod-required-anti-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app3: myapp3
tier: frontend
spec:
containers:
- name: myapp
image: docker.io/janakiramm/myapp:v1
imagePullPolicy: IfNotPresent
#pod-second的topologyKey为zone
[root@hd1 ~]# vim pod-required-anti-affinity-demo2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAntiAffinity: #指定是反亲和性
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions: #带有 app1 标签且值为 myapp1 的 Pod为不亲和对象
- key: app3
operator: In
values:
- myapp3
topologyKey: zone #修改此处为zone
#先部署第一个
[root@hd1 ~]# kubectl apply -f pod-required-anti-affinity-demo.yaml
pod/pod-first created
#可以看到,部署到了hd2节点
[root@hd1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first 1/1 Running 0 3s 10.244.59.140 hd2 <none> <none>
#再部署第二个
[root@hd1 ~]# kubectl apply -f pod-required-anti-affinity-demo2.yaml
pod/pod-second created
#查看发现,pod-second处于Pending状态
[root@hd1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first 1/1 Running 0 9s 10.244.59.141 hd2 <none> <none>
pod-second 0/1 Pending 0 3s <none> <none> <none> <none>
五、污点(Taints)与容忍度(Tolerations)
5.1 污点(Taints)
污点是节点(node)上的"标签",目的是拒绝pod的部署,
- 污点定义在节点 上,格式为
key=value: effect。 - effect(效果) 有三种,定义了污点对未容忍 Pod 产生的行为策略:
NoSchedule:新 Pod 若无容忍度,不会被调度到该节点(已有 Pod 不受影响)。PreferNoSchedule:软性禁止,调度器尽量不调度到该节点。NoExecute:新 Pod 无法调度到本节点,且已有 Pod 若没有对应容忍度,也会被驱逐。
典型应用 :Master 默认有 node-role.kubernetes.io/master:NoSchedule 污点,可以防止普通 Pod 调度到 Master 节点。
bash
# 查看所有节点的污点
[root@hd1 ~]# kubectl describe nodes | grep Taints
Taints: node-role.kubernetes.io/control-plane:NoSchedule
Taints: <none>
Taints: <none>
#查看污点的属性
[root@hd1 ~]# kubectl explain node.spec.taints
FIELDS:
effect <string> -required- #排斥等级
key <string> -required-
timeAdded <string> #这条命令本身不设置该字段。它是节点被打上污点后,由 Kubernetes API Server 自动添加的时间戳
value <string>
5.2 管理污点
注意:添加污点(Taint)的时候,排斥等级Effect 是必须的
添加污点 :
语法:kubectl taint node <nodename> key=value: effect
bash
# 给 hd3 添加污点,效果 NoSchedule
[root@hd11 ~]# kubectl taint node hd3 node-type=production:NoSchedule
#查看hd3的污点
[root@hd1 ~]# kubectl describe nodes hd3 | grep Taints
Taints: node-type=production:NoSchedule
测试 :创建普通 Pod 时,若没有容忍度,将不会被调度到 hd3。
bash
[root@hd1 ~]# vim pod-taint.yaml
apiVersion: v1
kind: Pod
metadata:
name: taint-pod
namespace: default
labels:
tomcat: tomcat-pod
spec:
containers:
- name: taint-pod
ports:
- containerPort: 8080
image: docker.io/library/tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
#应用
[root@hd1 ~]# kubectl apply -f pod-taint.yaml
#查看调度到哪里了
[root@hd1 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
taint-pod 1/1 Running 0 25s 10.244.59.142 hd2 <none> <none>
可以看到都被调度到hd2上了,因为hd3这个节点打了污点
给hd2 添加 NoExecute 污点(会驱逐已有 Pod):
bash
[root@hd1 ~]# kubectl taint node hd2 node-type=dev:NoExecute
#查看hd2上的老pod是否还在,NoExecute会驱逐老pod
[root@hd1 ~]#kubectl get pods -o wide
[root@hd1 ~]# kubectl get pod -o wide
No resources found in default namespace.
此时,hd2 上的已有 Pod(无对应容忍度)将被驱逐。
5.3 容忍度(Tolerations)
容忍度定义在 Pod 中,允许 Pod 调度到具有匹配污点的节点。容忍度与污点相匹配时,才能进行调度
字段 :spec.tolerations[]
支持两种操作符:
Equal:Key、Value、Effect 三者必须全等才能容忍。Exists:仅需 key 和 effect 匹配,value 可忽略。effect: ""(为空值时):无论 Equal 还是 Exists,留空都表示匹配该 Key 下的所有 Effect 类型(NoSchedule / PreferNoSchedule /NoExecute)
示例 :让 Pod 容忍 node-type=dev:NoExecute 污点。
bash
[root@hd1 ~]# vim pod-demo-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-deploy
namespace: default
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
tolerations:
- key: "node-type"
operator: "Equal"
value: "production"
effect: "NoExecute"
#执行
[root@hd1 ~]# kubectl apply -f pod-demo-1.yaml
#查看pod的状态
[root@hd1 ~]# kubectl get pods
myapp-deploy 1/1 Pending 0 11s hd2
#显示pending,因为我们使用的是equal(等值匹配),所以key和value,effect必须和node节点定义的污点完全匹配才可以。把上面配置value: "production"变成 value: "dev":
tolerations:
- key: "node-type"
operator: "Equal"
value: "dev"
effect: "NoExecute"
#删除
[root@hd1 ~]# kubectl delete -f pod-demo-1.yaml
#再次应用:
[root@hd1 ~]# kubectl apply -f pod-demo-1.yaml
#再次查看pod的状态
[root@hd1 ~]# kubectl get pods
myapp-deploy 1/1 running 0 11s hd2
#此时就已经正常部署到hd2了
operator的值是Equal ,意味着只有容忍度的的 key、value、effect 和hd2的污点( node-type=dev:NoExecute) 一致时才能容忍。
若改为 Exists 操作符,则仅需 key 与 effect 匹配即可容忍:
bash
#把如下部分进行修改
tolerations:
- key: "node-type"
operator: "Exists"
value: "" # 当operator是Exists时,value值可忽略
effect: "NoExecute"
#只要对应的键是存在的,effect是一样的,其值被自动定义成通配符
#先删除之前的pod
[root@hd1 ~]# kubectl delete -f pod-demo-1.yaml
#修改之后应用pod
[root@hd1 ~]# kubectl apply -f pod-demo-1.yaml
#查看,pod依然备调度到hd2上
[root@hd1 ~]# kubectl get pods
myapp-deploy 1/1 running 0 11s hd2
#再次修改:
tolerations:
- key: "node-type"
operator: "Exists"
value: ""
effect: ""
#当effect为空的时候意味着匹配所有污点效果,不管是什么排斥等级,都能容忍
#先删除pod
[root@hd1 ~]# kubectl delete -f pod-demo-1.yaml
#修改之后应用
[root@hd1 ~]# kubectl apply -f pod-demo-1.yaml
#查看
[root@hd1 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE
myapp-deploy 1/1 Running 0 3s
#还是调度到hd2
若 effect 留空,则表示容忍所有 effect 类型。
删除污点 (在污点定义的key或者key:value后加 -):
bash
[root@hd1 ~]# kubectl taint nodes hd2 node-type:NoExecute-
[root@hd1 ~]# kubectl taint nodes hd3 node-type-
六、Pod 常见状态和重启策略
6.1 Pod 状态(phase)
| 状态 | 说明 |
|---|---|
| Pending | Pod 已被 API Server 接受,但有一个或多个容器尚未创建或运行。常见原因:镜像拉取、调度未完成、存储挂载失败、亲和性/容忍度不匹配等。 |
| Running | Pod 已绑定到节点,所有容器已创建,且至少有一个容器正在运行或正在启动/重启中。 |
| Succeeded | 所有容器已成功终止(退出码 0),且不会重启。常见于 Job。 |
| Failed | 所有容器已终止,且至少一个容器以非 0 退出码失败(且重启策略为 Never)。 |
| Unknown | Pod 状态无法获取,通常因节点失联或 kubelet 无法上报。 |
6.2 常见异常状态
| 状态/条件 | 说明 |
|---|---|
| CrashLoopBackOff | 容器反复启动后崩溃,kubelet 按退避延迟尝试重启。 |
| ImagePullBackOff / ErrImagePull | 拉取镜像失败(镜像名错误、无权限、仓库不可达)。 |
| ContainerCreating | 容器正在创建(属于 Pending 的子阶段)。 |
| Terminating | Pod 正在被删除,处于优雅宽限期(可能执行 preStop 钩子)。 |
6.3 Pod 重启策略(restartPolicy)
| 策略 | 说明 | 适用场景 |
|---|---|---|
| Always | 容器无论退出码如何,都会重启。默认值。 | 长期运行的服务(Deployment 等)。 |
| OnFailure | 仅当容器以非 0 状态码退出时才重启;正常退出(0)不重启。 | Job(批处理任务)。 |
| Never | 无论退出码如何,都不重启。 | 一次性任务(调试、数据迁移)。 |
Docker默认重启策略为no,k8s默认为always,二者整合相反
七、Pod 生命周期(重点)
7.1 特殊容器
- Pause 容器 (基础架构容器):
- 每个 Pod 内部都有一个 Pause 容器(由 kubelet 自动创建)。
- 它负责共享命名空间(网络、IPC、UTS 等),是所有业务容器的"基石"。
- Pod 的 IP 地址实际属于 Pause 容器,其他容器共享之。
可以看到k8s中每一个容器都有一个同时创建的Pause容器

- Init 容器 (初始化容器):
- 在主容器启动之前 运行,且必须按顺序成功完成,之后才会启动下一个 Init 容器或主容器。
- 用于执行初始化任务,如等待依赖服务、数据准备、权限设置等。

7.2 Pod 启动流程
- Pod 被调度到节点。
- kubelet 创建 Pause 容器,获取网络命名空间等。
- 按顺序启动 Init 容器(若有多个),每个必须成功退出。
- 启动主容器(业务容器),并将它们加入 Pause 容器的命名空间。
- 主容器启动后,立即执行
postStart钩子(若定义)。如果钩子执行失败,容器会被终止并根据重启策略处理。 - 运行过程中,存活性探针(livenessProbe) 和 就绪性探针(readinessProbe) 周期性执行。
- 当 Pod 被删除时,先执行
preStop钩子(若定义),然后发送 SIGTERM 信号,优雅终止。
pod在整个生命周期中有非常多的用户行为:
1、初始化容器完成环境初始化
2、主容器启动
3、在主容器运行中可以做一些健康检测,如liveness probe,readness probe
7.3 探针(Probe)类型
- livenessProbe (存活性探针):判断容器是否存活。失败则 kubelet 杀死容器,并根据重启策略重启。(属于周期性探针)
- readinessProbe (就绪性探针):判断容器是否准备好接收流量。失败则 Pod 的
Ready条件为 false,Service 不会将请求转发给它。(属于周期性探针) - startupProbe(启动探针):用于慢启动容器(如java等),在它成功之前,liveness 和 readiness 探针会被禁用;若失败,容器被杀死。
需要注意的是,如果没有配置探针检测,则默认三者是成功的
7.4 探测方法
目前LivenessProbe和ReadinessProbe两种探针都支持下面三种探测方法:
- ExecAction:在容器内执行命令,退出码为 0 表示成功。
- TCPSocketAction:尝试建立 TCP 连接,成功则健康。
- HTTPGetAction:发起 HTTP GET 请求,状态码 200~399 表示健康。
7.5 探针参数
查看livenessprobe的字段:
bash
[root@hd1 ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
#ExecAction方式,在容器内执行命令。退出码 0 表示成功,其他表示失败
exec <Object>
#连续失败次数才认为失败,默认为3次
failureThreshold <integer>
#使用 gRPC 健康检查协议执行 gRPC 调用
grpc <Object>
#HTTPGetAction方式,对指定 IP:端口/路径执行 HTTP GET 请求。2xx 或 3xx 表示成功
httpGet <Object>
#启动后首次探测的等待时间(秒)
initialDelaySeconds <integer>
#探测间隔(秒)
periodSeconds <integer>
#连续成功次数才认为成功(liveness存活探针 必须为 1)
successThreshold <integer>
#尝试在指定端口上打开 TCP 连接。如果连接建立,则视为成功
tcpSocket <Object>
#在探测失败导致容器终止后,kubelet 向容器发送 SIGTERM 信号与强制 SIGKILL 之间的可选宽限期。覆盖 Pod 级别设置(Pod 与容器级别)
terminationGracePeriodSeconds <integer>
#探测超时时间(秒)。默认值为 1。如果花费的时间超过此值,探测将失败
timeoutSeconds <integer>
注意,readinessProbe 和 livenessProbe 的参数基本一致,用法也基本一致
探针(Probe)有许多可选字段,可以用来更加精确的控制Liveness和Readiness两种探针的行为
| 参数 | 说明 | 默认值 |
|---|---|---|
initialDelaySeconds |
启动后首次探测的等待时间(秒) | 0 |
periodSeconds |
探测间隔(秒) | 10 |
timeoutSeconds |
探测超时时间(秒) | 1 |
successThreshold |
连续成功次数才认为成功(liveness存活探针 必须为 1) | 1 |
failureThreshold |
连续失败次数才认为失败 | 3 |
readinessProbe 和 livenessProbe 可以使用相同探测方式,只是对 Pod 的处置方式不同:
- readinessProbe 当检测失败后,将 Pod 的 IP:Port 从对应的 EndPoint 列表中删除
- livenessProbe 当检测失败后,将杀死容器并根据 Pod 的重启策略来决定作出对应的措施
7.6 LivenessProbe 示例
Exec 方式
bash
[root@hd1 ~]# vim liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: docker.io/library/busybox:1.28
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600 #创建文件,30秒后删除
livenessProbe: #指定存活性探测
exec: #采用ExecAction的探测方法
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5 # 初始延迟:容器启动后等待 5 秒才开始探测
periodSeconds: 5 # 探测周期:每隔 5 秒执行一次上述探测命令
#应用
[root@hd1 ~]# kubectl apply -f liveness-exec.yaml
pod/liveness-exec created
#查看pod状态
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 0 5s
#过一段时间再次查看,发现已经重启了一次
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 (37s ago) 113s
#再过一段时间查看,发现pod进入了 CrashLoopBackOff 状态。这个状态是kubelet的避险机制,避免反复重启导致资源浪费或者系统崩溃
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 CrashLoopBackOff 5 (28s ago) 7m59s
#接下来直接删掉即可
[root@hd1 ~]# kubectl delete -f liveness-exec.yaml
pod "liveness-exec" deleted
说明:容器启动后创建 /tmp/healthy,30 秒后删除。探针每隔 5 秒检查该文件是否存在,若不存在则探测失败,容器将被重启。
HTTP 方式:
bash
[root@hd1 ~]# vim liveness-http.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-http
labels:
test: liveness
spec:
containers:
- name: liveness
image: janakiramm/myapp:v1
imagePullPolicy: IfNotPresent
livenessProbe: #指定存活性探测
initialDelaySeconds: 20 #延迟加载时间
periodSeconds: 5 #重试时间间隔
timeoutSeconds: 10 #超时时间设置
httpGet:
scheme: HTTP
port: 80
path: /index.html
#应用
[root@hd1 ~]# kubectl apply -f liveness-http.yaml
pod/liveness-http created
#查看
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-http 1/1 Running 0 13m
#更改为不存在内容
path: /abc.html
#删除原先的pod
[root@hd1 ~]# kubectl delete -f liveness-http.yaml
pod "liveness-http" deleted
#再次创建
[root@hd1 ~]# kubectl apply -f liveness-http.yaml
pod/liveness-http created
#等待一段时间,查看状态
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-http 1/1 Running 1 (1s ago) 37s
#再过一段时间,查看状态
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-http 0/1 CrashLoopBackOff 5 (12s ago) 4m22s
若 /index.html 无法访问(返回非 2xx/3xx),则容器重启。
7.7 ReadinessProbe 探针使用示例
就绪探针健康检查失败之后,不会让此pod对外提供服务
bash
[root@hd1 ~]# vim readiness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-boot
labels:
app: boot
spec:
containers:
- name: readyness1
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
ports:
- name: server
containerPort: 80
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 80
path: /index.html
#应用
[root@hd1 ~]# kubectl apply -f readiness-exec.yaml
pod/pod-boot created
#查看状态
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-boot 1/1 Running 0 51s
若就绪探测失败,Pod 的 READY 列显示为 0/1,Service 不会将流量转发给它。
bash
#将这个改为不存在的路径
path: /haha.html
#先删除原pod,
[root@hd1 ~]# kubectl delete -f readiness-exec.yaml
pod "pod-boot" deleted
#再执行
[root@hd1 ~]# kubectl apply -f readiness-exec.yaml
pod/pod-boot created
#查看pod状态。因为探测失败,所以READY列始终显示0/1
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-boot 0/1 Running 0 102s
关于k8s命令中单复数与简写的补充
Kubernetes API 定义的资源类型名称通常有 单数 、复数 和 简写 三种形式,都是有效的。
| 资源类型 | 单数 | 复数 | 简写 |
|---|---|---|---|
| Pod | pod |
pods |
po |
| Service | service |
services |
svc |
| Deployment | deployment |
deployments |
deploy |
| Node | node |
nodes |
no |
| Namespace | namespace |
namespaces |
ns |
| ConfigMap | configmap |
configmaps |
cm |
| PersistentVolume | persistentvolume |
persistentvolumes |
pv |
| PersistentVolumeClaim | persistentvolumeclaim |
persistentvolumeclaims |
pvc |
| Ingress | ingress |
ingresses |
ing |
bash
# 以下三种写法完全等价
kubectl label pod pod-first ns=df
kubectl label pods pod-first ns=df
kubectl label po pod-first ns=df
这样的设计更加人性化,并且能有效减少输出(如PersistentVolume可简写为pv),但在脚本中建议使用完整或复数形式以增强可读性
本章节排错过程
在进行7.6章节 Exec 方式 探查的实验时,pod一直处于ContainerCreating状态
bash
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 ContainerCreating 0 72s
经排查,发现是集群底层的 Calico 网络插件(CNI)和 Kubernetes API Server 之间的认证出了问题:
bash
[root@hd1 ~]# kubectl describe pod liveness-exec
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m11s default-scheduler Successfully assigned default/liveness-exec to hd2
Warning FailedCreatePodSandBox 3m10s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = [failed to set up sandbox container "bf0fbf52ad1e86063e1705ebe022eaf1104318d6095e7d2ee8c299658a73ad37" network for pod "liveness-exec": networkPlugin cni failed to set up pod "liveness-exec_default" network: plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized, failed to clean up sandbox container "bf0fbf52ad1e86063e1705ebe022eaf1104318d6095e7d2ee8c299658a73ad37" network for pod "liveness-exec": networkPlugin cni failed to teardown pod "liveness-exec_default" network: plugin type="calico" failed (delete): error getting ClusterInformation: connection is unauthorized: Unauthorized]
Normal SandboxChanged 13s (x14 over 3m10s) kubelet Pod sandbox changed, it will be killed and re-created.
猜测是因为集群运行时间较长导致calico-node Pod 的证书/令牌过期,或者节点上的 CNI 配置文件(/etc/cni/net.d/ 下的 .conflist 文件)里引用的 kubeconfig 凭证无效。
解决方法:重启 Calico 的 DaemonSet
bash
# 强制删除所有 calico-node Pod(它们会由 DaemonSet 自动重建)
[root@hd1 ~]# kubectl delete pods -n kube-system -l k8s-app=calico-node
pod "calico-node-6c9q8" deleted
pod "calico-node-l9t55" deleted
pod "calico-node-nhhg7" deleted
#再次查看,发现pod可以正常运行了
[root@hd1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 0 4m59s
总结
- Pod 是 K8s 最小部署单元,可包含一个或多个共享网络/存储的容器。
- 通过 YAML 定义 Pod 是最佳实践,可使用
kubectl explain辅助编写。 - 命名空间 提供多租户隔离,可配合 ResourceQuota 限制资源。
- 标签 实现资源分组与筛选,是 K8s 管理的基础。
- 调度 可通过
nodeName、nodeSelector、节点亲和性 、Pod 亲和性/反亲和性 灵活控制。 - 污点与容忍度 用于节点排斥特定 Pod,实现专用节点或驱逐异常 Pod。
- Pod 状态 有 Pending、Running、Succeeded、Failed、Unknown 等,异常状态需结合事件排查。
- 重启策略 决定容器退出后的行为(Always / OnFailure / Never)。
- 生命周期 涉及 Pause 容器、Init 容器、主容器,以及 postStart / preStop 钩子和三种探针(liveness、readiness、startup),保障应用的高可用和优雅上下线。
以上内容涵盖了 Pod 的核心概念、操作方法和典型配置,可作为日常运维和开发的参考手册。