使用 Docker 构建 Docker 镜像

您可以将 GitLab CI/CD 与 Docker 结合使用来创建 Docker 镜像。例如,您可以创建应用程序的 Docker 镜像,对其进行测试,然后将其发布到容器镜像库。

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

在 runner 上,如果你不想通过启用 privileged 模式来构建 Docker 镜像,你可以使用 Docker 替代方案

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

要为 CI/CD 作业启用 Docker 命令,您可以使用:

使用 shell executor

要在 CI/CD 作业中包含 Docker 命令,您可以将 runner 配置为使用 shell executor。在此配置中,gitlab-runner 用户运行 Docker 命令,但需要权限才能这样做。

  1. 安装 GitLab Runner。
  2. 注册 一个 runner。 选择 shell executor。例如:

    sudo gitlab-runner register -n \
      --url https://gitlab.com/ \
      --registration-token REGISTRATION_TOKEN \
      --executor shell \
      --description "My Runner"
    
  3. 在安装了极狐GitLab Runner 的服务器上,安装 Docker Engine。查看支持的平台列表。

  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

    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 组的安全性

使用 Docker-in-Docker

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

  • 您注册的 runner 使用 Docker executor 或 Kubernetes executor。
  • Executor 使用 Docker 提供的 Docker 容器镜像 来运行您的 CI/CD 作业。

Docker 镜像安装了所有的 docker 工具,并且可以在特权模式下,在镜像的上下文中运行作业脚本。

我们建议您使用 Docker-in-Docker with TLS enabled

您应该始终指定镜像的特定版本,例如 docker:20.10.16。 如果使用像 docker:stable 这样的标签,你就无法控制使用哪个版本。 可能会导致不可预测的行为,尤其是在发布新版本时。

将 Docker executor 与 Docker-in-Docker 一起使用

您可以使用 Docker executor 在 Docker 容器中运行作业。

在 Docker executor 中启用 TLS 的 Docker-in-Docker

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

caution 此任务启用--docker-privileged。当您这样做时,您实际上是在禁用容器的所有安全机制并将您的主机暴露给特权升级。这样做会导致 container breakout。有关更多信息,请参阅有关运行时权限和 Linux 功能 的官方 Docker 文档。

要在启用 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:20.10.16" \
      --docker-privileged \
      --docker-volumes "/certs/client"
    
    • 这个命令注册一个新的 runner 来使用 docker:20.10.16 镜像。要启动构建和服务容器,它使用 privileged 模式。如果您想使用 Docker-in-Docker,您必须始终在 Docker 容器中使用 privileged = true
    • 此命令为服务和构建容器挂载 /certs/client,这是 Docker 客户端使用该目录中的证书所必需的。有关使用 TLS 的 Docker 如何工作的更多信息,请参阅 https://hub.docker.com/_/docker/#tls

    前面的命令创建了一个类似于这样的 config.toml 条目:

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

    image: docker:20.10.16
    
    variables:
      # When you use the dind service, you must instruct Docker to talk with
      # the daemon started inside of the service. The daemon is available
      # with a network connection instead of the default
      # /var/run/docker.sock socket. Docker 19.03 does this automatically
      # by setting the DOCKER_HOST in
      # https://github.com/docker-library/docker/blob/d45051476babc297257df490d22cbd806f1b11e4/19.03/docker-entrypoint.sh#L23-L29
      #
      # The 'docker' hostname is the alias of the service container as described at
      # https://docs.gitlab.com/ee/ci/services/#accessing-the-services.
      #
      # Specify to Docker where to create the certificates. Docker
      # creates them automatically on boot, and creates
      # `/certs/client` to share between the service and job
      # container, thanks to volume mount from config.toml
      DOCKER_TLS_CERTDIR: "/certs"
    
    services:
      - docker:20.10.16-dind
    
    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 和构建容器之间使用共享卷上的 Unix 套接字

使用 Docker 执行器且启用了 TLS 的 Docker-in-Docker 模式中,在 volumes = ["/certs/client", "/cache"] 里定义的目录在各次构建之间是持久存在的。如果多个使用 Docker 执行器运行器的 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"] # Temporary volume shared between build and service containers.

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

job:
  variables:
    # This variable is shared by both the DinD service and Docker client.
    # For the service, it will instruct DinD to create `docker.sock` here.
    # For the client, it tells the Docker client which Docker Unix socket to connect to.
    DOCKER_HOST: "unix:///runner/services/docker/docker.sock"
  services:
    - docker:24.0.5-dind
  image: docker:24.0.5
  script:
    - docker version
在 Docker executor 中禁用 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:20.10.16"
    privileged = true
    disable_cache = false
    volumes = ["/cache"]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

您现在可以在作业脚本中使用 docker。请注意包含 docker:20.10.16-dind 服务:

image: docker:20.10.16

variables:
  # When using dind service, you must instruct docker to talk with the
  # daemon started inside of the service. The daemon is available with
  # a network connection instead of the default /var/run/docker.sock socket.
  #
  # The 'docker' hostname is the alias of the service container as described at
  # https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#accessing-the-services
  #
  # If you're using GitLab Runner 12.7 or earlier with the Kubernetes executor and Kubernetes 1.6 or earlier,
  # the variable must be set to tcp://localhost:2375 because of how the
  # Kubernetes executor connects services to the job container
  # DOCKER_HOST: tcp://localhost:2375
  #
  DOCKER_HOST: tcp://docker:2375
  #
  # This instructs Docker not to start over TLS.
  DOCKER_TLS_CERTDIR: ""

services:
  - docker:20.10.16-dind

before_script:
  - docker info

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

将 Kubernetes executor 与 Docker-in-Docker 一起使用

您可以使用 Kubernetes executor 在 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:19.03.13-dind 服务:

    image: docker:19.03.13
    
    variables:
      # When using dind service, you must instruct Docker to talk with
      # the daemon started inside of the service. The daemon is available
      # with a network connection instead of the default
      # /var/run/docker.sock socket.
      DOCKER_HOST: tcp://docker:2376
      #
      # The 'docker' hostname is the alias of the service container as described at
      # https://docs.gitlab.com/ee/ci/services/#accessing-the-services.
      # If you're using GitLab Runner 12.7 or earlier with the Kubernetes executor and Kubernetes 1.6 or earlier,
      # the variable must be set to tcp://localhost:2376 because of how the
      # Kubernetes executor connects services to the job container
      # DOCKER_HOST: tcp://localhost:2376
      #
      # Specify to Docker where to create the certificates. Docker
      # creates them automatically on boot, and creates
      # `/certs/client` to share between the service and job
      # container, thanks to volume mount from config.toml
      DOCKER_TLS_CERTDIR: "/certs"
      # These are usually specified by the entrypoint, however the
      # Kubernetes executor doesn't run entrypoints
      # https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4125
      DOCKER_TLS_VERIFY: 1
      DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
    
    services:
      - docker:19.03.13-dind
    
    before_script:
      - docker info
    
    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 使用 DOCKER_TLS_CERTDIR: "" 启动时禁用 TLS。

例如:

  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:
        - docker:24.0.5-dind
      before_script:
        - docker info
    
    variables:
      # When using dind service, you must instruct Docker to talk with
      # the daemon started inside of the service. The daemon is available
      # with a network connection instead of the default
      # /var/run/docker.sock socket.
      DOCKER_HOST: tcp://docker:2375
      #
      # The 'docker' hostname is the alias of the service container as described at
      # https://docs.gitlab.com/ee/ci/services/#accessing-the-services.
      # If you're using GitLab Runner 12.7 or earlier with the Kubernetes executor and Kubernetes 1.6 or earlier,
      # the variable must be set to tcp://localhost:2376 because of how the
      # Kubernetes executor connects services to the job container
      # DOCKER_HOST: tcp://localhost:2376
      #
      # This instructs Docker not to start over 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-compose,请遵循 docker-compose 安装说明
  • 缓存:每个作业都在新环境中运行。并发作业工作正常,因为每个构建都有自己的 Docker engine 实例,并且它们不会相互冲突。但是,作业可能会变慢,因为没有层缓存。
  • 存储驱动程序:默认情况下,早期版本的 Docker 使用 vfs 存储驱动程序,它为每个作业复制文件系统。Docker 17.09 及更高版本使用 --storage-driver overlay2,这是推荐的存储驱动程序。有关详细信息,请参阅使用 OverlayFS 驱动程序
  • 根文件系统:因为 docker:20.10.16-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 executor 与 Docker 套接字绑定一起使用

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

如果你绑定了 Docker 套接字,那么你就不能将 docker:24.0.5-dind 作为服务了。卷绑定也会影响服务,它们是不兼容的。

要使 Docker 在镜像上下文中可用,您需要将 /var/run/docker.sock 挂载到启动的容器中。要使用 Docker executor 执行此操作,您需要将 "/var/run/docker.sock:/var/run/docker.sock" 添加到 [runners.docker] 部分的卷中。

您的配置应如下所示:

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

您也可以在注册 runner 时通过提供以下选项来执行此操作:

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-in-Docker 设置,诸如使用 CodeClimate 进行代码质量扫描,您可能需要匹配主机和容器路径以确保正确执行。有关详细信息,请参阅使用私有 runner 进行 基于CodeClimate 的扫描

docker:dind 服务启用镜像库镜像

当 Docker 守护进程在服务容器内启动时,它使用默认配置。您可能需要配置镜像库镜像 以提高性能,并确保您不会达到 Docker Hub 速率限制。

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

您可以将额外的 CLI 标志附加到 dind 服务以设置镜像库镜像:

services:
  - name: docker:19.03.13-dind
    command: ["--registry-mirror", "https://registry-mirror.example.com"]  # Specify the registry mirror to use
GitLab Runner 配置文件中的服务

如果您是 GitLab Runner 管理员,则可以指定 command 来为 Docker 守护程序配置镜像库镜像。必须为 Docker 或 Kubernetes executor 定义 dind 服务。

Docker:

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

Kubernetes:

[[runners]]
  ...
  name = "kubernetes"
  [runners.kubernetes]
    ...
    privileged = true
    [[runners.kubernetes.services]]
      name = "docker:19.03.13-dind"
      command = ["--registry-mirror", "https://registry-mirror.example.com"]
极狐GitLab Runner 配置文件中的 Docker executor

如果您是 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 executor

如果您是极狐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
note 确保使用极狐GitLab Runner 的 Kubernetes executor,用于在其中创建作业 Pod 的命名空间。

创建 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 守护进程,您可以有效地禁用容器的所有安全机制,并使您的主机暴露在权限提升中,这可能导致 container breakout。例如,如果一个项目运行 docker rm -f $(docker ps -a -q),它将删除 GitLab Runner 容器。
  • 并发作业可能不起作用;如果您的测试创建具有特定名称的容器,它们可能会相互冲突。
  • 任何由 Docker 命令生成的容器都是 runner 的 siblings,而不是 runner 的子节点。这可能具有不适合您的工作流程的复杂性和限制。
  • 将源仓库中的文件和目录共享到容器中可能无法按预期工作。卷挂载是在 host machine context 中完成的,而不是构建容器。例如:

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

您不需要像使用 Docker-in-Docker executor 时那样包含 docker:20.10.16-dind 服务:

image: docker:20.10.16

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 的最新版本(Docker 1.13 及更高版本)可以在 docker build 步骤中使用预先存在的镜像作为缓存。这大大加快了构建过程。

使用 OverlayFS 驱动程序

默认情况下,当使用 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,则可以通过在 [[runners]] 部分中设置 DOCKER_DRIVER 环境变量来为每个项目启用驱动程序 config.toml 文件:

environment = ["DOCKER_DRIVER=overlay2"]

如果您正在运行多个 runner,则必须修改所有配置文件。

Docker 替代方案

To build Docker images without enabling privileged mode on the runner, you can use one of these alternatives: 要想在 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:
    # Use vfs with buildah. Docker offers overlayfs as a default, but Buildah
    # cannot stack overlayfs on top of another overlayfs filesystem.
    STORAGE_DRIVER: vfs
    # Write all image metadata in the docker format, not the standard OCI format.
    # Newer versions of docker can handle the OCI format, but older versions, like
    # the one shipped with Fedora 30, cannot handle the format.
    BUILDAH_FORMAT: docker
    FQ_IMAGE_NAME: "$CI_REGISTRY_IMAGE/test"
  before_script:
    # GitLab container registry credentials taken from the
    # [predefined CI/CD variables](../variables/index.md#predefined-cicd-variables)
    # to authenticate to the registry.
    - 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 Container Registry

构建 Docker 镜像后,您可以将其推送到内置的 GitLab Container Registry。

故障排查

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

当您使用 Docker-in-Docker v19.03 或更高版本时,这是一个常见错误。

出现此问题是因为 Docker 自动在 TLS 上启动。

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

当服务的镜像名称包括镜像库主机名时,可能会发生此问题。例如:

image: docker:20.10.16

services:
  - registry.hub.docker.com/library/docker:20.10.16-dind

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

image: docker:20.10.16

services:
  - name: registry.hub.docker.com/library/docker:20.10.16-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.tomlprivileged = true 的设置是否正确。
  2. 确保 CI 作业具有正确的 Runner 标签以使用这些特权 Runner。

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

这是 Docker Engine 20.10 引入的已知不兼容性。

当宿主机使用 Docker Engine 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 Engine 的版本为 19.06.x 或更低版本)不会出现问题。对于最佳策略,您应该经常测试和更新作业环境版本到最新版本。这带来了新的功能、改进的安全性和 - 对于这个特定案例 - 使升级底层 Docker Engine 在 runner 的主机上对作业透明。