- 在你的 CI/CD 作业中启用 Docker 命令
- 在 Docker-in-Docker 中与注册表认证
- 使用 Docker 层缓存加快 Docker-in-Docker 构建
- 使用 OverlayFS 驱动
- Docker 替代方案
- 使用极狐GitLab 容器注册表
-
疑难解答
- 错误:
docker: Cannot connect to the Docker daemon at tcp://docker:2375
- Docker
no such host
错误 - 错误:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock
- 错误:
unauthorized: incorrect username or password
- 错误:连接时出错:
no such host
- 错误:
cgroups: cgroup mountpoint does not exist: unknown
- 错误:
failed to verify certificate: x509: certificate signed by unknown authority
- 错误:
{{< 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 命令,但需要权限才能执行。
- 安装极狐GitLab Runner。
-
注册 一个 runner。选择
shell
执行器。例如:sudo gitlab-runner register -n \ --url "https://gitlab.com/" \ --registration-token REGISTRATION_TOKEN \ --executor shell \ --description "My Runner"
-
在安装极狐GitLab Runner 的服务器上,安装 Docker 引擎。
-
将
gitlab-runner
用户添加到docker
组:sudo usermod -aG docker gitlab-runner
-
验证
gitlab-runner
是否有访问 Docker 的权限:sudo -u gitlab-runner -H docker info
-
在极狐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
) 意味着:
- 你的注册 runner 使用 Docker 执行器 或 Kubernetes 执行器。
- 执行器使用 Docker 提供的 Docker 容器镜像来运行你的 CI/CD 作业。
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:
- 安装 极狐GitLab Runner。
-
从命令行注册极狐GitLab Runner。使用
docker
和privileged
模式: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]
- 此命令注册一个新的 runner 使用
-
你现在可以在作业脚本中使用
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:
-
使用 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"
-
你现在可以在作业脚本中使用
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: ""
。
例如:
-
使用 Helm chart,更新
values.yml
文件:runners: config: | [[runners]] [runners.kubernetes] image = "ubuntu:20.04" privileged = true
-
你现在可以在作业脚本中使用
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
服务必须定义为 Docker 或 Kubernetes 执行器。
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
。
要求
- 确保使用最近的内核,最好是
>= 4.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:
- 构建一个 Docker 镜像。
- 将其推送到 极狐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 启动。
- 如果这是你第一次设置,请参阅 使用 Docker 镜像的 Docker 执行器。
当尝试在 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
),则会发生此错误。如果:
- 你的 CI/CD 镜像依赖于遗留变量,如
DOCKER_PORT_2375_TCP
。 -
runner 特性标志
FF_NETWORK_PER_BUILD
设置为true
。 -
DOCKER_HOST
未明确设置。
你的作业将失败并出现此错误。
错误: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
服务:
- 检查
config.toml
中是否设置了privileged = true
。 - 确保 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 build
或 docker 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 操作将因证书错误而失败。
要解决此问题:
-
将你的根证书存储为名为
CA_CERTIFICATE
的 CI/CD 变量。证书格式如下:-----BEGIN CERTIFICATE----- (certificate content) -----END CERTIFICATE-----
-
配置你的流水线以在启动 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}"