跳到主要内容

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 在生命周期中所处的阶段。

阶段说明
PendingPod 已被接受,但容器尚未创建。包括等待调度和拉取镜像的时间
RunningPod 已绑定到节点,所有容器已创建,至少一个容器正在运行
Succeeded所有容器成功终止,不会重启
Failed所有容器终止,至少一个容器失败退出
Unknown无法获取 Pod 状态,通常是与节点通信失败

Pod 条件(Conditions)

Pod 有一个 PodConditions 列表,表示 Pod 是否通过了某些条件:

条件类型说明
PodScheduledPod 已被调度到节点
PodReadyToStartContainersPod 的沙箱已创建,网络已配置(Beta)
Initialized所有 Init 容器已完成
ContainersReady所有容器都已就绪
ReadyPod 可以服务请求,应加入 Service 的负载均衡
DisruptionTargetPod 即将因干扰而终止(如抢占、驱逐)

查看 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 # 可选,默认使用默认服务

探针配置参数

参数默认值说明
initialDelaySeconds0容器启动后首次探测的等待时间
periodSeconds10探测间隔
timeoutSeconds1探测超时时间
successThreshold1从失败到成功需要的连续成功次数
failureThreshold3从成功到失败需要的连续失败次数

探针配置示例

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 是可压缩资源,超过限制会被限流

内存资源

  • 单位:KiMiGiTi
  • 内存是不可压缩资源,超过限制容器会被 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 字段
ENTRYPOINTcommand
CMDargs

安全上下文

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禁止提权
capabilitiesLinux 能力控制

生命周期钩子

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 被删除

注意事项

  1. postStart 与容器入口点并行执行,不保证顺序
  2. 钩子执行失败会导致容器终止
  3. preStop 钩子的执行时间受 terminationGracePeriodSeconds 限制
  4. 钩子应该设计为幂等的,可能被多次调用

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 容器的特点

  1. 顺序执行:Init 容器按定义顺序依次执行
  2. 必须成功:所有 Init 容器成功完成后,主容器才能启动
  3. 不支持探针:Init 容器不支持 liveness/readiness 探针
  4. 独立资源:可以设置独立的资源限制和镜像

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 的核心知识:

  1. Pod 概念:最小部署单元,容器组共享网络和存储
  2. 生命周期:阶段(Pending、Running、Succeeded、Failed)、条件、容器状态
  3. 重启策略:Always、OnFailure、Never
  4. 探针:存活探针、就绪探针、启动探针的配置和使用
  5. 配置详解:资源配额、环境变量、安全上下文
  6. Volume:临时存储和持久化存储
  7. Init 容器和 Sidecar:初始化和辅助容器
  8. 调度:节点选择器、亲和性、污点容忍
  9. 管理命令:创建、调试、查看状态
  10. 最佳实践:使用控制器、资源限制、健康检查、安全配置

练习

  1. 创建一个包含两个容器的 Pod,共享一个 Volume
  2. 为 Pod 添加存活探针和就绪探针
  3. 使用节点选择器将 Pod 调度到特定节点
  4. 使用 kubectl 调试 Pod,进入容器查看状态
  5. 创建一个使用 Init 容器初始化数据的 Pod
  6. 使用 Ephemeral Container 调试运行中的 Pod

准备好继续学习了吗?点击下一章了解 Deployment!