Pod 详解
Pod 是 Kubernetes 中最小且最简单的部署单元,是 Kubernetes 集群中最基本的构建块。理解 Pod 是掌握 Kubernetes 的第一步。
什么是 Pod?
Pod 是 Kubernetes 中的最小部署单元,代表一个或多个共享存储和网络的容器组。Pod 的名称来源于"豌豆荚"(pea pod),寓意着它是一个包含多个"种子"(容器)的单元。
Pod 的本质
Pod 的共享上下文是一组 Linux 命名空间、cgroup 和其他隔离机制。在同一 Pod 内,容器共享:
- 网络命名空间:所有容器共享同一个 IP 地址和端口空间
- 存储命名空间:可以共享 Volume
- IPC 命名空间:可以通过进程间通信机制互相通信
- UTS 命名空间:共享主机名
这意味着 Pod 内的容器可以通过 localhost 互相访问,就像它们运行在同一台机器上一样。
Pod 的两种使用模式
单容器 Pod:最常见的方式,一个 Pod 运行一个容器。这种情况下,Pod 可以看作是容器的包装器,Kubernetes 管理 Pod 而不是直接管理容器。
多容器 Pod:当多个容器需要紧密协作时,可以放在同一个 Pod 中。这些容器共享资源和依赖,形成一个内聚的服务单元。常见场景包括:
- 主容器 + 日志收集 sidecar
- 主容器 + 代理 sidecar(如 Envoy)
- 主容器 + 配置更新 sidecar
重要原则:只有需要紧密协作、共享资源的容器才应该放在同一个 Pod 中。如果容器之间只是通过网络通信,它们应该分开运行在不同的 Pod 中。
Pod 生命周期
理解 Pod 的生命周期对于正确配置和管理应用至关重要。Pod 从创建到终止会经历多个阶段和状态变化。
Pod 阶段(Phase)
Pod 的 status 字段包含一个 PodStatus 对象,其中有一个 phase 字段,表示 Pod 在生命周期中所处的阶段。
| 阶段 | 说明 |
|---|---|
Pending | Pod 已被接受,但容器尚未创建。包括等待调度和拉取镜像的时间 |
Running | Pod 已绑定到节点,所有容器已创建,至少一个容器正在运行 |
Succeeded | 所有容器成功终止,不会重启 |
Failed | 所有容器终止,至少一个容器失败退出 |
Unknown | 无法获取 Pod 状态,通常是与节点通信失败 |
Pod 条件(Conditions)
Pod 有一个 PodConditions 列表,表示 Pod 是否通过了某些条件:
| 条件类型 | 说明 |
|---|---|
PodScheduled | Pod 已被调度到节点 |
PodReadyToStartContainers | Pod 的沙箱已创建,网络已配置(Beta) |
Initialized | 所有 Init 容器已完成 |
ContainersReady | 所有容器都已就绪 |
Ready | Pod 可以服务请求,应加入 Service 的负载均衡 |
DisruptionTarget | Pod 即将因干扰而终止(如抢占、驱逐) |
查看 Pod 条件:
kubectl describe pod <pod-name> | grep -A 5 Conditions
容器状态
除了 Pod 整体的阶段,Kubernetes 还跟踪每个容器的状态:
| 状态 | 说明 |
|---|---|
Waiting | 容器正在等待启动,如拉取镜像、应用 Secret |
Running | 容器正在执行,没有问题 |
Terminated | 容器已执行完成或失败退出 |
查看容器状态:
kubectl describe pod <pod-name>
# 输出中会显示每个容器的 State 字段
重启策略
Pod 的 restartPolicy 字段决定了容器退出后的行为:
| 策略 | 说明 |
|---|---|
Always | 总是重启容器(Deployment 默认) |
OnFailure | 仅在容器失败(退出码非 0)时重启 |
Never | 从不重启容器 |
重启延迟机制:
当容器失败时,kubelet 会以指数退避方式延迟重启:
- 第 1 次重启:等待 10 秒
- 第 2 次重启:等待 20 秒
- 第 3 次重启:等待 40 秒
- ...
- 最大等待:300 秒(5 分钟)
一旦容器正常运行 10 分钟,kubelet 会重置重启延迟计时器。
CrashLoopBackOff:
当容器持续崩溃时,状态会显示 CrashLoopBackOff。这是由于重启延迟机制导致的,表示 kubelet 正在等待下一次重启尝试。排查方法:
# 查看容器日志
kubectl logs <pod-name>
# 查看上一个容器的日志(如果容器已重启)
kubectl logs <pod-name> --previous
# 查看 Pod 事件
kubectl describe pod <pod-name>
探针(Probes)
探针是 kubelet 对容器进行的定期诊断,用于确定容器的健康状态。正确配置探针对于应用的可靠性至关重要。
探针类型
| 类型 | 说明 |
|---|---|
livenessProbe | 存活探针,检测容器是否存活。失败则重启容器 |
readinessProbe | 就绪探针,检测容器是否准备好接收流量。失败则从 Service 端点移除 |
startupProbe | 启动探针,检测容器是否已启动。在成功之前,其他探针不会运行 |
探测机制
每种探针都支持三种探测方式:
exec - 执行命令:
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "pg_isready -U postgres"
httpGet - HTTP 请求:
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: health-check
initialDelaySeconds: 30
periodSeconds: 10
tcpSocket - TCP 端口检查:
livenessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 15
periodSeconds: 20
grpc - gRPC 健康检查(v1.24+):
livenessProbe:
grpc:
port: 50051
service: liveness # 可选,默认使用默认服务
探针配置参数
| 参数 | 默认值 | 说明 |
|---|---|---|
initialDelaySeconds | 0 | 容器启动后首次探测的等待时间 |
periodSeconds | 10 | 探测间隔 |
timeoutSeconds | 1 | 探测超时时间 |
successThreshold | 1 | 从失败到成功需要的连续成功次数 |
failureThreshold | 3 | 从成功到失败需要的连续失败次数 |
探针配置示例
apiVersion: v1
kind: Pod
metadata:
name: app-with-probes
spec:
containers:
- name: app
image: myapp:latest
# 存活探针:检测容器是否存活
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30 # 等待应用启动
periodSeconds: 10 # 每 10 秒检查一次
timeoutSeconds: 5 # 超时时间
failureThreshold: 3 # 连续失败 3 次则重启
# 就绪探针:检测容器是否准备好接收流量
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
# 启动探针:处理慢启动应用
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 允许最多 30 次失败
periodSeconds: 10 # 每 10 秒检查一次
# 最长等待 30 * 10 = 300 秒启动
探针最佳实践
存活探针 vs 就绪探针:
- 存活探针失败:容器被重启
- 就绪探针失败:容器从 Service 端点移除,但继续运行
不要让存活探针依赖外部服务(如数据库)。如果数据库暂时不可用,存活探针失败会导致容器重启,这通常不是你想要的行为。此时应该使用就绪探针。
启动探针的使用:
对于启动时间较长的应用(如 JVM 应用),使用启动探针可以避免存活探针在应用启动期间就失败。启动探针成功后,存活探针和就绪探针才开始工作。
Pod 配置详解
镜像配置
spec:
containers:
- name: my-app
# 指定镜像
image: nginx:1.25
# 镜像拉取策略
imagePullPolicy: Always # Always/IfNotPresent/Never
# 拉取私有镜像
imagePullSecrets:
- name: my-registry-secret
imagePullPolicy 说明:
| 策略 | 说明 |
|---|---|
Always | 总是拉取镜像 |
IfNotPresent | 本地没有时才拉取(默认) |
Never | 从不拉取,只使用本地镜像 |
资源配额
资源配额是 Kubernetes 调度和资源管理的关键。正确配置资源请求和限制可以确保应用获得足够的资源,同时避免资源浪费。
spec:
containers:
- name: app
resources:
# 请求:调度时需要的最小资源
requests:
memory: "128Mi"
cpu: "100m" # 100m = 0.1 个 CPU
# 限制:容器可以使用的最大资源
limits:
memory: "256Mi"
cpu: "500m"
requests vs limits:
| 配置 | 作用时机 | 说明 |
|---|---|---|
requests | 调度时 | 决定 Pod 可以调度到哪些节点 |
limits | 运行时 | 限制容器实际使用的资源 |
CPU 资源:
1= 1 个 CPU 核心100m= 0.1 个 CPU 核心(100 毫核)- CPU 是可压缩资源,超过限制会被限流
内存资源:
- 单位:
Ki、Mi、Gi、Ti - 内存是不可压缩资源,超过限制容器会被 OOM Killed
环境变量
spec:
containers:
- name: app
env:
# 简单值
- name: APP_ENV
value: "production"
# 来自 ConfigMap
- name: CONFIG_VALUE
valueFrom:
configMapKeyRef:
name: my-config
key: config.key
# 来自 Secret
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
# 来自 Pod 字段
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# 来自容器资源
- name: MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.memory
从 ConfigMap 或 Secret 引入所有变量:
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret
启动命令
spec:
containers:
- name: my-app
image: my-app:latest
# 覆盖 ENTRYPOINT
command: ["/bin/sh", "-c"]
# 覆盖 CMD
args:
- "echo Starting app && nginx -g 'daemon off;'"
# 工作目录
workingDir: /app
command 和 args 与 Docker 的关系:
| Docker 字段 | Kubernetes 字段 |
|---|---|
| ENTRYPOINT | command |
| CMD | args |
安全上下文
spec:
# Pod 级别安全设置
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: app
# 容器级别安全设置
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
常用安全配置:
| 配置 | 说明 |
|---|---|
runAsNonRoot | 要求容器以非 root 用户运行 |
runAsUser | 指定运行用户 UID |
readOnlyRootFilesystem | 根文件系统只读 |
allowPrivilegeEscalation | 禁止提权 |
capabilities | Linux 能力控制 |
生命周期钩子
Kubernetes 为容器提供了生命周期钩子,让你可以在容器生命周期的特定时刻执行代码。
postStart 钩子
postStart 在容器创建后立即执行,与容器的入口点并行执行:
spec:
containers:
- name: my-app
image: my-app:latest
lifecycle:
postStart:
exec:
command:
- /bin/sh
- -c
- |
echo "Container started"
echo "$(date) - Pod started" >> /var/log/startup.log
常见用途:
- 写入启动标记文件
- 注册服务到服务发现
- 初始化配置
preStop 钩子
preStop 在容器终止之前执行,会阻塞容器的终止流程:
spec:
containers:
- name: web
image: nginx:1.25
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
echo "Container stopping"
nginx -s quit
sleep 10
常见用途:
- 优雅关闭连接
- 从服务发现注销
- 清理资源
与终止流程的关系
1. 用户执行 kubectl delete pod
↓
2. Pod 状态变为 Terminating
↓
3. 执行 preStop 钩子(阻塞)
↓
4. preStop 完成或超时
↓
5. 发送 SIGTERM 信号给容器
↓
6. 等待 terminationGracePeriodSeconds
↓
7. 如果容器还在运行,发送 SIGKILL
↓
8. Pod 被删除
注意事项:
postStart与容器入口点并行执行,不保证顺序- 钩子执行失败会导致容器终止
preStop钩子的执行时间受terminationGracePeriodSeconds限制- 钩子应该设计为幂等的,可能被多次调用
Volume 存储
Volume 是 Pod 中容器可以访问的目录,用于数据持久化和容器间共享数据。
emptyDir
临时共享存储,Pod 删除时数据也会被删除:
spec:
volumes:
- name: cache
emptyDir:
sizeLimit: 100Mi # 可选大小限制
containers:
- name: app
volumeMounts:
- name: cache
mountPath: /tmp/cache
使用场景:
- 临时缓存
- 容器间共享数据
- 检查点数据
hostPath
挂载节点上的文件系统:
spec:
volumes:
- name: logs
hostPath:
path: /var/log/myapp
type: Directory # Directory/File/Socket 等
注意:hostPath 有安全风险,生产环境应谨慎使用。
PersistentVolumeClaim
使用持久化存储:
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
containers:
- name: app
volumeMounts:
- name: data
mountPath: /data
ConfigMap 和 Secret
spec:
volumes:
# 挂载 ConfigMap
- name: config
configMap:
name: app-config
items:
- key: config.yaml
path: config.yaml
# 挂载 Secret
- name: secrets
secret:
secretName: app-secret
containers:
- name: app
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
- name: secrets
mountPath: /etc/secrets
readOnly: true
Init 容器
Init 容器是在主容器启动之前运行的容器,用于完成初始化工作。
基本用法
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
initContainers:
# 等待依赖服务
- name: init-db
image: busybox:1.36
command: ['sh', '-c', 'until nc -z db-service 5432; do sleep 2; done']
# 初始化数据
- name: init-data
image: busybox:1.36
command: ['sh', '-c', 'cp /config/default.conf /data/app.conf']
volumeMounts:
- name: config
mountPath: /config
- name: data
mountPath: /data
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
volumeMounts:
- name: data
mountPath: /app/data
volumes:
- name: config
configMap:
name: app-config
- name: data
emptyDir: {}
Init 容器的特点
- 顺序执行:Init 容器按定义顺序依次执行
- 必须成功:所有 Init 容器成功完成后,主容器才能启动
- 不支持探针:Init 容器不支持 liveness/readiness 探针
- 独立资源:可以设置独立的资源限制和镜像
Sidecar 容器(v1.33+)
从 Kubernetes 1.33 开始,可以在 initContainers 中定义 sidecar 容器,通过设置 restartPolicy: Always 使其在 Pod 整个生命周期内运行:
spec:
initContainers:
- name: logging-sidecar
image: fluent/fluent-bit:1.8
restartPolicy: Always # Sidecar 标识
# 将在整个 Pod 生命周期内运行
containers:
- name: main-app
image: myapp:latest
Ephemeral Containers
Ephemeral Containers 是临时容器,用于调试正在运行的 Pod。
添加调试容器
# 为目标 Pod 添加调试容器
kubectl debug -it my-pod --image=busybox:1.36 --target=my-container
# 使用网络调试镜像
kubectl debug -it my-pod --image=nicolaka/netshoot -- bash
# 复制 Pod 进行调试
kubectl debug my-pod -it --copy-to=my-pod-debug --image=busybox:1.36
调试场景
网络调试:
kubectl debug -it my-pod --image=nicolaka/netshoot -- bash
# 在容器内执行
curl http://service-name:port
dig service-name
tcpdump -i eth0
文件系统检查:
kubectl debug -it my-pod --image=busybox:1.36 --target=my-container -- sh
# 共享进程命名空间,可以查看主容器的文件
Pod 调度
节点选择器
spec:
# 简单节点选择器
nodeSelector:
disktype: ssd
zone: west
节点亲和性
spec:
affinity:
nodeAffinity:
# 硬性要求
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-west-2a
- us-west-2b
# 软性偏好
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
Pod 亲和性和反亲和性
spec:
affinity:
# Pod 亲和性:与其他 Pod 靠近
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: cache
topologyKey: kubernetes.io/hostname
# Pod 反亲和性:与其他 Pod 分开
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web
topologyKey: kubernetes.io/hostname
污点和容忍
# 节点污点(在节点上设置)
# kubectl taint nodes node1 dedicated=gpu:NoSchedule
# Pod 容忍
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
污点效果:
| 效果 | 说明 |
|---|---|
NoSchedule | 不调度新 Pod |
PreferNoSchedule | 尽量不调度 |
NoExecute | 不调度新 Pod,驱逐已有 Pod |
Pod 管理命令
创建和删除
# 从 YAML 创建 Pod
kubectl apply -f pod.yaml
# 命令行创建(调试用)
kubectl run my-pod --image=nginx --port=80
# 查看 Pod
kubectl get pods
kubectl get pods -o wide
# 查看 Pod 详情
kubectl describe pod my-pod
# 查看 Pod 日志
kubectl logs my-pod
kubectl logs -f my-pod # 实时跟踪
# 删除 Pod
kubectl delete pod my-pod
kubectl delete -f pod.yaml
调试命令
# 进入容器
kubectl exec -it my-pod -- /bin/sh
# 多容器 Pod 指定容器
kubectl exec -it my-pod -c container-name -- /bin/sh
# 端口转发
kubectl port-forward my-pod 8080:80
# 复制文件
kubectl cp my-pod:/app/logs ./logs
kubectl cp ./config.yaml my-pod:/app/config.yaml
# 调试 Pod
kubectl debug -it my-pod --image=busybox
查看状态
# 查看 Pod 状态
kubectl get pod my-pod -o yaml
kubectl get pod my-pod -o json
# 查看 Pod 事件
kubectl describe pod my-pod | grep -A 20 Events
# 查看容器日志
kubectl logs my-pod -c container-name
kubectl logs my-pod --previous # 上一个容器的日志
# 查看资源使用
kubectl top pod my-pod
最佳实践
1. 使用 Controller 管理 Pod
不要直接创建 Pod,而是使用 Deployment、StatefulSet 等控制器:
# 不推荐:直接创建 Pod
apiVersion: v1
kind: Pod
metadata:
name: my-pod
# 推荐:使用 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:latest
2. 设置资源限制
始终为容器设置资源请求和限制:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
3. 配置健康检查
为关键服务配置存活探针和就绪探针:
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
4. 使用非 root 用户运行
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
5. 敏感信息使用 Secret
不要在镜像或环境变量中硬编码敏感信息:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
6. 标签和注解
为 Pod 添加有意义的标签和注解:
metadata:
labels:
app: my-app
version: v1.0.0
environment: production
annotations:
description: "Web frontend service"
owner: "team-frontend"
小结
本章我们学习了 Pod 的核心知识:
- Pod 概念:最小部署单元,容器组共享网络和存储
- 生命周期:阶段(Pending、Running、Succeeded、Failed)、条件、容器状态
- 重启策略:Always、OnFailure、Never
- 探针:存活探针、就绪探针、启动探针的配置和使用
- 配置详解:资源配额、环境变量、安全上下文
- Volume:临时存储和持久化存储
- Init 容器和 Sidecar:初始化和辅助容器
- 调度:节点选择器、亲和性、污点容忍
- 管理命令:创建、调试、查看状态
- 最佳实践:使用控制器、资源限制、健康检查、安全配置
练习
- 创建一个包含两个容器的 Pod,共享一个 Volume
- 为 Pod 添加存活探针和就绪探针
- 使用节点选择器将 Pod 调度到特定节点
- 使用 kubectl 调试 Pod,进入容器查看状态
- 创建一个使用 Init 容器初始化数据的 Pod
- 使用 Ephemeral Container 调试运行中的 Pod
准备好继续学习了吗?点击下一章了解 Deployment!