首页 » K8s » 第4章 工作负载(Workloads)

第4章 工作负载(Workloads)

 

本章目录(点击跳转):

本章目标:掌握 Kubernetes 四种核心工作负载——Deployment、StatefulSet、DaemonSet、Job/CronJob,能根据应用特点选对工具,并会做滚动升级和回滚。

4.1 工作负载是什么

前面直接创建 Pod,删除后 K8s 不会帮你补。工作负载(Workload)就是在 Pod 之上的一层"管理者":

你声明"我要什么",工作负载控制器负责永远让现实符合声明:副本数、滚动更新、自愈、重启策略。

选型口诀:

应用特点 用什么 例子
无状态(任意副本可互换) Deployment(最常用) Web 服务、API
有状态(需要固定名字、固定存储) StatefulSet 数据库、Kafka
每台节点都要跑一个 DaemonSet 日志采集、监控 agent
跑一次就结束 Job 数据迁移、报表
定时跑一次 CronJob 每天备份

4.2 Deployment(重中之重)

4.2.1 完整 YAML 模板

# web-deploy.yaml —— 以后复制这个模板改就行
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: default
  labels:
    app: web
spec:
  replicas: 3                          # 副本数
  selector:                            # 管理哪些 Pod(必须匹配 template 标签)
    matchLabels:
      app: web
  strategy:                            # 更新策略
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1                      # 更新时最多多出 1 个
      maxUnavailable: 0                # 更新时最多 0 个不可用(不中断服务)
  template:                            # Pod 模板
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
          readinessProbe:
            httpGet:
              path: /
              port: 80
            periodSeconds: 5
kubectl apply -f web-deploy.yaml
kubectl get deploy web
kubectl get pods -l app=web -w

输出:

NAME   READY   UP-TO-DATE   AVAILABLE   AGE
web    3/3     3            3           10s

小白注意 selector.matchLabels 和 template.metadata.labels 必须一致,否则 apply 会报错。Deployment 创建后 selector 不可修改。

4.2.2 Deployment 背后还有一个 ReplicaSet

Deployment 通过管理 ReplicaSet 来管理 Pod(一层间接,为了升级时新旧两组 Pod 并存)。你一般不用直接创建 ReplicaSet,但要知道它存在:

kubectl get rs -l app=web
# 会看到 web-xxxxx(Deployment 自动生成的)

4.2.3 扩缩容

kubectl scale deployment web --replicas=5
kubectl get pods -l app=web -w     # 看新 Pod 逐个起来
kubectl scale deployment web --replicas=2

自动扩缩容 HPA(提前预告,生产常用):

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70     # CPU 超过 70% 就扩容

minikube 默认没装 metrics-server,HPA 需要它。minikube addons enable metrics-server 可开启。

4.2.4 滚动升级(发版)

把镜像从 1.27 升到 1.28,全程不停机:

# 方式一:命令行
kubectl set image deployment/web web=nginx:1.28

# 方式二:改 YAML 后 apply(推荐,有版本记录)
# 把 web-deploy.yaml 里 image 改成 nginx:1.28
kubectl apply -f web-deploy.yaml

观察升级过程:

kubectl rollout status deployment/web
kubectl get pods -l app=web -w
# 新 Pod 先起、就绪后,旧 Pod 才删 —— 这就是滚动更新
kubectl rollout history deployment/web
# REVISION 号每次更新 +1

4.2.5 回滚(发版翻车救星)

# 回滚到上一版
kubectl rollout undo deployment/web

# 回滚到指定版本
kubectl rollout undo deployment/web --to-revision=2

# 查看历史
kubectl rollout history deployment/web

# 演练一下:
kubectl set image deployment/web web=nginx:1.28
kubectl rollout undo deployment/web        # 退回 1.27
kubectl get pods -l app=web

4.2.6 暂停与恢复批量更新

一批改动不想每步都触发滚动更新:

kubectl rollout pause deployment/web
kubectl set image deployment/web web=nginx:1.28   # 只记录,不执行
kubectl set env deployment/web APP_VERSION=v2
kubectl rollout resume deployment/web             # 一次性滚动

4.3 StatefulSet(有状态应用)

数据库这类应用有三个特殊需求:

  1. 固定的网络标识:mysql-0、mysql-1、mysql-2,重启后名字不变;
  2. 稳定的存储:每个副本绑定自己专属的 PVC,Pod 重建后数据还在;
  3. 有序的启停:按 0、1、2 顺序启动,按 2、1、0 顺序停止。

StatefulSet 就是为这三点设计的。

4.3.1 完整示例:一个 3 副本的 etcd 演示

# stateful-demo.yaml
apiVersion: v1
kind: Service
metadata:
  name: etcd-headless
spec:
  clusterIP: None                 # Headless Service:不给虚拟IP
  selector:
    app: etcd
  ports:
    - port: 2380
      name: peer
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: etcd
spec:
  serviceName: etcd-headless      # 必须指向一个 Headless Service
  replicas: 3
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
    spec:
      containers:
        - name: etcd
          image: bitnami/etcd:3.5
          env:
            - name: ETCD_INITIAL_CLUSTER
              value: etcd-0=http://etcd-0:2380,etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380
          ports:
            - containerPort: 2379
  volumeClaimTemplates:           # 每个副本自动创建一个 PVC
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi
kubectl apply -f stateful-demo.yaml
kubectl get pods -l app=etcd -o wide
# 名字是 etcd-0, etcd-1, etcd-2
kubectl get pvc
# 自动生成 data-etcd-0, data-etcd-1, data-etcd-2

# 每个 Pod 有独立 DNS 名(靠 headless service):
kubectl exec -it etcd-0 -- ping -c1 etcd-1

注意:删除 StatefulSet 时,PVC 默认不会删(数据安全优先),需要手动清理:

kubectl delete pvc -l app=etcd
kubectl delete -f stateful-demo.yaml

4.4 DaemonSet(每台节点一个)

场景:日志采集 agent(Fluentd)、监控 agent(node-exporter)、网络插件(CNI)。

# daemonset-demo.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-logger
spec:
  selector:
    matchLabels:
      app: node-logger
  template:
    metadata:
      labels:
        app: node-logger
    spec:
      containers:
        - name: logger
          image: busybox:1.36
          command: ["sh", "-c", "while true; do echo \"$(hostname) $(date)\"; sleep 5; done"]
kubectl apply -f daemonset-demo.yaml
kubectl get ds node-logger
# minikube 只有 1 个节点,所以只有 1 个 Pod;
# 多节点集群里,每加一个节点,就自动多一个 Pod
kubectl get pods -o wide -l app=node-logger
kubectl delete -f daemonset-demo.yaml

4.5 Job 与 CronJob(任务型)

4.5.1 Job:跑一次就结束

# job-demo.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: backup-job
spec:
  completions: 1          # 需要成功完成的次数
  backoffLimit: 3         # 失败重试 3 次
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: backup
          image: busybox:1.36
          command: ["sh", "-c", "for i in 1 2 3 4 5; do echo 备份进度 $i; sleep 2; done; echo 完成"]
kubectl apply -f job-demo.yaml
kubectl get job backup-job
kubectl get pods -l job-name=backup-job
kubectl logs job/backup-job
# 状态 Succeeded 后 Job 完成;再想跑就删除重建
kubectl delete job backup-job

并行 Job(completions=4, concurrencyPolicy=Allow, 可加 parallelism=4):4 个子任务同时跑,全部成功才算成功。

4.5.2 CronJob:定时任务

# cronjob-demo.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hourly-report
spec:
  schedule: "0 * * * *"      # crontab 格式:每小时整点
  concurrencyPolicy: Forbid   # 上一次没跑完就不启动新的
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: report
              image: busybox:1.36
              command: ["sh", "-c", "echo 生成 $(date) 的报表"]
kubectl apply -f cronjob-demo.yaml
kubectl get cronjob hourly-report
# 想立即跑一次(不用等整点):
kubectl create job run-from-cron --from=cronjob/hourly-report
kubectl get jobs
kubectl delete -f cronjob-demo.yaml

crontab 格式速记:分 时 日 月 星期,*/10 * * * * 表示每 10 分钟。

4.6 四种工作负载对比总结

Deployment StatefulSet DaemonSet Job/CronJob
Pod 命名 web-随机哈希 固定序号 0,1,2 随机 随机+序号
副本位置 任意节点 任意节点(有序启停) 每节点一个 跑完即止
存储 共享/无 每副本独立 PVC 无 无
更新 滚动 有序逐个 逐节点 无
典型场景 Web/API DB/MQ 节点级 agent 批处理/定时

4.7 本章小结

  • 无状态应用 → Deployment,会 4 件套:apply、scale、rollout status、rollout undo。
  • 有状态 → StatefulSet + Headless Service + volumeClaimTemplates。
  • 每节点一个 → DaemonSet;跑一次 → Job;定时 → CronJob。
  • maxUnavailable: 0 + 就绪探针 = 升级零停机。

下一章:让应用能被访问——Service 与网络。

原文链接:第4章 工作负载(Workloads),转载请注明来源!

赞 0