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

叠甲前言

本文仅作为个人学习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):

bash 复制代码
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg

添加官方GPG密钥

bash 复制代码
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软件源

bash 复制代码
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

bash 复制代码
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

验证安装

bash 复制代码
sudo docker run hello-world

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

添加国内镜像

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

新建一个daemon.json文件,位置在 /etc/docker/daemon.json

json文件填入以下内容:

bash 复制代码
{
    "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"
    ]
}

更新配置

bash 复制代码
sudo systemctl daemon-reload

重启Docker

bash 复制代码
sudo systemctl restart docker

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

bash 复制代码
docker info

如果出现镜像地址,说明配置成功了

Docker拉取Nginx镜像

bash 复制代码
docker pull nginx

Docker安装NodeJS镜像

bash 复制代码
docker pull node:20

Docker安装GitLab-Runner镜像

创建gitlab-runner配置文件目录

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

bash 复制代码
sudo mkdir -p /srv/gitlab-runner/config

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

bash 复制代码
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值,云主机执行:

bash 复制代码
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配置参考 和注解:

它本身并不难理解,常用的也就那几个,花点时间认真看一下文档学习一下,

可以参考我的(你的项目具体要看你自己的业务):

bash 复制代码
# 使用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 配置参考 和注解:

可以参考我的(你的项目具体要看你自己的业务):

bash 复制代码
# 定义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 这一栏进行扩充:
bash 复制代码
volumes = ["/cache", "/usr/bin/docker:/usr/bin/docker", "/var/run/docker.sock:/var/run/docker.sock"]

解释说明:
Docker 2375问题解决

相关推荐
TRACER~852 小时前
移动端自动化Appium框架
运维·appium·自动化
菜鸟是大神4 小时前
【已解决】docker: Error response from daemon: Get “https://registry-1.docker.io/v2/“: net/http: request c
http·docker·容器
kong@react4 小时前
docker安装 Elasticsearch、Kibana、IK 分词器
elasticsearch·docker·jenkins
MurphyStar4 小时前
Ubuntu22.04.5 LTS安装与使用Docker
运维·docker·容器
贺贺丿4 小时前
Docker2-容器应用工具及docker命令
linux·运维·docker·容器·自动化·云计算
亿刀5 小时前
【学习VPN之路】路由表
android·docker
超龄超能程序猿6 小时前
图片查重从设计到实现(2)Milvus安装准备etcd介绍、应用场景及Docker安装配置
docker·etcd·milvus
菜鸟起航ing6 小时前
SaaS型小程序自动化发布解决方案
运维·小程序·自动化
江湖有缘7 小时前
【Docker项目实战】在Docker环境下部署go-file文件分享工具
docker·容器·golang
云飞云共享云桌面7 小时前
制造工厂高效出图新技术——共享云桌面
运维·服务器·网络·3d·自动化·制造