点击在我的博客 xuxusheng.com 中查看,有更好的排版哦~
一、背景
当项目比较少,或者更新不频繁的时候(或者技术比较菜的时候),手动部署部署还能够接受,但是一旦部署次数频繁了,手动操作就是一件痛苦万分的事情了。
公司内部使用的是 jenkins
,从成熟稳定的角度来看,是非常符合要求的,但是针对个人项目,第一没有公司的项目那么复杂,第二在流程上也不需要考虑多人协作、测试等需求,这个时候 jenkins
就显得过于庞大了一点(奇丑无比)。
调研了一圈目前可以使用的主流(免费)CI
工具,最终决定采用 GitLab CI
来作为自己的博客和个人项目的 CI
平台。
二、简介
GitLab CI
的 UI
非常清新,文档也非常全面,同时使用起来也非常简单,简直是程序员居家必备的好帮手。
GitLab CI
本身只是作为仓库里附加的一个功能,并不提供项目构建的环境,或者可以将 GitLab CI
理解成一个调度员,所做的事情就是决定何时触发构建任务,以及在哪个环境下执行构建任务等等。
三、初识 GitLab Runner
GitLab Runner 是一个开源项目,用于运行您的任务并将结果发送回 GitLab。它与 GitLab CI 一起使用,GitLab CI 是 GitLab 随附的开源持续集成服务,用于协调任务。
英文好的同学建议(英文不好的同学也建议)直接阅读官方文档:GitLab Runner
就我个人理解来看,GitLab Runner
其实就是将某台服务器,注册成为 GitLab CI
的任务执行单元,用于在 CI
过程中,执行相应的任务,类似于一个分布式系统的 master
和 slave
的角色。
而 Gitlab CI
对 Runner
并没有什么严格的要求,可以是一台 linux
实体机,也可以是 Virtual Box
里的虚拟机,甚至可以是某个 Docker
容器。
针对不同的 Runner
,可以在注册时划分不同的固有角色(可执行的任务不同,详情参见文档:Executors),以及打上不同的自定义标签,以便于 GitLab CI
在调度时灵活分配任务,多个 Runner
并行执行任务等等。
GitLab
针对免费用户,提供了每个月 2000 分钟的免费构建时间,在不超过这个时间时,可以直接使用 GitLab
提供的共享 Runner
。
但是使用别人的服务器来进行构建任务,总是会暴露一些敏感信息出去,所以自建 GitLab Runner
,就是最佳选择了。
四、自建 GitLab Runner
前面提到了 GitLab Runner
其实就是一台执行任务的机器而已,GitLab
也提供了现成的脚本,将当前机器注册为 Runner
。
但是当我们期望在同一个系统下,部署不同种类的 Runner
来执行不同的任务,比如一个 Runner
专门执行构建任务,另一个 Runner
专门执行部署任务。
或者说我们期望注册一个 Runner
,但是又不希望影响系统现有的环境时,直接执行脚本将宿主机注册为 Runner
的方式就不太合适了。
Virtual Box
创建多个虚拟机当然可以解决这个问题,但是创建多个虚拟机对资源的消耗问题也是显而易见的。
这个时候 Docker
就又出来拯救世界了。
早在调研 jenkins
的时候,就期望能够使用 docker
来部署 jenkins
,然后在这个 jenkins
容器中,执行包含了 docker
命令的构建脚本,这就涉及到了在 docker
容器中执行 docker
命令的问题,解决起来真的是一把辛酸泪。
得益于 docker in docker
(参见:https://hub.docker.com/_/docker?tab=description&page=1),这个坑总算是能够填了。
Gitlab CI
对 Docker
的支持非常好,文档之类的东西非常全面,建议直接阅读官方文档即可:
实在不想看官方文档,也可以继续往下阅读。
1. 创建 Runner 容器
创建一个 gitlab-runner-docker
目录,然后新建一个 docker-compose.yml
文件,内容如下:
version: "3"
services:
app:
image: gitlab/gitlab-runner
container_name: gitlab-runner-docker
restart: always
volumes:
- ./config:/etc/gitlab-runner
- /var/run/docker.sock:/var/run/docker.sock
在 gitlab-runner-docker
目录下执行 docker-compose up --build -d
命令,docker ps -a
即可看见刚才创建的容器,同时目录下会生成一个 config
目录,用于存放 Runner
的配置文件。
2.注册 Runner
执行命令 docker exec -it gitlab-runner-docker gitlab-runner register
(或者进入容器内部执行 gitlab-runner register
也可以)
接下来会看到一系列的输入项,一步一步输入即可。
a) 输入 GitLab
的地址。
如果是使用的官方的 GitLab
,就输入 https://gitlab.com
,自建的 GitLab
就输入自己的IP或域名即可。
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://gitlab.com
b) 输入 Token
来注册 Runner
。
在 GitLab
的仓库 Setting -> CI/CD
设置页面中,展开 Runners
部分,即可看到生成的 Token
,复制粘贴即可。
Please enter the gitlab-ci token for this runner
xxxToken
c) 输入一段 Runner
的描述,之后可以在 GitLab
管理页面进行修改。
Please enter the gitlab-ci description for this runner
[hostame] my-runner
d) 输入 Runner
关联的标签,之后可以在 GitLab
管理页面进行修改。
Please enter the gitlab-ci tags for this runner (comma separated):
my-tag,another-tag
e) Enter the Runner executor:
GitLab Runner
内置了多种 executor
,不同类型的 executor
的区别,可以参考文档:Executors。
这里我们填写 docker
。
Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
docker
f) If you chose Docker as your executor, you’ll be asked for the default image to be used for projects that do not define one in .gitlab-ci.yml:
如果选择了 executor
为 docker
,那么就需要选择一个默认的镜像。
最开始接触的时候,这里可能会有点难以理解,需要说明的是,我们刚才使用 docker
创建的这个 Runner
容器,并不是构建脚本直接执行的环境。
打个比方,在构建任务中,我们需要执行一段 js
脚本,那么我们就需要 nodejs
环境,也就是说需要创建一个 nodejs
容器,那么这个容器,会由刚才创建的 Runner
容器来进行创建。
也就是说我们实际上是在一个 Docker
容器中,再创建另一个容器,而再次创建出来的这个容器,才是构建任务实际运行的环境。
针对 GitLab CI
的每一阶段的构建任务,我们都可以指定不同的 Docker
镜像,当执行到某个任务时,Runner
就会根据这个任务指定的镜像创建出相应的容器,再在这个容器内执行任务。
而这一步需要指定的默认镜像,就是当 .gitlab-ci.yml
文件中没有指定任务需要的镜像时使用的。
Please enter the Docker image (eg. ruby:2.1):
alpine:latest
官方文档的例子中,默认镜像使用的是 alpine:latest
,是个精简版的 linux
镜像,也就意味着啥工具都没有,每次执行构建任务前先装 git
等一系列工具显然不合理。
推荐使用 tico/docker
作为默认镜像,参见:https://hub.docker.com/r/tico/docker,这个镜像在官方 docker
镜像的基础上,加入了 curl
、php
、git
等等一系列常用的工具。
是的,没错,这个镜像是基于官方 docker
镜像的,这就意味着,我们仍然可以在这个镜像生成的容器中执行 docker
命令,这样在构建脚本中,就可以完美调用 docker
命令了。
3. 配置 Runner
进入 config
目录,会发现一个 config.toml
文件,里面是 gitlab-runner
相关的配置信息。
concurrent = 1
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "home-runner-docker"
url = "https://gitlab.com"
token = "xxxxxxxxxxxxxxx"
executor = "docker"
[runners.docker]
tls_verify = false
image = "tico/docker"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
当执行构建任务时如果出现以下报错,请将上面的配置文件中的 privileged
的值改为 true
。
参考:Use docker-in-docker with privileged mode
五、使用心得
以我的博客为例,首先是一个专门用来执行构建任务 Runner
容器。
当接收到博客的构建任务时,创建一个基于 tico/docker
镜像的容器,然后再在这个容器中执行构建脚本。
而构建脚本中,又调用 docker
命令创建了一个 nodejs
容器来进行打包编译,然后再将 build
之后生成的静态文件移入一个 nginx
镜像,作为最终部署使用的镜像并上传到阿里云容器服务。
接着 GitLab CI
会将部署任务发送至另一个专门用来执行部署任务的 Runner
容器,这个 Runner
容器会 ssh
登录上目标服务器,拉取最新的镜像并运行。
然后博客自动部署完毕啦,欢迎访问:
https://www.xuxusheng.com
贴出我的博客的 .gitlab-ci.yml
文件感受一下:
stages:
- build
- test
- release
# 构建
build:
stage: build
tags:
- xs1
- docker
image: tico/docker:latest
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375/
DOCKER_DRIVER: overlay2
DOCKER_REPO: $docker_repo
before_script:
- echo 'before_script'
- docker login --username=$docker_user -p $docker_pwd $docker_host
script:
- sh ./ci/build/build.sh
only:
- master
# 发布
release:
stage: release
tags:
- xs1
- ssh
variables:
DOCKER_REPO: $docker_repo
PROJECT_NAME: $project_name
SERVER: $deploy_server
PORT: $port
script:
- sh ./ci/release/release.sh
environment:
name: production
only:
- master
构建过程欣赏: