Skip to content
第 7 章 运维 ⏱ 13 分钟阅读

第 7 章:K8s 部署实战 ​

学习目标 ​

  • 滚动更新 + 回滚
  • Ingress 七层路由 + HTTPS
  • StatefulSet 跑数据库
  • 持久卷(PVC)
  • 避开部署过程中的常见坑

一、滚动更新策略 ​

yaml
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

更新镜像:

bash
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 列。

二、健康探针(三类) ​

yaml
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 七层路由) ​

bash
# minikube 上启用
minikube addons enable ingress
yaml
apiVersion: 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: 8080

TLS 证书用 cert-manager 自动签:

yaml
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:

yaml
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: 10Gi
yaml
apiVersion: 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) ​

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard          # 动态供给

挂进 Pod:

yaml
spec:
  containers:
  - name: app
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: app-data

StorageClass 自动供给(NFS / 云盘):

yaml
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 ​

一次性任务 ​

yaml
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"]

定时任务 ​

yaml
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 水平自动扩缩 ​

bash
kubectl autoscale deploy myapp --cpu-percent=70 --min=2 --max=10

需要先装 metrics-server:

bash
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

动手练习 ​

  1. 创建 nginx Deployment,改镜像版本看滚动更新过程(rollout status)
  2. 用 kubectl rollout undo 回滚到上一版
  3. 配 Ingress 路由 /api 到 api-svc,/ 到 web-svc,curl 验证分流
  4. 给 MySQL StatefulSet 挂 PVC,扩到 3 副本(单实例测试即可),验证 Pod 名带 -0、-1、-2

下一章:第 8 章:CI/CD(GitHub Actions) →

本站基于 VitePress 构建 · 由 StackHub 团队维护