Skip to content

生产环境 Web 发布规范

本文档定义 Web 应用生产环境发布的标准流程和最佳实践

一、 发布概述

针对定制化项目的多环境部署需求(跨云厂商/私有化/集群/跳板机等场景),制定以下通用发布原则:

  1. 部署标准化

    • 优先采用阿里云云效流水线实现自动化构建部署
    • 私有化环境配合客户现有CI/CD体系
  2. 架构适配性

    • 复杂架构项目需定制部署方案(如Docker/K8S/CDN等)
  3. 核心优势

    • 标准化发布流程,降低人为错误风险
    • 自动化构建部署,提升发布效率
    • 严格的代码审核机制
    • 基于Git标签的版本控制体系

二、 Git发布规范

分支说明:

  • master: 基准分支,对齐线上生产环境代码
  • feature/*: 功能开发分支,基于master创建
  • release/{YYYYMMDD}[_suffix]: 发布分支。基于master创建

功能发布流程图:

流程说明:

  1. 从master迁出feature分支进行功能开发。不同功能使用不同的feature分支,功能分支在master分支有更新或者定期合并同步master分支。
  2. 测试上线时:从master拉取release/{YYYYMMDD}[_suffix]分支
  3. 合并需要发布的feature分支。
  4. 通过云效流水线发布release/{YYYYMMDD}[_suffix]分支
  5. 发布成功后合并回master并打标签

信息

这个发布流程可以通过人工进行管理。但是不仅繁琐并且容易出错。所以下面介绍如何使用CI工具进行自动化发布流程

注意事项

  • 确保所有代码变更已通过测试
  • 发布过程中如遇问题,及时回滚
  • 保留完整的发布记录和版本标签
  • 定期清理过期的发布分支和标签

三、 云效流水线配置

我们可以使用云效的分支模式流水线进行发布。通过此方式可以 自动管理发布分支和分支合并操作

3.1 流水线步骤

简单来说,云效流水线发布上线可以简单拆分为下面几个步骤阶段。

  1. 设置代码源:

    • 选择仓库git地址
    • 【重要】开启分支模式
  2. 【可选】源码扫描阶段:

    • 代码质量扫描
    • 代码安全扫描
  3. 构建阶段

    • 设置环境:安装指定版本Node.js、设置pnpm、设置缓存配置等
    • 构建:执行安装依赖和构建脚本命令
    • 产物处理:缓存构建产物到云效供下一步使用 或 直接上传至OSS
  4. 部署阶段:按照部署方式不同分为

    部署至OSS

    • 【可选】清理bucket内容
    • 上传构建产物至bucket

    部署至云服务器

    • 【可选】 审批:生产环境发布需要人工
    • 下载构建产物到目标主机
    • 解压并部署到指定目录
    • 【可选】支持多环境部署(通过${stage}变量区分)

    部署至K8S集群

    • 构建Dokcer镜像
    • 推送至K8S集群服务
  5. 收尾阶段

    • 打发布标签
    • 自动合并代码回master分支
    • 【可选】删除流水线自动创建的relase分支和对应的feature分支。建议自动删除release分支。feature分支由相关开发人员自行删除。

四、 流水线配置

根据上面的步骤阶段我们来看下具体的配置方式。

云效目前支持两种配置方式:YMAL方式可视化方式。我们默认使用 YAML方式

配置流水线- YAML 格式

下面提供默认的 YAML 模板配置 流水线。你可以 在云效 模板里面选择对应的模板

注意事项:

  • 变量替换: 搜索并替换所有<< xx >>标记的内容。这些都是不同项目必须要修改的内容
  • 根据项目实际情况调整Node版本和构建命令
  • 部署目录需要提前在目标主机创建好
  • 处理好自己的服务链接和授权
  • 根据实际情况选择部署到服务器还是OSS

配置缓存

按下图配置.pnpm-store 以配合流水线缓存 加快pnpm 项目构建速度。 如果你的项目不是pnpm安装依赖的可以忽略

参数: /root/.pnpm-store

mDtuHHULl7lN

部署到OSS

云效提供了流水线模板。你可以直接选择使用

cBwTrtAmGX8h

yaml
# 设置代码源
sources:
  my_repo:
    type: codeup # 默认是公司云效
    name: <<项目名称>>
    endpoint: <<git@codeup.aliyun.com:your-project-group/your-project-repo.git>> # 替换为项目git地址
    certificate: 
      type: serviceConnection
      serviceConnection: <<your-service-connection-id>> # 你的服务链接。如果没有自己创建一个
    branch: master
    branchMode:  # 启用分支模式
      baseBranch: master # 默认master基准分支 

# 构建阶段
stages:
  node_build_stage:
    name: "构建"
    jobs:
      node_build_job:
        name: "Node.js 构建"
        runsOn: 
          group: public/cn-beijing
        steps:
          # 执行构建命令
          setup_node_step:
            name: "安装Node"
            step: NodeBuild
            with:
              versionType: "custom"
              customNodeVersion: "18.19.0" # 可以按照要求进行修改
              run: |
                # 安装pnpm@8
                npm install pnpm@8.9.0 -g
                pnpm -v
                node -v
                # 这里注意 需要在流水线配置页面顶部 【变量和缓存】配置项中添加 /root/.pnpm-store的配置
                # 设置缓存仓库 与 【变量与缓存】中设置配合使用
                pnpm config set store-dir /root/.pnpm-store
                # 安装依赖。这里可以优化 pnpm install --filter "@wmeimob/backend..." --no-frozen-lockfile
                # filter的名称查看相关项目包中package.json中的name字段
                pnpm install --no-frozen-lockfile
                # 执行构建命令
                cd <<packages/backend>> && pnpm build

          # 部署阶段
          # [可选] 清理原型oss仓库下内容。如果不清除则新老版本可并存。坏处是占用会越来越大
          oss_delete_step:
            name: "清理仓库"
            step:  OSSDelete
            with:
              serviceConnection: <<your-service-connection-id>>
              region:  <<cn-shanghai>>
              bucket: <<bucket>>
              batchDelete: true

          oss_upload_step:
            name: "OSS 上传"
            step: "OSSUpload"
            with:
              serviceConnection: <<your-service-connection-id>>
              region: <<cn-shanghai>>
              bucket: <<bucket>>
              sourceFilePath: "<<sourceFilePath>>" # 构建产物目录。例如 "packages/backend/dist/"
              targetFilePath: ""

  end_stage:
    name: '收尾阶段'
    jobs: 
      manul_job: 
        name: 代码合并审批
        component: ManualValidate
        with:
          validatorType: pipelineRoles           #验证者类型为流水线角色
          validateMethod: or
          timeoutTime: '0'   # 0 表示无超时时间
          validatorPipelineRoles: member

      git_merge_job:
        name: 发布分支合并进主分支
        condition: |
          succeed('manul_job')
        steps: 
          merge_step:
            step: MergeBranch
            name: Git代码合并
            with:
              targetBranch: master
              deleteSourceBranch: true
              # deleteMatchedBranch: true
              # branchFilter: ^feature.*

      git_tag_job:
        name: 打发布Tag
        condition: |
          succeed('manul_job')
        steps: 
          merge_step:
            step: AddGitTag
            name: 打发布Tag
            with:
              gitTag: ${CI_COMMIT_REF_NAME}

部署到服务器

6T8WjTdS11f2

yaml
# 设置代码源
sources:
  my_repo:
    type: codeup # 默认是公司云效
    name: <<项目名称>>
    endpoint: <<git@codeup.aliyun.com:your-project-group/your-project-repo.git>> # 替换为项目git地址
    certificate: 
      type: serviceConnection
      serviceConnection: <<your-service-connection-id>> # 你的服务链接。如果没有自己创建一个
    branch: master
    branchMode:  # 启用分支模式
      baseBranch: master # 默认master基准分支 


# 构建阶段
stages:
  node_build_stage:
    name: "构建"
    jobs:
      node_build_job:
        name: "Node.js 构建"
        runsOn: 
          group: public/cn-beijing
        steps:
          # 执行构建命令
          setup_node_step:
            name: "安装Node"
            step: NodeBuild
            with:
              versionType: "custom"
              customNodeVersion: "18.19.0" # 可以按照要求进行修改
              run: |
                # 安装pnpm@8
                npm install pnpm@8.9.0 -g
                pnpm -v
                node -v
                # 这里注意 需要在流水线配置页面顶部 【变量和缓存】配置项中添加 /root/.pnpm-store的配置
                # 设置缓存仓库 与 【变量与缓存】中设置配合使用
                pnpm config set store-dir /root/.pnpm-store
                # 安装依赖。这里可以优化 pnpm install --filter "@wmeimob/backend..." --no-frozen-lockfile
                # filter的名称查看相关项目包中package.json中的name字段
                pnpm install --no-frozen-lockfile
                # 执行构建命令
                cd <<packages/backend>> && pnpm build
          # 将构建产物缓存在流水线中供后续使用
          artifact_upload_step:
            name: "构建物上传"
            step: ArtifactUpload
            with:
              uploadType: flowPublic
              artifact: "Artifacts_${PIPELINE_ID}_${DATETIME}"
              filePath: "./dist" # 一般来说都是dist 按需修改

  vm_deploy_stage:
    name: "部署"
    jobs:
      # 这是部署到服务器上的代码
      vm_deploy_job:
        name: "主机部署"
        component: "VMDeploy"
        with:
          downloadArtifact: true
          useEncode: false
          machineGroup: <<your-machine-group-id>> # 主机ID 需要先新增主机
          executeUser: "root"
          artifact: $[stages.node_build_stage.node_build_job.artifact_upload_step.artifacts.Artifacts_${PIPELINE_ID}_${DATETIME}] # 使用上面缓存的构建产物 按路径来的
          # 部署到服务器上名称
          artifactDownloadPath: "/opt/package_${DATETIME}.tgz"
          # 部署到主机后执行的命令
          run: |
            # 核心逻辑为
            # 1.创建文件夹 一般静态页面放在 /var/www/下 通过nginx解析
            mkdir -p /var/www/backend
            echo /var/www/backend
            
            # 2.[可选] 清除旧文件。如果不清除则可保证新老并存
            rm -rf /var/www/backend/*

            # 3.将上传的包移动到对应文件夹内
            mv /opt/package_${DATETIME}.tgz /var/www/backend/package_${DATETIME}.tgz

            # 4.进入目录
            cd /var/www/backend

            # 5.执行解压操作
            tar zxvf package_${DATETIME}.tgz
    
  end_stage:
    name: '收尾阶段'
    jobs: 
      manul_job: 
        name: 代码合并审批
        component: ManualValidate
        with:
          validatorType: pipelineRoles           #验证者类型为流水线角色
          validateMethod: or
          timeoutTime: '0'   # 0 表示无超时时间
          validatorPipelineRoles: member

      git_merge_job:
        name: 发布分支合并进主分支
        condition: |
          succeed('manul_job')
        steps: 
          merge_step:
            step: MergeBranch
            name: Git代码合并
            with:
              targetBranch: master
              deleteSourceBranch: true
              # deleteMatchedBranch: true
              # branchFilter: ^feature.*

      git_tag_job:
        name: 打发布Tag
        condition: |
          succeed('manul_job')
        steps: 
          merge_step:
            step: AddGitTag
            name: 打发布Tag
            with:
              gitTag: ${CI_COMMIT_REF_NAME}

部署到K8S集群

略...

配置流水线 - 可视化模式

部署到OSS

AeBhtzjMCIFu