4

image.png

背景

在 上一轮优化 里, 我们通过优化一些构建工具和流程, 把构建耗时优化到了 4min 左右,整体发布耗时从 15min 优化到了 8 min 左右, 有较大提升, 但是依旧存在提升空间。

经过一些思考与测试,给出技术方案,并落地到了 WMS 业务中, 效果如下:

image.png

与原流程相比之, 发布耗时由 8 min 降低到了 1 ~2 min 左右。

下面我主要介绍一下方案细节,总结改造过程中遇到的问题, 希望对大家有所帮助。

正文

整体方案设计

1. 现有架构

image.png

现有架构不足:

每次执行 jenkins 都要重新开始执行构建、打包操作,非常的耗时。Jenkins 没有留存打包结果,导致无法做回滚等操作。

当前构建流程中存在大量无效且耗时的构建可以去掉,如使用 docker 打包了镜像,但对于该项目而言是无用的。

架构改进

image.png

CI/CD 前置打包

MR 之后, 会触发 CI, 执行构建任务,构建过程在 2 min 左右, 构建成功之后, 会把对应的结果推送到对应的分支保存起来。

Jenkins 构建阶段, 直接从对应的分支取回打包结果, 推送到静态资源服务器,这个过程非常快, 1 分钟之内即可完成发布。

需要注意的是: 由于是在 gitlab 中构建的, 这时候会缺少一些环境信息, 比如 CID。

WMS业务中并没有用这个参数做一些特殊的逻辑, 所以没有影响。 如果项目需要用到 CID, 则需要额外处理。

发布与回滚

image.png

发布流程直接发布经过 gitlab CICD打包出的结果分支,体现在 jenkins流程中,就相当于是仅仅复制了一份结果内容到目标静态服务器回滚操作直接打包 build-release分支的上一个 tag 即可。

uat、 release 发布之后, 资源会被打上 tag 保存在 gitlab 中, 一个 tag 对应一份资源,借助 lasg_tag 即可实现快速回滚。

具体实现步骤

  1. 修改 deploy.json 与 .gitlab-ci.yml
"project_dir_depth": 2, 
"project_name": "wmsuiv2",
"module_name": "static", 
"build": {
"commands": ["echo 'deploy websitestatic'"], 
"upload_static": {
  "static_dir": "dist", "enable_cdn": false
},
"docker_image": {
"base_image": "harbor.shopeemobile.com/shopee/nodejs-base:14",
"dependent_libraries_files": [],
"run_commands": ["pip install -U shopee-deploy-common"]
// ... 

// .gitlab-ci.yml

image: harbor.shopeemobile.com/shopee/nodejs-base:14

variables:
env: '${CI_COMMIT_REF_NAME}' ENV: '${CI_COMMIT_REF_NAME}'

stages:
build

build:
stage: build 
rules:
if: $CI_COMMIT_REF_NAME == 'uat' && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == null
if: $CI_COMMIT_REF_NAME == 'release' && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == null
if: $CI_COMMIT_REF_NAME =~ /version\/release-(.*)$/ && $CI_PIPELINE_SOURCE == 'web' variables:
GITLAB_URL: 'https://gitlab-ci-token:${CI_JOB_TOKEN}@git.garena.com/shopee/bg-logistics/tianlu/fe/wms-ui.git' GITLAB_W_URL: 'https://${USERNAME}:${PERSONAL_ACCESS_TOKEN}@git.garena.com/shopee/bg-logistics/tianlu/fe/wms-
ui.git' script:
echo "CI_COMMIT_BRANCH:${CI_COMMIT_BRANCH};CI_COMMIT_TAG:${CI_COMMIT_TAG}; CI_DEFAULT_BRANCH:${CI_DEFAULT_BRANCH};CI_MERGE_REQUEST_TARGET_BRANCH_NAME=${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}"
npm install --registry=https://npm.shopee.io
echo "[sys] npm install [finish]"
npm run build
echo "[sys] npm run build [finish]"
if [[ "$CI_COMMIT_REF_NAME" =~ ^version/release-.*$ ]]; then
TARGET_BRANCH="temp/build-staging"
else
TARGET_BRANCH="temp/build-${CI_COMMIT_REF_NAME}"
fi
git clone -b ${TARGET_BRANCH} ${GITLAB_URL}
cd wms-ui/
echo "TARGET_BRANCH:${TARGET_BRANCH}"
echo "CI_COMMIT_REF_NAME:${CI_COMMIT_REF_NAME}"
git config --global user.name "wms.frontend"
git config --global user.email "wms.frontend@shopee.com"
rm -rf ./dist
cp -rf ../dist .
git add .
git commit -am "git ci push" -q
git push ${GITLAB_W_URL} ${TARGET_BRANCH}
echo "[sys] git push to ${TARGET_BRANCH} [successful]"
if [[ "$CI_COMMIT_REF_NAME" == "release" ]]; then
TAG="tag-$(date +%y%m%d-v%H%M)"
echo -e "[sys] tag name ${TAG}"
git tag ${TAG}
git push ${GITLAB_W_URL} ${TAG}
echo "[push succcessful]"
fi

这个过程完成之后, 后续的操作:

  1. 创建对应的分支

以 test 分支为例, 从 test 分支 拉一个新分支: build-test, 用于存放打包结果。

这个分支内,只保留三个文件/文件夹:deploy、dist、.gitignore, 其他内容全部删除。

其中,deploy是用于给jenkins读取构建脚本使用;

dist目录为gitlab CI/CD后续打包的产物;
.gitignore为需要排除提交的目录及文件;

特别注意:
.gitignore 中不能忽略 /dist 。

项目设置中, 要把 reject unverified users 选项关掉, 否则会导致推送失败。

image.png

其他细节:

  • 镜像必须保持跟 jenkins 脚本配置一样
  • 需要使用 gitlab-ci-token来在脚本命令中拉取代码Push 阶段是必须依赖于 build 阶段
    应用范围为 uat,release,version/release-xxx分支,其中前两个为合代码时自动触发,版本分支需要人工在 CI/C -> pipeline -> 点 ‘run pipeline’按钮触发

跨账户提交步骤:

将公用账户 wms.frontend 加入到该项目,并设置为 maintainer. (公用账户申请地址: 链接, WMS 项目组 已经申请,不用额外申请) 使用 wms.frontend登录,在个人账号的 edit profile 中的 Access Tokens创建一个 Personal Access Token,得到 username 和token。

将 username 和 token 存到对应项目的 CI/CD 下的 variables 中,这样就可以在 cicd 脚本中通过变量 ${username} 获取到验证的信息然后上面在git push前,需要将git config 账户配置成公共账户信息,这样就可以使用他的token进行提交操作了。
image.png

完成以上配置, MR 之后, 就会自动执行构建, 并推送结果。

image.png

到此, 第一步就完成了, 之后就是在 DMS 中创建发布任务。

与现有流程不同的只是选择的构建分支不同。

image.png

以 test 为例,现有流程选择的是 test 分支, 新流程需要选择: temp/build-test 分支, 毕竟在这个过程中 ,jenkins 只是对 temp/build-test 分支中的文件做了一次拷贝, 并推送静态文件服务器中。

至此,改造就完成了。

image.png

最后需要提醒的是:

merge 之后,会有大约 2 分钟的构建耗时,如果此时立刻去 jenkins 发布, 则可能发布的是旧的构建结果, 这虽然没有破坏性, 但与预期不符。

对应措施是:

添加 seatalk 消息群通知,构建完成之后,提醒相关发布人员, 完成发布。

兜底措施:

如果遇到极端情况导致 gitlab 无法完成构建, 也可以改回原有的配置, 走原有流程。

操作步骤:

把 deploy.json 和 .gitlabyml 重置,合并到对应分支。
在 Jenkins 构建原始分支(test、uat、release)即可。

思考与延伸

构建与发布之间存在时间差, 这个问题看时候能与 DMS 团队沟通, 开放一个消息通知能力, 自动完成这个过程,减少对人的依赖。

添加机器人通知的方式也不复杂, 实现如下:

// ...
- >
sh -c "curl -i -X POST -H 'Content-Type: application/json' -d '{\"tag\":\"text\",\"text\":{\"content\": \" WMS-UI FE:${TAG}\"}}' https://openapi.seatalk.io/webhook/group/${groudId}"
- fi
// ...

总结

传统的发布与回滚,依赖一次构建, 本方案的核心思路是: 借助第三方存储,保存打包产物,以此减少构建时间。

类似的方案还有很多, 也不仅仅这一种解决方式,在此不做延伸。

本篇被容就这么多, 希望能给大家一些启发, 谢谢。

才疏学浅, 如果错误, 还请指正, 谢谢。


皮小蛋
8k 声望12.8k 粉丝

积跬步,至千里。