排查 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 dockersudo status dockersudo service docker status,或者使用 Windows 實用程式檢查服務狀態。

最後,你可以在程序列表中檢查 dockerd 程序,使用 pstop 等命令。

檢查你的客戶端連線到的主機

要檢視你的客戶端連線到的主機,請檢查環境中的 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 的此特定錯誤訊息,請繼續到 下一節 以瞭解解決方法。

如果你使用作業系統的 init 指令碼啟動 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.jsonhosts 金鑰中指定的 IP 地址,而不是套接字。

重要

在 Docker Desktop for Windows 或 Docker Desktop for Mac 上不支援在 daemon.json 中設定 hosts

記憶體不足問題

如果你的容器嘗試使用超過系統可用記憶體的記憶體,你可能會遇到記憶體不足 (OOM) 異常,並且容器或 Docker 守護程式可能會被核心 OOM 殺手停止。要防止這種情況發生,請確保你的應用程式在具有足夠記憶體的主機上執行,並參閱 瞭解記憶體不足的風險

核心相容性

如果你的核心版本低於 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-network 手動配置網路,並且系統使用 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 透過將 DNS 請求新增到 /etc/resolv.conf 來快取 DNS 請求。dnsmasq 例項執行在迴環地址上,例如 127.0.0.1127.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. 如果需要,請驗證 Docker 容器是否可以解析內部主機名,方法是 ping 該主機名。

    $ 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 伺服器。


無法刪除檔案系統

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/` 內的檔案系統控制代碼使用 `statfs` 或 `fstatfs` 並且沒有關閉它們,就會出現此問題。

通常,我們建議不要以這種方式繫結掛載 `/var/lib/docker`。但是,`cAdvisor` 需要這種繫結掛載來實現核心功能。

如果你不確定哪個程序導致錯誤中提到的路徑繁忙併阻止其刪除,可以使用 `lsof` 命令查詢其程序。例如,對於上面的錯誤

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

要解決此問題,請停止繫結掛載 `/var/lib/docker` 的容器,然後再次嘗試刪除其他容器。