1. 概述

在使用 Jenkins 构建流程时,你可能遇到过这样的报错:“Cannot connect to the Docker daemon”。这个错误通常会让人非常头疼,尤其是在前一天构建还一切正常的情况下。

这个问题的根本原因在于 Jenkins 无法与 Docker 的后台服务(daemon)通信。本文将解释这个问题的成因,并提供多种解决方案,帮助你快速定位并修复问题。


2. Docker Daemon 连接问题详解

在解决问题之前,我们需要了解 Jenkins 和 Docker 是如何通信的,这样才能选择合适的解决方案并避免未来出现类似问题。

2.1. 什么是 Docker Daemon?

Docker Daemon 是 Docker 架构的核心组件,它是一个后台服务,负责执行构建镜像、运行容器、管理网络等任务。

你可以把它想象成一家餐厅的后厨:我们在前台点菜(使用 Docker CLI 命令),后厨(Daemon)负责实际的制作。Docker Daemon 通常通过 Unix 套接字 /var/run/docker.sock 来接收命令。

需要注意的是,Docker Daemon 需要 root 权限,因为它需要操作内核级别的功能,比如命名空间(namespaces)和控制组(cgroups)。

2.2. Jenkins 是如何与 Docker 交互的?

Jenkins 通过 Docker CLI 来执行构建命令。例如,在 Jenkins Pipeline 中,我们可能会这样写:

stage('Build') {
    steps {
        sh 'docker build -t myapp .'
    }
}

这个命令是以 jenkins 用户身份执行的。如果该用户没有权限访问 Docker 套接字,就会报错。换句话说,Jenkins 默认不具备任何特殊权限,它和普通系统用户一样需要授权才能访问 Docker。

2.3. 常见错误信息及其含义

常见的错误信息主要有两种:

  1. 连接失败:

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

    这说明 Jenkins 找不到 Docker Daemon,可能是服务未启动,或套接字文件缺失。

  2. 权限不足:

    Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
    

    这说明 Jenkins 找到了 Daemon,但没有访问权限。这类问题通常与 Unix 文件权限有关。


3. 连接问题的根本原因

理解这些错误的成因有助于我们对症下药。

3.1. 权限问题

这是最常见的原因。Docker 默认限制了对 Docker 套接字的访问权限。我们可以通过以下命令查看:

$ ls -la /var/run/docker.sock
srw-rw---- 1 root docker 0 Jul 24 08:28 /var/run/docker.sock

只有 root 用户和 docker 组的成员才能访问这个套接字。如果 Jenkins 用户不在 docker 组中,就无法访问。

3.2. Docker 服务配置问题

有时候 Docker 服务本身配置有问题。比如服务没有运行,或者 Daemon 被配置为只监听 TCP 端口而不是 Unix 套接字。

我们可以用以下命令检查服务状态:

$ systemctl status docker

如果服务未运行,Jenkins 自然无法连接。此外,如果 Daemon 被配置为监听 TCP,例如:

ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375

那么我们就需要通过指定 -H 参数或设置 DOCKER_HOST 环境变量来连接。

3.3. 套接字访问问题

如果 Jenkins 是运行在容器中的,那么它默认是无法访问主机上的 Docker 套接字的。除非我们显式地将套接字挂载到容器中,例如:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

否则,容器是无法访问主机资源的,这是 Docker 安全隔离机制的一部分。


4. 解决方案 1:将 Jenkins 用户加入 Docker 组

这是最常见、最直接的解决方案,适用于 Jenkins 直接部署在主机上的场景。

4.1. 创建 Docker 组(如不存在)

大多数 Docker 安装会自动创建 docker 组。我们可以通过以下命令检查:

getent group docker

如果没有输出,就手动创建:

sudo groupadd docker

4.2. 将 Jenkins 用户加入 Docker 组

执行以下命令:

sudo usermod -aG docker jenkins

⚠️ 注意:这个变更不会立即生效,需要重启 Jenkins 服务。

4.3. 验证用户组权限

使用以下命令验证:

id jenkins

输出中应包含 docker 组。

4.4. 重启 Jenkins 并测试

重启服务:

sudo systemctl restart jenkins

然后在 Jenkins Pipeline 中测试:

pipeline {
    agent any
    stages {
        stage('Test Docker Access') {
            steps {
                sh 'docker version'
                sh 'docker ps'
            }
        }
    }
}

✅ 如果输出了 Docker 版本和容器列表,说明配置成功。


5. 解决方案 2:通过 TCP 暴露 Docker API

适用于 Jenkins 与 Docker 分布在不同机器上的场景。

5.1. 修改 Docker 服务配置

找到服务文件路径:

systemctl show -p FragmentPath docker

编辑该文件,找到 ExecStart 行并修改为:

ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375 --containerd=/run/containerd/containerd.sock

5.2. 重载并重启服务

sudo systemctl daemon-reload
sudo systemctl restart docker

验证端口是否监听:

netstat -tlnp | grep 2375

现在你可以在 Jenkins 中使用 -H 参数连接 Docker:

docker -H tcp://localhost:2375 ps

6. 解决方案 3:在容器化 Jenkins 中挂载 Docker 套接字

适用于 Jenkins 部署在容器中的场景。

6.1. Docker 套接字挂载原理

通过挂载主机的 /var/run/docker.sock 到容器中,可以让 Jenkins 容器控制主机的 Docker Daemon。

⚠️ 注意:这种方式会赋予容器完全的主机 Docker 控制权限,需谨慎使用。

6.2. Docker Compose 示例配置

version: '3.8'
services:
  jenkins:
    image: jenkins/jenkins:lts
    ports:
      - "8080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    user: jenkins

6.3. Kubernetes Pod 模板配置

spec:
  containers:
  - name: jenkins-agent
    volumeMounts:
    - name: docker-sock
      mountPath: /var/run/docker.sock
  volumes:
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock

⚠️ 注意:Kubernetes 的安全策略可能默认禁止 hostPath 挂载,需要根据集群配置调整 PodSecurityPolicySecurityContextConstraints


7. 总结

Jenkins 与 Docker Daemon 的连接问题主要由以下三类原因引起:

  • ✅ 权限不足(用户不在 docker 组)
  • ✅ Docker 服务未运行或配置异常(仅监听 TCP)
  • ✅ 套接字未正确挂载(容器化部署)

根据部署环境选择合适的解决方案:

  • 主机部署 Jenkins:✅ 加入 docker 组
  • 不同主机部署:✅ 配置 TCP 监听
  • 容器化部署:✅ 挂载 Docker 套接字

无论哪种方式,都要注意安全控制,避免暴露不必要的权限。



原始标题:Troubleshoot Docker Daemon Connection Issues in Jenkins