在 Docker 容器中运行 CI/CD 作业

您可以在单独的、隔离的 Docker 容器中运行 CI/CD 作业。

如果您在本地机器上运行 Docker,您可以在容器中运行测试,而不是在专用 CI/CD 服务器上进行测试。

要在 Docker 容器中运行 CI/CD 作业,您需要:

  1. 注册一个 runner,让所有作业都在 Docker 容器中运行。在注册期间选择 Docker executor。
  2. 指定在哪个容器中运行作业。在您的 .gitlab-ci.yml 文件中指定一个镜像。
  3. 可选。在容器中运行其他服务,如 MySQL。在您的 .gitlab-ci.yml 文件中指定 services

注册一个使用 Docker executor 的 runner

要将 GitLab Runner 与 Docker 一起使用,您需要注册一个使用 Docker executor 的 runner。

此示例显示如何设置临时模板以提供 services:

cat > /tmp/test-config.template.toml << EOF
[[runners]]
[runners.docker]
[[runners.docker.services]]
name = "postgres:latest"
[[runners.docker.services]]
name = "mysql:latest"
EOF

然后使用此模板注册 runner:

sudo gitlab-runner register \
  --url "https://gitlab.example.com/" \
  --registration-token "PROJECT_REGISTRATION_TOKEN" \
  --description "docker-ruby:2.6" \
  --executor "docker" \
  --template-config /tmp/test-config.template.toml \
  --docker-image ruby:2.6

注册的 runner 使用 ruby:2.6 Docker 镜像并运行两个服务,postgres:latestmysql:latest,这两个服务都可以在构建过程中访问。

什么是镜像

image 关键字是 Docker executor 用来运行 CI/CD 作业的 Docker 镜像的名称。

默认情况下,executor 从 Docker Hub 拉取镜像。 但是,您可以在 gitlab-runner/config.toml 文件中配置镜像库位置。 比如你可以设置 Docker 拉取策略,使用本地镜像。

有关镜像和 Docker Hub 的更多信息,请参阅 Docker Fundamentals

.gitlab-ci.yml 文件中定义 image

您可以定义用于所有作业的镜像,以及要在运行时使用的 services 列表:

default:
  image: ruby:2.6

  services:
    - postgres:11.7

  before_script:
    - bundle install

test:
  script:
    - bundle exec rake spec

镜像名称必须采用以下格式之一:

  • image: <image-name>(与使用带有 latest 标签的 <image-name> 相同)
  • image: <image-name>:<tag>
  • image: <image-name>@<digest>

扩展的 Docker 配置选项

您可以为 imageservices 条目使用字符串或映射:

  • 字符串必须包含完整镜像名称(如果要从 Docker Hub 以外的镜像库下载镜像,则包括镜像库)。
  • 映射必须至少包含 name 选项,它与用于字符串设置的镜像名称相同。

例如,以下两个定义是等价的:

  • imageservices 的字符串:

    image: "registry.example.com/my/image:latest"
    
    services:
      - postgresql:14.3
      - redis:latest
    
  • imageservices 的映射。image:name 是必需的:

    image:
      name: "registry.example.com/my/image:latest"
    
    services:
      - name: postgresql:14.3
      - name: redis:latest
    

脚本在哪里执行

当 CI 作业在 Docker 容器中运行时,before_scriptscriptafter_script 命令在 /builds/<project-path>/ 目录中运行。 您的镜像可能定义了不同的默认 WORKDIR。要移动到您的 WORKDIR,请将 WORKDIR 保存为环境变量,以便您可以在作业运行时在容器中引用它。

覆盖镜像的 entrypoint

在解释可用的 entrypoint 覆盖方法之前,让我们描述一下 runner 如何启动。它为 CI/CD 作业中使用的容器使用 Docker 镜像:

  1. runner 使用定义的 entrypoint 启动一个 Docker 容器。Dockerfile 中的默认值可能会在 .gitlab-ci.yml 文件中被覆盖。
  2. runner 将自己附加到正在运行的容器上。
  3. runner 准备一个脚本(before_script, scriptafter_script 的组合)。
  4. runner 将脚本发送到容器的 shell stdin 并接收输出。

要覆盖 Docker 镜像的 entrypoint,请在 .gitlab-ci.yml 文件中定义一个空的 entrypoint,这样 runner 就不会启动无用的 shell 层。但是,这不适用于所有 Docker 版本。

  • 对于 Docker 17.06 及更高版本,entrypoint 可以设置为空值。
  • 对于 Docker 17.03 及更早版本,entrypoint 可以设置为 /bin/sh -c/bin/bash -c 或镜像中可用的等效 shell。

image:entrypoint 的语法类似于 Dockerfile 的 ENTRYPOINT

假设您有一个带有 SQL 数据库的 super/sql:experimental 镜像。您想将其用作作业的基本镜像,因为您想使用此数据库二进制文件执行一些测试。 我们还假设这个镜像配置了 /usr/bin/super-sql run 作为 entrypoint。当容器在没有附加选项的情况下启动时,它会运行数据库的进程。runner 期望镜像没有 entrypoint 或 entrypoint 准备启动 shell 命令。

使用扩展的 Docker 配置选项,而不是:

  • 基于 super/sql:experimental 创建自己的镜像。
  • ENTRYPOINT 设置为 shell。
  • 在 CI 作业中使用新镜像。

您现在可以在 .gitlab-ci.yml 文件中定义一个 entrypoint

对于 Docker 17.06 及更高版本:

image:
  name: super/sql:experimental
  entrypoint: [""]

对于 Docker 17.03 及更早版本:

image:
  name: super/sql:experimental
  entrypoint: ["/bin/sh", "-c"]

config.toml 中定义镜像和服务

寻找 [runners.docker] 部分:

[runners.docker]
  image = "ruby:latest"
  services = ["mysql:latest", "postgres:latest"]

以这种方式定义的镜像和服务将添加到该 runner 运行的所有作业中。

从私有 Container Registry 访问镜像

要访问私有容器镜像库,GitLab Runner 进程可以使用:

  • 静态定义的凭据。也就是说,特定镜像库的用户名和密码。
  • 凭据存储。有关更多信息,请参阅相关 Docker 文档
  • Credential Helpers。有关更多信息,请参阅相关 Docker 文档。 要定义应使用哪个选项,runner 进程按以下顺序读取配置:

  • 一个 DOCKER_AUTH_CONFIG CI/CD 变量
  • 在 runner 的 config.toml 文件中设置的 DOCKER_AUTH_CONFIG 环境变量。
  • 运行进程的用户的 $HOME/.docker 目录中的 config.json 文件。如果提供了 --user 标志来以非特权用户身份运行子进程,则使用主运行进程用户的主目录。

要求和限制

  • 适用于 GitLab Runner 13.1 及更高版本中的 Kubernetes executor。
  • 凭据存储Credential Helpers 需要将二进制文件添加到 GitLab Runner $PATH,并且需要访问权限才能这样做。因此,这些功能在共享 runner,或用户无权访问安装 runner 的环境的任何其他 runner 上不可用。

使用静态定义的凭据

您可以采用两种方法来访问私有镜像库。两者都需要使用适当的身份验证信息设置 CI/CD 变量 DOCKER_AUTH_CONFIG

  1. Per-job:要配置一个作业以访问私有镜像库,请将 DOCKER_AUTH_CONFIG 添加为 CI/CD 变量
  2. Per-runner:要配置 runner 使其所有作业都可以访问私有镜像库,请在 runner 的配置中添加 DOCKER_AUTH_CONFIG 作为环境变量。

有关每个示例,请参见下文。

确定您的 DOCKER_AUTH_CONFIG 数据

例如,假设您要使用 registry.example.com:5000/private/image:latest 镜像。此镜像是私有的,需要您登录到私有容器镜像库。

我们还假设以下登录凭据:

registry registry.example.com:5000
username my_username
password my_password

使用以下方法之一来确定 DOCKER_AUTH_CONFIG 的值:

  • 在本地机器上执行 docker login

    docker login registry.example.com:5000 --username my_username --password my_password
    

    然后复制 ~/.docker/config.json 的内容。

    如果您不需要从计算机访问镜像库,则可以执行 docker logout

    docker logout registry.example.com:5000
    
  • 在某些设置中,Docker 客户端可能使用可用的系统密钥存储来存储 docker login 的结果。在这种情况下,无法读取 ~/.docker/config.json,因此您必须准备所需的 base64 编码版本的 ${username}:${password} 并手动创建 Docker 配置 JSON。 打开终端并执行以下命令:

    # The use of printf (as opposed to echo) prevents encoding a newline in the password.
    printf "my_username:my_password" | openssl base64 -A
    
    # Example output to copy
    bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=
    

    创建 Docker JSON 配置内容如下:

    {
        "auths": {
            "registry.example.com:5000": {
                "auth": "(Base64 content from above)"
            }
        }
    }
    

配置作业

要为 registry.example.com:5000 配置具有访问权限的单个作业,请按照下列步骤操作:

1.创建一个 CI/CD 变量 DOCKER_AUTH_CONFIG,以 Docker 配置文件的内容为值:

   {
       "auths": {
           "registry.example.com:5000": {
               "auth": "bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ="
           }
       }
   }
  1. 您现在可以使用您的 .gitlab-ci.yml 文件中的 imageservices 中定义的 registry.example.com:5000 中的任何私有镜像:

    image: registry.example.com:5000/namespace/image:tag
    

    在上面的示例中,GitLab Runner 在 registry.example.com:5000 中查找镜像 namespace/image:tag

您可以根据需要为任意数量的镜像库添加配置,如上所述将更多镜像库添加到 "auths" 哈希。

Runner 在任何地方都需要完整的 hostname:port 组合以匹配 DOCKER_AUTH_CONFIG。例如,在 .gitlab-ci.yml 文件中指定了registry.example.com:5000/namespace/image:tag,那么 DOCKER_AUTH_CONFIG 也必须指定 registry.example.com:5000。仅指定 registry.example.com 不起作用。

配置 runner

如果您有许多访问同一个镜像库的流水线,您应该在 runner 级别设置镜像库访问。这允许流水线作者只需在适当的 runner 上运行作业即可访问私有镜像库。它还有助于简化镜像库更改和凭证轮换。

这意味着该 runner 上的任何作业都可以以相同的权限访问镜像库,即使跨项目也是如此。如果您需要控制对镜像库的访问,则需要确保控制对 runner 的访问。

要将 DOCKER_AUTH_CONFIG 添加到 runner:

  1. 修改 runner 的 config.toml 文件如下:

    [[runners]]
      environment = ["DOCKER_AUTH_CONFIG={\"auths\":{\"registry.example.com:5000\":{\"auth\":\"bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=\"}}}"]
    
    • DOCKER_AUTH_CONFIG 数据中包含的双引号必须用反斜杠转义。这可以防止它们被解释为 TOML。
    • environment 选项是一个列表。您的 runner 可能有现有条目,您应该将其添加到列表中,而不是替换它。
  2. 重新启动 runner 服务。

使用凭证存储

要配置凭据存储:

  1. 要使用凭据存储,您需要一个外部帮助程序来与特定的钥匙串或外部存储进行交互。确保帮助程序在 GitLab Runner $PATH 中可用。

  2. 让 GitLab Runner 使用它。有两种方法可以实现:

    • 创建一个 CI/CD 变量 DOCKER_AUTH_CONFIG,以 Docker 配置文件的内容为值:

        {
          "credsStore": "osxkeychain"
        }
      
    • 或者,如果您正在运行私有化部署的 runner,请将上述 JSON 添加到 ${GITLAB_RUNNER_HOME}/.docker/config.json。GitLab Runner 读取此配置文件并使用此特定仓库所需的 helper。

credsStore 用于访问 所有 镜像库。 如果您同时使用来自私有镜像库的镜像和来自 Docker Hub 的公共镜像,则从 Docker Hub 拉取将失败。Docker 守护进程尝试对所有镜像库使用相同的凭据。

使用 Credential Helpers

引入于 GitLab Runner 12.0。

例如,假设您要使用 <aws_account_id>.dkr.ecr.<region>.amazonaws.com/private/image:latest 镜像。此镜像是私有的,需要您登录私有容器镜像库。

要为 <aws_account_id>.dkr.ecr.<region>.amazonaws.com 配置访问权限,请按照以下步骤操作:

  1. 确保 docker-credential-ecr-login 在极狐GitLab Runner $PATH 中可用。
  2. 有以下任何 AWS 凭证设置。确保 GitLab Runner 可以访问凭据。
  3. 让 GitLab Runner 使用它有两种方法:

    • 创建一个 CI/CD 变量 DOCKER_AUTH_CONFIG,以 Docker 配置文件的内容为值:

      {
        "credHelpers": {
          "<aws_account_id>.dkr.ecr.<region>.amazonaws.com": "ecr-login"
        }
      }
      

      这会将 Docker 配置为对特定镜像库使用 Credential Helper。

      相反,您可以将 Docker 配置为对所有 Amazon Elastic Container Registry (ECR) 镜像库使用 Credential Helper:

      {
        "credsStore": "ecr-login"
      }
      
    • 或者,如果您正在运行私有化部署的 runner,请将之前的 JSON 添加到 ${GITLAB_RUNNER_HOME}/.docker/config.json。GitLab Runner 读取此配置文件并使用此特定仓库所需的 helper。

  4. 您现在可以使用来自 <aws_account_id>.dkr.ecr.<region>.amazonaws.com 的任何私有镜像,该镜像在.gitlab-ci.yml 文件中的 image 和/或 services 中定义:

    image: <aws_account_id>.dkr.ecr.<region>.amazonaws.com/private/image:latest
    

    在示例中,GitLab Runner 在 <aws_account_id>.dkr.ecr.<region>.amazonaws.com 中查找镜像 private/image:latest

您可以根据需要为任意数量的镜像库添加配置,将更多镜像库添加到 "credHelpers" 哈希。

使用校验和确保您的镜像安全

我们建议在 .gitlab-ci.yml 文件中的作业定义中使用镜像校验和,来验证镜像的完整性。失败的镜像完整性验证将阻止您使用修改后的容器。

要使用镜像校验和,您必须在末尾附加校验和:

image: ruby:2.6.8@sha256:d1dbaf9665fe8b2175198e49438092fdbcf4d8934200942b94425301b17853c7

要获取图像校验和,请在镜像 TAG 选项卡上查看 DIGEST 列。 例如,查看 Ruby 镜像。 校验和是一个随机字符串,如 6155f0235e95

您还可以使用命令 docker images --digests 获取系统上任何镜像的校验和:

❯ docker images --digests
REPOSITORY                                                        TAG       DIGEST                                                                    (...)
gitlab/gitlab-ee                                                  latest    sha256:723aa6edd8f122d50cae490b1743a616d54d4a910db892314d68470cc39dfb24   (...)
gitlab/gitlab-runner                                              latest    sha256:4a18a80f5be5df44cb7575f6b89d1fdda343297c6fd666c015c0e778b276e726   (...)