Pod 的本质
Pod 是 Kubernetes 中最小的可部署的计算单元。它本质上是一个不可变的实体。一旦被创建,其核心定义(如镜像、命令、环境变量、卷等)就是不可变的。基本上(非全部)对 Pod 定义的修改都需要通过替换来实现,即创建一个新的 Pod 来替代旧的 Pod。
Pod 的创建流程
Pod 的创建是一个由多个组件协同完成的过程
提交请求:
kubectl
等客户端工具将 Pod 的配置信息(YAML/JSON)发送给 Kubernetes API Server。验证与存储:
API Server 会验证请求的合法性和权限,然后将 Pod 的配置信息作为一条期望状态 (Desired State) 的记录写入到集群的持久化存储中心(通常是 etcd)。大部分对 Pod 定义的修改都需要通过替换来实现,即创建一个新的 Pod 来替代旧的 Pod。调度 (Scheduling):
Scheduler 持续监听 API Server,发现有一个新 Pod 被创建,且它的nodeName
字段为空,这意味着它需要被调度。- 调度器根据 Pod 的资源请求、亲和性、污点容忍度等策略,从集群中选择一个最合适的 Node。
- 选择完成后,调度器将绑定信息(将这个 Pod 绑定到某个 Node)写回 API Server。
- API Server 再次更新 etcd 中的数据,将 Pod 的
nodeName
字段填上。
执行与启动:
目标 Node 上的 kubelet 组件监听到有新的 Pod 被调度到它所在的节点。- kubelet 从 API Server 获取 Pod 的详细配置。
- kubelet 根据配置,调用本地的容器运行时(如 containerd, CRI-O),下载所需镜像,并启动容器。
- kubelet 还会根据配置执行容器探针(Liveness, Readiness, Startup)进行健康检查。
状态上报:
kubelet 将 Pod 和容器的实际状态 (Actual State)(如Running
,Pending
,Failed
)持续报告给 API Server,最终写入 etcd。
至此,一个 Pod 完成创建并运行。
Pod 的更新机制
正如 Pod 的本质是不可变的一样,Pod 的更新操作本质上都是替换,即“删除旧 Pod,创建新 Pod”。
在生产环境中,我们通常通过 Deployment
等控制器来管理 Pod 的更新,实现无缝的滚动更新 (Rolling Update)。
滚动更新流程
触发更新:
用户修改 Deployment 的配置(如镜像版本)并提交。API Server 将新的 Deployment 期望状态写入 etcd。创建新 ReplicaSet:
Deployment 控制器注意到期望状态的变化。它会创建一个新的 ReplicaSet,其 Pod 模板指向新的配置(如新镜像)。逐步替换:
- 新的 ReplicaSet 的副本数从 0 开始逐步增加(例如,先增加到 1)。
- 同时,旧的 ReplicaSet 的副本数从 N 开始逐步减少(例如,先减少到 N-1)。
- Kubernetes 会保证在更新过程中,可用的 Pod 总数始终不低于期望副本数的一定比例(默认 75%,可通过
maxUnavailable
配置),以确保服务不中断。
完成与回滚:
- 过程持续进行:
(旧:N, 新:0)
->(旧:N-1, 新:1)
-> … ->(旧:0, 新:N)
。 - 旧的 ReplicaSet 会被保留(但其副本数缩容为 0),以便于需要时快速回滚。
- 过程持续进行:
Pod 的部分修改
虽然 Pod 的核心定义不可变,但部分元数据 (Metadata) 或状态字段是可以直接更新的:
字段类型 | 可更新字段示例 | 说明 |
---|---|---|
元数据 (Metadata) | labels |
动态添加/修改标签,不会触发 Pod 重启,但可能影响 Service 端点选择。 |
annotations |
动态更新注解,常用于工具(监控、链路追踪)动态注入配置信息。 | |
Spec (部分字段) | spec.activeDeadlineSeconds |
更新 Pod 的活动截止时间。 |
spec.tolerations (不推荐) |
理论上可添加容忍度,但行为不确定,因为调度已完成。 | |
Status | 所有状态字段 (如 phase , conditions , podIP ) |
由 Kubernetes 系统组件(kubelet)自动更新,反映 Pod 的实际状态。用户不能手动修改。 |
不可变的字段:containers
[](镜像、命令、端口等)、volumes
、initContainers
[] 以及调度相关配置。
Init 容器
Init 容器是一种特殊的Pod容器,在应用容器启动之前运行,用于执行初始化任务。
特点
- Init 容器会按顺序逐个运行。每个都必须成功完成(退出码为 0),下一个才能开始。
- 在所有 Init 容器成功完成之前,Pod 的
status.initContainerStatuses
字段会显示状态,并且应用容器不会启动。 - 拥有自己的镜像,通常包含应用镜像中不存在的工具(如
curl
,nslookup
,dig
)。 - 与应用容器共享 Volume,可用于传递配置或数据。
示例与操作
# myapp.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp-container
image: myapp:1.0
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;']
# 部署 Pod
kubectl apply -f myapp.yaml
# 查看状态
kubectl get pod myapp-pod
# 查看详细信息(观察 Init 容器状态)
kubectl describe pod myapp-pod
边车容器 (Sidecar Container)
- 边车容器是与主应用容器并行运行的辅助容器,旨在增强或扩展主容器的功能。
- K8S将边车容器作为一个特殊的Init容器
特点
- 与主容器同时启动、同时运行、同时终止,生命周期完全一致。
- 与主容器共享相同的网络命名空间(同一 IP)、Volume(共享文件)等。
- 为主容器提供额外能力(如日志收集、代理、监控),但主容器可能对其无感知。
官方文档勘误
官方文档错误地将边车容器配置为 initContainers
。
正确示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
# 主应用容器
- name: myapp
image: alpine:latest
command: ['sh', '-c', 'while true; do echo "$(date): Log entry" >> /opt/logs.txt; sleep 1; done']
volumeMounts:
- name: data
mountPath: /opt
# 边车容器
- name: logshipper-sidecar
image: alpine:latest
command: ['sh', '-c', 'tail -n+1 -f /opt/logs.txt'] # 持续跟踪日志
volumeMounts:
- name: data
mountPath: /opt
volumes:
- name: data
emptyDir: {}
主容器和边车容器的区分
在 Kubernetes 中,Pod 内的所有容器在技术上是平等的。“主容器”和“边车容器”的区分完全是一种基于设计和意图的逻辑概念:
- 主容器 (Main Container):承载 Pod 主要业务功能的容器。如果删除它,Pod 就失去了核心价值。
- 边车容器 (Sidecar Container):辅助或增强主容器功能的容器。删除它,主容器依然能工作,但会失去一些增强特性(如日志外发、高级网络功能)。
- 例如:日志收集器 (Fluentd)、服务网格代理 (Envoy)、监控导出器。
容器的“主”“从”角色是由其功能决定的,而不是由它们在 YAML 文件中的声明顺序决定的。 交换 containers
列表中的顺序不会改变它们的角色,但可能会影响启动时序和依赖关系。
总结
特性 | 应用容器 (Main Container) | 边车容器 (Sidecar Container) | Init 容器 (Init Container) |
---|---|---|---|
目的 | 实现 Pod 的主要业务逻辑 | 辅助、增强应用容器的功能 | 为应用容器做初始化准备 |
生命周期 | 与 Pod 生命周期一致 | 与 Pod 生命周期一致 | 在应用容器之前运行 |
运行方式 | 与其它容器并行运行 | 与主容器并行运行 | 严格顺序执行,必须成功退出 |
启动次数 | 持续运行,可能重启 | 持续运行,可能重启 | 一次性运行,完成后即终止 |
典型用例 | Web 服务器、数据库、计算任务 | 日志收集器、服务网格代理、监控导出器 | 等待依赖服务、拉取密钥/配置、数据库迁移 |
在 Job 中的行为 | 任务主体,其成功退出决定 Job 完成 | 需配合主容器退出,否则会阻塞 Job 完成 | 正常执行,不影响 Job 完成判定 |