Gitlab Runner Lxc Executor

于 2024-03-12 发布

此前写过一篇文章介绍如何用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-essentialruby-full,以及openssh-clientrsync等。其中作为Runner必须装的有gitgit-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.shprepare.shrun.shcleanup.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.shlxc delete注释掉,用lxc listCONTAINER_IDlxc 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.shcleanup.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_PROXYHTTP_PROXY以及NO_PROXY就行。

记得配置好NO_PROXY,否则会遇到莫名其妙的gnutls_handshake() failed: The TLS connection was non-properly terminated.

Seealso

本文基于Gitlab的官方教程:Using LXD with the Custom executor,但是作出了以下重要改动:

  1. 创建一个通用container,在每次runner启动的时候用copy功能直接复制,避免了重复安装依赖;
  2. 创建cache volume,并在每次runner启动并copy了容器之后attach到容器中,提供cache volume。

关于Gitlab Runner的cache,参见:Caching in GitLab CI/CD,特别是#per-cache-fallback-keys#cache-ruby-dependencies

目录