Docker配置Gitlab-runner实现自动化容器化部署前端项目

发布于:2025-07-25 ⋅ 阅读:(19) ⋅ 点赞:(0)

叠甲前言

本文仅作为个人学习GitLab的CI/CD功能记录,不适合作为专业性指导,如有纰漏,烦请君指正。

云主机注册Gitlab Runner 自动化构建部署的弊端

前一文中,我们在Linux云主机上注册了Gitlab-runner, 每次在gitlab流水线上发起构建部署时,云主机上的gitLab-runner先校验环境,再拉取项目依赖,然后将打包生成的产物放置到云主机的本地目录里,最后用nginx将公共访问端口映射到产物放置的目录。它不可避免的会存在3个问题:

  1. 依赖冲突,资源未隔离。 如果项目很多,管理将会非常麻烦,尤其还存在项目之间依赖资源冲突【比如 A项目用node14,B项目用node20】,构建需要频繁切换环境。同时一些项目如果要清理移除【或者更新】,那么需要去一一寻找并清理此项目独有的冗余依赖和资源,同时又需要避免一些依赖可能其他项目正在使用,所以会不可避免存在项目与项目之间相互影响、资源泄漏。
  2. 可移植性差。 如果云主机故障、停用、资源超额、续费昂贵等,要将已有项目迁移,那么就存在新云主机环境配置等各种繁琐操作【比如 不仅需要从新走一遍配置流程,同时不同操作系统对同一工具存在配置差异】,如果项目很多,尤其是项目迭代很久,使用到的各类资源多时,迁移工作会更加麻烦。
  3. 安全性问题。 云主机上放置了各种项目的各种依赖和资源,打包产物也是直接挂到云主机上,通过nginx将公共访问端口映射到产物放置的目录,会存在安全性问题,一些违规脚本可能可以直接操作到云主机上的目录和文件。

针对这3个问题,我们采用目前主流的自动化容器化部署工具Docker来解决这些问题。

Docker

什么是Docker?Docker有什么用?
Docker 是一个开源的容器化平台,它允许开发者将应用程序及其所有依赖(如代码、运行时环境、库、配置文件等)打包到一个标准化的容器中,从而实现 “一次构建,到处运行” 的目标。
大白话就是:Docker 解决了 “在我电脑上能运行,在你电脑上却不行” 的环境一致性问题。

Docker由什么构成?
Docker由镜像、容器、容器引擎构成。

镜像是什么?
镜像就是一个模版,包含了应用运行的所有环境、依赖、代码。
举个例子:
我们要打包一个前端项目,那么我们会先基于NodeJs镜像为基础,下载项目依赖到/node_modules,然后用webpack打包成一个静态资源,再以此用Docker 构建成一个项目镜像。
其中,NodeJs镜像也可以被其他项目所使用,你构建出来的项目镜像也可以在不同容器中加载,镜像就像一个规定好具体功能的模版,一次创建后,可以多次使用,嵌套使用。
镜像本身不可修改,只可读。

容器是什么?
容器就是基于镜像创建的运行实例,是一个完全独立的运行单元。容器会牢牢包裹镜像,在镜像基础上添加了一层可写层,所有运行时的修改【如文件创建修改】都会保存到这一层,不会影响到原始镜像。你可以理解为,镜像是“类”, 容器是基于类构建的“对象”。

容器引擎是什么?
容器引擎是 Docker 的核心运行环境,负责管理镜像和容器的生命周期(创建、启动、停止、删除等)

怎么使用Docker?

在使用之前,我们需要知道 配置这一套自动化部署流程的最终目地是什么:

即我们修改了前端项目代码,提交到Gitlab后,只需要在Gitlab上 CI/CD流水线
点击一下构建、发布就可以将最新的修改迅速部署到测试、线上环境。

Docker在上述流程中承担的工作就是 处理构建打包,并将打包产物通过创建容器加载镜像的方式部署
其实流程很简单,可以看下方简化图:
在这里插入图片描述

准备工作

  1. 拥有一台云主机
  2. 拥有一个Gitlab管理员权限账号,并将项目代码远程地址链接到Gitlab。

安装Docker

云主机运行(我的是Linux):

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

添加官方GPG密钥

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL --connect-timeout 10 --max-time 30 https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

添加Docker软件源

echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

安装Docker Engine

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

验证安装

sudo docker run hello-world

出现这条信息说明docker安装成功。

添加国内镜像

考虑到官方镜像仓库服务器距离东大比较远,建议添加国内镜像:

新建一个daemon.json文件,位置在 /etc/docker/daemon.json
在这里插入图片描述
json文件填入以下内容:

{
    "registry-mirrors": [
        "https://docker.registry.cyou",
        "https://docker-cf.registry.cyou",
        "https://dockercf.jsdelivr.fyi",
        "https://docker.jsdelivr.fyi",
        "https://dockertest.jsdelivr.fyi",
        "https://mirror.aliyuncs.com",
        "https://dockerproxy.com",
        "https://mirror.baidubce.com",
        "https://docker.m.daocloud.io",
        "https://docker.nju.edu.cn",
        "https://docker.mirrors.sjtug.sjtu.edu.cn",
        "https://docker.mirrors.ustc.edu.cn",
        "https://mirror.iscas.ac.cn",
        "https://docker.rainbond.cc"
    ]
}

更新配置

sudo systemctl daemon-reload

重启Docker

sudo systemctl restart docker

查看国内镜像配置是否成功

docker info

如果出现镜像地址,说明配置成功了
在这里插入图片描述

Docker拉取Nginx镜像

docker pull nginx

在这里插入图片描述

Docker安装NodeJS镜像

docker pull node:20

在这里插入图片描述

Docker安装GitLab-Runner镜像

创建gitlab-runner配置文件目录
(之后你的runner有什么配置需要改的可以直接在这个目录找到 /srv/gitlab-runner/config )

sudo mkdir -p /srv/gitlab-runner/config

安装镜像并启动gitlab-runner容器

sudo docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest

在这里插入图片描述
在这里插入图片描述

在Docker的Gitlab-Runner容器中注册Runner

什么是注册?

Gitlab流水线自动化构建、部署等任务Job需要指定一个执行者,也就是用哪一个gitlab-runner去完成这个任务。而gitlab-runner只有注册后才会与gitlab项目/项目群组关联到。

生成一个群组gitlab-runner
在 项目的 setting->CI/CD->runner 选择创建群组runner

群组runner允许在同一个群组下,所有的项目都可以使用它去执行流水线任务

在这里插入图片描述
拷贝到 url 和 token值,云主机执行:

sudo docker exec -it gitlab-runner gitlab-runner register \
  --non-interactive \
  --url "这里填Url" \
  --registration-token "这里填token" \
  --executor "docker" \
  --docker-image alpine:latest \
  --description "docker-runner" \
  --tag-list "docker,linux,可以填自定义标签" \
  --run-untagged="true" \
  --locked="false" \
  --access-level="not_protected"

参数说明
--tag-list: 就是runner的标签列表,我们在编辑项目.gitlab-ci.yml文件时,可以给Job任务设置标签,这样执行任务时,就会去找用于这个标签的runner。

验证是否注册成功
在 项目的 setting->CI/CD->runner中可以看到状态:
在这里插入图片描述

配置项目的Dockerfile

在前端项目的根目录新建一个Dockerfile文件:
具体语法含义见 Docker官方文档:Dockerfile配置参考 和注解:
在这里插入图片描述
它本身并不难理解,常用的也就那几个,花点时间认真看一下文档学习一下,
可以参考我的(你的项目具体要看你自己的业务):

# 使用Nodejs20镜像环境,并命名为 builder阶段
FROM node:20 As builder
# 将builder阶段工作目录设置为 /app
WORKDIR /app
# 将项目所有文件复制到/app目录
COPY . .
# 允许传入构建环境【stage测试环境, product 线上环境】
ARG BUILD_SCRIPT=stage
# 安装项目依赖以及执行前端脚本【npm run xxx这个取决于你项目打包命令是怎样的,我的会打包生成build文件夹[线上环境是build_online文件夹]】
RUN npm install && npm run $BUILD_SCRIPT
# 分环境统一输出目录
RUN if [ "$BUILD_SCRIPT" = "product" ]; then \
      cp -r /app/build_online /app/dist; \
    else \
      cp -r /app/build /app/dist; \
    fi

# 使用nginx最新版镜像环境
FROM nginx:latest
# 将builder阶段的/app/dist目录下的生成文件 复制到 nginx镜像的默认静态资源目录
COPY --from=builder /app/dist /usr/share/nginx/html
# 声明容器对外暴露80端口(nginx默认端口)
EXPOSE 80
# 启动nginx映射服务
CMD ["nginx", "-g", "daemon off;"]

配置.gitlab-ci.yml文件

在前端项目的根目录新建一个.gitlab-ci.yml文件:
在这里插入图片描述
具体语法含义见 GitLab 官方文档:CI/CD 配置参考 和注解:
可以参考我的(你的项目具体要看你自己的业务):

# 定义Job任务脚本流程顺序:构建 -> 部署(测试) -> 发布(线上)
stages:
  - build
  - deploy
  - publish

# 执行构建测试环境脚本
Build_Test_Job:
  stage: build     # 流程阶段
  image: docker:latest
  services:
    - docker:dind  # dind意思是Docker in Docker, 让你的 CI-Job能在Runner容器里直接执行 Docker 命令(比如 docker build、docker run 等),就像在物理机上一样。
  when: manual     # 手动触发, 可以自定义条件触发 
  only:
    - /^\d{8}$/    # 限制分支,正则表达式匹配只有分支名包含8个数字的才能生成此脚本【具体原因见末尾《1》】
  tags: 
    - webPro       # tags匹配Runner,只有Runner具有这个Tag,任务才能在这个Runner上执行,【查看Runner-Tag见《2》】
  script:
    - echo "开始构建测试环境Docker镜像..."    # echo 是输出日志
    - docker build --build-arg BUILD_SCRIPT=stage -t webpack_study:latest .  # docker构建一个镜像 命名为webpack_study标签为latest
    - docker save webpack_study:latest -o webpack_study.tar # 将构建好的镜像保存为tar文件
    - echo "测试环境Docker镜像构建并打包为tar文件完成!"
    - echo "尝试导出构建产物dist文件夹和打包镜像..."
    - docker create --name temp_container webpack_study:latest  # 创建一个临时容器加载镜像
    - docker cp temp_container:/usr/share/nginx/html ./dist     # 从临时容器中导出build产物
    - docker rm temp_container                                  # 删除临时容器
    - echo "导出构建产物dist文件夹和打包镜像完成..."
  artifacts:
    paths:
      - webpack_study.tar  # docker产物
      - dist

Deploy_Test_Job:
  stage: deploy
  image: docker:latest
  services:
    - docker:dind
  when: manual
  only:
    - /^\d{8}$/
  tags: 
    - webPro
  needs: ["Build_Test_Job"] # 执行必要的前置条件
  script:
    - echo "开始在Docker容器上部署测试环境镜像..."
    - docker load -i webpack_study.tar              # 加载build阶段产物(打包的镜像)到本地
    - docker stop docker_webpack_study || true      # 如果有 容器叫 docker_webpack_study, 将其停止并移除
    - docker rm docker_webpack_study || true
    - docker run -d --name docker_webpack_study -p 8087:80 webpack_study:latest # 启动容器docker_webpack_study加载镜像webpack_study:latest把容器的80端口映射到服务器的8087端口
    - echo "测试环境Docker容器上部署镜像完成!"

# 执行构建线上环境脚本
Build_Online_Job:
  stage: build     # 流程阶段
  image: docker:latest
  services:
    - docker:dind
  when: manual     # 手动触发, 可以自定义条件触发 
  only:
    - /^\d{8}$/    # 限制分支,正则表达式匹配只有分支名包含8个数字的才能生成此脚本【具体原因见末尾《1》】
  tags: 
    - webPro       # tags匹配Runner,只有Runner具有这个Tag,任务才能在这个Runner上执行,【查看Runner-Tag见《2》】
  script:
    - echo "开始构建线上环境Docker镜像..."    # echo 是输出日志
    - docker build --build-arg BUILD_SCRIPT=product -t webpack_study_online:latest .  # docker构建一个镜像 命名为webpack_study标签为latest
    - docker save webpack_study_online:latest -o webpack_study_online.tar # 将构建好的镜像保存为tar文件
    - echo "线上环境Docker镜像构建并打包为tar文件完成!"
    - echo "尝试导出构建产物dist_online文件夹和打包镜像..."
    - docker create --name temp_container webpack_study_online:latest  # 创建一个临时容器加载镜像
    - docker cp temp_container:/usr/share/nginx/html ./dist_online     # 从临时容器中导出build产物
    - docker rm temp_container                                  # 删除临时容器
    - echo "导出构建产物dist_online文件夹和打包镜像完成..."
  artifacts:
    paths:
      - webpack_study_online.tar  # docker产物
      - dist_online

Publish_Online_Job:
  stage: publish
  image: docker:latest
  services:
    - docker:dind
  when: manual
  only:
    - /^\d{8}$/
  tags: 
    - webPro
  needs: ["Build_Online_Job"] # 执行必要的前置条件
  script:
    - echo "开始在Docker容器上部署线上环境镜像..."
    - docker load -i webpack_study_online.tar              # 加载build阶段产物(打包的镜像)到本地
    - docker stop docker_webpack_study_online || true      # 如果有 容器叫 docker_webpack_study, 将其停止并移除
    - docker rm docker_webpack_study_online || true
    - docker run -d --name docker_webpack_study_online -p 8089:80 webpack_study_online:latest # 启动容器docker_webpack_study加载镜像webpack_study:latest把容器的80端口映射到服务器的8087端口
    - echo "线上环境Docker容器上部署镜像完成!"

#《1》
# 需求决定:前端项目因为每周五都要发一次版本,分支名字就是 20250718,分支上线后会合并到main分支,main分支的代码就是稳定的,所以打包构建分支只允许周分支生成Job
#《2》
# GitLab -> Project -> setting -> CI/CD -> Runner
# 云服务器测试环境端口是:8087
# 云服务器线上环境端口是:8089
# 这里的端口需要你在云服务器上开通入口安全组。

到此,我们修改了前端项目代码,提交到Gitlab后,只需要在Gitlab上 CI/CD流水线
点击一下构建、发布就可以将最新的修改迅速部署到测试、线上环境。

在这里插入图片描述

其他

  1. 不止前端项目,什么安卓、IOS、Flutter、RN、后端项目等其他项目其实都可以用Docker与本文相同的方式实现容器化部署,唯一区别的就是在打包的脚本内容不同,比如 安卓的要到gradle命令, IOS要用到xcodebuild命令等。

完整Demo项目地址
Github Docker部署前端项目仓库地址
tips: 记得拉20240718 分支

可能遇到的问题

  1. Docker执行流水线构建时,出现 2375问题:
    在这里插入图片描述
    解决方案
    找到 /srv/gitlab-runner/config 目录下的 config.toml文件:
    在这里插入图片描述
    volumes 这一栏进行扩充:
    在这里插入图片描述
volumes = ["/cache", "/usr/bin/docker:/usr/bin/docker", "/var/run/docker.sock:/var/run/docker.sock"]

解释说明:
Docker 2375问题解决


网站公告

今日签到

点亮在社区的每一天
去签到