Docker daemon 故障排除
本页介绍如何对 Docker daemon 进行故障排除和调试(如果您遇到问题)。
您可以在 daemon 上开启调试,以了解 daemon 的运行时活动并协助故障排除。如果 daemon 没有响应,您还可以通过向 Docker daemon 发送 SIGUSR
信号,强制将所有线程的完整堆栈跟踪添加到 daemon 日志中。
Daemon
无法连接到 Docker daemon
Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?
此错误可能表明
- Docker daemon 未在您的系统上运行。启动 daemon 并再次尝试运行该命令。
- 您的 Docker 客户端正在尝试连接到另一个主机上的 Docker daemon,而该主机无法访问。
检查 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 daemon。如果未设置,则 Docker 客户端设置为连接到本地主机上运行的 Docker daemon。如果设置有误,请使用以下命令取消设置
$ unset DOCKER_HOST
您可能需要编辑 ~/.bashrc
或 ~/.profile
等文件中的环境,以防止 DOCKER_HOST
变量被错误设置。
如果 DOCKER_HOST
按预期设置,请验证 Docker daemon 正在远程主机上运行,并且防火墙或网络中断没有阻止您连接。
排除 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,您可能需要调整您的标志或 daemon.json
以消除冲突。
注意
如果您看到关于
hosts
的此特定错误消息,请继续阅读下一节以了解解决方法。
如果您使用操作系统的 init 脚本启动 Docker,您可能需要以特定于操作系统的方式覆盖这些脚本中的默认设置。
使用 systemd 配置 daemon 主机
一个特别难以排除的配置冲突示例是,当您想指定与默认地址不同的 daemon 地址时。Docker 默认监听一个套接字。在使用 systemd
的 Debian 和 Ubuntu 系统上,这意味着启动 dockerd
时始终使用主机标志 -H
。如果您在 daemon.json
中指定 hosts
条目,这会导致配置冲突,并导致 Docker daemon 启动失败。
为了解决此问题,请创建一个新文件 /etc/systemd/system/docker.service.d/docker.conf
,其内容如下,以移除默认启动 daemon 时使用的 -H
参数。
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
在其他时候,您可能需要通过 systemd
配置 Docker,例如配置 HTTP 或 HTTPS 代理。
注意
如果您覆盖此选项,但在手动启动 Docker 时没有在
daemon.json
中指定hosts
条目或使用-H
标志,Docker 将无法启动。
在尝试启动 Docker 之前,运行 sudo systemctl daemon-reload
。如果 Docker 成功启动,它现在将在 daemon.json
的 hosts
键中指定的 IP 地址上监听,而不是套接字。
重要
在 Docker Desktop for Windows 或 Docker Desktop for Mac 上不支持在
daemon.json
中设置hosts
。
内存不足问题
如果您的容器尝试使用的内存超出系统可用内存,您可能会遇到内存不足 (OOM) 异常,并且容器或 Docker daemon 可能会被内核 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 swap 限制功能
在 Ubuntu 或 Debian 主机上,处理镜像时您可能会看到类似以下的消息。
WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.
如果您不需要这些功能,可以忽略此警告。
您可以在 Ubuntu 或 Debian 上按照以下说明开启这些功能。即使 Docker 未运行,内存和 swap 记账也会带来大约总可用内存 1% 的开销和总体性能下降 10%。
以具有
sudo
权限的用户身份登录 Ubuntu 或 Debian 主机。编辑
/etc/default/grub
文件。添加或编辑GRUB_CMDLINE_LINUX
行,以添加以下两个键值对GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
保存并关闭文件。
更新 GRUB 引导加载程序。
$ sudo update-grub
如果您的 GRUB 配置文件语法不正确,将会发生错误。在这种情况下,请重复步骤 2 和 3。
更改将在系统重新启动后生效。
网络
IP 转发问题
如果您使用 systemd 版本 219 或更高版本手动配置网络,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
通过将其添加到 /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
daemon 标志更改配置文件的位置。以下说明假设配置文件的位置是 /etc/docker/daemon.json
。
创建或编辑 Docker daemon 配置文件,该文件默认为
/etc/docker/daemon.json
,它控制 Docker daemon 的配置。$ sudo nano /etc/docker/daemon.json
添加一个
dns
键,其值为一个或多个 DNS 服务器 IP 地址。{ "dns": ["8.8.8.8", "8.8.4.4"] }
如果文件已有内容,您只需添加或编辑
dns
行。如果您的内部 DNS 服务器无法解析公共 IP 地址,请至少包含一个能够解析的 DNS 服务器。这样做可以使您连接到 Docker Hub,并使您的容器解析互联网域名。保存并关闭文件。
重启 Docker daemon。
$ sudo service docker restart
通过尝试拉取镜像来验证 Docker 是否可以解析外部 IP 地址
$ docker pull hello-world
如有必要,通过 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 daemon 的配置以使用特定 IP 地址,请按照这些说明在 NetworkManager 中关闭 dnsmasq
。
编辑
/etc/NetworkManager/NetworkManager.conf
文件。在
dns=dnsmasq
行的开头添加#
字符将其注释掉。# dns=dnsmasq
保存并关闭文件。
重启 NetworkManager 和 Docker。或者,您也可以重启系统。
$ sudo systemctl restart network-manager $ sudo systemctl restart docker
在 RHEL、CentOS 或 Fedora 上关闭 dnsmasq
关闭
dnsmasq
服务$ sudo systemctl stop dnsmasq $ sudo systemctl disable dnsmasq
使用 Red Hat 文档手动配置 DNS 服务器。
Docker 网络消失
如果 Docker 网络(例如 docker0
网桥或自定义网络)随机消失或出现工作异常,可能是由于其他服务干扰或修改了 Docker 接口。已知主机上管理网络接口的工具有时也会不恰当地修改 Docker 接口。
请参阅以下各节,了解如何根据主机上的网络管理工具配置您的网络管理器,将 Docker 接口设置为未受管理。
- 如果安装了
netscript
,请考虑卸载它 - 配置网络管理器以将 Docker 接口视为未受管理
- 如果您使用的是 Netplan,您可能需要应用自定义 Netplan 配置
卸载 netscript
如果您的系统安装了 netscript
,很可能通过卸载它来解决此问题。例如,在基于 Debian 的系统上
$ sudo apt-get remove netscript-2.4
未受管理的 Docker 接口
在某些情况下,网络管理器默认会尝试管理 Docker 接口。您可以尝试通过编辑系统的网络配置设置,明确将 Docker 网络标记为未受管理。
如果您使用的是 NetworkManager
,请编辑 /etc/network/interfaces
下的系统网络配置
在
/etc/network/interfaces.d/20-docker0
创建一个文件,内容如下:iface docker0 inet manual
请注意,此示例配置仅“取消管理”默认的
docker0
网桥,而不管理自定义网络。重启
NetworkManager
使配置更改生效。$ systemctl restart NetworkManager
验证
docker0
接口是否处于unmanaged
状态。$ nmcli device
如果您在将 systemd-networkd
用作网络守护进程的系统上运行 Docker,请通过在 /etc/systemd/network
下创建配置文件将 Docker 接口配置为未受管理。
创建
/etc/systemd/network/docker.network
文件,内容如下:# Ensure that the Docker interfaces are un-managed [Match] Name=docker0 br-* veth* [Link] Unmanaged=yes
重新加载配置。
$ sudo systemctl restart systemd-networkd
重启 Docker daemon。
$ sudo systemctl restart docker
验证 Docker 接口是否处于
unmanaged
状态。$ networkctl
阻止 Netplan 覆盖网络配置
在通过 Netplan 通过 cloud-init
的系统上,您可能需要应用自定义配置,以防止 netplan
覆盖网络管理器配置。
按照未受管理的 Docker 接口中的步骤创建网络管理器配置。
在
/etc/netplan/50-cloud-init.yml
下创建一个netplan
配置文件。以下示例配置文件是一个起点。请根据您想要未受管理的接口进行调整。不正确的配置可能导致网络连接问题。
/etc/netplan/50-cloud-init.ymlnetwork: ethernets: all: dhcp4: true dhcp6: true match: # edit this filter to match whatever makes sense for your system name: en* renderer: networkd version: 2
应用新的 Netplan 配置。
$ sudo netplan apply
重启 Docker 守护进程
$ sudo systemctl restart docker
验证 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
当您 bind-mount(绑定挂载) /var/lib/docker/
时,这实际上会将所有其他正在运行的容器的所有资源作为文件系统挂载到挂载 /var/lib/docker/
的容器内。当您尝试删除其中任何容器时,删除尝试可能会失败,并出现如下错误:
Error: Unable to remove filesystem for
74bef250361c7817bee19349c93139621b272bc8f654ae112dd4eb9652af9515:
remove /var/lib/docker/containers/74bef250361c7817bee19349c93139621b272bc8f654ae112dd4eb9652af9515/shm:
Device or resource busy
如果 bind-mount 了 /var/lib/docker/
的容器对 /var/lib/docker/
内的文件系统句柄使用了 statfs
或 fstatfs
并且没有关闭它们,就会出现此问题。
通常,我们建议不要以这种方式 bind-mount /var/lib/docker
。但是,cAdvisor
的核心功能需要这种 bind-mount。
如果不确定是哪个进程导致错误中提到的路径被占用而无法删除,可以使用 lsof
命令查找该进程。例如,对于上面的错误:
$ sudo lsof /var/lib/docker/containers/74bef250361c7817bee19349c93139621b272bc8f654ae112dd4eb9652af9515/shm
为解决此问题,请停止 bind-mount 了 /var/lib/docker
的容器,然后再次尝试删除另一个容器。