跳到主要内容

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

内存资源:

  • 单位: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 字段
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!