Kubernetes:Pod 的生命周期与管理实战

大家好!在云原生的世界里,Kubernetes (K8s) 无疑是容器编排领域的王者。作为 K8s 中最核心、最基础的资源对象,Pod 的概念理解和管理能力是每一位 K8s 使用者的必备技能。今天,我们就来一起深入探讨 Pod 的奥秘,并通过实战演练掌握其创建、查看、交互和删除的全过程。

准备工作:规整你的资源清单

在 K8s 中,我们通常使用 YAML 文件来定义(声明)我们期望的资源状态,这些文件被称为“资源清单”(Manifests)。为了更好地管理这些文件,养成良好的习惯非常重要。

# 推荐创建一个专门存放 K8s 资源清单的目录结构
[root@master ~]# mkdir -pv /data/k8s-manifests/pods
[root@master ~]# cd /data/k8s-manifests/pods
  • 说明:这里使用了 /data/k8s-manifests/ 作为示例,你可以根据自己的服务器规划选择合适的路径。清晰的目录结构有助于项目的维护和协作。

揭秘资源清单:YAML 文件的四大核心要素

一个标准的 K8s 资源清单(以 Pod 为例)通常包含以下几个关键字段:

  1. apiVersion: 定义了创建该对象所使用的 Kubernetes API 的版本。不同的资源类型(Kind)可能属于不同的 API Group 和 Version。例如,核心资源如 Pod 通常使用 v1
  2. kind: 指定了要创建的资源类型。比如 Pod, Deployment, Service, Namespace 等。
  3. metadata: 包含了资源的元数据,用于唯一标识和组织资源。常见的子字段有:
    • name: 资源的名称,在同一个 Namespace 下必须唯一。
    • namespace (可选): 资源所属的命名空间。如果省略,则默认为 default 命名空间。
    • labels (可选): 键值对标签,用于筛选和组织资源。
    • annotations (可选): 键值对注解,用于存储非识别性的元数据,通常给工具或系统使用。
  4. spec: 这部分是资源的核心,定义了你期望该资源达到的状态。对于 Pod 来说,spec 中最重要的就是定义其包含的容器(containers)列表,以及其他运行策略(如调度约束、存储卷、网络设置等)。

还有一个重要的部分,虽然不由我们编写,但需要了解:

  1. status: 描述了资源的实际运行状态。这部分由 Kubernetes 系统(主要是 Kubelet 和 Controller Manager)动态维护和更新,我们通常只读不写。

什么是 Pod?K8s 的原子调度单元

你可能听说过 Docker 容器,但在 Kubernetes 的世界里,Pod 是最小的可部署和可调度的计算单元

可以将 Pod 想象成一个“豌豆荚”(Pod 的中文直译),而容器则是里面的“豌豆”。一个 Pod 可以包含一个或多个紧密关联的容器。

关键特性

  • 共享网络和存储:同一个 Pod 内的所有容器共享相同的网络命名空间(即同一个 IP 地址和端口空间)和存储卷(Volumes)。它们可以通过 localhost 相互通信。
  • 原子性:Pod 内的容器总是被一起调度到同一个节点(Node)上,并且“同生共死”。它们作为一个整体单元进行管理。

虽然可以直接创建 Pod,但在生产环境中,我们通常不直接管理 Pod,而是通过更高级别的控制器(如 Deployment, StatefulSet, DaemonSet)来管理 Pod 的生命周期,以实现应用的弹性伸缩、滚动更新和自愈能力。不过,理解 Pod 是掌握这些高级概念的基础。

实战一:创建并管理一个单容器 Pod

让我们来创建一个简单的 Pod,它只运行一个 Web 应用容器。

1. 编写 Pod 资源清单 (01-my-app-pod.yaml)

# 指定 API 版本
apiVersion: v1
# 指定资源类型为 Pod
kind: Pod
# 元数据信息
metadata:
  # Pod 的名称
  name: my-app-v1
  # (可选) 添加标签,方便后续管理
  labels:
    app: my-app
    version: v1
# 定义期望状态 (Specification)
spec:
  # (注意) 显式指定 nodeName 通常仅用于测试或特殊场景
  # 在生产中,应让 K8s 调度器自动选择合适的节点
  # nodeName: worker232 
  
  # 定义 Pod 内的容器列表
  containers:
    # 第一个(也是唯一一个)容器
    - name: my-app-container # 容器名称,在 Pod 内唯一
      # 指定容器镜像
      image: registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 # 替换为你实际使用的镜像
      # (可选) 定义容器端口,仅作信息声明,不影响网络
      ports:
        - containerPort: 80 
  • 注意
    • 上面 YAML 中的 nodeName: worker232 字段会强制将 Pod 调度到名为 worker232 的节点上。在生产环境中,强烈建议不要使用 nodeName,让 Kubernetes 调度器根据资源需求、亲和性/反亲和性规则等自动选择最佳节点。这里保留它是为了演示,但在实际应用中请注释掉或删除它。
    • metadata.labels 是非常重要的,后续会用到它来选择和管理 Pod。
    • spec.containers[].ports 字段主要是信息性的,方便用户和其他工具了解容器暴露的端口。它并不直接影响容器的网络访问,实际的网络策略由 Service 或 Ingress 等资源控制。

2. 创建 Pod 资源

我们推荐使用 kubectl apply 命令来创建或更新资源。

[root@master pods]# kubectl apply -f 01-my-app-pod.yaml
pod/my-app-v1 created

# 再次执行 apply,如果资源未变化,则提示 unchanged,不会报错
[root@master pods]# kubectl apply -f 01-my-app-pod.yaml
pod/my-app-v1 unchanged 

kubectl apply vs kubectl create

  • 相同点: 如果资源不存在,两者都能创建资源。
  • 不同点:
    • create: 如果资源已存在,执行会报错 AlreadyExists。它更像是一次性的创建动作。
    • apply: 如果资源已存在,它会尝试将现有资源的状态更新到 YAML 文件中定义的状态。如果无变化,则提示 unchangedapply声明式的,支持幂等性操作,意味着你可以安全地重复执行同一个 apply 命令。
  • 生产实践: 强烈推荐始终使用 kubectl apply 来管理 K8s 资源。这使得 CI/CD 流程更简单、更健壮,无论是首次部署还是后续更新,都使用同一个命令。

3. 查看 Pod 状态

# 查看当前 namespace 下的所有 Pod 列表
[root@master pods]# kubectl get pods

NAME        READY   STATUS    RESTARTS   AGE
my-app-v1   1/1     Running   0          1m

# 查看更详细的信息,包括 Pod IP 和所在 Node
[root@master pods]# kubectl get pods -o wide

NAME        READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
my-app-v1   1/1     Running   0          90s   10.100.1.5   worker232   <none>           <none> 

字段解读:

  • NAME: Pod 的名称。
  • READY: 就绪的容器数量 / Pod 内总容器数量1/1 表示 Pod 内有 1 个容器,且这 1 个容器已经准备好对外提供服务(通过了 Readiness Probe,如果定义了的话)。
  • STATUS: Pod 的当前生命周期阶段。常见状态有:
    • Pending: Pod 已被 K8s 接受,但一个或多个容器尚未创建成功。通常在拉取镜像或等待调度。
    • Running: Pod 已绑定到节点,所有容器都已创建。至少有一个容器仍在运行,或者正在启动/重启。
    • Succeeded: Pod 中的所有容器都已成功终止,并且不会再重启。通常用于 Job 类型的 Pod。
    • Failed: Pod 中的所有容器都已终止,但至少有一个容器是以非零状态(即错误)退出的。
    • Unknown: 通常是由于无法与 Pod 所在的节点通信导致无法获取 Pod 状态。
  • RESTARTS: Pod 内所有容器总重启次数。注意,这里的重启是指容器进程异常退出后,Kubelet 尝试重新启动它。这与 docker restart 不同。高重启次数通常意味着应用存在问题。
  • AGE: Pod 从创建到现在所经过的时间。
  • IP: Pod 的 IP 地址。这个 IP 地址是在集群内部可达的。
  • NODE: Pod 当前运行在哪个工作节点上。

获取更深入的 Pod 信息

# 查看 Pod 的详细信息,包括事件、配置、状态等,非常适合排错
[root@master pods]# kubectl describe pod my-app-v1 

# 查看 Pod 内特定容器的日志
[root@master pods]# kubectl logs my-app-v1 
# 如果 Pod 内有多个容器,需要指定容器名:
# kubectl logs my-app-v1 -c my-app-container 

4. 删除 Pod 资源

# 通过文件名删除
[root@master pods]# kubectl delete -f 01-my-app-pod.yaml
pod "my-app-v1" deleted

# 或者通过 Pod 名称直接删除
# kubectl delete pod my-app-v1

# 确认 Pod 已被删除
[root@master pods]# kubectl get pods
No resources found in default namespace.

实战二:部署一个多容器 Pod (Sidecar 模式)

一个 Pod 内运行多个容器是非常常见的模式,最典型的应用场景就是 Sidecar(边车)模式。例如,一个主应用容器,旁边跟一个负责日志收集、监控数据暴露、网络代理等的辅助容器。这些容器紧密协作,共享生命周期和网络环境。

1. 环境准备 (可选,仅作示例)

假设我们需要一个主应用 xiuxian:v2 和一个辅助的 alpine 容器。确保这些镜像在你的集群节点可以访问的镜像仓库中。如果使用私有仓库(如 Harbor),节点需要配置访问权限。

  • (原始笔记中的镜像准备步骤是特定环境的操作,这里假设镜像已存在于 harbor.oldboyedu.com 或其他你的集群可访问的仓库中)

2. 编写多容器 Pod 资源清单 (02-my-app-with-sidecar.yaml)

apiVersion: v1
kind: Pod
metadata:
  name: my-app-v2
  labels:
    app: my-app
    version: v2
spec:
  # 同样,生产环境不建议指定 nodeName
  # nodeName: worker232
  containers:
    # 第一个容器:主应用
    - name: my-app-main # 主容器名称
      image: harbor.oldboyedu.com/oldboyedu-web/xiuxian:v2 # 主应用镜像
      ports:
        - containerPort: 80
        
    # 第二个容器:辅助容器 (Sidecar)
    - name: my-sidecar # Sidecar 容器名称
      image: harbor.oldboyedu.com/oldboyedu-linux/alpine:3.20.2 # 使用 Alpine 作为示例
      # --- 让 Sidecar 容器保持运行的技巧 ---
      # 方式一:使用标准输入保持阻塞 (如原始笔记)
      # stdin: true
      # tty: true # 通常 stdin 和 tty 一起使用
      # 方式二:执行一个永远不会退出的命令 (更常用)
      command: ["sh", "-c", "sleep infinity"] 
  • 关键点spec.containers 是一个列表,可以定义多个容器。
  • Sidecar 存活技巧:很多基础镜像(如 Alpine, BusyBox)默认启动后如果没有前台任务就会立即退出。为了让 Sidecar 容器持续运行,我们需要一些技巧:
    • 方法一 (stdin: true, tty: true): 分配一个伪终端并保持标准输入打开,这会让容器的入口进程(通常是 shell)保持在前台运行状态。
    • 方法二 (command: ["sleep", "infinity"] 或类似命令)这是更推荐、更通用的方法。覆盖容器的默认启动命令,让它执行一个永远不会结束的命令(如 sleep infinity),从而保持容器运行。

3. 创建并查看多容器 Pod

[root@master pods]# kubectl apply -f 02-my-app-with-sidecar.yaml
pod/my-app-v2 created

[root@master pods]# kubectl get pods -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
my-app-v2   2/2     Running   0          30s   10.100.1.8   worker232   <none>           <none> 

注意 READY 字段变成了 2/2,表示 Pod 内的两个容器都已成功启动并处于就绪状态。

实战三:与 Pod 内的容器交互 (kubectl exec)

有时我们需要进入 Pod 内的某个容器执行命令,进行调试或检查。kubectl exec 就是为此而生的。

1. 在单容器 Pod 中执行命令

# 在 my-app-v1 Pod 中执行 ifconfig 命令
[root@master pods]# kubectl exec my-app-v1 -- ifconfig 
# '--' 用于分隔 kubectl 命令和要在容器内执行的命令

eth0      Link encap:Ethernet  HWaddr 0E:DD:73:C9:60:A7  
          inet addr:10.100.1.9  Bcast:10.100.1.255  Mask:255.255.255.0
          ...

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          ...

2. 在多容器 Pod 中执行命令

当 Pod 内有多个容器时,你需要指定要操作哪个容器。

# 不指定容器名,默认在 Pod 的第一个容器 (my-app-main) 中执行
[root@master pods]# kubectl exec my-app-v2 -- ifconfig
Defaulted container "my-app-main" out of: my-app-main, my-sidecar # K8s 提示了它选择的默认容器
eth0      Link encap:Ethernet  HWaddr 2A:A5:6C:D7:B4:E2  
          inet addr:10.100.1.8  Bcast:10.100.1.255  Mask:255.255.255.0
          ...

# 使用 -c 参数显式指定在 my-app-main 容器中执行
[root@master pods]# kubectl exec my-app-v2 -c my-app-main -- ifconfig 
eth0      Link encap:Ethernet  HWaddr 2A:A5:6C:D7:B4:E2  
          inet addr:10.100.1.8  Bcast:10.100.1.255  Mask:255.255.255.0
          ...

# 使用 -c 参数显式指定在 my-sidecar 容器中执行
[root@master pods]# kubectl exec my-app-v2 -c my-sidecar -- ifconfig 
eth0      Link encap:Ethernet  HWaddr 2A:A5:6C:D7:B4:E2  
          inet addr:10.100.1.8  Bcast:10.100.1.255  Mask:255.255.255.0
          ... 

重要发现: 观察上面 my-app-mainmy-sidecar 容器中 ifconfig 的输出,你会发现它们的IP 地址是完全相同的 (10.100.1.8)!这验证了我们之前提到的:同一个 Pod 内的所有容器共享同一个网络命名空间。它们可以使用 localhost 互相访问对方监听的端口。

3. 获取交互式 Shell (进入容器)

如果你想在容器内部执行多条命令,可以使用 -it 参数获取一个交互式的 TTY。

# 进入 my-app-v2 Pod 的 my-app-main 容器,启动 sh shell
[root@master pods]# kubectl exec -it my-app-v2 -c my-app-main -- sh
/ # hostname  # 在容器内执行命令
my-app-v2
/ # ps -ef     # 查看容器内的进程
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
   ...
/ # exit       # 退出容器 shell
[root@master pods]# 

# 进入 my-app-v2 Pod 的 my-sidecar 容器
[root@master pods]# kubectl exec -it my-app-v2 -c my-sidecar -- sh
/ # hostname
my-app-v2
/ # ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 sh -c sleep infinity # 看到我们定义的 sleep 命令
    6 root      0:00 sh
   12 root      0:00 ps -ef
/ # exit
[root@master pods]# 
  • -i (stdin): 保持标准输入打开。
  • -t (tty): 分配一个伪终端。
  • -- sh (或 /bin/bash): 指定要在容器内启动的 shell 程序。

生产环境核心建议 (非常重要!)

  1. 避免直接管理 Pod: 在生产环境中,几乎从不直接创建独立的 Pod。因为它们缺乏自愈、扩展和更新能力。一旦 Pod 所在的节点故障或 Pod 自身崩溃,它不会自动恢复。
  2. 拥抱控制器: 使用 Deployment (用于无状态应用)、StatefulSet (用于有状态应用)、DaemonSet (用于确保每个节点运行一个 Pod 副本) 等控制器来管理你的应用。这些控制器会维护你期望的 Pod 副本数量,处理滚动更新、回滚,并确保 Pod 在节点故障时能在其他节点重新调度。
  3. 定义资源请求和限制 (Resource Requests & Limits): 在 Pod 的 spec.containers[].resources 字段中,为每个容器设置 requests (K8s 调度器需要保证的最小资源) 和 limits (容器能使用的最大资源)。这对于保证集群稳定性和资源公平使用至关重要。
    resources:
      requests:
        memory: "64Mi" # 请求 64 Mebibytes 内存
        cpu: "250m"    # 请求 0.25 CPU 核 (millicores)
      limits:
        memory: "128Mi" # 限制最多使用 128 Mebibytes 内存
        cpu: "500m"    # 限制最多使用 0.5 CPU 核
    
  4. 配置存活探针和就绪探针 (Liveness & Readiness Probes):
    • Liveness Probe: Kubelet 使用它来检测容器是否还在运行。如果探测失败,Kubelet 会杀死该容器并根据 Pod 的重启策略尝试重启它。
    • Readiness Probe: Kubelet 使用它来检测容器是否准备好处理请求。如果探测失败,该 Pod 的 IP 地址会从关联的 Service 的 Endpoints 列表中移除,使其暂时不接收流量,直到探测成功。
    livenessProbe:
      httpGet:
        path: /healthz # 应用提供的健康检查接口
        port: 80
      initialDelaySeconds: 5 # Pod 启动后 5 秒开始探测
      periodSeconds: 10     # 每 10 秒探测一次
    readinessProbe:
      httpGet:
        path: /ready   # 应用提供的就绪检查接口
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 10
    
  5. 利用标签 (Labels) 和选择器 (Selectors): 善用 metadata.labels 来标记你的 Pod,然后通过 Service 或其他控制器使用 Label Selectors 来关联和管理这些 Pod。

总结

我们今天从 Pod 的基本概念出发,学习了如何编写 Pod 的 YAML 资源清单,并通过 kubectl applykubectl getkubectl describekubectl logskubectl execkubectl delete 等命令,实战演练了单容器和多容器 Pod 的创建、查看、交互和删除。我们还特别强调了在生产环境中使用控制器、资源管理和健康检查的重要性。

掌握 Pod 是你深入 Kubernetes 世界的第一步。希望这篇结合了实战和生产建议的文章能帮助你更好地理解和运用 Pod,为后续学习更高级的 K8s 概念打下坚实的基础。

如果你觉得这篇文章对你有帮助,欢迎点赞、分享,并在评论区留下你的想法和问题!下次我们将继续探索 Kubernetes 的其他核心组件,敬请期待!

posted on 2025-04-21 16:39  Leo-Yide  阅读(191)  评论(0)    收藏  举报