博客:https://www.mintimate.cn Mintimate’s Blog,只为与你分享
在上一篇文章中,我们介绍了如何使用Docker搭建自己的GitLab代码托管平台。
GitLab作为一个成熟的DevOps工具,不仅提供了代码托管的功能,还内置了强大的CI/CD流水线。利用GitLab的CI/CD能力,配置Pipeline来实现自动化的编译、测试和部署。
本文将继续上篇的内容,重点介绍在自己搭建的GitLab平台上,如何配置Pipeline实现CI/CD:
- 学习Pipeline的基本概念、语法结构;
- 借助GitLab Runner来执行Pipeline中的job;
- 配置.gitlab-ci.yml文件,实现代码提交后自动编译、测试、部署到服务器。
其实配置起来,和GitHub Action类似,写法也和Docker Compose也类似。
GitLab Runner
GitLab Runner是GitLab CI/CD的核心组件,用于运行Pipeline中的 jobs。
它是一个独立的应用程序,需要单独安装。主要有以下几个主要版本:
- Shell Runner:最基础的Runner版本,在本地直接调用shell命令来执行jobs。
- Docker Runner:使用Docker容器来运行jobs,提供隔离的运行环境。
- Kubernetes Runner:在Kubernetes集群上运行jobs,可以动态规模扩展。
- Custom Runner:支持使用其他脚本语言如Python、Ruby来自定义Runner,提高扩展性。
- Group Runner:顾名思义,是面向特定组织或组内项目统一提供服务的Runner。
工作流程
为什么GitLab使用Pipeline流水线需要另外安装或者激活GitLab Runner呢? 其实GitLab Runner和GitLab基本是相互独立的。官方的流程图:
简单复述一下:
好的,关于GitLab Runner和GitLab的交互作业流程,可以这样理解:
- 开发者提交代码到GitLab,触发一个Pipeline。
- GitLab生成一个唯一的token,标识这个job,然后将这个job以及token发送给指定的Runner。
- Runner使用这个token来请求增加权限,访问代码仓库和构建工件。
- GitLab验证token,授权Runner获得临时访问权限。
- Runner利用授权的token,拉取代码,运行scripts,产生构建工件。
- 构建完成后,Runner上传工件到GitLab,在工作流中标记job成功或失败。
- GitLab使用token验证上传,确保来自授权的Runner,避免非法上传。
- GitLab标记job状态为success或failed。
这实现了一个 tokenized 的工作流,保证了job的安全性。
Runner和GitLab之间通过token来验证对方身份和授权,实现了解耦和安全隔离,避免因为Runner的部署影响到GitLab的正常运行。
版本对比
我们参考官方文档:https://docs.gitlab.com/runner/executors/
我这里总结一下:
Runner版本 | 执行方式 | 运行环境 | 优点 | 缺点 |
---|---|---|---|---|
Shell | 在本地直接调用shell,如bash、sh和zsh来运行jobs | 直接在本地GitLab Runner主机上运行 | 配置简单,无需其他依赖 | 无法提供隔离的运行环境,可能影响主机 |
Docker | 在指定的Docker镜像容器内运行jobs | Docker容器内运行,与主机隔离 | 提供隔离的运行环境,不会影响主机 | 需要在本地安装Docker环境 |
Kubernetes | 在Kubernetes集群内创建Pod运行jobs | Kubernetes Pod中运行 | 可以动态扩展,灵活调度资源 | 需要准备Kubernetes集群环境 |
Custom | 自定义脚本语言,如Python、Ruby来运行jobs | 根据脚本语言的运行时 | 高度灵活,可自定义编程语言 | 需要自行开发自定义的Runner |
Group | 针对特定GitLab组织或组项目运行jobs | 根据具体组设置 | 方便组内job共享,统一管理 | 不如单个项目拥有的Runner灵活 |
Autoscaling | 基于指标自动扩缩容Runner池 | 不同的云平台 | 根据负载自动调整Runner数量 | 需要了解特定云平台API |
我们GitLab都是Docker部署的,所以Shell的方式是走不通了;为了方便操作,我这里演示Docker版本。
注册Runner
我们这次演示注册Docker版本的Runner,在服务器上部署Runner之前,我们需要在GitLab的Web控制面板上,创建一个Runner实例,
我的服务器是腾讯云的轻量应用服务器,使用Debian的Linux镜像,所以我这里选择Linux:
重要的来了,出现了配置命令:
这个时候,我们需要记下:
- url: 你GitLab的直连域名/IP;
- token: 用于权限和交互的token,只会出现一次。
假设,url为example.gitlab,com
,token为helloworld
。
Docker部署Runner
在一台安装好Docker的服务器设备上,我们运行GitLab Runner的镜像拉取:
代码语言:shell复制sudo docker run -d --name gitlab-runner --restart always
-v /dockerData/gitlab-runner:/etc/gitlab-runner
-v /var/run/docker.sock:/var/run/docker.sock
gitlab/gitlab-runner:latest
其中:
-v /dockerData/gitlab-runner:/etc/gitlab-runner
: 将/dockerData/gitlab-runner目录挂载到容器内的/etc/gitlab-runner,用于保存runner的配置文件-v /var/run/docker.sock:/var/run/docker.sock
: 将docker宿主机的docker.sock挂载到容器内,这样runner容器可以访问宿主机的docker服务。
查看日志,如果报的是找不到配置文件,那么是无需担心的:
接下来,我们需要关联到GitLab平台上。
关联到GitLab
刚刚,我们已经新建了应该runner的注册信息:
url为
example.gitlab.com
,token为helloworld
这个时候,我们需要在部署了GitLab Runner的Docker服务器上,关联上:
代码语言:txt复制sudo docker run --rm -it -v /dockerData/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register
依次填写我们的url和token:
后面的配置,依次为:别名、executor 类型、默认的镜像。
这里只是默认镜像,.gitlab-ci.yml
没有为某个 job 指定镜像,会使用默认镜像。比如这里设置默认的镜像为node:20.8.0-bullseye
。
到此,我们的Runner就应该关联和注册好了:
Pipeline流水线
接下来,我们编写一个流水线。这里简单介绍一下,具体可以参考官网:
- https://docs.gitlab.com/ee/ci/pipelines/
简单概括一下GitLab Pipeline的主要步骤: 在每次的Git Push后,GitLab会读取根目录.gitlab-ci.yml
文件进行构建、测试和打包等操作。
gitlab-ci.yaml语法
.gitlab-ci.yml 使用 YAML 格式,主要包含以下几类语法:
全局定义
配置执行器,指令等全局信息。例如:
代码语言:yaml复制image: ruby:2.6
services:
- mysql
作业定义
每个作业至少包含 script 定义。例如:
代码语言:yaml复制build:
script:
- gem install bundler
- bundle install
阶段定义
用 stage 指定作业执行阶段。例如:
代码语言:yaml复制stages:
- build
- test
- deploy
build_job:
stage: build
作业规则
定义作业的规则、环境等信息。例如:
代码语言:yaml复制rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
environment: production
定义作业间依赖关系。例如:
代码语言:yaml复制deploy:
needs: [build, test]
我们可以组合这些语法构建完整的管道配置。
环境变量
有时候,我们需要设置一下服务器的密钥、服务器的IP,直接在.gitlab-ci.yaml
内写肯定是不安全的。我们可以设置环境变量:
之后,在编写时候调用:
Demo: 部署VitePress
接下来,我们演示一下如何在Pipeline部署我们的VitePress。是一款不错的文档框架,改天有机会和大家介绍一下:
我们写一个.gitlab-ci.yaml
:
image: node:lts-buster
cache: # 设置缓存
paths:
- .vitepress/dist/ # 缓存生成出来的文件目录
stages: # 全部的步骤
- build
- deploy
build-job: # 打包阶段
stage: build
script:
- echo "设置国内更新源"
- npm config set registry http://mirrors.cloud.tencent.com/npm/ # 设置国内源
- echo "执行依赖的更新"
- npm install
- echo "执行打包操作"
- npm run build
deploy-job: # 部署阶段
stage: deploy
script:
- echo "开始部署……"
- cp /etc/apt/sources.list /etc/apt/sources.list.bak
- echo "设置更新源"
- bash -c 'echo "deb http://mirrors.cloud.tencent.com/debian/ buster main non-free contrib" > /etc/apt/sources.list'
- bash -c 'echo "deb-src http://mirrors.cloud.tencent.com/debian/ buster main non-free contrib" >> /etc/apt/sources.list'
- bash -c 'echo "deb http://mirrors.cloud.tencent.com/debian/ buster-updates main non-free contrib" >> /etc/apt/sources.list'
- bash -c 'echo "deb-src http://mirrors.cloud.tencent.com/debian/ buster-updates main non-free contrib" >> /etc/apt/sources.list'
- bash -c 'echo "deb http://mirrors.cloud.tencent.com/debian/ buster-backports main non-free contrib" >> /etc/apt/sources.list'
- bash -c 'echo "deb-src http://mirrors.cloud.tencent.com/debian/ buster-backports main non-free contrib" >> /etc/apt/sources.list'
- bash -c 'echo "deb http://security.debian.org/ buster/updates main contrib non-free" >> /etc/apt/sources.list'
- bash -c 'echo "deb-src http://security.debian.org/ buster/updates main contrib non-free" >> /etc/apt/sources.list'
- apt update
- echo "安装rsync"
- apt install rsync -y
- echo "设置SSH无感验证"
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- mkdir -p ~/.ssh
- eval $(ssh-agent -s)
- '[[ -f /.dockerenv ]] && echo -e "Host *ntStrictHostKeyChecking nonn" > ~/.ssh/config'
- echo "SCP传输"
- ssh-add <(echo "$MINE_SERVER_KEY")
- rsync -r --delete .vitepress/dist/ ${MINE_SERVER_USER}@${MINE_SERVER_IP}:${MINE_SERER_PATH}
- echo "应用部署完成"
主要分为两个阶段:
- 打包阶段: 把VitePress打包为静态文件,用于部署到Nginx服务器上;
- 部署阶段: 将打包出来的静态文件,使用rsync部署到远程服务器上的Ngixn目录。
打包阶段
- 使用node LTS buster系统镜像,提供构建环境
- 设置npm国内源,加速下载依赖
- 安装依赖
- 执行打包构建,生成结果存放在缓存目录
- 缓存目录可重复利用,加速后续构建
部署阶段
- 替换apt源,安装rsync,提高部署效率
- 配置SSH免密登录,实现无感部署
- 使用rsync将打包结果同步到服务器
- 环境变量隐藏服务器信息,保证安全
- 完成结果部署到服务器指定目录
这样通过打包和部署两个阶段,使用脚本自动化执行,利用缓存、环境配置等特性,可以高效、安全的实现持续集成和持续部署。
最后在一次的Git提交后,可以触发打包和部署:
其实点进去也可以看到具体的日志:
说实话,比较难的是SSH的密钥添加部分,需要在GitLab CI作业中配置 SSH 免密登录,主要步骤:
代码语言:shell复制# 检查是否存在 ssh-agent,如果不存在则安装 openssh-client
'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
# 创建 .ssh 目录
mkdir -p ~/.ssh
# 启动 ssh-agent 管理私钥
eval $(ssh-agent -s)
# 如果在 Docker 中运行,配置 SSH 免密登录
# [[ -f /.dockerenv ]] 检查是否存在 Docker 环境标志文件
# echo -e "Host *ntStrictHostKeyChecking nonn"
# 向 ~/.ssh/config 写入禁用 StrictHostKeyChecking 的配置
[[ -f /.dockerenv ]] && echo -e "Host *ntStrictHostKeyChecking nonn" > ~/.ssh/config
实现了无需输入密码就能 SSH 登录到 GitLab Runner 中,从而自动化部署到服务器。
当然,流水线的其他配置还可以很多,有时间,我们单独起一篇文章为大家讲解~~
END
文章演示了如何通过Docker来部署Runner,并与GitLab项目实例关联注册,使其可以运行流水线作业。
一系列操作,让我们的GitLab更加丰富多彩。当然,有机会,教大家更详细的书写Pipeline的流水线脚本,让大家像写GitHub Action一样能熟练地书写脚本。
什么? 你还没用过GitHub Action? 改天有机会,也教大家如何进行书写~~
我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表