此前写过一篇文章介绍如何用Virtualbox Executor和Docker Executor来提供Gitlab Runner,但是这两个executor部署都比较繁琐复杂,特别是Virtualbox在同为AMD64 Linux的情况下可以用Linux Container来更加方便地进行配置。本文记录的是如何配置一个基于Linux Container的Gitlab Runner。
关于安装Linux Container,这篇文章已经介绍过这里就不重复了。
准备模板Container
为了节省时间,我们可以提前准备一个模板Container,这样就不用每次都安装dependency了。在接下来的步骤里我们创建一个名为grbase
(gitlab runner base)的ubuntu 22.04 amd64容器:
1
2
3
4
# 使用清华镜像源,使用储存池pool1存储数据
lxc launch mirror-images:ubuntu/jammy/amd64 grbase -s pool1
# 连接容器
lxc exec grbase bash
在容器里面根据自己的需要安装各种依赖,比如常见的build-essential
、ruby-full
,以及openssh-client
、rsync
等。其中作为Runner必须装的有git
、git-lfs
和(直接野binary安装的)gitlab-runner
:
1
2
3
4
5
6
# git和git-lfs
curl -s "https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh" | sudo bash
apt install -y git git-lfs
# 野binary安装gitlab-runner
curl -L --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64"
chmod +x /usr/local/bin/gitlab-runner
准备Runner所需的Script
在准备好模板Container之后就可以根据官方的docs来准备script了。我们按照官方的做法把base.sh
、prepare.sh
、run.sh
和cleanup.sh
都放到/opt/lxd-driver
里面,分别包含以下内容:
1
2
3
4
5
6
#!/usr/bin/bash
# /opt/lxd-driver/base.sh
CONTAINER_ID="runner-$CUSTOM_ENV_CI_RUNNER_ID-project-$CUSTOM_ENV_CI_PROJECT_ID-concurrent-$CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID-$CUSTOM_ENV_CI_JOB_ID"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/bash
# /opt/lxd-driver/prepare.sh
currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source ${currentDir}/base.sh # Get variables from base.
set -eo pipefail
# trap any error, and mark it as a system failure.
trap "exit $SYSTEM_FAILURE_EXIT_CODE" ERR
start_container () {
# 如果有同名container就删掉
if lxc info "$CONTAINER_ID" >/dev/null 2>/dev/null ; then
echo 'Found old container, deleting'
lxc delete -f "$CONTAINER_ID"
fi
# 这里就是跟官方不同的地方,我们复制一份模板(grbase)来做runner
lxc copy grbase "$CONTAINER_ID"
# 需要手动start
lxc start "$CONTAINER_ID"
# Wait for container to start, we are using systemd to check this,
# for the sake of brevity.
for i in $(seq 1 10); do
if lxc exec "$CONTAINER_ID" -- sh -c "systemctl isolate multi-user.target" >/dev/null 2>/dev/null; then
break
fi
if [ "$i" == "10" ]; then
echo 'Waited for 10 seconds to start container, exiting..'
# Inform GitLab Runner that this is a system failure, so it
# should be retried.
exit "$SYSTEM_FAILURE_EXIT_CODE"
fi
sleep 1s
done
}
echo "Running in $CONTAINER_ID"
start_container
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/bash
# /opt/lxd-driver/run.sh
currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source ${currentDir}/base.sh # Get variables from base.
lxc exec "$CONTAINER_ID" /bin/bash < "${1}"
if [ $? -ne 0 ]; then
# Exit using the variable, to make the build as failure in GitLab
# CI.
exit $BUILD_FAILURE_EXIT_CODE
fi
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env bash
# /opt/lxd-driver/cleanup.sh
currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source ${currentDir}/base.sh # Get variables from base.
echo "Deleting container $CONTAINER_ID"
lxc delete -f "$CONTAINER_ID"
其中,如果刚开始时总是fail、需要进入run之后的容器进行debug的话,可以把cleanup.sh
的lxc delete
注释掉,用lxc list
看CONTAINER_ID
并lxc exec
连接。
config.toml
准备好script之后,需要注册这个runner。获取runner token的过程这里省略,需要对gitlab-runner的配置文件进行修改。由于lxd相关命令只能由root来执行,所以我们用的是System的gitlab-runner,配置文件是/etc/gitlab-runner/config.toml
;其他内容不重要,这个executor只需要这些内容:
1
2
3
4
5
6
7
8
9
10
11
[[runners]]
name = "lxd-driver"
url = "你的gitlab的网址"
token = "你的token"
executor = "custom"
builds_dir = "/builds"
cache_dir = "/cache"
[runners.custom]
prepare_exec = "/opt/lxd-driver/prepare.sh" # Path to a bash script to create lxd container and download dependencies.
run_exec = "/opt/lxd-driver/run.sh" # Path to a bash script to run script inside the container.
cleanup_exec = "/opt/lxd-driver/cleanup.sh" # Path to bash script to delete container.
More
Cache
Cache功能,观察上面的config.toml可以留意到cache_dir = "/cache"
;这意味着container内的/cache
路径是用于cache功能的。但是lxd executor每次执行完毕之后都会delete掉容器,导致cache也被删掉;想要持久化,就只能另外挂载一个volume进去。
我的配置的lxd的储存backend是zfs,所以先创建一个volume:
1
2
# 在pool1上创建一个名为sharedcache的volume
lxc storage volume create pool1 sharedcache
然后修改prepare.sh
和cleanup.sh
,在start容器之后把volume挂载到/cache
,在delete容器之前detach
(其他内容省略):
1
2
3
4
# prepare.sh
# ...其他内容不变
# 在lxc start "$CONTAINER_ID"的下一行添加:
lxc storage volume attach pool1 sharedcache "$CONTAINER_ID" /cache
1
2
3
4
# cleanup.sh
# ...其他内容不变
# 在lxc delete -f "$CONTAINER_ID"的前一行添加:
lxc storage volume detach pool1 sharedcache "$CONTAINER_ID"
关于cache本身怎么配置,文末有指向Gitlab的Docs的链接。
Proxy
在lxd下,配置得当的话是可以从容器访问host的端口的,也就可以共享host提供的代理服务。只需要在repo的Settings -> CI/CD -> Variables
里面设置好HTTPS_PROXY
、HTTP_PROXY
以及NO_PROXY
就行。
记得配置好NO_PROXY
,否则会遇到莫名其妙的gnutls_handshake() failed: The TLS connection was non-properly terminated.
Seealso
本文基于Gitlab的官方教程:Using LXD with the Custom executor,但是作出了以下重要改动:
- 创建一个通用container,在每次runner启动的时候用copy功能直接复制,避免了重复安装依赖;
- 创建cache volume,并在每次runner启动并copy了容器之后attach到容器中,提供cache volume。
关于Gitlab Runner的cache,参见:Caching in GitLab CI/CD,特别是#per-cache-fallback-keys、#cache-ruby-dependencies。