Kubernetes(K8s)笔记Day03: Pod命名空间,标签,Pod 的调度,污点与容忍度,Pod 常见状态和重启策略,Pod 生命周期

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

导入镜像 (使用 containerdctr 命令):

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 文件的技巧

  1. 查询官方文档(不推荐)

    来到官网中文站,进入文档,搜索Pod:

    往下翻,就能找到最简单的示例

    但是官方文档信息量太大、不够直观,所以仅推荐在学习相关概念时阅读官方文档,学习yaml文件编写并不推荐

  2. 使用 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

这条命令可以直接输出模板内容,配合重定向,可以直接生成模板文件

  1. 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>
......
  1. 查看已存在 Pod 的 YAML 资源清单
    获取已存在的 Pod 的完整定义,并以 YAML 格式输出显示:
bash 复制代码
[root@hd1 ~]# kubectl get pod nginx1 -o yaml

事实上,本命令通常用于排查错误

  1. 使用 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 集群级别的资源 ,用于将同一个物理集群划分为多个虚拟集群
  • 适合多团队、多项目、多环境(如 testdevprod)的场景。
  • 对于小规模集群(几十个节点以内),通常不需要额外创建命名空间。

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 调度到具有特定标签的节点上。

步骤

  1. 给节点打标签:
bash 复制代码
[root@hd11 ~]# kubectl label nodes hd3 disk=ceph
  1. 编写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 的值为 foobar。若没有节点满足,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>
#可以看到,他们被部署到了不同的主机上。

topologyKeyzone ,且多个节点同属一个 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 启动流程

  1. Pod 被调度到节点。
  2. kubelet 创建 Pause 容器,获取网络命名空间等。
  3. 按顺序启动 Init 容器(若有多个),每个必须成功退出。
  4. 启动主容器(业务容器),并将它们加入 Pause 容器的命名空间。
  5. 主容器启动后,立即执行 postStart 钩子(若定义)。如果钩子执行失败,容器会被终止并根据重启策略处理。
  6. 运行过程中,存活性探针(livenessProbe)就绪性探针(readinessProbe) 周期性执行。
  7. 当 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两种探针都支持下面三种探测方法:

  1. ExecAction:在容器内执行命令,退出码为 0 表示成功。
  2. TCPSocketAction:尝试建立 TCP 连接,成功则健康。
  3. 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 管理的基础。
  • 调度 可通过 nodeNamenodeSelector节点亲和性Pod 亲和性/反亲和性 灵活控制。
  • 污点与容忍度 用于节点排斥特定 Pod,实现专用节点或驱逐异常 Pod。
  • Pod 状态 有 Pending、Running、Succeeded、Failed、Unknown 等,异常状态需结合事件排查。
  • 重启策略 决定容器退出后的行为(Always / OnFailure / Never)。
  • 生命周期 涉及 Pause 容器、Init 容器、主容器,以及 postStart / preStop 钩子和三种探针(liveness、readiness、startup),保障应用的高可用和优雅上下线。

以上内容涵盖了 Pod 的核心概念、操作方法和典型配置,可作为日常运维和开发的参考手册。

相关推荐
张忠琳1 小时前
【NVIDIA】NVIDIA k8s-device-plugin v0.19.3 资源管理器模块深度分析之三
云原生·容器·架构·kubernetes·nvidia
六bring个六1 小时前
git使用笔记
笔记·git
图件制作GIS,储量计算1 小时前
未来已到,抓住机遇
运维·服务器
砚凝霜1 小时前
软考网络工程师|第 3 章 以太网帧、物理层标准、VLAN 完整备考笔记
网络·笔记
xuhe21 小时前
一劳永逸!解决 AutoDL 系统盘(30GB)爆满与 pip 缓存迁移
linux·python·ai·jupyter
hanlin031 小时前
刷题笔记:力扣第704、977、209题(数组相关)
笔记·算法·leetcode
CIO_Alliance2 小时前
AI+iPaaS解决方案:跨系统业务流程自动化助力企业AI化转型
运维·人工智能·自动化
学无止境_永不停歇2 小时前
9. 缓冲区
linux·服务器·c++
dok122 小时前
Johannes 《Linux内核模块与设备驱动开发:编写Linux驱动程序》(4) devicefile 设备号相关
linux·运维·服务器