Skip to content

Deployment 发布

无状态服务最常见的需求:跑 N 个相同副本,换镜像时逐个替换不中断服务,出问题能快速退回上一版。Deployment 通过 ReplicaSet 管理 Pod 副本来实现这一套。

没有 Deployment 时,要手动创建 ReplicaSet、盯着 Pod 数量、滚动更新时自己写脚本逐批替换。Deployment 把这些自动化了。

Deployment → ReplicaSet → Pod

Deployment 不直接管每个 Pod——它创建 ReplicaSet 作为版本载体。每次更新镜像或模板,Deployment 创建一个新 ReplicaSet,在新 ReplicaSet 里创建 Pod,旧 ReplicaSet 逐步缩容:

旧 ReplicaSet 的 Pod 缩到 0 后,ReplicaSet 本身保留——用于回滚:

bash
kubectl -n demo get deploy,rs,pod -l app=web
kubectl -n demo rollout history deploy/web

基础 Deployment

先从一个能跑的最简 Deployment 开始:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: harbor.example.com/demo/web:v1.0.0
          ports:
            - containerPort: 8080

apply 之后,Deployment 控制器创建 ReplicaSet,scheduler 给 Pod 选节点,kubelet 创建容器。kubectl get deploy,rs,pod -n demo 可以看到三者的关系。

revisionHistoryLimit 控制保留多少个旧 ReplicaSet(默认 10)。历史多了浪费 etcd 空间——大部分集群设 3~5 够用。

这个最简 Deployment 有个关键缺口——没有 readinessProbe。更新镜像时新 Pod 一启动就接入 Service,如果应用启动慢,请求会失败。

滚动更新

滚动更新逐批创建新 Pod、等待 Ready、删除旧 Pod。更新节奏由两个参数控制:

yaml
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0
参数含义影响
maxSurge更新期间允许超出期望副本数的最大 Pod 数值越大更新越快,但瞬时资源占用更高
maxUnavailable更新期间允许不可用的最大 Pod 数值越大更新越快,但可用容量下降越多

副本数很少时(比如 2 个副本),maxUnavailable: 1 会让可用容量瞬间降 50%。小规模服务通常设 maxUnavailable: 0 配合 maxSurge: 1——先建新的,等 Ready 再删旧的。

更新镜像:

bash
kubectl -n demo set image deploy/web web=harbor.example.com/demo/web:v1.0.1
kubectl -n demo rollout status deploy/web

rollout status 会阻塞到更新完成或超时(progressDeadlineSeconds,默认 600 秒)。

readinessProbe 和滚动更新的关系

没有 readinessProbe 时,滚动更新的节奏完全由容器启动速度决定——容器一启动就被认为 Ready,旧 Pod 立刻删除。新 Pod 启动后连不上数据库、缓存没热、配置没加载完成——请求已经在往它打了。

加上 readinessProbe:

yaml
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5
  failureThreshold: 3   # 连续失败 3 × 5 = 15 秒后判定不 Ready

加上之后,滚动更新会等新 Pod 的 readinessProbe 通过才删旧 Pod。readinessProbe 一直不通过时,滚动更新卡住——新 Pod 不 Ready,旧 Pod 不删除。这是保护机制,不是 bug。排查时看新 Pod 的 readinessProbe 为什么失败,而不是急着加 maxUnavailable

回滚

bash
kubectl -n demo rollout history deploy/web           # 查看历史
kubectl -n demo rollout history deploy/web --revision=2  # 看某个版本的具体内容
kubectl -n demo rollout undo deploy/web              # 回滚到上一版
kubectl -n demo rollout undo deploy/web --to-revision=2   # 回滚到指定版本

回滚的边界:rollout undo 只恢复 Deployment 的 Pod 模板——镜像、环境变量、资源请求。它不回滚数据库 schema 变更、不回滚配置中心里的配置、不回滚外部 API 的接口版本。只靠 Deployment 回滚对于数据库 schema 有变更的发布是不够的——schema 迁移脚本的向前兼容和回滚脚本要单独设计。

暂停和恢复

连续改多个字段时(换镜像 + 调资源 + 改环境变量),每次 apply 都触发一次滚动重建。可以先暂停,变更堆积后在恢复时一次触发:

bash
kubectl -n demo rollout pause deploy/web
kubectl -n demo set image deploy/web web=harbor.example.com/demo/web:v1.0.2
kubectl -n demo set resources deploy/web -c web --requests=cpu=200m,memory=256Mi
kubectl -n demo rollout resume deploy/web

发布排查

发布卡住时:

bash
kubectl -n demo rollout status deploy/web
kubectl -n demo describe deploy web
kubectl -n demo get rs,pod -l app=web -o wide
现象原因方向排查
新 Pod ImagePullBackOff镜像 tag 写错、仓库认证、节点到仓库网络不通Events → crictl pull 在节点试
新 Pod Running 但不 ReadyreadinessProbe 失败、应用启动异常、依赖不可用kubectl logskubectl describe pod 看 probe 事件
新 Pod Pending资源不足、调度规则、PVCkubectl describe pod 看调度事件
rollout 超时progressDeadlineSeconds 到期,新副本一直不 Ready新 Pod 的状态和日志

发布故障分成两类:平台没创建起 Pod(看 Events 和节点)和 Pod 创建起来了但不能接流量(看探针、应用日志、入口指标)。两条路径的排查入口不同。