1. 为什么现代项目离不开 DevOps 和 CI/CD?

这篇文章围绕两个问题展开:

  1. DevOps 的核心思想是什么?
  2. CI/CD 如何实现自动化部署?尤其是如何结合 Gitee Actions 落地?

先给一个简短回答:

DevOps 的核心思想是打通开发、测试、运维、安全和业务之间的协作壁垒,用自动化、标准化和可观测性贯穿代码开发、测试、构建、发布、运行、监控和反馈的全生命周期。CI/CD 是 DevOps 落地的重要工程手段:代码提交后自动触发流水线,完成测试、构建、打包、镜像推送、部署和健康检查,从而减少人工操作、缩短交付周期、降低上线风险。

现代企业项目通常不是“开发写完代码,手工传到服务器运行”这么简单。真实项目需要考虑:

  • 多人协作如何避免代码冲突;
  • 每次提交如何自动验证质量;
  • 测试环境和生产环境如何保持一致;
  • 上线过程如何标准化、可追踪、可回滚;
  • 部署失败如何快速恢复;
  • 线上故障如何及时发现和定位;
  • 如何让业务快速迭代,同时保持系统稳定。

所以,DevOps 和 CI/CD 的价值不是“炫技”,而是让软件交付从人工经验变成可重复、可审计、可恢复的工程流程。


2. DevOps 的核心思想

2.1 DevOps 不是一个工具

很多人第一次接触 DevOps 时,会把它理解成 Jenkins、Docker、Kubernetes、Gitee Actions、GitLab CI 这类工具。

这种理解不完整。

DevOps 不是某一个工具,而是一套工程文化和协作体系。工具只是落地 DevOps 的手段。

更准确地说:

DevOps 是让开发、测试、运维、安全、业务等角色围绕软件交付目标协作,通过自动化流程、标准化环境和可观测系统,持续、稳定、快速地交付软件。

它强调几个关键词:

  • 协作;
  • 自动化;
  • 标准化;
  • 持续交付;
  • 快速反馈;
  • 可观测性;
  • 稳定性;
  • 可回滚。

2.2 传统开发与运维割裂的问题

在没有 DevOps 思想的团队中,常见问题是开发和运维割裂。

典型场景包括:

  • 开发说:“我本地能跑。”
  • 测试说:“测试环境又挂了。”
  • 运维说:“你们给的包缺配置。”
  • 上线靠手工复制文件、手工执行命令;
  • 不同服务器环境不一致;
  • 部署步骤写在某个人的笔记里;
  • 上线失败后不知道当前版本是什么;
  • 线上故障发生后缺少日志、监控和告警;
  • 回滚依赖人工记忆。

这些问题本质上不是某个人不负责,而是流程没有标准化、自动化和可观测。

DevOps 要解决的就是这种割裂:

需求 -> 开发 -> 测试 -> 构建 -> 发布 -> 运行 -> 监控 -> 反馈

让整个链路变成一个持续闭环。


2.3 DevOps 的核心目标

DevOps 的目标可以概括为:

目标说明
缩短交付周期从代码提交到上线的时间更短
降低上线风险用自动化测试、灰度、回滚减少事故
标准化流程每次发布都走同一套流程,而不是靠人记命令
提高系统稳定性通过监控、告警、日志快速发现问题
提高协作效率开发、测试、运维围绕同一条流水线协作
快速反馈构建失败、测试失败、部署失败能及时反馈给团队

一句话理解:

DevOps 的目标不是“更快上线”这么简单,而是在更快交付的同时,让上线过程更稳定、更可控、更可追踪。


2.4 DevOps 的关键实践

企业中常见的 DevOps 实践包括:

实践作用
Git 版本控制管理代码、配置、部署脚本、变更历史
分支策略控制开发、测试、预发、生产发布节奏
持续集成每次提交后自动构建和测试
持续交付/部署自动把构建产物发布到目标环境
自动化测试用单元测试、集成测试、接口测试保证质量
容器化用 Docker 统一运行环境
基础设施即代码用脚本或配置管理环境,减少手工操作
可观测性日志、指标、链路追踪、告警
回滚机制新版本异常时快速恢复到稳定版本
安全扫描检查依赖漏洞、镜像漏洞、敏感信息泄露

这些实践共同构成了企业软件交付的工程基础。


3. CI/CD 基础概念

3.1 CI:持续集成

CI 是 Continuous Integration,中文叫持续集成。

它关注的是:代码提交后,能不能尽快验证这次变更是否破坏了项目。

典型 CI 流程包括:

  1. 拉取代码;
  2. 安装依赖;
  3. 代码格式检查;
  4. 静态扫描;
  5. 单元测试;
  6. 集成测试;
  7. 编译或打包;
  8. 生成构建产物;
  9. 构建 Docker 镜像。

CI 的重点不是部署,而是尽早发现问题。

例如:

开发者提交代码
        ↓
自动运行测试
        ↓
测试失败,阻止合并
        ↓
测试通过,允许进入后续发布流程

3.2 CD:持续交付与持续部署

CD 有两个常见含义:

名称英文含义
持续交付Continuous Delivery自动构建到“可发布状态”,但是否发布到生产可能需要人工审批
持续部署Continuous Deployment流水线通过后自动部署到目标环境,不需要人工干预

两者的区别在于:

  • 持续交付:自动准备好,但上线可能需要人工点确认;
  • 持续部署:自动准备好,并自动上线。

企业生产环境通常不会简单地“一 push 到 main 就直接上线”。更常见的是:

  • pull request / merge request 校验;
  • 测试环境自动部署;
  • 预发环境验证;
  • 人工审批;
  • tag 或 release 分支触发生产发布;
  • 灰度发布;
  • 健康检查;
  • 失败回滚。

3.3 CI/CD 不是一条命令,而是一条流水线

一条典型企业流水线可能是:

提交代码
  ↓
触发流水线
  ↓
安装依赖
  ↓
代码规范检查
  ↓
单元测试 / 集成测试
  ↓
安全扫描
  ↓
构建应用
  ↓
构建 Docker 镜像
  ↓
推送镜像仓库
  ↓
部署测试环境
  ↓
预发验证 / 审批
  ↓
部署生产环境
  ↓
健康检查
  ↓
监控观察
  ↓
失败回滚

这条链路越标准,发布越稳定;链路越依赖人工经验,风险越高。


4. 基于 Gitee Actions 的自动化部署思路

4.1 Gitee Actions 在 CI/CD 中的角色

Gitee Actions 是 Gitee 代码平台提供的自动化流水线能力。它可以在代码仓库发生事件时自动执行任务,例如:

  • push 到某个分支;
  • 提交 pull request;
  • 创建 tag;
  • 手动触发流水线;
  • 定时任务。

在 CI/CD 中,它通常负责:

  • 拉取代码;
  • 安装依赖;
  • 运行测试;
  • 构建项目;
  • 构建 Docker 镜像;
  • 推送镜像到容器仓库;
  • 通过 SSH 或其他方式触发部署;
  • 输出构建日志和部署结果。

需要注意:Gitee Actions、Gitee Go 或企业版流水线的 YAML 语法和可用插件可能会因平台版本、企业实例配置而不同。下面示例重点展示工程思路,实际落地时应以当前 Gitee 官方文档和你所在企业的流水线配置为准。


4.2 示例场景

假设我们有一个后端服务:

  • 代码托管在 Gitee;
  • 服务使用 Docker 打包;
  • 镜像推送到镜像仓库;
  • 生产服务器安装了 Docker;
  • Gitee Actions 通过 SSH 登录服务器;
  • 服务器拉取新镜像并重启容器。

整体流程如下:

开发者 push 到 main 分支
        ↓
触发 Gitee Actions
        ↓
拉取代码
        ↓
运行测试
        ↓
构建 Docker 镜像
        ↓
推送镜像仓库
        ↓
SSH 登录服务器
        ↓
拉取新镜像
        ↓
停止旧容器
        ↓
启动新容器
        ↓
健康检查
        ↓
发布成功或失败回滚

4.3 前置准备

在写流水线之前,需要先准备好这些条件:

准备项说明
Gitee 仓库存放业务代码和流水线配置
Dockerfile定义如何构建应用镜像
镜像仓库存放构建好的 Docker 镜像
Linux 服务器运行生产或测试环境服务
Docker 环境服务器需要安装 Docker
SSH 凭证Actions 需要远程连接服务器
Gitee Secrets保存密码、私钥、Token 等敏感信息
健康检查接口例如 /health 或 /actuator/health

建议在 Gitee Secrets 中配置:

Secret 名称含义
SERVER_HOST服务器 IP 或域名
SERVER_USERSSH 登录用户
SERVER_PORTSSH 端口,通常是 22
SERVER_SSH_KEYSSH 私钥
DOCKER_USERNAME镜像仓库用户名
DOCKER_PASSWORD镜像仓库密码或访问令牌
REGISTRY_HOST镜像仓库地址

不要把这些敏感信息写进代码仓库。


5. Gitee Actions YAML 示例

下面示例展示一条常见流水线:main 分支触发,先测试,再构建 Docker 镜像,推送镜像仓库,最后通过 SSH 部署。

由于不同 Gitee Actions/Gitee Go 环境对上下文变量、第三方 action、YAML 字段支持可能不同,下面示例以“类 Actions 工作流”的方式表达核心思路。实际项目中要根据你仓库中的 Gitee Actions 模板调整变量名和插件。

name: docker-ci-cd-deploy

on:
  push:
    branches:
      - main

env:
  IMAGE_NAME: registry.example.com/team/my-app
  CONTAINER_NAME: my-app
  APP_PORT: 8080

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Set image tag
        run: |
          echo "IMAGE_TAG=${GITEE_SHA:-$(git rev-parse --short HEAD)}" >> $GITHUB_ENV

      - name: Run tests
        run: |
          echo "这里执行项目测试命令"
          # Java 示例:mvn test
          # Node.js 示例:npm ci && npm test
          # Python 示例:pip install -r requirements.txt && pytest

      - name: Login to Docker registry
        run: |
          echo "${{ secrets.DOCKER_PASSWORD }}" | docker login "${{ secrets.REGISTRY_HOST }}" \
            --username "${{ secrets.DOCKER_USERNAME }}" \
            --password-stdin

      - name: Build Docker image
        run: |
          docker build -t $IMAGE_NAME:$IMAGE_TAG .
          docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest

      - name: Push Docker image
        run: |
          docker push $IMAGE_NAME:$IMAGE_TAG
          docker push $IMAGE_NAME:latest

      - name: Deploy by SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SERVER_SSH_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa

          ssh -p "${{ secrets.SERVER_PORT }}" \
            -o StrictHostKeyChecking=no \
            "${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}" "
              docker login ${{ secrets.REGISTRY_HOST }} \
                --username '${{ secrets.DOCKER_USERNAME }}' \
                --password '${{ secrets.DOCKER_PASSWORD }}' &&

              docker pull $IMAGE_NAME:$IMAGE_TAG &&

              docker stop $CONTAINER_NAME || true &&
              docker rm $CONTAINER_NAME || true &&

              docker run -d \
                --name $CONTAINER_NAME \
                --restart always \
                -p 80:$APP_PORT \
                -e APP_ENV=prod \
                $IMAGE_NAME:$IMAGE_TAG &&

              docker image prune -f
            "

这个示例的核心阶段是:

  1. Checkout source code:拉取代码;
  2. Set image tag:生成镜像版本;
  3. Run tests:执行自动化测试;
  4. Login to Docker registry:登录镜像仓库;
  5. Build Docker image:构建镜像;
  6. Push Docker image:推送镜像;
  7. Deploy by SSH:远程部署。

注意事项:

  • 示例里的 ${{ secrets.xxx }} 表示从 Gitee Secrets 中读取敏感信息;
  • StrictHostKeyChecking=no 简化了示例,但生产环境更推荐提前配置 known_hosts;
  • latest 只适合作为辅助标签,不应该作为唯一生产版本依据;
  • $GITHUB_ENV 是类 GitHub Actions 的环境变量写法,如果你的 Gitee Actions 环境不支持,需要改成对应平台支持的变量传递方式;
  • 如果企业 Gitee 实例使用 Gitee Go 的 stages/jobs/steps 语法,应按企业模板改写。

6. Gitee Go 风格的流水线结构示例

部分 Gitee 流水线平台更接近 stages -> jobs -> steps 的结构。下面给一个结构化示例,用于理解流水线阶段划分。

name: deploy-my-app

stages:
  - name: test
    jobs:
      - name: unit-test
        steps:
          - run: mvn test

  - name: build
    jobs:
      - name: docker-build
        steps:
          - run: docker build -t registry.example.com/team/my-app:${GITEE_COMMIT_SHA} .
          - run: docker push registry.example.com/team/my-app:${GITEE_COMMIT_SHA}

  - name: deploy
    jobs:
      - name: deploy-by-ssh
        steps:
          - run: |
              ssh deploy@your-server "
                docker pull registry.example.com/team/my-app:${GITEE_COMMIT_SHA} &&
                docker stop my-app || true &&
                docker rm my-app || true &&
                docker run -d --name my-app --restart always -p 80:8080 registry.example.com/team/my-app:${GITEE_COMMIT_SHA}
              "

这个示例更强调阶段划分:

  • test:只负责测试;
  • build:只负责构建和推送镜像;
  • deploy:只负责部署。

真实项目中,变量名、密钥引用方式、触发条件和 runner 环境要按 Gitee 当前流水线配置调整。


7. Dockerfile 示例

下面以 Java/Spring Boot 服务为例,给一个简化 Dockerfile。

FROM eclipse-temurin:17-jre

WORKDIR /app

COPY target/app.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

这个 Dockerfile 的含义是:

  • 使用 JRE 运行环境作为基础镜像;
  • 设置容器工作目录为 /app;
  • 把构建好的 jar 包复制进镜像;
  • 暴露 8080 端口;
  • 容器启动时执行 java -jar app.jar。

更成熟的企业项目可以使用多阶段构建、非 root 用户、固定基础镜像 digest、镜像扫描等方式增强安全性和可复现性。


8. docker run 部署命令示例

流水线部署时,本质上会在服务器执行类似命令:

docker run -d \
  --name my-app \
  --restart always \
  -p 80:8080 \
  -e APP_ENV=prod \
  registry.example.com/team/my-app:commit-abc1234

参数解释:

参数说明
-d后台运行容器
--name my-app指定容器名称
--restart always容器异常退出或服务器重启后自动拉起
-p 80:8080宿主机 80 端口映射到容器 8080 端口
-e APP_ENV=prod传入环境变量
registry.example.com/team/my-app:commit-abc1234指定镜像和版本

不要只依赖 latest,更推荐用 commit id、构建号或语义化版本号,例如:

my-app:commit-abc1234
my-app:1.2.3
my-app:20260717-1030

这样出问题时才知道当前运行的是哪个版本,也方便回滚。


9. 部署脚本示例

为了让 YAML 更清晰,也可以把服务器上的部署逻辑写成脚本,例如 /opt/my-app/deploy.sh。

#!/usr/bin/env bash
set -euo pipefail

IMAGE_NAME="$1"
IMAGE_TAG="$2"
CONTAINER_NAME="my-app"
APP_PORT="8080"

FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"

current_image=""
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
  current_image=$(docker inspect --format='{{.Config.Image}}' "${CONTAINER_NAME}")
fi

docker pull "${FULL_IMAGE}"

docker stop "${CONTAINER_NAME}" || true
docker rm "${CONTAINER_NAME}" || true

docker run -d \
  --name "${CONTAINER_NAME}" \
  --restart always \
  -p 80:${APP_PORT} \
  -e APP_ENV=prod \
  "${FULL_IMAGE}"

sleep 5

if ! curl -f http://localhost/health; then
  echo "Health check failed"

  docker logs "${CONTAINER_NAME}" --tail 100 || true
  docker stop "${CONTAINER_NAME}" || true
  docker rm "${CONTAINER_NAME}" || true

  if [ -n "${current_image}" ]; then
    echo "Rollback to ${current_image}"
    docker run -d \
      --name "${CONTAINER_NAME}" \
      --restart always \
      -p 80:${APP_PORT} \
      -e APP_ENV=prod \
      "${current_image}"
  fi

  exit 1
fi

docker image prune -f

这个脚本体现了几个企业级思路:

  • 部署前记录当前镜像;
  • 新版本启动后做健康检查;
  • 健康检查失败时打印日志;
  • 尝试回滚到上一版本;
  • 清理无用镜像。

实际生产中还要考虑更严格的错误处理、并发部署锁、日志归档、通知告警和权限控制。


10. 健康检查与回滚

10.1 健康检查

部署不是容器启动成功就结束。还要确认应用真的能对外提供服务。

常见健康检查命令:

curl -f http://localhost:8080/health

Spring Boot 项目常见:

curl -f http://localhost:8080/actuator/health

如果健康检查失败,流水线应该失败,并触发回滚或告警。


10.2 回滚示例

如果新版本有问题,可以回滚到上一版本镜像:

docker pull registry.example.com/team/my-app:commit-previous

docker stop my-app || true
docker rm my-app || true

docker run -d \
  --name my-app \
  --restart always \
  -p 80:8080 \
  -e APP_ENV=prod \
  registry.example.com/team/my-app:commit-previous

回滚的前提是:

  • 每个版本都有明确镜像 tag;
  • 旧版本镜像没有被删除;
  • 部署时记录了当前稳定版本;
  • 数据库变更也有兼容或回滚策略。

很多线上事故的根源不是不能发布,而是不能快速、安全地回滚。


11. 企业级 CI/CD 关键注意事项

11.1 密钥和敏感信息管理

不要把以下信息写进代码仓库:

  • 服务器密码;
  • SSH 私钥;
  • 镜像仓库密码;
  • 数据库密码;
  • Redis 密码;
  • API Key;
  • Token;
  • 生产环境配置。

应该使用:

  • Gitee Secrets;
  • 环境变量;
  • 配置中心;
  • 密钥管理系统;
  • 只读或最小权限账号。

密钥管理原则:

原则说明
不入库密码、私钥、Token 不写进 Git
最小权限部署账号只拥有必要权限
定期轮换密钥泄露风险随时间增加
审计追踪谁在何时使用了什么凭证要可追踪
环境隔离测试和生产使用不同凭证

11.2 分支策略与环境隔离

企业中常见分支和环境对应关系:

分支环境典型策略
feature/*临时开发不自动部署生产
dev开发环境自动部署开发环境
test测试环境自动部署测试环境
release/*预发环境自动部署预发或人工审批
main/master生产环境合并审批后发布
tag生产版本常用于正式发布

建议:

  • 生产环境不要允许任意分支部署;
  • main 分支要有保护规则;
  • 合并前必须通过测试;
  • 生产发布最好有审批或 tag 控制;
  • 测试、预发、生产环境配置要隔离。

11.3 质量门禁

CI/CD 不应该只是“自动部署”,还应该在部署前拦截低质量代码。

常见质量门禁包括:

  • 代码格式检查;
  • 静态代码扫描;
  • 单元测试;
  • 集成测试;
  • 接口测试;
  • 依赖漏洞扫描;
  • Docker 镜像漏洞扫描;
  • 敏感信息扫描;
  • 代码覆盖率门禁;
  • 构建产物签名。

如果没有质量门禁,流水线只是在更快地把问题发布到线上。


11.4 镜像版本管理

生产环境不建议只使用:

my-app:latest

原因:

  • 不知道 latest 对应哪次提交;
  • 回滚困难;
  • 审计困难;
  • 多环境之间容易混乱。

推荐使用不可变版本:

my-app:commit-a1b2c3d
my-app:build-1024
my-app:1.4.2
my-app:20260717-1530

企业实践中,通常会同时打两个 tag:

docker build -t my-app:commit-a1b2c3d .
docker tag my-app:commit-a1b2c3d my-app:latest

其中 commit tag 用于审计和回滚,latest 只用于辅助使用。


11.5 简单 SSH 部署的局限性

SSH 部署很适合学习和小型项目,但在大型企业中会遇到局限:

问题说明
单机风险一台服务器部署失败会直接影响服务
短暂中断docker stop 后再 docker run 可能有服务空窗
权限风险SSH 私钥权限过大可能造成安全问题
审计不足谁触发了什么命令需要额外记录
扩展性有限多节点部署、灰度、回滚会变复杂

更成熟的方案包括:

  • Docker Compose;
  • Kubernetes Deployment;
  • Helm;
  • Argo CD;
  • Jenkins / GitLab CI / Gitee 企业流水线;
  • 蓝绿部署;
  • 金丝雀发布;
  • 滚动更新。

11.6 可观测性:日志、监控、告警

自动部署完成后,还需要知道服务是否真的稳定。

至少要关注:

  • 应用日志;
  • 容器日志;
  • 错误率;
  • 请求耗时;
  • CPU 使用率;
  • 内存使用率;
  • 磁盘空间;
  • 接口健康状态;
  • 依赖服务状态;
  • 告警通知。

上线不是流水线显示成功就结束。企业项目通常还需要发布后观察窗口,例如观察 10 到 30 分钟关键指标是否异常。


12. 实战演练:从代码提交到自动上线

下面把完整过程串起来。

12.1 开发提交代码

开发者完成需求后提交代码,并推送到 Gitee:

git add .
git commit -m "feat: add order query api"
git push origin main

12.2 Gitee Actions 自动触发

main 分支 push 触发流水线。

流水线开始执行:

  1. 拉取代码;
  2. 安装依赖;
  3. 执行测试;
  4. 构建 Docker 镜像;
  5. 推送镜像仓库;
  6. SSH 登录服务器;
  7. 拉取新镜像;
  8. 重启容器;
  9. 健康检查。

12.3 测试失败

如果测试失败:

CI failed

应该停止后续构建和部署,开发者修复后重新提交。

这就是 CI 的价值:尽早拦截问题。

12.4 部署失败

如果部署后健康检查失败:

  • 输出容器日志;
  • 标记流水线失败;
  • 尝试回滚上一版本;
  • 发送告警;
  • 开发者根据日志定位问题。

12.5 发布成功

如果健康检查通过:

  • 流水线标记成功;
  • 记录版本号;
  • 观察日志和指标;
  • 发布完成。

13. 常见误区

13.1 误区一:DevOps 就是买一个工具

不对。

工具只是手段。没有协作流程、质量门禁、环境标准化、监控告警和回滚机制,买再多工具也不等于 DevOps。


13.2 误区二:CI/CD 就是把手动部署命令搬到流水线

不够。

真正的 CI/CD 应该包括测试、构建、版本管理、部署、健康检查、失败处理、回滚和通知。


13.3 误区三:push 到 main 就自动上生产才叫先进

不一定。

生产环境是否自动部署,要看团队成熟度、业务风险、测试覆盖率、回滚能力和审批要求。

很多企业更适合:

main 分支构建 -> 预发验证 -> 人工审批 -> 生产发布

13.4 误区四:latest 可以作为唯一版本

不建议。

latest 无法表达具体提交,也不利于审计和回滚。生产环境应该使用明确版本 tag。


13.5 误区五:自动部署成功就说明系统正常

不一定。

容器启动成功,不代表接口正常;接口正常,也不代表业务链路正常。还要看健康检查、日志、监控和业务指标。


13.6 误区六:SSH 部署适合所有企业项目

不一定。

SSH 部署简单直接,适合小型项目和学习阶段。大型企业项目通常需要更成熟的部署平台、权限控制、审计、灰度发布和多节点调度。


14. 真实项目落地版总结

DevOps 解决的是软件交付体系问题,CI/CD 是 DevOps 落地的重要自动化管道。

一条合格的企业级 CI/CD 流水线,不应该只包含“构建”和“部署”,还应该包含:

  1. 代码提交触发;
  2. 自动化测试;
  3. 质量门禁;
  4. 镜像构建;
  5. 镜像版本管理;
  6. 镜像推送;
  7. 环境隔离;
  8. 安全密钥管理;
  9. 自动部署;
  10. 健康检查;
  11. 日志监控;
  12. 失败回滚;
  13. 发布审计。

Gitee Actions 可以帮助 Gitee 项目快速搭建这条自动化链路,但企业级交付能力的关键不只是写一份 YAML,而是建立:

  • 标准化流程;
  • 自动化质量检查;
  • 安全的凭证管理;
  • 可追踪的镜像版本;
  • 可回滚的部署机制;
  • 可观测的运行系统;
  • 清晰的分支和环境策略。

更多推荐