我是如何用gitlab-ci、docker解决前端部署问题的

679 阅读4分钟

几个月之前,我们前端的项目打包部署是由测试同学来做的。前端想部署一个东西到测试环境查看效果都要让测试同学帮忙部署。还经常遇到因为网络原因npm install下载失败,或者因为node版本问题打包失败,docker镜像跑不起来等各种各样的问题。顺利的时候半天搞定,不顺利的时候可能得好几天才能搞定。终于我忍不了了,申请了一台测试服务器的账号。

解决两个问题

  1. 环境问题,包下载失败,部署失败等。
  2. 部署主动权,怎么样让前端同学自己部署。直接去服务器部署效率低,容易出错,尤其很多前端同学linux服务器都没接触过。所以考虑用Gitlab CI来做自动部署。

gitlab、gitlab-ci、gitlab-runner的关系

我花了很长时间捋清楚 gitlab、gitlab-ci、gitlab-runner的关系。

这里画个简易的图简单介绍。个人理解,错误请指正。

image.png

我对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} 

最后是这个样子

image.png

为什么打包和部署都使用同一个stage,不分开操作?

之前是做的分开的,在提交代码后会立即执行打包,需要部署时执行第二个stage 去部署。后来因为占用runner并发数量,默认同时只能执行一个,可以改。人多了,代码改动太频繁了。而且我们的测试都是不同的功能/分支在不同的机器测试,一般不会在多个测试机器共用一个分支去测试,没有必要每次改动都打包还没有复用。

至此,各种打包环境问题和部署主动权都解决了。

总结 首先搞明白了gitlab、gitlab-ci、gitlab-runner的关系。需要安装并注册gitlab-runner。然后配置.gitlab-ci.yml,还需要写一些shell脚本。打包并优化docker镜像。部署到指定机器。