几个月之前,我们前端的项目打包部署是由测试同学来做的。前端想部署一个东西到测试环境查看效果都要让测试同学帮忙部署。还经常遇到因为网络原因npm install下载失败,或者因为node版本问题打包失败,docker镜像跑不起来等各种各样的问题。顺利的时候半天搞定,不顺利的时候可能得好几天才能搞定。终于我忍不了了,申请了一台测试服务器的账号。
解决两个问题
- 环境问题,包下载失败,部署失败等。
- node版本问题如何限制项目使用指定的node版本 - 掘金 (juejin.cn)
- npm install失败,之前用的node-sass 经常下载失败,使用dart sass 替代
- docker跑不起来,内核不一样,需要到不同的机器打包docker 镜像。
- 部署主动权,怎么样让前端同学自己部署。直接去服务器部署效率低,容易出错,尤其很多前端同学linux服务器都没接触过。所以考虑用Gitlab CI来做自动部署。
gitlab、gitlab-ci、gitlab-runner的关系
我花了很长时间捋清楚 gitlab、gitlab-ci、gitlab-runner的关系。
这里画个简易的图简单介绍。个人理解,错误请指正。
我对gitlab-ci、gitlab-runner理解
- gitlab-ci 是gitlab自带的
- gitlab-runner 是需要另外安装的
- gitlab-runner 可以安装到任意服务器,不一定要和gitlab在一起
- 可以在多个服务器安装 gitlab-runner
- 安装好gitlab-runner后,可以使用命令gitlab-runner register注册runner
- 注册runner需要gitlab地址和token(绑定,通过gitlab ci来找到runner)
- Runner 有3种
- Shard Runner 所有项目都可以用,管理员才可以注册
- Group Runner 项目组内共享,注册需要项目组owner权限
- Specific Runners 指定项目使用
- 注册runner时executor 参数我用的shell,简单。
- 注册完成后,就可以在项目根目录下创建.gitlab-ci.yml 文件
- 如果项目里有 gitlab-ci.yml 配置文件,提交代码时就会触发CI,gitlab-ci 会根据 yml 配置文件找到指定的runner,执行任务
打包 Docker Image
我们有部署平台,都是docker容器部署,部署时需要一个docker image,这里我们先看怎么打包一个docker image。后面再说与部署平台联动来实现部署。
首先配置.gitlab-ci.yml
# .gitlab-ci.yml
stages: # ci执行的阶段,这里只有build
- build
# dev自动打包并上传镜像
auto_dev_image:
stage: build # 在哪个stage执行
script:
- chmod +x ./deploy/build-push-img.dev.sh # 获得执行权限
- ./deploy/build-push-img.dev.sh # 执行命令,打包docker镜像
retry: 2 # 错误或者失败后重试2次
only:
- dev # 只在dev分支改动时触发
tags:
- ai_fe_group_runner # 指定使用的runner,我使用的group_runner
根据以上配置,dev分支代码改动时,gitlab-ci 就会从stages的第一个阶段开始执行,通知指定的runner拉取对应分支代码开始执行 script
shell 脚本完全可以直接写到 script 配置项里
# ./deploy/build-push-img.dev.sh
#!/bin/bash
IMAGE_PREFIX=ccr.ccs.tencentyun.com/name_space**** # 我们使用的腾讯云容器镜像服务
IMAGE_NAME_PROD=image_name # 镜像名
IMAGE_TAG=`date +'%Y%m%d%H%M'`-`git rev-parse --short HEAD` # 镜像tag/版本号
PROD_IMAGE=$IMAGE_PREFIX/${IMAGE_NAME_PROD}:${IMAGE_TAG} # 拼起来就是一个完整的镜像地址
echo ${PROD_IMAGE}
docker build -t $PROD_IMAGE . --network="host" # 打包镜像,默认使用项目根目录下 Dockerfile
sudo docker push $PROD_IMAGE # 上传镜像,前提是这个机器已经登录了docker
curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=****' -H 'Content-type:application/json' -d "{\"msgtype\":\"markdown\", \"markdown\":{\"content\":\"**${IMAGE_NAME_PROD} **自动构建镜像:$PROD_IMAGE\"}}" # 企业微信机器人通知打包完成
docker rmi $PROD_IMAGE # 删除本地镜像
上面脚本执行完就可以打包docker image上传至腾讯云,同时企业微信机器人将镜像名发送到群,用于后续部署。
# Dockerfile 用于构建docker镜像
FROM node:16.13.0 AS build # 基础镜像,使用指定的node版本,与开发环境保持一致。
WORKDIR /app/ # 创建工作目录
# 将项目中的文件拷贝至docker打包工作目录,后续步骤会用到的文件
COPY ./misc/ /app/misc/ # 拷贝工具函数,项目校验,npm install、npm run build时会使用
COPY ./config/ /app/config/ # 拷贝配置文件,npm install、npm run build时会使用
COPY package*.json /app/ # 拷贝package*.json
RUN npm ci # 下载项目依赖
COPY . /app/ # 拷贝所有文件
RUN npm run build # 打包文件
FROM nginx:stable-alpine # 更换基础镜像,这样可以减小包体积
ADD ./deploy/default.conf /etc/nginx/conf.d/default.conf
ADD ./deploy/nginx.conf /etc/nginx/nginx.conf
COPY --from=build /app/dist /usr/share/nginx/html # 从前一个基础镜像中拷贝dist目录到nginx目录
EXPOSE 80 # 声明端口
CMD ["nginx", "-g", "daemon off;"] # 运行容器时执行,后台启动nginx
为什么使用多次COPY,不一次性COPY呢? 因为每执行一次都会生成一个层,层是可以缓存的。只要文件不改动,层就可以使用缓存中已经构建好的。这样配置文件不改动的时候,npm ci 就会使用缓存中的结果,不会重新下载。提升了打包速度。
部署
我们有自己的部署平台,所有的部署记录都会在平台上。为了一致性,我们也用这个部署。使用pathon3 写了个脚本,这个脚本会调用部署平台的部署接口来实现部署。
#!/bin/bash
python3 /home/user/opt/auto_script/deploy/deploy.py ${PROJECT_NAME} ${TEST_ENV_IMAGE} ${ENV}
镜像打包完成后,执行python3脚本,这个脚本是在服务器上的,因为服务端部署也想这么用。运行时传入项目名称、镜像、哪台机器。 运行环境在.gitlab-ci.yml 中指定。
deploy_204:
stage: build
variables:
ENV: 204 # 申明变量,部署机器为204
script:
- chmod +x ./deploy/deploy-test.sh
- ./deploy/deploy-test.sh $ENV # 部署传递环境变量过去
when: manual # 手动触发,打包并部署
retry: 2
tags:
- ai_fe_group_runner
#!/bin/bash
ENV=$1 # 接收变量
# 省略
# 打包镜像
# *****
# 上传镜像
# 省略
python3 /home/user/opt/auto_script/deploy/deploy.py ${PROJECT_NAME} ${TEST_ENV_IMAGE} ${ENV}
最后是这个样子
为什么打包和部署都使用同一个stage,不分开操作?
之前是做的分开的,在提交代码后会立即执行打包,需要部署时执行第二个stage 去部署。后来因为占用runner并发数量,默认同时只能执行一个,可以改。人多了,代码改动太频繁了。而且我们的测试都是不同的功能/分支在不同的机器测试,一般不会在多个测试机器共用一个分支去测试,没有必要每次改动都打包还没有复用。
至此,各种打包环境问题和部署主动权都解决了。
总结 首先搞明白了gitlab、gitlab-ci、gitlab-runner的关系。需要安装并注册gitlab-runner。然后配置.gitlab-ci.yml,还需要写一些shell脚本。打包并优化docker镜像。部署到指定机器。