文章目录


前言

Pod 作为 Kubernetes 集群中最小的部署单元,是理解容器编排与资源管理的核心基石。无论是单容器应用的简单部署,还是多容器协作的复杂场景,Pod 都承担着资源共享、网络通信与生命周期管理的关键角色。本文将从 Pod 的基础概念出发,逐步深入其使用方式、资源配置、健康检查等进阶内容,并结合实操案例,帮助你全面掌握 Pod 的设计逻辑与实战技巧,为后续学习控制器、服务发现等 Kubernetes 核心功能打下坚实基础。


一、Pod的详解

1.1 Pod基础概念

Pod 是 Kubernetes 中的最小部署单元,它代表集群中的一个运行进程。一个 Pod 可以包含一个或多个紧密耦合的容器,这些容器共享网络和存储资源。在 Kubernetes 中,Pod 是管理容器的基础结构,其他如 Deployment、StatefulSet、DaemonSet 等控制器对象都围绕 Pod 进行扩展。

  1. 最小部署的单元
  2. 包含一个或者多个容器(一组容器的集合)
  3. 一个 pod 中的容器共享网络命名空间和存储资源
  4. pod 是短暂的

1.2 Kubernetes集群中Pod的使用方式

1.2.1 单容器 Pod

最常见的用法,一个 Pod 只包含一个容器。这种情况下,Pod 就相当于容器的封装,Kubernetes 管理的是 Pod,而不是容器。

apiVersion: v1
kind: Pod
metadata:
  name: single-container-pod
spec:
  containers:
  - name: nginx
    image: nginx:1.14

1.2.2 多容器 Pod

一个 Pod 可以包含多个容器,这些容器通常有共同的资源需求,如共享网络和存储,彼此间可以通过 localhost 进行通信。

apiVersion: v1
kind: Pod
metadata:
  name: multi-container-pod
spec:
  containers:
  - name: app
    image: my-app:latest
  - name: sidecar # 边车,一般放filebeat日志收集或者Prometheus监控
    image: log-collector:latest

1.3 Pause 容器(基础容器)

在每个 Pod 中,都会有一个特殊的容器被称为 Pause 容器,它的主要职责是提供 Pod 的 Linux 命名空间基础。它使得 Pod 内的所有容器共享网络和存储资源。Pause 容器运行时,它会创建一个 Linux 命名空间,其他容器将共享该命名空间来进行网络通信和存储操作。

1.4 Pod 中的共享资源

1.4.1 网络共享

每个 Pod 分配一个唯一的 IP 地址,Pod 内的所有容器共享该 IP 和端口。它们能够通过 localhost 进行通信。Pod 中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)

  1. 每个 pod 分配一个独特 IP 地址
  2. pod 内部所有容器共享这个 IP 和端口,能通过 localhost 直接通信
  3. 与外界通信时,必须使用宿主机的端口映射

1.4.2 存储共享

Pod 可以指定多个共享的 Volume,这些 Volume 可以被 Pod 内的所有容器访问,确保容器重启后数据不会丢失。

  1. Pod 可以指定多个共享的 Volume,所有容器都可以访问
  2. Volume 可以用来持久化存储,确保容器重启后数据不会丢失

1.5 总结

每个 Pod 都有一个特殊的被称为“基础容器”的 Pause 容器。Pause 容器对应的镜像属于 Kubernetes 平台的一部分,除了 Pause 容器,每个 Pod 还包含一个或者多个紧密相关的用户应用容器。

Pod 包含一个特殊的“基础容器”的 Pause 容器,它负责 pod 网络和存储资源,除了 pause 容器,pod 还包含多个应用容器,他们可以共同工作。

1.6 Pod 的使用场景

  1. 单一进程应用:最常见的用法,一个 Pod 用于运行一个容器。
  2. 多进程协作:多个容器在同一个 Pod 内共享网络和存储资源,适用于紧密耦合的服务。

1.7 Pod 的类型

1.7.1 自主式 Pod

这种 Pod 不具备自我修复的能力。如果它所在的节点故障,Pod 会被删除,且不会自动恢复。

1.7.2 控制器管理的 Pod

Kubernetes 中通常通过控制器(如 Deployment、StatefulSet)来管理 Pod。控制器提供副本管理、滚动升级、自动修复等功能。

1.8 Pod 容器的分类

1.8.1 基础容器(infrastructure container)

维护整个 Pod 网络和存储空间
node 节点中操作
启动一个 Pod 时,k8s 会自动启动一个基础容器
cat /opt/kubernetes/cfg/kubelet
......
--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0"

每次创建 Pod 时候就会创建,运行的每一个 Pod 都有一个 pause-amd64 的基础容器自动会运行,对于用户是透明的
docker ps -a
registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0   "/pause"

1.8.2 初始化容器(init containers)

1.8.2.1 Init 容器启动过程

Init 容器必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以 Init 容器能够提供了一种简单的阻塞或延迟应用容器的启动的方法。

Init 容器与普通的容器非常像,除了以下两点:

  • Init 容器总是运行到成功完成为止,如果 init 失败会进行全部重新 init,并且是串行初始化
    启动 --》运行 --》 结束 --》 退出 成功 再运行下一个 Init
  • 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成启动和退出
    如果 Pod 的 Init 容器失败,k8s 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的重启策略(restartPolicy)为 Never,它不会重新启动。
1.8.2.2 Init 容器的作用

因为 init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:

  • Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。例如,没有必要仅为了在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具而去 FROM 一个镜像来生成一个新的镜像。
    独立使用的工具
  • Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
  • 应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
  • Init 容器能以不同于 Pod 内应用容器的文件系统视图运行。因此,Init 容器可具有访问 Secrets 的权限,而应用容器不能够访问。
  • 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod 内的所有的应用容器会并行启动。

1.8.3 应用容器(Main container)

1.8.3.1 并行启动
官网示例:
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/init-containers/
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

这个例子是定义了一个具有 2 个 Init 容器的简单 Pod。第一个等待 myservice 启动,第二个等待 mydb 启动。一旦这两个 Init 容器都启动完成,Pod 将启动 spec 中的应用容器。

先查看,如果找不到错误信息:

kubectl describe pod myapp-pod

使用 logs 查看,指定好 init 容器:

kubectl logs myapp-pod -c init-myservice
vim myservice.yaml
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
kubectl create -f myservice.yaml
kubectl get svc
kubectl get pods -n kube-system
kubectl get pods
vim mydb.yaml
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377
kubectl create -f mydb.yaml
kubectl get pods
1.8.3.2 总结 pod init 初始化
  • 在 Pod 启动过程中,Init 容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。
  • 如果由于运行时或失败退出,将导致容器启动失败,它会根据 Pod 的 restartPolicy 指定的策略进行重试。然而,如果 Pod 的 restartPolicy 设置为 Always,Init 容器失败时会使用 RestartPolicy 策略。
  • 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态,但应该会将 Initializing 状态设置为 true。
  • 如果 Pod 重启,所有 Init 容器必须重新执行。
  • 对 Init 容器 spec 的修改被限制在容器 image 字段,修改其他字段都不会生效。更改 Init 容器的 image 字段,等价于重启该 Pod。
  • Init 容器具有应用容器的所有字段。除了 readinessProbe,因为 Init 容器无法定义不同于完成(completion)的就绪(readiness)之外的其他状态。这会在验证过程中强制执行。
  • 在 Pod 中的每个 app 和 Init 容器的名称必须唯一;与任何其它容器共享同一个名称,会在验证时抛出错误

1.9 配置的核心部分

1.9.1 Pod 的镜像拉取策略

Pod 中的容器需要通过指定镜像来启动,镜像拉取策略有以下几种:

  • IfNotPresent:如果本地已有镜像,不会拉取新的镜像,仅在本地缺失时拉取。
  • Always(默认策略):每次启动 Pod 时都会拉取镜像。
  • Never:不拉取镜像,仅使用本地镜像。
imagePullPolicy: Always

可以通过以下命令查看:

kubectl edit pod [pod名]

官方示例:https://kubernetes.io/docs/concepts/containers/images

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: private-image-test-1
spec:
  containers:
    - name: uses-private-image
      image: $PRIVATE_IMAGE_NAME
      imagePullPolicy: Always
      command: [ "echo", "SUCCESS" ]
EOF
// master01 上操作
kubectl edit deployment/nginx-deployment
......
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.15.4
        imagePullPolicy: IfNotPresent							# 镜像拉取策略为 IfNotPresent
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always				# Pod的重启策略为 Always,默认值
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
......
1.9.1.1 案例1
1)创建测试案例
mkdir /opt/demo
cd /opt/demo
vim pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-test1
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: Always
      command: [ "echo", "SUCCESS" ]
kubectl create -f pod1.yaml
kubectl get pods -o wide

此时输出可能为:

pod-test1                         0/1     CrashLoopBackOff   4          3m33s

// 此时 Pod 的状态异常,原因是 echo 执行完进程终止,容器生命周期也就结束了

kubectl describe pod pod-test1
......
Events:
  Type     Reason     Age                 From                    Message
  ----     ------     ----                ----                    -------
  Normal   Scheduled  2m10s               default-scheduler       Successfully assigned default/pod-test1 to 192.168.80.11
  Normal   Pulled     46s (x4 over 119s)  kubelet, 192.168.80.11  Successfully pulled image "nginx"
  Normal   Created    46s (x4 over 119s)  kubelet, 192.168.80.11  Created container
  Normal   Started    46s (x4 over 119s)  kubelet, 192.168.80.11  Started container
  Warning  BackOff    19s (x7 over 107s)  kubelet, 192.168.80.11  Back-off restarting failed container
  Normal   Pulling    5s (x5 over 2m8s)   kubelet, 192.168.80.11  pulling image "nginx"

// 可以发现 Pod 中的容器在生命周期结束后,由于 Pod 的重启策略为 Always,容器再次重启了,并且又重新开始拉取镜

// 修改 pod1.yaml 文件

cd /opt/demo
vim pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-test1
spec:
  containers:
    - name: nginx
      image: nginx:1.14							# 修改 nginx 镜像版本
      imagePullPolicy: Always
      #command: [ "echo", "SUCCESS" ]			# 删除

// 删除原有的资源

kubectl delete -f pod1.yaml 

// 更新资源

kubectl apply -f pod1.yaml 

// 查看 Pod 状态

kubectl get pods -o wide
NAME                         READY   STATUS             RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS 
pod-test1                    1/1     Running            0          10m     10.244.2.24   node02   <none>           <none>

// 在任意 node 节点上使用 curl 查看头部信息

curl -I http:// 10.244.2.24
HTTP/1.1 200 OK
Server: nginx/1.14.2
......

1.9.2 Pod 的重启策略(restartPolicy)

Pod 的重启策略定义了容器退出后 Kubernetes 如何处理容器的重启。主要有以下三种策略:

  • Always:无论容器的退出状态如何,都会重新启动。
  • OnFailure:只有容器非正常退出(即退出码非 0)时,才会重启容器。
  • Never:容器退出后不会重启,一般测试环境,退出了也无关紧要可以使用。
restartPolicy: Always

# 注意:K8S 中不支持重启 Pod 资源,只有删除重建

kubectl edit deployment nginx-deployment
......
 restartPolicy: Always
1.9.2.1 案例1
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 30; exit 3
kubectl apply -f pod3.yaml
// 查看 Pod 状态,等容器启动后 30 秒后执行 exit 退出进程进入 error 状态,就会重启次数加 1
kubectl get pods
NAME                              READY   STATUS             RESTARTS   AGE
foo                               1/1     Running            1          50s
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 30; exit 3
  restartPolicy: Never
# 注意:跟 container 同一个级别
kubectl apply -f pod3.yaml
// 容器进入 error 状态不会进行重启
kubectl get pods -w

二、Pod 的进阶

2.1 Pod 资源限制

在 Kubernetes 中,为了合理管理集群中的资源,容器的 CPU 和内存资源都可以设置请求值(requests)和限制值(limits)。这些设置确保了容器的资源分配和限制,避免资源争用和过度使用。

2.1.1 资源请求与限制(Request & Limit)

  • 请求(request):当容器启动时,Kubernetes 会根据容器的资源请求来决定容器应该调度到哪个节点上。这个值表示容器在运行时最少需要的资源。
  • 限制(limit):容器在运行时可以使用的最大资源量。如果容器超过了这个限制,Kubernetes 会采取措施来控制容器的资源使用,防止过度消耗。
官网示例:
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/

// Pod 和 容器 的资源请求和限制:
spec.containers[].resources.requests.cpu		// 定义创建容器时预分配的 CPU 资源
spec.containers[].resources.requests.memory		// 定义创建容器时预分配的内存资源
spec.containers[].resources.limits.cpu			// 定义 cpu 的资源上限 
spec.containers[].resources.limits.memory		// 定义内存的资源上限

2.2 资源单位

2.2.1 CPU 资源单位

在 Kubernetes 中,CPU 的单位表示方式如下:

  • 1 CPU = 1 vCPU(或 1 核心超线程)
  • CPU 请求和限制使用 “m”(毫核)作为单位。
    • 例如:500m 表示半个 CPU,100m 表示 0.1 个 CPU,1 表示一个完整的 CPU。
  • 带小数的 CPU 也是支持的,表示容器能获得的 CPU 时间片。例如,0.25 表示该容器最多可以使用一个 CPU 的四分之一。

2.2.2 内存资源单位

内存的单位有两种表示方式:

  • 二进制单位:如 1Gi, 1Mi, 1Ki,分别为 1024Mi, 1024Ki。Kubernetes 的内存限制通常使用这些基于 2 的指数单位。
  • 十进制单位:如 1GB, 1MB 等,表示为以 10 为底的单位。

需要注意的是,在存储设备中,标示的单位(如 GB)是基于十进制,而操作系统通常使用二进制单位(如 GiB)。因此,1 GiB 的内存比 1 GB 多出大约 73MB。

参考:https://kubernetes.io/zh-cn/docs/concepts/configuration/manage-resources-containers/

2.2.3 Pod 案例

以下是两个常见的 Kubernetes Pod 配置示例,展示了如何为容器指定资源请求和限制:

2.2.3.1 示例 1:资源请求和限制配置
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"   # 最少 64Mi 内存
        cpu: "250m"      # 最少 0.25 CPU
      limits:
        memory: "128Mi"  # 最大 128Mi 内存
        cpu: "500m"      # 最大 0.5 CPU
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"   # 最少 64Mi 内存
        cpu: "250m"      # 最少 0.25 CPU
      limits:
        memory: "128Mi"  # 最大 128Mi 内存
        cpu: "500m"      # 最大 0.5 CPU
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: web
    image: nginx
    env:
    - name: WEB_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"   
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "abc123"
    resources:
      requests:
        memory: "64Mi" 
        cpu: "0.5"      
      limits:
        memory: "128Mi"   
        cpu: "1"        

分析:

  • 每个容器的 请求资源 是:0.25 CPU64Mi 内存。
  • 每个容器的 限制资源 是:0.5 CPU128Mi 内存。
  • 该 Pod 的 总请求资源0.5 CPU128Mi 内存。
  • 该 Pod 的 总限制资源1 CPU256Mi 内存。
2.2.3.2 案例 1:不同容器资源配置
vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: web
    image: nginx
    env:
    - name: WEB_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"   # 最少 64Mi 内存
        cpu: "250m"      # 最少 0.25 CPU
      limits:
        memory: "128Mi"  # 最大 128Mi 内存
        cpu: "500m"      # 最大 0.5 CPU
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "abc123"
    resources:
      requests:
        memory: "512Mi"  # 最少 512Mi 内存,64Mi
        cpu: "0.5"       # 最少 0.5 CPU
      limits:
        memory: "1Gi"    # 最大 1Gi 内存,128Mi
        cpu: "1"         # 最大 1 CPU

分析:

  • Web 容器:
    • 请求:0.25 CPU64Mi 内存
    • 限制:0.5 CPU128Mi 内存
  • DB 容器:
    • 请求:0.5 CPU512Mi 内存
    • 限制:1 CPU1Gi 内存

该 Pod 的总请求资源为:

  • 0.75 CPU576Mi 内存

该 Pod 的总限制资源为:

  • 1.5 CPU1.128Gi 内存

2.2.4 调度与资源分配

当 Kubernetes 调度 Pod 时,它会根据容器的资源请求(requests)来选择适合的节点。调度器会尝试确保节点有足够的资源来满足这些请求。如果节点上的资源不够,调度器会将 Pod 调度到其他可用节点。

2.2.4.1 Pod 调度实例:
kubectl apply -f pod2.yaml
kubectl describe pod frontend

查看 Pod 分配资源情况:

kubectl get pods -o wide

输出结果将显示 Pod 是否成功启动,以及它的运行状态。

查看节点资源:

kubectl describe nodes node02

此命令展示了节点的资源使用情况。你可以看到该节点上各个 Pod 的请求与限制占用了多少资源,以及节点剩余的资源。

2.2.5 CPU 与内存资源分配的实际情况

例如,在你的虚拟机中有 2 个 CPU 核心,如果你为 Pod 设置了总共 1 个 CPU 的请求和 2 个 CPU 的限制,那么该 Pod 将占用节点上 50% 的 CPU 资源。

节点资源的分配情况可以通过以下命令进行查看:

kubectl describe nodes node02

输出的资源分配详细信息:

Namespace                  Name                           CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------                  ----                           ------------  ----------  ---------------  -------------
  default                    frontend                       500m (25%)    1 (50%)     128Mi (3%)       256Mi (6%)
  kube-system                kube-flannel-ds-amd64-f4pbp    100m (5%)     100m (5%)   50Mi (1%)        50Mi (1%)

2.2.6 总结

  1. 资源请求与限制:
    • requests 是容器启动时最少需要的资源,调度器依据该值选择节点。
    • limits 是容器能够使用的最大资源值,超出该值的资源请求会被限制。
  2. 自动匹配:
    • 如果未设置 requests,Kubernetes 会自动将其设置为与 limits 相同。
  3. 资源的分配:
    • Pod 中多个容器的资源请求与限制会被加总,以便监控和调整节点的资源分配。
  4. 资源单位:
    • CPU 使用 m(毫核)表示,例如:500m 表示 0.5 个 CPU。
    • 内存 使用标准的字节单位表示,通常推荐使用基于 2 的指数单位,如 Gi, Mi 等。

2.3 健康检查:又称为探针(Probe)

探针是由 kubelet 对容器执行的定期诊断。

2.3.1 探针的三种规则

  • livenessProbe:判断容器是否正在运行。如果探测失败,则 kubelet 会杀死容器,并且容器将根据 restartPolicy 来设置 Pod 状态。如果容器不提供存活探针,则默认状态为 Success。
  • readinessProbe:判断容器是否准备好接受请求。如果探测失败,端点控制器将从与 Pod 匹配的所有 service 地址 endpoints 中剔除删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success。
  • startupProbe(1.17 版本增加):判断容器内的应用程序是否已启动,主要针对于不能确定具体启动时间的应用。如果配置了 startupProbe 探测,则在 startupProbe 状态为 Success 之前,其他所有探针都处于无效状态,直到它成功后其他探针才起作用。如果 startupProbe 失败,kubelet 将杀死容器,容器将根据 restartPolicy 来重启。如果容器没有配置 startupProbe,则默认状态为 Success。

注:以上规则可以同时定义。在 readinessProbe 检测成功之前,Pod 的 running 状态是不会变成 ready 状态的。

2.3.2 Probe 支持三种检查方法

  • exec:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
  • tcpSocket:对指定端口上的容器的 IP 地址进行 TCP 检查(三次握手)。如果端口打开,则诊断被认为是成功的。
  • httpGet:对指定的端口和路径上的容器的 IP 地址执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

2.3.3 每次探测都将获得以下三种结果之一

  • 成功:容器通过了诊断。
  • 失败:容器未通过诊断。
  • 未知:诊断失败,因此不会采取任何行动。

官网示例:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

2.3.4 实例 1:exec 方式

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/busybox
    args:  
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 60
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      failureThreshold: 1 
      initialDelaySeconds: 5
      periodSeconds: 5

initialDelaySeconds:指定 kubelet 在执行第一次探测前应该等待 5 秒,即第一次探测是在容器启动后的第 6 秒才开始执行。默认是 0 秒,最小值是 0。

periodSeconds:指定了 kubelet 应该每 5 秒执行一次存活探测。默认是 10 秒。最小值是 1。

failureThreshold:当探测失败时,Kubernetes 将在放弃之前重试的次数。存活探测情况下的放弃就意味着重新启动容器。就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。

timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。(在 Kubernetes 1.20 版本之前,exec 探针会忽略 timeoutSeconds 探针会无限期地持续运行,甚至可能超过所配置的限期,直到返回结果为止。)

可以看到 Pod 中只有一个容器。kubelet 在执行第一次探测前需要等待 5 秒,kubelet 会每 5 秒执行一次存活探测。kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。当到达第 31 秒时,这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。

2.3.4.1 案例 1
vim exec.yaml

apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","touch /tmp/live ; sleep 30; rm -rf /tmp/live; sleep 3600"]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/live"]
      initialDelaySeconds: 1
      periodSeconds: 3
kubectl create -f exec.yaml
kubectl describe pods liveness-exec

在这里插入图片描述
可以看到因为删除了 /tmp/live 而导致了 livenessProbe 的失效,从而触发了重启策略。
在这里插入图片描述

2.3.5 示例 2:httpGet 方式

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

在这个配置文件中,可以看到 Pod 也只有一个容器。initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。

任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。

2.3.5.1 案例 2:httpGet 方式
vim httpget.yaml

apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget
  namespace: default
spec:
  containers:
  - name: liveness-httpget-container
    image: soscscs/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      httpGet:
        port: http
        path: /index.html # 对/index.html进行http访问
      initialDelaySeconds: 1 # 经过1秒的初始化延迟后开始进行存活监测
      periodSeconds: 3 # 每3秒进行一次
      timeoutSeconds: 10 # 10s没有回应算作超时
kubectl create -f httpget.yaml
kubectl exec -it liveness-httpget -- rm -rf /usr/share/nginx/html/index.html # 删除pod内的访问的页面进行测试
kubectl get pods

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3.6 示例 3:tcpSocket 方式

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

这个例子同时使用 readinessProbe 和 livenessProbe 探测。kubelet 会在容器启动 5 秒后发送第一个 readinessProbe 探测。这会尝试连接 goproxy 容器的 8080 端口。如果探测成功,kubelet 将继续每隔 10 秒运行一次检测。除了 readinessProbe 探测,这个配置包括了一个 livenessProbe 探测。kubelet 会在容器启动 15 秒后进行第一次 livenessProbe 探测。就像 readinessProbe 探测一样,会尝试连接 goproxy 容器的 8080 端口。如果 livenessProbe 探测失败,这个容器会被重新启动。

2.3.6.1 案例 3:tcpSocket
vim tcpsocket.yaml

apiVersion: v1
kind: Pod
metadata:
  name: probe-tcp
spec:
  containers:
  - name: nginx
    image: soscscs/myapp:v1
    livenessProbe:
      initialDelaySeconds: 5
      timeoutSeconds: 1
      tcpSocket:
        port: 8080 # 此处访问容器的8080端口会从开始就报错,因为soscscs/myapp:v1镜像仅开放了80端口
      periodSeconds: 10
      failureThreshold: 2
kubectl create -f tcpsocket.yaml
kubectl exec -it probe-tcp  -- netstat -natp

在这里插入图片描述
在这里插入图片描述

2.3.7 案例 4:就绪检测

vim readiness-httpget.yaml

apiVersion: v1
kind: Pod
metadata:
  name: readiness-httpget
  namespace: default
spec:
  containers:
  - name: readiness-httpget-container
    image: soscscs/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: 80
        path: /index1.html # 因为这个网页是index1,所以就绪检测是一定会失败的,正常pod里就没有index1
      initialDelaySeconds: 1
      periodSeconds: 3
    livenessProbe:
      httpGet:
        port: http
        path: /index.html
      initialDelaySeconds: 1
      periodSeconds: 3
      timeoutSeconds: 10
kubectl create -f readiness-httpget.yaml
kubectl exec -it readiness-httpget -- sh -c 'echo 123 > /usr/share/nginx/html/index1.html'
kubectl exec -it readiness-httpget sh
cd /usr/share/nginx/html/
ls
50x.html    index.html
echo 123 > index1.html 
exit
kubectl get pods 

在这里插入图片描述
在这里插入图片描述

2.3.8 案例 5:就绪检测 2

vim readiness-myapp.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myapp1
  labels:
     app: myapp
spec:
  containers:
  - name: myapp
    image: soscscs/myapp:v1
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 10 
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp2
  labels:
     app: myapp
spec:
  containers:
  - name: myapp
    image: soscscs/myapp:v1
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 10 
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp3
  labels:
     app: myapp
spec:
  containers:
  - name: myapp
    image: soscscs/myapp:v1
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 10 
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  type: ClusterIP
  ports:
  - name: http
    port: 80
    targetPort: 80
kubectl create -f readiness-myapp.yaml
kubectl get pods,svc,endpoints -o wide

在这里插入图片描述

2.3.9 案例6:启动、退出动作

vim post.yaml

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: soscscs/myapp:v1
    lifecycle:   #此为关键字段,用来定义容器启停时的钩子postStart、preStop
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler >> /var/log/nginx/message"]      
      preStop:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the poststop handler >> /var/log/nginx/message"]
    volumeMounts:
    - name: message-log
      mountPath: /var/log/nginx/
      readOnly: false
  initContainers:
  - name: init-myservice
    image: soscscs/myapp:v1
    command: ["/bin/sh", "-c", "echo 'Hello initContainers'   >> /var/log/nginx/message"]
    volumeMounts:
    - name: message-log
      mountPath: /var/log/nginx/
      readOnly: false
  volumes:
  - name: message-log
    hostPath:
      path: /data/volumes/nginx/log/
      type: DirectoryOrCreate
kubectl create -f post.yaml

kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE    IP            NODE     NOMINATED NODE   READINESS GATES
lifecycle-demo   1/1     Running   0          2m8s   10.244.2.28   node02   <none>           <none>

kubectl exec -it lifecycle-demo -- cat /var/log/nginx/message
Hello initContainers
Hello from the postStart handler

在这里插入图片描述

//在 node02 节点上查看
[root@node02 ~]# cd /data/volumes/nginx/log/
[root@node02 log]# ls
access.log  error.log  message
[root@node02 log]# cat message 
Hello initContainers
Hello from the postStart handler
#由上可知,init Container先执行,然后当一个主容器启动后,Kubernetes 将立即发送 postStart 事件。

//删除 pod 后,再在 node02 节点上查看
kubectl delete pod lifecycle-demo

在这里插入图片描述
由上可知,当在容器被终结之前, Kubernetes 将发送一个 preStop 事件。

2.3.9.1 拓展:hostPath的类型

在 Kubernetes 中,hostPath 卷的 type 字段用于指定主机路径的类型,确保挂载行为符合预期。DirectoryOrCreate 是其中一种常见类型,它的含义是:

  • 如果主机上的 path(即 /data/volumes/nginx/log/)不存在,Kubernetes 会自动在主机上创建该目录(权限为 0755,属主为 root)。
  • 如果该目录已存在,则直接使用现有目录。

除了 DirectoryOrCreatehostPath 还有其他常用的 type 取值,分别适用于不同场景:

  1. Directory
    • 要求主机上必须存在指定路径的目录,否则 Pod 会启动失败(报错 Error: failed to create directory)。
    • 适用于明确知道目录已存在的场景,避免意外创建目录。
  2. FileOrCreate
    • 如果主机上的 path 是文件且不存在,Kubernetes 会自动创建该文件(权限为 0644,属主为 root)。
    • 如果文件已存在,则直接使用现有文件。
    • 注意:只能用于文件,不能用于目录。
  3. File
    • 要求主机上必须存在指定路径的文件,否则 Pod 启动失败。
    • 适用于依赖特定文件存在的场景(如配置文件)。
  4. Socket
    • 要求主机上必须存在指定路径的 Unix 套接字文件,否则 Pod 启动失败。
    • 适用于需要访问主机上的套接字(如 Docker 守护进程套接字 /var/run/docker.sock)的场景。
  5. CharDevice
    • 要求主机上必须存在指定路径的字符设备文件,否则 Pod 启动失败。
    • 适用于需要访问主机字符设备(如 /dev/null/dev/tty)的场景。
  6. BlockDevice
    • 要求主机上必须存在指定路径的块设备文件,否则 Pod 启动失败。
    • 适用于需要直接访问主机块设备(如磁盘分区 /dev/sda1)的场景。
  7. “”(空字符串,默认值)
    • 不做类型校验,无论主机上的路径是文件、目录还是不存在,都会尝试挂载。
    • 风险:如果路径不存在或类型不符(如期望目录但实际是文件),可能导致容器启动异常,建议明确指定类型。

type 字段的作用是通过类型校验确保主机路径符合预期,避免因路径不存在或类型错误导致的容器异常。实际使用时,应根据需求选择最合适的类型(如临时目录用 DirectoryOrCreate,固定配置文件用 File)。

三、扩展

3.1 pod的状态

状态名称描述典型场景
PendingPod 已被 Kubernetes 系统接受,但尚未创建所有容器,或正在调度中1. 等待节点资源(CPU/内存不足)
2. 镜像拉取中
3. 初始化容器未完成
RunningPod 已绑定到节点,所有容器已创建,至少一个容器处于运行状态1. 主容器正常运行
2. 初始化容器已完成,主容器启动成功
SucceededPod 中所有容器均已成功终止,且不会重启1. 一次性任务完成(如 Job 类型的 Pod)
2. 所有容器正常退出(退出码 0)
FailedPod 中所有容器均已终止,且至少一个容器因失败终止(非 0 退出码)1. 容器崩溃(代码错误、资源耗尽)
2. 健康检查失败导致容器被终止
UnknownKubernetes 无法获取 Pod 状态(通常因节点通信故障)1. 节点与 API Server 断开连接
2. 节点故障或网络问题
CrashLoopBackOff容器反复崩溃并重启,Kubernetes 会逐步延长重启间隔(退避策略)1. 应用启动后立即崩溃(依赖缺失、配置错误)
2. 存活探针持续失败
ImagePullBackOff镜像拉取失败,Kubernetes 会重试拉取(逐步延长间隔)1. 镜像地址错误
2. 镜像仓库认证失败
3. 网络问题导致拉取超时
ErrImagePull镜像拉取直接失败(未进入退避重试阶段)1. 镜像不存在
2. 权限不足无法访问镜像仓库
ContainerCreating容器正在创建中(如拉取镜像、配置网络等)1. 首次启动容器
2. 容器重启过程中
TerminatingPod 正在被终止(如执行 kubectl delete pod 后)1. 用户手动删除 Pod
2. 控制器(如 Deployment)缩容或更新
NodeLostPod 所在节点已失联,Kubernetes 无法确认其状态1. 节点宕机
2. 节点网络彻底中断

补充说明:

  • 状态通常分为主状态(如 Pending、Running)和状态原因(如 CrashLoopBackOff、ImagePullBackOff),后者是主状态的补充说明。
  • 可通过 kubectl get pods 查看 Pod 状态,通过 kubectl describe pod <pod-name> 查看详细事件和状态转换原因。
  • 状态流转示例:PendingContainerCreatingRunningTerminating(正常终止);或 PendingImagePullBackOff(镜像拉取失败)。

总结

本文通过系统梳理 Pod 的基础理论与实操细节,覆盖了从概念定义到资源限制、健康检查的全流程知识,并结合案例验证了核心配置的实际效果。掌握这些内容后,你不仅能独立完成 Pod 的创建与运维,更能理解 Kubernetes 对容器进行编排管理的底层逻辑。
最后希望大家,夯实基础、继续努力、聚沙成塔、与君共勉之!!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐