从手工部署到可回滚发布:用CCE、镜像标签与HPA搭一条小团队的容器发布流水线

举报
yd_220626730 发表于 2026/11/02 09:27:15 2026/11/02
【摘要】 小团队最常见的发布方式,往往是这样的:有人登录服务器改了配置,有人直接覆盖了一个名为 latest 的镜像,出了问题再凭记忆往回找。流程不长,但每一步都不可追溯。本文把发布拆成镜像、部署、扩缩容和回滚四件事,用华为云容器引擎 CCE 搭一条成本不高、出了问题能迅速回到上一版的流水线。 一、先定三条规则第一,镜像标签不可变。生产环境不要用 latest,因为它在任何时刻都可能指向不同的内容,出...

小团队最常见的发布方式,往往是这样的:有人登录服务器改了配置,有人直接覆盖了一个名为 latest 的镜像,出了问题再凭记忆往回找。流程不长,但每一步都不可追溯。本文把发布拆成镜像、部署、扩缩容和回滚四件事,用华为云容器引擎 CCE 搭一条成本不高、出了问题能迅速回到上一版的流水线。

一、先定三条规则

第一,镜像标签不可变。生产环境不要用 latest,因为它在任何时刻都可能指向不同的内容,出了问题无法确认当时跑的是哪一版。建议用 Git 短提交号加构建日期,例如 app:20261102-a1b2c3d,看到标签就能对应到代码。

第二,发布过程可追溯。每一次上线都应留下三条记录:代码提交、镜像标签和部署时间。后续排查时,先看这三条,再看日志,而不是先猜测。

第三,回滚必须是一条现成命令,而不是临时拼凑的操作。回滚方案如果只存在于某一位同事的脑子里,故障时基本等于没有。

二、镜像:先把构建做干净

多阶段构建是值得优先做的一步。构建阶段安装依赖、编译产物,运行阶段只保留运行必需的文件和运行时。这样镜像更小,传输和启动更快,攻击面也更小。

# 构建阶段
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 运行阶段只保留产物
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
EXPOSE 3000
CMD ["node", "dist/server.js"]

构建和推送时,把标签写死,不要顺手打 latest:

docker build -t swr.cn-north-4.example.com/team/app:20261102-a1b2c3d .
docker push swr.cn-north-4.example.com/team/app:20261102-a1b2c3d

这里的仓库地址只是示例,实际使用时替换为自己的容器镜像服务 SWR 地址和命名空间。另外建议在流水线里固定基础镜像版本,例如 node:20-alpine,而不是 node:latest,否则基础镜像的变化也会悄悄进入生产。

三、在 CCE 里部署:让滚动发布代替整体替换

Deployment 的价值不在于它能启动容器,而在于它能按策略替换容器。下面的要点比 YAML 本身更重要:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
        - name: app
          image: swr.cn-north-4.example.com/team/app:20261102-a1b2c3d
          ports:
            - containerPort: 3000
          resources:
            requests:
              cpu: "250m"
              memory: "512Mi"
            limits:
              cpu: "1000m"
              memory: "1Gi"
          readinessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 20
            periodSeconds: 10

maxUnavailable 为 0 表示旧实例全部下线之前,新实例要先就绪,发布期间服务不出现空档。maxSurge 为 1 表示最多多出一个新实例,资源占用可控。

健康检查需要区分两种职责:就绪检查 readinessProbe 回答“能不能接流量”,存活检查 livenessProbe 回答“进程是不是已经卡死”。如果把两者写成同一个宽松接口,或者把启动时间很长的应用的存活检查设得太急,就会出现还没启动完成就被反复重启的情况。健康检查接口应当轻量,只检查自身是否可以处理请求,不要在里面做耗时的下游调用。

资源 requests 和 limits 也要成对出现。requests 决定调度时预留多少资源,limits 决定单个容器最多用多少。只写 limits 不写 requests,调度器可能把多个重负载实例排到同一节点;只写 requests 不写 limits,个别异常实例可能挤占整台节点。

四、HPA:扩缩容先看指标,再看阈值

水平 Pod 自动扩缩 HPA 的常见写法是按 CPU 利用率扩容:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: app
  minReplicas: 2
  maxReplicas: 6
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

这个配置有三点需要注意。第一,CPU 利用率是相对 requests 计算的,requests 写得过低,阈值就会失真。第二,内存不适合作为唯一的扩容信号,因为内存一般不会因为流量下降而自动释放,按内存扩容容易只扩不缩。第三,业务有明显波峰时,应当先确认最小副本数能否扛住冷启动和突发流量,而不是把全部希望寄托在扩容速度上。

五、发布与回滚:一套固定动作

一次规范的发布可以固定为以下顺序:推送带版本标签的镜像;更新 Deployment 的镜像字段;观察滚动发布状态;用健康检查和业务指标确认新版本;确认无异常后再结束发布。

kubectl set image deployment/app app=swr.cn-north-4.example.com/team/app:20261102-a1b2c3d
kubectl rollout status deployment/app
kubectl rollout history deployment/app

如果新版本出现错误率上升、关键接口超时或健康检查持续失败,不要先在生产上改代码热修。优先回滚到上一个已知良好的版本,再到开发环境复现和修复:

kubectl rollout undo deployment/app

回滚后同样要观察 rollout 状态和业务指标,确认旧版本确实恢复了流量。把“先回滚、再排查”写进团队约定,可以避免故障期间一边修代码、一边猜配置的混乱。

六、容易踩的几个坑

一是把数据库密码、第三方密钥直接写进镜像或代码仓库。正确做法是使用 Kubernetes Secret 或云上的凭据管理方式注入,镜像本身不携带敏感信息。

二是配置和代码混在一起。环境差异应当通过环境变量和配置文件解决,而不是为每个环境单独打一个镜像。

三是发布时一次性替换全部实例。副本数本来就不多时,全量替换等于主动制造停机窗口,滚动策略和最小可用副本数要先于发布流程确定。

四是日志和指标只在出问题时才想起来。容器日志应当集中采集,关键接口的错误率、延迟和 Pod 重启次数应当有固定查看入口,否则回滚决策只能靠感觉。

结尾:一张发布检查表

上线前可以逐项确认:镜像标签是不是不可变的版本标签;Deployment 是不是滚动发布,maxUnavailable 是不是 0;健康检查是不是区分了就绪和存活;HPA 的最小副本数和上限是不是合理;回滚命令是不是已经验证过。如果这五项都成立,小团队的发布就已经比“直接覆盖 latest”可靠得多。容器平台解决的是部署方式问题,而可追溯、可回滚的习惯,才是发布真正稳定的来源。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。