{{< details >}}

  • Tier: 基础版, 专业版, 旗舰版
  • Offering: JihuLab.com, 私有化部署

{{< /details >}}

你可以使用极狐GitLab CI/CD 与 Docker 创建 Docker 镜像。例如,你可以创建应用程序的 Docker 镜像,对其进行测试,并将其推送到容器注册表。

要在 CI/CD 作业中运行 Docker 命令,你必须配置极狐GitLab Runner 以支持 docker 命令。此方法需要 privileged 模式。

如果你希望在 runner 上构建 Docker 镜像而不启用 privileged 模式,你可以使用 Docker 替代方案

在你的 CI/CD 作业中启用 Docker 命令

要在你的 CI/CD 作业中启用 Docker 命令,你可以使用:

使用 Shell 执行器

要在你的 CI/CD 作业中包含 Docker 命令,你可以配置你的 runner 使用 shell 执行器。在此配置中,gitlab-runner 用户运行 Docker 命令,但需要权限才能执行。

  1. 安装极狐GitLab Runner。
  2. 注册 一个 runner。选择 shell 执行器。例如:

    sudo gitlab-runner register -n \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor shell \
      --description "My Runner"
    
  3. 在安装极狐GitLab Runner 的服务器上,安装 Docker 引擎。

  4. gitlab-runner 用户添加到 docker 组:

    sudo usermod -aG docker gitlab-runner
    
  5. 验证 gitlab-runner 是否有访问 Docker 的权限:

    sudo -u gitlab-runner -H docker info
    
  6. 在极狐GitLab 中,添加 docker info.gitlab-ci.yml 来验证 Docker 是否正常工作:

    default:
      before_script:
        - docker info
    
    build_image:
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

现在你可以使用 docker 命令(如果需要,还可以安装 Docker Compose)。

当你将 gitlab-runner 添加到 docker 组时,实际上是授予了 gitlab-runner 完全的 root 权限。

使用 Docker-in-Docker

“Docker-in-Docker” (dind) 意味着:

Docker 镜像包括所有的 docker 工具,并可以在特权模式下运行作业脚本。

你应该使用启用 TLS 的 Docker-in-Docker,这是由 JihuLab.com 实例 runners 支持的。

你应该始终固定镜像的特定版本,比如 docker:24.0.5。如果使用像 docker:latest 这样的标签,你无法控制使用哪个版本。当新版本发布时可能会导致兼容性问题。

使用 Docker 执行器与 Docker-in-Docker

你可以使用 Docker 执行器在 Docker 容器中运行作业。

Docker 执行器中启用 TLS 的 Docker-in-Docker

Docker 守护程序支持通过 TLS 进行连接。在 Docker 19.03.12 及更高版本中,TLS 是默认设置。

{{< alert type=”warning” >}}

此任务启用了 --docker-privileged,这实际上禁用了容器的安全机制,并将你的主机暴露于权限升级。这种操作可能会导致容器突破。

{{< /alert >}}

要使用启用 TLS 的 Docker-in-Docker:

  1. 安装 极狐GitLab Runner
  2. 从命令行注册极狐GitLab Runner。使用 dockerprivileged 模式:

    sudo gitlab-runner register -n \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor docker \
      --description "My Docker Runner" \
      --docker-image "docker:24.0.5" \
      --docker-privileged \
      --docker-volumes "/certs/client"
    
    • 此命令注册一个新的 runner 使用 docker:24.0.5 镜像(如果作业级别未指定)。为了启动构建和服务容器,它使用 privileged 模式。如果你想使用 Docker-in-Docker,你必须始终在 Docker 容器中使用 privileged = true
    • 此命令为服务和构建容器挂载 /certs/client,这是 Docker 客户端使用该目录中的证书所需的。

    前面的命令创建了一个 config.toml 条目,类似于以下示例:

    [[runners]]
      url = "https://gitlab.com/"
      token = TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:24.0.5"
        privileged = true
        disable_cache = false
        volumes = ["/certs/client", "/cache"]
      [runners.cache]
        [runners.cache.s3]
        [runners.cache.gcs]
    
  3. 你现在可以在作业脚本中使用 docker。你应该包括 docker:24.0.5-dind 服务:

    default:
      image: docker:24.0.5
      services:
        - docker:24.0.5-dind
      before_script:
        - docker info
    
    variables:
      # 使用 dind 服务时,你必须指示 Docker 与服务内启动的守护进程通信。守护进程可通过网络连接使用,而不是默认的 /var/run/docker.sock 套接字。Docker 19.03 通过在 https://github.com/docker-library/docker/blob/d45051476babc297257df490d22cbd806f1b11e4/19.03/docker-entrypoint.sh#L23-L29 中设置 DOCKER_HOST 自动执行此操作。
      #
      # 'docker' 主机名是服务容器的别名,详见 https://docs.gitlab.com/ee/ci/services/#accessing-the-services。
      #
      # 指定 Docker 在何处创建证书。Docker 会在启动时自动创建这些证书,并创建 `/certs/client` 以在服务和作业容器之间共享,感谢 config.toml 中的卷挂载。
      DOCKER_TLS_CERTDIR: "/certs"
    
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

使用 Docker-in-Docker 和构建容器之间共享卷上的 Unix 套接字

Docker 执行器中启用 TLS 的 Docker-in-Docker 方法中定义的 volumes = ["/certs/client", "/cache"] 目录是 在构建之间持久化。如果多个使用 Docker 执行器 runner 的 CI/CD 作业启用了 Docker-in-Docker 服务,那么每个作业都会写入目录路径。这种方法可能导致冲突。

为了解决这个冲突,可以使用 Docker-in-Docker 服务和构建容器之间共享卷上的 Unix 套接字。这种方法提高了性能,并在服务和客户端之间建立了安全连接。

以下是使用构建和服务容器之间临时共享卷的示例 config.toml

[[runners]]
  url = "https://gitlab.com/"
  token = TOKEN
  executor = "docker"
  [runners.docker]
    image = "docker:24.0.5"
    privileged = true
    volumes = ["/runner/services/docker"] # 构建和服务容器之间的临时共享卷。

Docker-in-Docker 服务创建一个 docker.sock。Docker 客户端通过 Docker Unix 套接字卷连接到 docker.sock

job:
  variables:
    # 此变量由 DinD 服务和 Docker 客户端共享。对于服务,它将指示 DinD 在此处创建 `docker.sock`。对于客户端,它告诉 Docker 客户端连接到哪个 Docker Unix 套接字。
    DOCKER_HOST: "unix:///runner/services/docker/docker.sock"
  services:
    - docker:24.0.5-dind
  image: docker:24.0.5
  script:
    - docker version

Docker 执行器中禁用 TLS 的 Docker-in-Docker

有时禁用 TLS 是有正当理由的。例如,你无法控制正在使用的极狐GitLab Runner 配置。

假设 runner 的 config.toml 类似于:

[[runners]]
  url = "https://gitlab.com/"
  token = TOKEN
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "docker:24.0.5"
    privileged = true
    disable_cache = false
    volumes = ["/cache"]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

你现在可以在作业脚本中使用 docker。你应该包括 docker:24.0.5-dind 服务:

default:
  image: docker:24.0.5
  services:
    - docker:24.0.5-dind
  before_script:
    - docker info

variables:
  # 使用 dind 服务时,你必须指示 docker 与服务内部启动的守护进程通信。守护进程可通过网络连接使用,而不是默认的 /var/run/docker.sock 套接字。
  #
  # 'docker' 主机名是服务容器的别名,详见 https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#accessing-the-services。
  #
  DOCKER_HOST: tcp://docker:2375
  #
  # 这指示 Docker 不要通过 TLS 启动。
  DOCKER_TLS_CERTDIR: ""

build:
  stage: build
  script:
    - docker build -t my-docker-image .
    - docker run my-docker-image /script/to/run/tests

Docker 执行器中启用代理的 Docker-in-Docker

你可能需要配置代理设置以使用 docker push 命令。

有关更多信息,请参阅 使用 dind 服务时的代理设置

使用 Kubernetes 执行器与 Docker-in-Docker

你可以使用 Kubernetes 执行器 在 Docker 容器中运行作业。

Kubernetes 中启用 TLS 的 Docker-in-Docker

要在 Kubernetes 中使用启用 TLS 的 Docker-in-Docker:

  1. 使用 Helm chart,更新 values.yml 文件 以指定卷挂载。

    runners:
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = true
          [[runners.kubernetes.volumes.empty_dir]]
            name = "docker-certs"
            mount_path = "/certs/client"
            medium = "Memory"
    
  2. 你现在可以在作业脚本中使用 docker。你应该包括 docker:24.0.5-dind 服务:

    default:
      image: docker:24.0.5
      services:
        - name: docker:24.0.5-dind
          variables:
            HEALTHCHECK_TCP_PORT: "2376"
      before_script:
        - docker info
    
    variables:
      # 使用 dind 服务时,你必须指示 Docker 与服务内部启动的守护进程通信。守护进程可通过网络连接使用,而不是默认的 /var/run/docker.sock 套接字。
      DOCKER_HOST: tcp://docker:2376
      #
      # 'docker' 主机名是服务容器的别名,详见 https://docs.gitlab.com/ee/ci/services/#accessing-the-services。
      #
      # 指定 Docker 在何处创建证书。Docker 会在启动时自动创建这些证书,并创建 `/certs/client` 以在服务和作业容器之间共享,感谢 config.toml 中的卷挂载。
      DOCKER_TLS_CERTDIR: "/certs"
      # 这些通常由入口点指定,但 Kubernetes 执行器不运行入口点 https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4125。
      DOCKER_TLS_VERIFY: 1
      DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
    
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Kubernetes 中禁用 TLS 的 Docker-in-Docker

要在 Kubernetes 中使用禁用 TLS 的 Docker-in-Docker,你必须适应上面的示例:

  • values.yml 文件中删除 [[runners.kubernetes.volumes.empty_dir]] 部分。
  • 将端口从 2376 更改为 2375,使用 DOCKER_HOST: tcp://docker:2375
  • 指示 Docker 以禁用 TLS 的方式启动,使用 DOCKER_TLS_CERTDIR: ""

例如:

  1. 使用 Helm chart,更新 values.yml 文件

    runners:
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = true
    
  2. 你现在可以在作业脚本中使用 docker。你应该包括 docker:24.0.5-dind 服务:

    default:
      image: docker:24.0.5
      services:
        - name: docker:24.0.5-dind
          variables:
            HEALTHCHECK_TCP_PORT: "2375"
      before_script:
        - docker info
    
    variables:
      # 使用 dind 服务时,你必须指示 Docker 与服务内部启动的守护进程通信。守护进程可通过网络连接使用,而不是默认的 /var/run/docker.sock 套接字。
      DOCKER_HOST: tcp://docker:2375
      #
      # 'docker' 主机名是服务容器的别名,详见 https://docs.gitlab.com/ee/ci/services/#accessing-the-services。
      #
      # 这指示 Docker 不要通过 TLS 启动。
      DOCKER_TLS_CERTDIR: ""
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Docker-in-Docker 的已知问题

Docker-in-Docker 是推荐的配置,但你应该注意以下问题:

  • docker-compose 命令:默认情况下,此配置中没有此命令。
  • 缓存:每个作业都在一个新的环境中运行。因为每次构建都有自己的 Docker 引擎实例, 并发作业不会导致冲突。然而,由于没有层缓存,作业可能会变得较慢。请参阅 Docker 层缓存
  • 存储驱动:默认情况下,较早版本的 Docker 使用 vfs 存储驱动,为每个作业复制文件系统。Docker 17.09 及更高版本使用 --storage-driver overlay2,这是推荐的存储驱动。有关详细信息,请参阅 使用 OverlayFS 驱动
  • 根文件系统:由于 docker:24.0.5-dind 容器和 runner 容器不共享其根文件系统,你可以使用作业的工作目录作为子容器的挂载点。例如,如果你有文件要与子容器共享,可以在 /builds/$CI_PROJECT_PATH 下创建一个子目录,并将其用作挂载点。

    variables:
      MOUNT_POINT: /builds/$CI_PROJECT_PATH/mnt
    script:
      - mkdir -p "$MOUNT_POINT"
      - docker run -v "$MOUNT_POINT:/mnt" my-docker-image
    

使用 Docker 执行器与 Docker 套接字绑定

要在你的 CI/CD 作业中使用 Docker 命令,可以将 /var/run/docker.sock 绑定挂载到容器中。然后 Docker 在镜像的上下文中可用。

如果你绑定了 Docker 套接字,则不能使用 docker:24.0.5-dind 作为服务。卷绑定也会影响服务,使它们不兼容。

要在镜像上下文中提供 Docker,你需要将 /var/run/docker.sock 挂载到启动的容器中。要使用 Docker 执行器执行此操作,请将 "/var/run/docker.sock:/var/run/docker.sock" 添加到 Volumes in the [runners.docker] section

你的配置应该类似于以下示例:

[[runners]]
  url = "https://gitlab.com/"
  token = RUNNER_TOKEN
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "docker:24.0.5"
    privileged = false
    disable_cache = false
    volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
  [runners.cache]
    Insecure = false

要在注册 runner 时挂载 /var/run/docker.sock,请包括以下选项:

sudo gitlab-runner register -n \
  --url "https://gitlab.com/" \
  --registration-token REGISTRATION_TOKEN \
  --executor docker \
  --description "My Docker Runner" \
  --docker-image "docker:24.0.5" \
  --docker-volumes /var/run/docker.sock:/var/run/docker.sock

对于 docker-windows 执行器,使用类似于以下示例的配置:

[[runners]]
  url = "https://gitlab.example.com/"
  token = RUNNER_TOKEN
  executor = "docker-windows"
  [runners.docker]
    tls_verify = false
    image = "docker:windowsservercore-ltsc2022"
    privileged = false
    disable_cache = false
    volumes = ["//./pipe/docker_engine://./pipe/docker_engine", "/cache"]
  [runners.cache]
    Insecure = false

对于像 使用 CodeClimate 进行代码质量扫描 这样的复杂 Docker-in-Docker 设置,你必须匹配主机和容器路径以进行正确执行。有关更多详细信息,请参阅 使用私有 runner 进行基于 CodeClimate 的扫描

docker:dind 服务启用注册表镜像

当 Docker 守护程序在服务容器内部启动时,它使用默认配置。你可能希望配置注册表镜像以提高性能并确保不会超出 Docker Hub 速率限制。

.gitlab-ci.yml 文件中的服务

你可以向 dind 服务添加额外的 CLI 标志以设置注册表镜像:

services:
  - name: docker:24.0.5-dind
    command: ["--registry-mirror", "https://registry-mirror.example.com"]  # 指定要使用的注册表镜像

极狐GitLab Runner 配置文件中的服务

如果你是极狐GitLab Runner 管理员,你可以指定 command 来配置 Docker 守护程序的注册表镜像。dind 服务必须定义为 DockerKubernetes 执行器

Docker:

[[runners]]
  ...
  executor = "docker"
  [runners.docker]
    ...
    privileged = true
    [[runners.docker.services]]
      name = "docker:24.0.5-dind"
      command = ["--registry-mirror", "https://registry-mirror.example.com"]

Kubernetes:

[[runners]]
  ...
  name = "kubernetes"
  [runners.kubernetes]
    ...
    privileged = true
    [[runners.kubernetes.services]]
      name = "docker:24.0.5-dind"
      command = ["--registry-mirror", "https://registry-mirror.example.com"]

极狐GitLab Runner 配置文件中的 Docker 执行器

如果你是极狐GitLab Runner 管理员,你可以为每个 dind 服务使用镜像。更新 配置 以指定 卷挂载

例如,如果你有一个 /opt/docker/daemon.json 文件,其中包含以下内容:

{
  "registry-mirrors": [
    "https://registry-mirror.example.com"
  ]
}

更新 config.toml 文件以将文件挂载到 /etc/docker/daemon.json。这会为 每个 极狐GitLab Runner 创建的容器挂载文件。dind 服务检测到此配置。

[[runners]]
  ...
  executor = "docker"
  [runners.docker]
    image = "alpine:3.12"
    privileged = true
    volumes = ["/opt/docker/daemon.json:/etc/docker/daemon.json:ro"]

极狐GitLab Runner 配置文件中的 Kubernetes 执行器

如果你是极狐GitLab Runner 管理员,你可以为每个 dind 服务使用镜像。更新 配置 以指定 ConfigMap 卷挂载

例如,如果你有一个 /tmp/daemon.json 文件,其中包含以下内容:

{
  "registry-mirrors": [
    "https://registry-mirror.example.com"
  ]
}

使用此文件的内容创建一个 ConfigMap。你可以使用类似以下的命令进行操作:

kubectl create configmap docker-daemon --namespace gitlab-runner --from-file /tmp/daemon.json

{{< alert type=”note” >}}

你必须使用极狐GitLab Runner 的 Kubernetes 执行器用于创建作业 pod 的命名空间。

{{< /alert >}}

ConfigMap 创建后,你可以更新 config.toml 文件,将文件挂载到 /etc/docker/daemon.json。此更新为 每个 极狐GitLab Runner 创建的容器挂载文件。dind 服务检测到此配置。

[[runners]]
  ...
  executor = "kubernetes"
  [runners.kubernetes]
    image = "alpine:3.12"
    privileged = true
    [[runners.kubernetes.volumes.config_map]]
      name = "docker-daemon"
      mount_path = "/etc/docker/daemon.json"
      sub_path = "daemon.json"

Docker 套接字绑定的已知问题

当你使用 Docker 套接字绑定时,你可以避免在特权模式下运行 Docker。然而,此方法的影响是:

  • 通过共享 Docker 守护程序,你实际上禁用了所有容器的安全机制,并将主机暴露于权限提升。这可能导致容器突破。例如,如果一个项目运行 docker rm -f $(docker ps -a -q),它将删除极狐GitLab Runner 容器。
  • 并发作业可能无法工作。如果你的测试创建具有特定名称的容器,它们可能会相互冲突。
  • 任何由 Docker 命令创建的容器都是 runner 的同级,而不是 runner 的子级。这可能会对你的工作流程造成影响。
  • 从源代码库中共享文件和目录到容器可能无法按预期工作。卷挂载是在主机机器的上下文中完成的,而不是构建容器。例如:

     docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
    

你不需要像使用 Docker-in-Docker 执行器时那样包括 docker:24.0.5-dind 服务:

default:
  image: docker:24.0.5
  before_script:
    - docker info

build:
  stage: build
  script:
    - docker build -t my-docker-image .
    - docker run my-docker-image /script/to/run/tests

在 Docker-in-Docker 中与注册表认证

当你使用 Docker-in-Docker 时,标准认证方法 不起作用,因为服务启动了一个新的 Docker 守护程序。你应该与注册表认证

使用 Docker 层缓存加快 Docker-in-Docker 构建

使用 Docker-in-Docker 时,Docker 每次创建构建时都会下载镜像的所有层。你可以使用 Docker 层缓存加快构建

使用 OverlayFS 驱动

{{< alert type=”note” >}}

JihuLab.com 上的实例 runners 默认使用 overlay2 驱动。

{{< /alert >}}

默认情况下,使用 docker:dind 时,Docker 使用 vfs 存储驱动,每次运行时复制文件系统。你可以使用不同的驱动来避免这种磁盘密集型操作,例如 overlay2

要求

  1. 确保使用最近的内核,最好是 >= 4.2
  2. 检查是否加载了 overlay 模块:

    sudo lsmod | grep overlay
    

    如果没有看到结果,则模块未加载。要加载模块,使用:

    sudo modprobe overlay
    

    如果模块已加载,你必须确保模块在重启时加载。在 Ubuntu 系统上,通过将以下行添加到 /etc/modules 来执行此操作:

    overlay
    

按项目使用 OverlayFS 驱动

你可以通过在 .gitlab-ci.yml 中使用 DOCKER_DRIVER CI/CD 变量 为每个项目单独启用驱动:

variables:
  DOCKER_DRIVER: overlay2

为每个项目使用 OverlayFS 驱动

如果你使用自己的 runners,你可以通过在 config.toml 文件的 [[runners]] 部分 中设置 DOCKER_DRIVER 环境变量来为每个项目启用驱动:

environment = ["DOCKER_DRIVER=overlay2"]

如果你运行多个 runners,则必须修改所有配置文件。

阅读有关 runner 配置使用 OverlayFS 存储驱动 的更多信息。

Docker 替代方案

要在 runner 上构建 Docker 镜像而不启用特权模式,你可以使用以下替代方案之一:

Buildah 示例

要在极狐GitLab CI/CD 中使用 Buildah,你需要一个具有以下执行器之一的 runner

在此示例中,你使用 Buildah:

  1. 构建一个 Docker 镜像。
  2. 将其推送到 极狐GitLab 容器注册表

在最后一步中,Buildah 使用项目根目录下的 Dockerfile 构建 Docker 镜像。最后,它将镜像推送到项目的容器注册表:

build:
  stage: build
  image: quay.io/buildah/stable
  variables:
    # 使用 vfs 与 buildah。Docker 提供 overlayfs 作为默认选项,但 Buildah 不能在另一个 overlayfs 文件系统之上堆叠 overlayfs。
    STORAGE_DRIVER: vfs
    # 将所有镜像元数据写入 docker 格式,而不是标准的 OCI 格式。较新的 docker 版本可以处理 OCI 格式,但较旧的版本(例如 Fedora 30 附带的版本)无法处理该格式。
    BUILDAH_FORMAT: docker
    FQ_IMAGE_NAME: "$CI_REGISTRY_IMAGE/test"
  before_script:
    # 极狐GitLab 容器注册表凭据取自 [预定义的 CI/CD 变量](../variables/_index.md#define-a-cicd-variable-in-the-ui) 以进行注册表认证。
    - echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
  script:
    - buildah images
    - buildah build -t $FQ_IMAGE_NAME
    - buildah images
    - buildah push $FQ_IMAGE_NAME

如果你使用极狐GitLab Runner Operator 部署到 OpenShift 集群,请尝试 使用 Buildah 在无根容器中构建镜像的教程

使用极狐GitLab 容器注册表

构建 Docker 镜像后,你可以将其推送到 极狐GitLab 容器注册表

疑难解答

错误:docker: Cannot connect to the Docker daemon at tcp://docker:2375

当你使用 Docker-in-Docker v19.03 或更高版本时,此错误很常见:

docker: Cannot connect to the Docker daemon at tcp://docker:2375. Is the docker daemon running?

此错误发生是因为 Docker 自动以 TLS 启动。

当尝试在 Kubernetes 执行器中访问 Docker-in-Docker 服务时,此错误也可能发生,因为服务尚未完全启动。

Docker no such host 错误

你可能会收到错误提示: docker: error during connect: Post https://docker:2376/v1.40/containers/create: dial tcp: lookup docker on x.x.x.x:53: no such host

此问题可能发生在服务的镜像名称 包含注册表主机名 时。例如:

default:
  image: docker:24.0.5
  services:
    - registry.hub.docker.com/library/docker:24.0.5-dind

服务的主机名是 从完整镜像名称派生 的。然而,预期的较短服务主机名是 docker。要允许服务解析和访问,为服务名称 docker 添加显式别名:

default:
  image: docker:24.0.5
  services:
    - name: registry.hub.docker.com/library/docker:24.0.5-dind
      alias: docker

错误:Cannot connect to the Docker daemon at unix:///var/run/docker.sock

当尝试运行 docker 命令以访问 dind 服务时,你可能会收到以下错误:

$ docker ps
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

确保你的作业已定义以下环境变量:

  • DOCKER_HOST
  • DOCKER_TLS_CERTDIR(可选)
  • DOCKER_TLS_VERIFY(可选)

你可能还需要更新提供 Docker 客户端的镜像。例如,docker/compose 镜像已过时,应替换为 docker

如果你的作业之前依赖于已弃用的 Docker --link 参数派生的环境变量(如 DOCKER_PORT_2375_TCP),则会发生此错误。如果:

你的作业将失败并出现此错误。

错误:unauthorized: incorrect username or password

此错误在使用已弃用的变量 CI_BUILD_TOKEN 时出现:

Error response from daemon: Get "https://registry-1.docker.io/v2/": unauthorized: incorrect username or password

为了防止用户收到此错误,你应该:

  • 使用 CI_JOB_TOKEN
  • gitlab-ci-token/CI_BUILD_TOKEN 更改为 $CI_REGISTRY_USER/$CI_REGISTRY_PASSWORD

错误:连接时出错:no such host

此错误表明 dind 服务启动失败:

error during connect: Post "https://docker:2376/v1.24/auth": dial tcp: lookup docker on 127.0.0.11:53: no such host

检查作业日志是否出现 mount: permission denied (are you root?)。例如:

Service container logs:
2023-08-01T16:04:09.541703572Z Certificate request self-signature ok
2023-08-01T16:04:09.541770852Z subject=CN = docker:dind server
2023-08-01T16:04:09.556183222Z /certs/server/cert.pem: OK
2023-08-01T16:04:10.641128729Z Certificate request self-signature ok
2023-08-01T16:04:10.641173149Z subject=CN = docker:dind client
2023-08-01T16:04:10.656089908Z /certs/client/cert.pem: OK
2023-08-01T16:04:10.659571093Z ip: can't find device 'ip_tables'
2023-08-01T16:04:10.660872131Z modprobe: can't change directory to '/lib/modules': No such file or directory
2023-08-01T16:04:10.664620455Z mount: permission denied (are you root?)
2023-08-01T16:04:10.664692175Z Could not mount /sys/kernel/security.
2023-08-01T16:04:10.664703615Z AppArmor detection and --privileged mode might break.
2023-08-01T16:04:10.665952353Z mount: permission denied (are you root?)

这表明极狐GitLab Runner 没有权限启动 dind 服务:

  1. 检查 config.toml 中是否设置了 privileged = true
  2. 确保 CI 作业具有正确的 runner 标签以使用这些特权 runners。

错误:cgroups: cgroup mountpoint does not exist: unknown

Docker 引擎 20.10 引入了已知的不兼容性。

当主机使用 Docker 引擎 20.10 或更新版本时,版本低于 20.10 的 docker:dind 服务无法按预期工作。

虽然服务本身启动没有问题,但尝试构建容器镜像会导致错误:

cgroups: cgroup mountpoint does not exist: unknown

要解决此问题,请将 docker:dind 容器更新到至少 20.10.x 的版本,例如 docker:24.0.5-dind

相反的配置(docker:24.0.5-dind 服务和主机上的 Docker 引擎版本为 19.06.x 或更旧)则没有问题。最佳策略是频繁测试和更新作业环境版本至最新。这带来了新功能、改进的安全性以及对 runner 主机上底层 Docker 引擎升级的透明处理。

错误:failed to verify certificate: x509: certificate signed by unknown authority

当在 Docker-in-Docker 环境中执行 Docker 命令(如 docker builddocker pull)时使用自定义或私有证书(例如 Zscaler 证书)时可能会出现此错误:

error pulling image configuration: download failed after attempts=6: tls: failed to verify certificate: x509: certificate signed by unknown authority

此错误发生是因为 Docker-in-Docker 环境中 Docker 命令使用两个独立的容器:

  • 构建容器运行 Docker 客户端(/usr/bin/docker)并执行作业的脚本命令。
  • 服务容器(通常命名为 svc)运行处理大多数 Docker 命令的 Docker 守护程序。

当你的组织使用自定义证书时,两个容器都需要这些证书。没有在两个容器中进行适当的证书配置,连接到外部注册表或服务的 Docker 操作将因证书错误而失败。

要解决此问题:

  1. 将你的根证书存储为名为 CA_CERTIFICATECI/CD 变量。证书格式如下:

    -----BEGIN CERTIFICATE-----
    (certificate content)
    -----END CERTIFICATE-----
    
  2. 配置你的流水线以在启动 Docker 守护程序之前在服务容器中安装证书。例如:

    image_build:
      stage: build
      image:
        name: docker:19.03
      variables:
        DOCKER_HOST: tcp://localhost:2375
        DOCKER_TLS_CERTDIR: ""
        CA_CERTIFICATE: "$CA_CERTIFICATE"
      services:
        - name: docker:19.03-dind
          command:
            - /bin/sh
            - -c
            - |
              echo "$CA_CERTIFICATE" > /usr/local/share/ca-certificates/custom-ca.crt && \
              update-ca-certificates && \
              dockerd-entrypoint.sh || exit
      script:
        - docker info
        - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD $DOCKER_REGISTRY
        - docker build -t "${DOCKER_REGISTRY}/my-app:${CI_COMMIT_REF_NAME}" .
        - docker push "${DOCKER_REGISTRY}/my-app:${CI_COMMIT_REF_NAME}"