Help us learn about your current experience with the documentation. Take the survey.

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

  • Tier: Free, Premium, Ultimate
  • Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated

你可以在专用CI/CD构建服务器或本地机器上托管的Docker容器中运行CI/CD作业。

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

  1. 注册一个Runner并配置它使用 Docker执行器
  2. .gitlab-ci.yml 文件中指定你要运行CI/CD作业的容器镜像。
  3. 可选。在容器中运行其他服务(如MySQL)。通过在你的 .gitlab-ci.yml 文件中指定 services 来实现。

注册使用Docker执行器的Runner

要使用带有Docker的GitLab Runner,你需要 注册一个Runner,该Runner使用Docker执行器。

此示例展示了如何设置临时模板以提供服务:

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/" \
  --token "$RUNNER_TOKEN" \
  --description "docker-ruby:2.6" \
  --executor "docker" \
  --template-config /tmp/test-config.template.toml \
  --docker-image ruby:3.3

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

什么是镜像

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

默认情况下,执行器从 Docker Hub 拉取镜像。但是,你可以在 gitlab-runner/config.toml 文件中配置注册表位置。例如,你可以设置 Docker拉取策略 以使用本地镜像。

有关镜像和Docker Hub的更多信息,请参见 Docker概述

镜像要求

任何用于运行CI/CD作业的镜像都必须安装以下应用程序:

  • shbash
  • grep

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

你可以定义一个用于所有作业的镜像,以及要在运行时使用的服务列表:

default:
  image: ruby:2.6
  services:
    - postgres:11.7
  before_script:
    - bundle install

test:
  script:
    - bundle exec rake spec

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

  • image: <镜像名称>(等同于使用带latest标签的<镜像名称>
  • image: <镜像名称>:<标签>
  • image: <镜像名称>@<摘要>

扩展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/<项目路径>/ 目录下运行。你的镜像可能定义了不同的默认 WORKDIR。若要移动到你的 WORKDIR,请将 WORKDIR 保存为环境变量,以便在作业运行期间在容器中引用它。

覆盖镜像的入口点

在解释可用的入口点覆盖方法前,先描述下 Runner 是如何启动的。它使用 Docker 镜像来运行 CI/CD 作业中的容器:

  1. Runner 使用定义好的入口点启动一个 Docker 容器。这个入口点的默认值来自 Dockerfile,可能会被 .gitlab-ci.yml 文件覆盖。
  2. Runner 附加到正在运行的容器上。
  3. Runner 准备一个脚本(由 before_scriptscriptafter_script 组合而成)。
  4. Runner 将脚本发送到容器的 shell stdin 并接收输出。

若要覆盖 Docker 镜像的 entrypoint,可在 .gitlab-ci.yml 文件中:

  • 对于 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 作为入口点。当容器未添加额外选项启动时,它会运行数据库进程。Runner 期望镜像没有入口点,或者入口点已准备好启动 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 中定义镜像和服务

config.toml 文件中,你可以定义:

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

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

从私有容器注册表中访问镜像

要访问私有容器注册表,GitLab Runner 进程可以使用:

当你使用同一 GitLab 实例上的 GitLab 容器注册表 时,GitLab 会为此注册表提供默认凭证。使用这些凭证时,会使用 CI_JOB_TOKEN 进行身份验证。要使用 job token,启动作业的用户必须拥有托管私有镜像的项目中至少 Developer 角色。托管私有镜像的项目也必须允许其他项目通过 job token 进行身份验证,此访问默认是禁用的。更多详情,请参阅 CI/CD job token

要确定应使用哪个选项,Runner 进程按以下顺序读取配置:

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

要求和限制

  • 凭证存储凭证助手 要求二进制文件添加到 GitLab Runner 的 $PATH 中,且需有权限执行此操作。因此,这些功能在实例 Runner 或任何用户无法访问 Runner 安装环境的 Runner 上不可用。

使用静态定义的凭证

你可采用两种方式访问私有仓库。两者均需设置 CI/CD 变量 DOCKER_AUTH_CONFIG 并提供相应认证信息。

  1. 每个作业:若要为一个作业配置访问私有仓库,可将 DOCKER_AUTH_CONFIG 作为 CI/CD 变量 添加。
  2. 每个 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,需准备 ${username}:${password} 的 base64 编码版本并手动创建 Docker 配置 JSON。打开终端执行以下命令:

    # 使用 printf(而非 echo)可防止密码中的换行符被编码。
    printf "my_username:my_password" | openssl base64 -A
    
    # 示例输出供复制
    bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=

    若用户名含特殊字符(如 @),需用反斜杠(\)转义以避免认证问题。

    创建 Docker JSON 配置内容如下:

    {
        "auths": {
            "registry.example.com:5000": {
                "auth": "(来自上述的 Base64 内容)"
            }
        }
    }

配置作业

若要为单个作业配置对 registry.example.com:5000 的访问,按以下步骤操作:

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

    {
        "auths": {
            "registry.example.com:5000": {
                "auth": "bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ="
            }
        }
    }
  2. 现在,你可在 .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 的访问。

要给 Runner 添加 DOCKER_AUTH_CONFIG

  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 会读取此配置文件,并为特定仓库使用所需的辅助程序。

credsStore 用于访问所有注册表。若你同时使用私有注册表的镜像和 Docker Hub 的公开镜像,从 Docker Hub 拉取会失败——Docker 守护进程会尝试对所有注册表使用相同凭证。

使用凭证助手

举个例子,假设你想使用 <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 对特定注册表使用凭证助手。

      相反,你也可以配置 Docker 对所有 Amazon Elastic Container Registry (ECR) 注册表使用凭证助手:

      {
        "credsStore": "ecr-login"
      }

      若使用 {"credsStore": "ecr-login"},需在 AWS 共享配置文件(~/.aws/config)中明确设置区域。当 ECR 凭证助手检索授权令牌时,必须指定该区域。

    • 或者,若你运行自托管 Runner,将上述 JSON 添加到 ${GITLAB_RUNNER_HOME}/.docker/config.json。GitLab Runner 会读取此配置文件,并为特定仓库使用所需的辅助程序。

  4. 现在,你可在 .gitlab-ci.yml 文件的 image 和/或 services 中使用 <aws_account_id>.dkr.ecr.<region>.amazonaws.com 下的任意私有镜像:

    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   (...)

创建自定义 GitLab Runner Docker 镜像

你可以创建一个自定义 GitLab Runner Docker 镜像来打包 AWS CLI 和 Amazon ECR Credential Helper。此设置便于与 AWS 服务进行安全且高效的交互,尤其适用于容器化应用。例如,使用此设置管理、部署和更新 Amazon ECR 上的 Docker 镜像。此设置有助于避免耗时、易出错的手动配置和凭证管理。

  1. 将 GitLab 与 AWS 认证关联

  2. 创建包含以下内容的 Dockerfile

    # 控制软件包版本
    ARG GITLAB_RUNNER_VERSION=v17.3.0
    ARG AWS_CLI_VERSION=2.17.36
    
    # AWS CLI 和 Amazon ECR 凭证助手
    FROM amazonlinux as aws-tools
    RUN set -e \
        && yum update -y \
        && yum install -y --allowerasing git make gcc curl unzip \
        && curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" --output "awscliv2.zip" \
        && unzip awscliv2.zip && ./aws/install -i /usr/local/bin \
        && yum clean all
    
    # 下载并安装 ECR 凭证助手
    RUN curl --location --output  /usr/local/bin/docker-credential-ecr-login "https://github.com/awslabs/amazon-ecr-credential-helper/releases/latest/download/docker-credential-ecr-login-linux-amd64"
    RUN chmod +x /usr/local/bin/docker-credential-ecr-login
    
    # 配置 ECR 凭证助手
    RUN mkdir -p /root/.docker
    RUN echo '{ "credsStore": "ecr-login" }' > /root/.docker/config.json
    
    # 基于 GitLab Runner 的最终镜像
    FROM gitlab/gitlab-runner:${GITLAB_RUNNER_VERSION}
    
    # 安装必要软件包
    RUN apt-get update \
        && apt-get install -y --no-install-recommends jq procps curl unzip groff libgcrypt20 tar gzip less openssh-client \
        && apt-get clean && rm -rf /var/lib/apt/lists/*
    
    # 复制 AWS CLI 和 Amazon ECR 凭证助手的二进制文件
    COPY --from=aws-tools /usr/local/bin/ /usr/local/bin/
    
    # 复制 ECR 凭证助手配置
    COPY --from=aws-tools /root/.docker/config.json /root/.docker/config.json
  3. 要在 .gitlab-ci.yml 中构建自定义 GitLab Runner Docker 镜像,请包含以下示例:

    variables:
      DOCKER_DRIVER: overlay2
      IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
      GITLAB_RUNNER_VERSION: v17.3.0
      AWS_CLI_VERSION: 2.17.36
    
    stages:
      - build
    
    build-image:
      stage: build
      script:
        - echo "登录 GitLab 容器注册表..."
        - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
        - echo "构建 Docker 镜像..."
        - docker build --build-arg GITLAB_RUNNER_VERSION=${GITLAB_RUNNER_VERSION} --build-arg AWS_CLI_VERSION=${AWS_CLI_VERSION} -t ${IMAGE_NAME} .
        - echo "推送 Docker 镜像至 GitLab 容器注册表..."
        - docker push ${IMAGE_NAME}
      rules:
        - changes:
            - Dockerfile
  4. 注册 Runner