第 7 章:K8s 部署实战
学习目标
- 滚动更新 + 回滚
- Ingress 七层路由 + HTTPS
- StatefulSet 跑数据库
- 持久卷(PVC)
- 避开部署过程中的常见坑
一、滚动更新策略
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 多出来的副本数(+1,可设百分比)
maxUnavailable: 0 # 不可用副本数(0 = 永远全在线)
template:
spec:
containers:
- name: web
image: myapp:1.0更新镜像:
kubectl set image deploy/myapp web=myapp:2.0
kubectl rollout status deploy/myapp # 看进度
kubectl rollout history deploy/myapp # 看历史
kubectl rollout undo deploy/myapp # 回滚上一版
kubectl rollout undo deploy/myapp --to-revision=3 # 回滚指定版⚠️ 坑 1:
kubectl rollout undo只回退到上次状态,跨多次提交的回滚必须加--to-revision。忘了版本号就rollout history看REVISION列。
二、健康探针(三类)
spec:
containers:
- name: web
image: myapp:1.0
# 启动探针:慢启动应用专用,成功了才用 readiness/liveness
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30 次 * 10s = 5 分钟启动窗口
periodSeconds: 10
# 就绪探针:流量开关,通过才接流量
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
# 存活探针:挂了重启 Pod
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3⚠️ 坑 2:
livenessProbe失败会重启 Pod,但如果 /health 因为外部依赖(数据库)慢了导致失败,Pod 无限重启。建议让 /health 只检查自身状态,不依赖外部。 ⚠️ 坑 3:initialDelaySeconds设太短,应用还没起好就被判失败重启,陷入重启风暴。
三、Ingress(Nginx 七层路由)
# minikube 上启用
minikube addons enable ingressapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts: [app.example.com]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080TLS 证书用 cert-manager 自动签:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts: [app.example.com]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 80⚠️ 坑 4:
pathType有三个值:Exact(完全匹配)、Prefix(前缀)、ImplementationSpecific(默认,行为依赖 controller)。跨版本行为不一样,显式写Prefix最稳。
四、StatefulSet(有状态应用)
Deployment 的 Pod 是无身份的(随时换),有状态服务(数据库、ZooKeeper)必须用 StatefulSet:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: rootpw
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10GiapiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None # Headless Service,StatefulSet 必备
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306访问主机名固定:mysql-0.mysql.default.svc.cluster.local
⚠️ 坑 5:StatefulSet 必须配
clusterIP: None的 Service(Headless),否则 Pod 没有稳定 DNS 名,集群内互相访问不到。
五、持久卷(PVC)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard # 动态供给挂进 Pod:
spec:
containers:
- name: app
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-dataStorageClass 自动供给(NFS / 云盘):
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: disk.csi.azure.com # 各云不同(AWS EBS / GCP PD / 阿里云)
parameters:
skuName: Premium_LRS
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer⚠️ 坑 6:PVC
storageClassName写错,Pod 一直 Pending,Events 显示FailedBinding。kubectl get pvc看 STATUS,describe pvc看 Events。
六、Job / CronJob
一次性任务
apiVersion: batch/v1
kind: Job
metadata:
name: data-import
spec:
completions: 1 # 完成几次
backoffLimit: 3 # 失败重试 3 次
template:
spec:
restartPolicy: OnFailure
containers:
- name: importer
image: myapp:1.0
command: ["python", "import.py"]定时任务
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: mysql:8.0
command:
- /bin/sh
- -c
- "mysqldump -h mysql -uroot -prootpw appdb | gzip > /backup/db-$(date +%F).sql.gz"⚠️ 坑 7:CronJob
schedule用UTC时区,不是本地时间。中国时区差 8 小时,生产要么设TZ: Asia/Shanghai环境变量,要么自己换算。
七、HPA 水平自动扩缩
kubectl autoscale deploy myapp --cpu-percent=70 --min=2 --max=10需要先装 metrics-server:
minikube addons enable metrics-server
kubectl get hpa⚠️ 坑 8:HPA 要看 CPU/内存,先装 metrics-server,否则
kubectl top pod永远空,HPA永远显示<unknown>。
八、本章小结
| 场景 | 资源 |
|---|---|
| 无状态 | Deployment |
| 有状态 | StatefulSet |
| 一次性 | Job |
| 定时 | CronJob |
| 七层路由 | Ingress |
| 自动扩缩 | HPA |
| 持久化 | PVC + StorageClass |
动手练习
- 创建 nginx Deployment,改镜像版本看滚动更新过程(
rollout status) - 用
kubectl rollout undo回滚到上一版 - 配 Ingress 路由
/api到 api-svc,/到 web-svc,curl 验证分流 - 给 MySQL StatefulSet 挂 PVC,扩到 3 副本(单实例测试即可),验证 Pod 名带
-0、-1、-2