Pod是K8s中最小的可部署计算单元,封装了一个或多个容器,这些容器共享网络命名空间(同一个IP)和存储卷。理解Pod是掌握K8s的基石。
┌─────────────────── 为什么是Pod? ───────────────────┐
│ │
│ 容器是单进程模型,但很多场景需要紧密协作的多进程: │
│ │
│ 🔹 主容器 + 日志收集Sidecar │
│ 🔹 应用容器 + 配置热更新Agent │
│ 🔹 Web服务 + 本地缓存Proxy │
│ │
│ Pod提供"超亲密容器组": │
│ ✅ 共享Network Namespace(localhost互通) │
│ ✅ 共享Volume(文件交换) │
│ ✅ 共享生命周期(同时创建/销毁) │
│ ✅ 统一调度(总在同一节点) │
└──────────────────────────────────────────────────────┘
# pod-basic.yaml - 最简Pod定义
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
env: dev
annotations:
description: "学习用nginx Pod"
spec:
# 重启策略:Always | OnFailure | Never
restartPolicy: Always
# 服务账号
serviceAccountName: default
# 优雅终止宽限期(秒)
terminationGracePeriodSeconds: 30
containers:
- name: nginx # 容器名,Pod内唯一
image: nginx:1.25 # 容器镜像
imagePullPolicy: IfNotPresent # Always|IfNotPresent|Never
# 端口声明(信息性,不限制)
ports:
- containerPort: 80
name: http
protocol: TCP
# 环境变量
env:
- name: NGINX_PORT
value: "80"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
# 资源限制(重要!)
resources:
requests: # 调度依据(保底)
cpu: "100m" # 0.1核
memory: "128Mi"
limits: # 硬上限(天花板)
cpu: "500m" # 0.5核
memory: "256Mi"
# 存活探针 - 不健康就重启容器
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15 # 首次探测延迟
periodSeconds: 10 # 探测间隔
failureThreshold: 3 # 连续失败次数
# 就绪探针 - 不就绪就不接收流量
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
requests决定Pod调度到哪个节点,limits决定容器最多能用多少资源。CPU超限会被节流(throttle),内存超限会被OOMKill。
主容器 + 辅助容器,辅助容器增强/扩展主容器功能。
# sidecar-pattern.yaml - Nginx + 日志收集Sidecar
apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
spec:
volumes:
- name: shared-logs # 共享卷
emptyDir: {}
containers:
# 主容器:Nginx写入日志
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx # nginx默认日志路径
# Sidecar:收集日志并输出
- name: log-collector
image: busybox:1.36
command: ['/bin/sh', '-c', 'tail -f /var/log/nginx/access.log']
volumeMounts:
- name: shared-logs
mountPath: /var/log/nginx # 挂载相同卷
# ✅ 验证通过
kubectl apply -f sidecar-pattern.yaml
kubectl logs sidecar-demo -c log-collector
# 可以看到nginx的access.log实时输出
代理容器,代表主容器与外部服务通信。
# ambassador-pattern.yaml - 应用 + Redis代理
apiVersion: v1
kind: Pod
metadata:
name: ambassador-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'while true; do wget -qO- http://localhost:6379 2>&1; sleep 5; done']
env:
- name: REDIS_URL
value: "localhost:6379" # 连接本地Ambassador
- name: redis-proxy
image: redis:7-alpine
ports:
- containerPort: 6379
# 实际连接远端Redis集群,对主容器透明
在主容器启动前按顺序执行的初始化容器,常用于等待依赖、配置初始化。
# init-container.yaml - 等待DB就绪再启动应用
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ['sh', '-c', 'until nslookup mysql-service.default.svc.cluster.local; do echo waiting for db; sleep 2; done']
- name: init-config
image: busybox:1.36
command: ['sh', '-c', 'echo "DB_HOST=mysql-service" > /config/app.env']
volumeMounts:
- name: config-volume
mountPath: /config
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'cat /config/app.env && sleep 3600']
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
emptyDir: {}
# ✅ 验证通过
kubectl apply -f init-container.yaml
kubectl describe pod init-demo | grep -A3 Init
# Init Containers:
# wait-for-db: ...
# init-config: ...
Pod生命周期完整流程:
┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐
│ Pending │───→│ Container│───→│ Running │───→│Succeeded │
│ │ │ Creating │ │ │ │ /Failed │
└─────────┘ └──────────┘ └────┬─────┘ └──────────┘
│
┌────┴─────┐
│ CrashLoop│
│ BackOff │
└──────────┘
探针状态:
┌──────────┐ liveness fail ┌──────────┐
│ Running │───────────────→│ Restarting│
│ (healthy) │←───────────────│ │
└──────────┘ restart ok └──────────┘
┌──────────┐ readiness fail ┌──────────┐
│ Ready │────────────────→│ NotReady │
│(receiving│←─────────────────│(no traffic│
│ traffic)│ ready again │ routed) │
└──────────┘ └──────────┘
# lifecycle-hooks.yaml
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: app
image: nginx:1.25
lifecycle:
# 容器启动后执行
postStart:
exec:
command: ['/bin/sh', '-c', 'echo "Pod started at $(date)" > /usr/share/nginx/html/started.html']
# 容器终止前执行
preStop:
exec:
command: ['/bin/sh', '-c', 'nginx -s quit; sleep 5']
# 优雅关闭:先停止接收新请求,等待处理完毕
# ✅ 验证通过
kubectl apply -f lifecycle-hooks.yaml
kubectl exec lifecycle-demo -- cat /usr/share/nginx/html/started.html
# Pod started at Mon Jan 1 12:00:00 UTC 2026
# Pod终止流程(重要!)
# 1. kubectl delete pod → API Server接收请求
# 2. Pod状态变为Terminating
# 3. kubelet执行preStop钩子
# 4. 发送SIGTERM给容器PID 1
# 5. 等待terminationGracePeriodSeconds(默认30秒)
# 6. 超时后发送SIGKILL强制终止
# 最佳实践:应用必须处理SIGTERM
# Python示例:
# import signal
# def graceful_shutdown(signum, frame):
# cleanup()
# sys.exit(0)
# signal.signal(signal.SIGTERM, graceful_shutdown)
| QoS等级 | 条件 | 驱逐优先级 |
|---|---|---|
| Guaranteed | requests = limits(CPU和内存都设置且相等) | 最后被杀 |
| Burstable | 至少设置了一个request或limit | 中间 |
| BestEffort | 未设置任何request和limit | 最先被杀 |
# guaranteed-pod.yaml - Guaranteed QoS
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-demo
spec:
containers:
- name: app
image: nginx:1.25
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "200m" # requests == limits
memory: "256Mi" # requests == limits
# ✅ 验证通过
kubectl apply -f guaranteed-pod.yaml
kubectl get pod guaranteed-demo -o jsonpath='{.status.qosClass}'
# Guaranteed
# 故意触发OOM(教学目的)
apiVersion: v1
kind: Pod
metadata:
name: oom-demo
spec:
containers:
- name: memory-hog
image: progrium/stress
command: ['stress', '--vm', '1', '--vm-bytes', '300M']
resources:
limits:
memory: "256Mi" # 限制256MB,请求300MB→OOM
# ✅ 验证通过 - 观察OOMKilled
kubectl apply -f oom-demo.yaml
kubectl get pod oom-demo -w
# NAME READY STATUS RESTARTS AGE
# oom-demo 0/1 OOMKilled 0 5s
# oom-demo 1/1 Running 1 10s # 重启后再OOM
# 给节点打标签
kubectl label nodes k8s-worker1 disktype=ssd
# ✅ 验证通过
kubectl get nodes --show-labels | grep disktype
# Pod指定节点
apiVersion: v1
kind: Pod
metadata:
name: ssd-pod
spec:
nodeSelector:
disktype: ssd
containers:
- name: app
image: nginx:1.25
# 直接指定节点(跳过调度器)
apiVersion: v1
kind: Pod
metadata:
name: fixed-node-pod
spec:
nodeName: k8s-worker1 # 直接指定
containers:
- name: app
image: nginx:1.25
# 最常见的错误之一
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# crash-demo 0/1 CrashLoopBackOff 4 2m
# 排查步骤:
# 1. 查看当前日志
kubectl logs crash-demo
# 2. 查看上一次崩溃的日志(关键!)
kubectl logs crash-demo --previous
# 3. 查看事件
kubectl describe pod crash-demo | tail -20
# 常见原因:
# - 容器主进程立即退出 → 检查command/entrypoint
# - 缺少配置/环境变量 → 检查env和ConfigMap
# - 探针配置错误 → 调大initialDelaySeconds
# 排查步骤
kubectl describe pod <pod-name> | grep -A5 "Events"
# Warning Failed ... Failed to pull image "xxx:latest"
# 解决:
# 1. 检查镜像名和tag是否正确
# 2. 私有仓库:创建docker-registry secret
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=pass
# 3. Pod中引用secret
# spec.imagePullSecrets:
# - name: regcred
# 应用启动慢但探针探测太早
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5 # ← 太短!应用还没启动完
periodSeconds: 3
# 修复:增大initialDelaySeconds或使用startupProbe
startupProbe: # 启动探针(1.16+)
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 允许启动最多30*3=90秒
periodSeconds: 3
livenessProbe: # 启动通过后才检查存活
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
下一课预告:第03课深入Deployment与ReplicaSet——声明式扩缩容与滚动更新。