侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论
Web

使用 Gitlab 自动部署前端项目

barwe
2022-06-20 / 0 评论 / 0 点赞 / 1,589 阅读 / 5,934 字
温馨提示:
本文最后更新于 2022-06-20,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Gitlab Runner 是一个单独运行的服务,它可以集成到 Gitlab 服务中,用于每次跟新代码时自动执行一些程序,例如自动编译、自动部署等。

使用 Gitlab Runner 可以帮助我们避免重复无聊的打包和部署工作,我们只需要修改源代码并上传到 Gitlab,Gitlab 检测到更新后自动调用 Gitlab Runner 进行编译和部署。

获取注册令牌

在 Gitlab 项目侧边栏中找到 设置 - CI/CD - Runner 设置面板:

image-20220615092237295

如图所示位置标识出了注册 Gitlab Runner 需要的 Gitlab 地址和令牌。

Gitlab Runner 除了为单个项目配置外,还可以为项目组或者全局配置。

Gitlab Runner

在容器中启动服务

TEMP_DIR=${HOME}/dockers/gitlab-runner
docker run -d --name gitlab-runner --restart always -v ${TEMP_DIR}/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest

注册到 Gitlab

然后在容器中将启动的 Gitlab Runner 注册到指定的 Gitlab 中:

docker exec -it gitlab-runner bash
gitlab-runner register # 注册

#> Please enter the gitlab-ci coordinator URL
输入网址
#> Please enter the gitlab-ci token for this runner
输入令牌
#> Please enter the gitlab-ci description for this runner
输入 Runner 的描述信息
#> Please enter the gitlab-ci tags for this runner (comma separated):
输入 Runner 的标签,使用逗号分隔
#> Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
输入 docker
#> Please enter the Docker image (eg. ruby:2.1):
设置 executor 版本 alpine:latest

exit # 退出容器

注册完之后回到 Gitlab 查看令牌的那个地方,刷新页面,应该能看到我们刚刚注册的那个 Runner:

image-20220615100251768

默认配置文件

启动容器后能看到这个配置文件:config/config.toml

concurrent = 1 # 可以同时运行的作业数量
check_interval = 0 # 检查新作业的时间间隔

[session_server]
session_timeout = 1800 # 作业完成后会话可以保持活动状态的秒数

[[runners]]
name = "gmp web"
url = "xxx"
token = "xxx"
executor = "docker"
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false # 禁用对 Docker 守护程序连接的 TLS 验证
image = "alpine:latest" # 用于运行作业的镜像
privileged = false # 禁止容器以特权模式运行
disable_entrypoint_overwrite = false # 禁用镜像覆盖 entrypoint
oom_kill_disable = false # 如果发生内存不足 (OOM) 错误,不终止容器中的进程
disable_cache = false # 不禁用未映射到主机目录的本地缓存
volumes = ["/cache"] # 安装挂载卷,与 docker -v 的语法相同
shm_size = 0 # 镜像的共享内存大小(以字节为单位)

具体含义见 https://www.jianshu.com/p/6decaed7b648

官方参数列表见 https://docs.gitlab.com/runner/configuration/advanced-configuration.html

打包结果存储到宿主机

在上面注册的过程中,Runner 的 executor 选择了 Docker,而 Runner 本身也是以用 Docker 运行的,这意味着:使用容器运行的 Runner 会自己创建新的容器来处理触发的流水线。

CONTAINER ID   NAMES                                                                   CREATED          STATE
19f709df3c3f   runner-ypns6zu7-project-71-concurrent-0-a9716f543f0129db-build-3        24 seconds ago   running
ce4f11beb93a   runner-ypns6zu7-project-71-concurrent-0-a9716f543f0129db-predefined-2   2 minutes ago    exited
a98fdbb52925   runner-ypns6zu7-project-71-concurrent-0-a9716f543f0129db-predefined-1   2 minutes ago    exited
7a63f51f2317   runner-ypns6zu7-project-71-concurrent-0-a9716f543f0129db-predefined-0   2 minutes ago    exited
257a6991e425   gitlab-runner                                                           22 hours ago     running

那么 Runner 流水线触发的容器中生成的资源怎么同步到宿主机呢?Runner 的配置文件提供了 volumes 字段用于将生成的资源挂载到宿主机。

以前端项目为例,我们需要将打包好的 dist 目录转移到宿主机中。

首先我们需要在宿主机和容器的容器中确定一对用于挂载资源的路径,比如 /home/www/gmp,gmp 是我们的项目名称。

确保宿主机上存在该目录:

mkdir -p /home/www/gmp

然后在 Runner 的配置文件中声明挂载:

[[runners]]
[runners.docker]
volumes = [
    "/cache:/cache",
    "/home/www/gmp:/home/www/gmp",
]

此外,需要在流水线配置中创建挂载的打包存放目录:

before_script:
    - mkdir -p /home/www/gmp

这样在作业中将生成的打包资源拷贝到这个目录下,就能在宿主机上看到生成的结果了。

打包完成后重启 Nginx 服务器

Runner 自动为每一个作业创建一个容器,并在作业完成后自动移除容器,我们可以称之为 作业容器

作业容器负责打包应用,应用打包完成后还应该自动重启对应的 Nginx 服务器以刷新资源。

对应的 Nginx 服务器是在宿主机中以 Docker 容器运行的,因此要实现作业容器自动重启 Nginx 服务器,就需要让作业容器内部能够与宿主机的 Docker 守护进程通信。

实现容器内部与宿主机的 Docker 守护进程通信,我们只需要在创建容器时额外挂载两个东西:

[[runners]]
[runners.docker]
volumes = [
    # ...
    "/var/run/docker.sock:/var/run/docker.sock",
    "/usr/bin/docker:/usr/bin/docker"
]
  • /var/run/docker.sock 允许作业容器与宿主机的 Docker 守护进程通信
  • /usr/bin/docker 允许作业容器使用宿主机的 docker 命令来管理宿主机的容器

作业容器优先使用本地镜像

参考 https://docs.gitlab.com/runner/executors/docker.html#how-pull-policies-work

配置中的pull_policy属性用来配置作业容器镜像的拉取策略,取值有三:

  • never 只使用本地镜像仓库,需要预先准备好需要的镜像
  • if-not-present 优先使用本地镜像,本地没有时获取远程镜像
  • always 总是从远程仓库拉取镜像

所以只需要设置

[runners.docker]
pull_policy = "if-not-present"

一个完整的 Runner 配置文件

check_interval = 0
concurrent = 1

[session_server]
session_timeout = 1800

[[runners]]
executor = "docker"
name = "gmp web"
url = "xxx"
token = "xxx"
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
disable_cache = false
disable_entrypoint_overwrite = false
image = "alpine:latest"
oom_kill_disable = false
privileged = false
shm_size = 0
tls_verify = false
pull_policy = "if-not-present"
volumes = [
  "/cache:/cache",
  "/home/www/gmp:/home/www/gmp",
  "/var/run/docker.sock:/var/run/docker.sock",
  "/usr/bin/docker:/usr/bin/docker",
]

配置 .gitlab-ci.yml 文件

该文件会被 Gitlab 自动识别,Gitlab 根据其配置自动调用 Gitlab Runner 进行自动化打包和部署。

主要配置项:

  • image CI/CD 需要的环境,例如前端项目的打包部署需要 node 环境

  • before_script 开始执行步骤前需要做的一些准备工作,例如安装打包工具

  • after_script 在所有步骤执行完成之后做一些善后工作

  • cache 指定在执行任务过程中需要缓存的目录,例如 node_modules

  • stages 按顺序声明要执行的步骤,步骤的执行是阻塞式的

  • JOB_NAME 声明步骤中的各个任务,一个步骤可以包含多个任务,默认情况下同一个步骤中的任务并行执行

    • stage 该任务所属的步骤
    • tags 执行该任务的 Runner
    • only 只在指定分支或者标签上触发该任务
    • scripts 该任务需要执行的具体脚本
    • artifacts 将该任务生成的资源传递给下一个任务,配合 dependencies 使用
      • expire_in 设置资源过期时间,例如 60 mins
      • paths 需要传递的资源路径列表,例如 ['node_modules/']

下面是一条打包和部署的流水线:

variables:
    DIST_DIR: /home/www/gmp
    WEB_CONTAINER: gmp-web

before_script:
    - mkdir -p ${DIST_DIR}

cache:
    key: ${CI_BUILD_REF_NAME}
    paths:
        - node_modules/

stages:
    - deploy

deploy-job:
    image: 'node:14.17.4'
    stage: deploy
    tags: ['gmp_web']
    only: ['prod']
    script:
        - npm i -g pnpm
        - pnpm install
        - pnpm run build
        - rm -rf ${DIST_DIR}/*
        - cp -rf dist/* ${DIST_DIR}/

每个 Job 会生成一个 Container 并且在 Job 完成之后立即销毁。

设置触发流水线的分支

在上面的配置中,我们设置了只有 prod 分支发生更新时才触发流水线。在这个设想中,prod 分支作为正式发布的分支,master 分支作为日常维护的主分支,因为平时 master 分支更新比较频繁,如果检测 master 分支的更新触发流水线有点浪费资源,所以才新设了个 prod 分支,只在有需要时才将 master 的内容合并到 prod 上面。

因此考虑将 prod 分支保护起来:只允许合并到 prod,不允许推送到 prod。打开 设置 - 仓库 - 受保护分支 面板设置 prod 分支的保护策略:

image-20220615162418263

当需要打包部署时只需要将 master 分支合并到 prod 分支即可。

启动 Nginx 服务器

NAME=gmp-web
NGINX=/root/containers/$NAME

## 运行测试容器导出配置文件
mkdir -p $NGINX/conf.d && cd $NGINX
docker run -d --name $NAME -p 8081:80 nginx
docker cp $NAME:/etc/nginx/nginx.conf $NGINX/
docker cp $NAME:/etc/nginx/conf.d $NGINX/
docker rm -f $NAME

## 重新启动容器
docker run -d --name $NAME -p 81:80 \
-v $NGINX/html:/usr/share/nginx/html \
-v $NGINX/log:/var/log/nginx \
-v $NGINX/nginx.conf:/etc/nginx/nginx.conf \
-v $NGINX/conf.d:/etc/nginx/conf.d \
nginx

设置 API 转发:

server {
    location /api/ {
        proxy_pass xxxxxxxxxxxxx;
        rewrite ^/api/(.*)$ /$1 break;
    }
}

SPA 设置资源查找策略:

location / {
    root /usr/share/nginx/html;
    index index.html;
    try_files $uri $uri/ /index.html;
}
0

评论区