Docker守护进程故障排除

此页面描述了如果遇到问题如何对守护进程进行故障排除和调试。

你可以打开守护进程的调试功能,了解守护进程的运行时活动,并辅助进行故障排除。如果守护进程无响应,你也可以强制将所有线程的完整堆栈跟踪添加到守护进程日志中,方法是向Docker守护进程发送`SIGUSR`信号。

守护进程

无法连接到Docker守护进程

Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?

此错误可能表示

  • Docker守护进程未在你的系统上运行。启动守护进程并尝试再次运行命令。
  • 你的Docker客户端正在尝试连接到不同主机上的Docker守护进程,而该主机无法访问。

检查Docker是否正在运行

检查Docker是否正在运行的操作系统无关方法是使用`docker info`命令询问Docker。

你也可以使用操作系统实用程序,例如`sudo systemctl is-active docker`或`sudo status docker`或`sudo service docker status`,或使用Windows实用程序检查服务状态。

最后,你可以使用`ps`或`top`等命令在进程列表中检查`dockerd`进程。

检查客户端连接到的主机

要查看客户端连接到的主机,请检查环境中`DOCKER_HOST`变量的值。

$ env | grep DOCKER_HOST

如果此命令返回一个值,则Docker客户端设置为连接到该主机上运行的Docker守护进程。如果未设置,则Docker客户端设置为连接到本地主机上运行的Docker守护进程。如果设置错误,请使用以下命令取消设置它

$ unset DOCKER_HOST

你可能需要编辑诸如`~/.bashrc`或`~/.profile`之类的文件中的环境,以防止错误地设置`DOCKER_HOST`变量。

如果按预期设置了`DOCKER_HOST`,请验证Docker守护进程是否在远程主机上运行,以及防火墙或网络中断是否阻止你连接。

排查`daemon.json`和启动脚本之间的冲突

如果你使用`daemon.json`文件,并且还手动或使用启动脚本将选项传递给`dockerd`命令,并且这些选项冲突,则Docker将无法启动并出现错误,例如

unable to configure the Docker daemon with file /etc/docker/daemon.json:
the following directives are specified both as a flag and in the configuration
file: hosts: (from flag: [unix:///var/run/docker.sock], from file: [tcp://127.0.0.1:2376])

如果你看到类似于此错误的消息,并且你正在使用标志手动启动守护进程,则可能需要调整你的标志或`daemon.json`以消除冲突。

注意

如果你看到此特定关于`hosts`的错误消息,请继续到下一节了解解决方法。

如果你正在使用操作系统的初始化脚本启动Docker,则可能需要以特定于操作系统的方式覆盖这些脚本中的默认值。

使用systemd配置守护进程主机

一个难以排查的配置冲突的显著示例是,当你想指定与默认值不同的守护进程地址时。Docker默认情况下侦听套接字。在使用`systemd`的Debian和Ubuntu系统上,这意味着在启动`dockerd`时始终使用主机标志`-H`。如果在`daemon.json`中指定`hosts`条目,则会导致配置冲突,并导致Docker守护进程无法启动。

要解决此问题,请创建一个新文件`/etc/systemd/system/docker.service.d/docker.conf`,其内容如下,以删除默认情况下启动守护进程时使用的`-H`参数。

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd

在其他时候,你可能需要使用Docker配置`systemd`,例如配置HTTP或HTTPS代理

注意

如果你覆盖此选项而不指定`daemon.json`中的`hosts`条目或在手动启动Docker时使用`-H`标志,则Docker将无法启动。

在尝试启动 Docker 之前,请运行sudo systemctl daemon-reload。如果 Docker 成功启动,它现在将监听 `daemon.json` 文件中 `hosts` 密钥指定的 IP 地址,而不是套接字。

重要

在 Windows 版 Docker Desktop 或 Mac 版 Docker Desktop 上不支持在 `daemon.json` 中设置 `hosts`。

内存不足问题

如果您的容器尝试使用超过系统可用内存的内存,您可能会遇到内存不足 (OOM) 异常,并且容器或 Docker 守护进程可能会被内核 OOM killer 停止。为防止这种情况发生,请确保您的应用程序在具有足够内存的主机上运行,并参阅 了解运行内存不足的风险

内核兼容性

如果您的内核版本低于 3.10 或缺少内核模块,则 Docker 无法正确运行。要检查内核兼容性,您可以下载并运行 check-config.sh 脚本。

$ curl https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh > check-config.sh

$ bash ./check-config.sh

此脚本仅适用于 Linux。

内核cgroup交换空间限制能力

在 Ubuntu 或 Debian 主机上,使用镜像时可能会看到类似以下的消息。

WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.

如果您不需要这些功能,可以忽略此警告。

您可以按照以下说明在 Ubuntu 或 Debian 上启用这些功能。即使 Docker 未运行,内存和交换空间会计也会产生大约 1% 的总可用内存开销和 10% 的整体性能下降。

  1. 以具有 `sudo` 权限的用户身份登录到 Ubuntu 或 Debian 主机。

  2. 编辑 ` /etc/default/grub` 文件。添加或编辑 `GRUB_CMDLINE_LINUX` 行以添加以下两个键值对

    GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

    保存并关闭文件。

  3. 更新 GRUB 引导加载程序。

    $ sudo update-grub
    

    如果您的 GRUB 配置文件语法错误,则会发生错误。在这种情况下,请重复步骤 2 和 3。

    重新启动系统后,更改才会生效。

网络

IP转发问题

如果您使用 systemd 版本 219 或更高版本使用 `systemd-network` 手动配置网络,Docker 容器可能无法访问您的网络。从 systemd 版本 220 开始,给定网络的转发设置 ( `net.ipv4.conf.<interface>.forwarding`) 默认情况下为关闭。此设置阻止 IP 转发。它还会与 Docker 在容器内启用 `net.ipv4.conf.all.forwarding` 设置的行为冲突。

要在 RHEL、CentOS 或 Fedora 上解决此问题,请编辑 Docker 主机上 `/usr/lib/systemd/network/` 中的 `<interface>.network` 文件,例如 ` /usr/lib/systemd/network/80-container-host0.network`。

在 `[Network]` 部分中添加以下块。

[Network]
...
IPForward=kernel
# OR
IPForward=true

此配置允许按预期从容器进行 IP 转发。

DNS解析器问题

DNS resolver found in resolv.conf and containers can't use it

Linux 桌面环境通常运行一个网络管理器程序,该程序使用 `dnsmasq` 通过将 DNS 请求添加到 `/etc/resolv.conf` 来缓存 DNS 请求。`dnsmasq` 实例在环回地址(例如 `127.0.0.1` 或 `127.0.1.1`)上运行。它可以加快 DNS 查询速度并提供 DHCP 服务。这种配置在 Docker 容器内不起作用。Docker 容器使用它自己的网络命名空间,并将环回地址(例如 `127.0.0.1`)解析为自身,并且不太可能在其自身的环回地址上运行 DNS 服务器。

如果 Docker 检测到 `/etc/resolv.conf` 中引用的 DNS 服务器不是完全功能的 DNS 服务器,则会发生以下警告

WARNING: Local (127.0.0.1) DNS resolver found in resolv.conf and containers
can't use it. Using default external servers : [8.8.8.8 8.8.4.4]

如果您看到此警告,首先检查您是否使用 `dnsmasq`

$ ps aux | grep dnsmasq

如果您的容器需要解析网络内部的主机,则公共名称服务器是不够的。您有两种选择

  • 指定 Docker 要使用的 DNS 服务器。

  • 关闭 `dnsmasq`。

    关闭 `dnsmasq` 会将实际 DNS 命名服务器的 IP 地址添加到 `/etc/resolv.conf`,并且您会失去 `dnsmasq` 的好处。

您只需要使用其中一种方法。

为Docker指定DNS服务器

配置文件的默认位置是 `/etc/docker/daemon.json`。您可以使用 `--config-file` 守护程序标志更改配置文件的位置。以下说明假设配置文件的位置是 `/etc/docker/daemon.json`。

  1. 创建或编辑 Docker 守护程序配置文件,默认为 `/etc/docker/daemon.json` 文件,该文件控制 Docker 守护程序配置。

    $ sudo nano /etc/docker/daemon.json
    
  2. 添加一个 `dns` 密钥,其值是一个或多个 DNS 服务器 IP 地址。

    {
      "dns": ["8.8.8.8", "8.8.4.4"]
    }

    如果文件已有内容,您只需要添加或编辑 `dns` 行即可。如果您的内部 DNS 服务器无法解析公共 IP 地址,请至少包含一个可以解析公共 IP 地址的 DNS 服务器。这样做允许您连接到 Docker Hub,并允许您的容器解析互联网域名。

    保存并关闭文件。

  3. 重新启动 Docker 守护程序。

    $ sudo service docker restart
    
  4. 尝试拉取镜像以验证 Docker 是否可以解析外部 IP 地址

    $ docker pull hello-world
    
  5. 如有必要,请通过 ping 测试验证 Docker 容器是否可以解析内部主机名。

    $ docker run --rm -it alpine ping -c4 <my_internal_host>
    
    PING google.com (192.168.1.2): 56 data bytes
    64 bytes from 192.168.1.2: seq=0 ttl=41 time=7.597 ms
    64 bytes from 192.168.1.2: seq=1 ttl=41 time=7.635 ms
    64 bytes from 192.168.1.2: seq=2 ttl=41 time=7.660 ms
    64 bytes from 192.168.1.2: seq=3 ttl=41 time=7.677 ms
    

关闭`dnsmasq`


如果您不想更改 Docker 守护程序的配置以使用特定 IP 地址,请按照以下说明关闭 NetworkManager 中的 `dnsmasq`。

  1. 编辑 `/etc/NetworkManager/NetworkManager.conf` 文件。

  2. 通过在行首添加 `#` 字符来注释掉 `dns=dnsmasq` 行。

    # dns=dnsmasq

    保存并关闭文件。

  3. 重新启动 NetworkManager 和 Docker。或者,您可以重新启动系统。

    $ sudo systemctl restart network-manager
    $ sudo systemctl restart docker
    

要在 RHEL、CentOS 或 Fedora 上关闭 `dnsmasq`

  1. 关闭 `dnsmasq` 服务

    $ sudo systemctl stop dnsmasq
    $ sudo systemctl disable dnsmasq
    
  2. 使用 Red Hat 文档 手动配置 DNS 服务器。


Docker网络消失

如果 Docker 网络(例如 `docker0` 桥接或自定义网络)随机消失或其他方面似乎工作不正常,则可能是因为其他服务正在干扰或修改 Docker 接口。已知管理主机网络接口的工具有时也会不适当地修改 Docker 接口。

请参阅以下部分,了解如何根据主机上存在的网络管理工具配置网络管理器以将 Docker 接口设置为非托管状态的说明

卸载 `netscript`

如果您的系统上安装了 `netscript`,您可以通过卸载它来解决此问题。例如,在基于 Debian 的系统上

$ sudo apt-get remove netscript-2.4

取消管理 Docker 接口

在某些情况下,网络管理器将尝试默认管理 Docker 接口。您可以尝试通过编辑系统的网络配置设置将 Docker 网络显式标记为非托管。


如果您使用的是 `NetworkManager`,请编辑 `/etc/network/interfaces` 下的系统网络配置

  1. 使用以下内容在 `/etc/network/interfaces.d/20-docker0` 中创建一个文件

    iface docker0 inet manual

    请注意,此示例配置仅“取消管理”默认的 `docker0` 桥接,而不是自定义网络。

  2. 重新启动 `NetworkManager` 以使配置更改生效。

    $ systemctl restart NetworkManager
    
  3. 验证 `docker0` 接口是否具有 `unmanaged` 状态。

    $ nmcli device
    

如果您在使用 `systemd-networkd` 作为网络守护程序的系统上运行 Docker,请通过在 `/etc/systemd/network` 下创建配置文件来将 Docker 接口配置为非托管。

  1. 使用以下内容创建 `/etc/systemd/network/docker.network`

    # Ensure that the Docker interfaces are un-managed
    
    [Match]
    Name=docker0 br-* veth*
    
    [Link]
    Unmanaged=yes
  2. 重新加载配置。

    $ sudo systemctl restart systemd-networkd
    
  3. 重新启动 Docker 守护程序。

    $ sudo systemctl restart docker
    
  4. 验证 Docker 接口是否具有 `unmanaged` 状态。

    $ networkctl
    

防止Netplan覆盖网络配置

在使用 Netplan 通过 cloud-init 的系统上,您可能需要应用自定义配置以防止 `netplan` 覆盖网络管理器配置

  1. 按照取消管理 Docker 接口中的步骤创建网络管理器配置。

  2. 在 `/etc/netplan/50-cloud-init.yml` 下创建一个 `netplan` 配置文件。

    以下示例配置文件是一个起点。调整它以匹配您想要取消管理的接口。不正确的配置会导致网络连接问题。

    /etc/netplan/50-cloud-init.yml
    network:
      ethernets:
        all:
          dhcp4: true
          dhcp6: true
          match:
            # edit this filter to match whatever makes sense for your system
            name: en*
      renderer: networkd
      version: 2
  3. 应用新的 Netplan 配置。

    $ sudo netplan apply
    
  4. 重新启动 Docker 守护程序

    $ sudo systemctl restart docker
    
  5. 验证 Docker 接口是否具有 `unmanaged` 状态。

    $ networkctl
    

无法删除文件系统

Error: Unable to remove filesystem

某些基于容器的实用程序,例如 Google cAdvisor,会将Docker系统目录(例如/var/lib/docker/)挂载到容器中。例如,cadvisor 的文档指示您按如下方式运行 cadvisor 容器

$ sudo docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:rw \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  google/cadvisor:latest

当您绑定挂载/var/lib/docker/时,这实际上会将所有其他正在运行的容器的所有资源作为文件系统挂载到挂载了/var/lib/docker/的容器中。当您尝试删除这些容器中的任何一个时,删除尝试可能会失败并出现如下错误

Error: Unable to remove filesystem for
74bef250361c7817bee19349c93139621b272bc8f654ae112dd4eb9652af9515:
remove /var/lib/docker/containers/74bef250361c7817bee19349c93139621b272bc8f654ae112dd4eb9652af9515/shm:
Device or resource busy

如果绑定挂载/var/lib/docker/的容器在/var/lib/docker/内的文件系统句柄上使用statfsfstatfs并且没有关闭它们,则会出现此问题。

通常,我们建议不要以这种方式绑定挂载/var/lib/docker。但是,cAdvisor 需要此绑定挂载才能实现核心功能。

如果您不确定哪个进程导致错误中提到的路径繁忙并阻止其删除,您可以使用lsof命令查找其进程。例如,对于上面的错误

$ sudo lsof /var/lib/docker/containers/74bef250361c7817bee19349c93139621b272bc8f654ae112dd4eb9652af9515/shm

要解决此问题,请停止绑定挂载/var/lib/docker的容器,然后再次尝试删除其他容器。