Docker學習總結(46)——生產環境中遇到的Docker常見異常錯誤總結

一、docker ps 無響應, Node 節點表現爲 NotReady。

運行信息

$ docker -v
$ Docker version 17.03.2-ce, build f5ec1e2
$ docker-containerd -v
$ containerd version 0.2.3 commit:4ab9917febca54791c5f071a9d1f404867857fcc
$ docker-runc -v
$ runc version 1.0.0-rc2
$ commit: 54296cf40ad8143b62dbcaa1d90e520a2136ddfe
$ spec: 1.0.0-rc2-dev

啓用 Docker Debug 模式

有兩種方法可以啓用調試。 建議的方法是在 daemon.json 文件中將 debug 設置爲 true。 此方法適用於每個 Docker 平臺。1.編輯 daemon.json 文件,該文件通常位於 /etc/docker/ 中。 如果該文件尚不存在,您可能需要創建該文件。 2.增加以下設置

{
 "debug": true
}

3.向守護程序發送 HUP 信號以使其重新加載其配置。

sudo kill -SIGHUP $(pidof dockerd)

可以看到 Docker debug 級別的日誌:

Dec 14 20:04:45 dockerd[7926]: time="2018-12-14T20:04:45.788669544+08:00" level=debug msg="Calling GET /v1.27/containers/json?all=1&filters=%7B%22label%22%3A%7B%22io.kubernetes.docker.type%3Dpodsandbox%22%3Atrue%7D%7D&limit=0"
Dec 14 20:04:45 dockerd[7926]: time="2018-12-14T20:04:45.790628950+08:00" level=debug msg="Calling GET /v1.27/containers/json?all=1&filters=%7B%22label%22%3A%7B%22io.kubernetes.docker.type%3Dcontainer%22%3Atrue%7D%7D&limit=0"
Dec 14 20:04:46 dockerd[7926]: time="2018-12-14T20:04:46.792531056+08:00" level=debug msg="Calling GET /v1.27/containers/json?all=1&filters=%7B%22label%22%3A%7B%22io.kubernetes.docker.type%3Dpodsandbox%22%3Atrue%7D%7D&limit=0"
Dec 14 20:04:46 dockerd[7926]: time="2018-12-14T20:04:46.794433693+08:00" level=debug msg="Calling GET /v1.27/containers/json?all=1&filters=%7B%22label%22%3A%7B%22io.kubernetes.docker.type%3Dcontainer%22%3Atrue%7D%7D&limit=0"
Dec 14 20:04:47 dockerd[7926]: time="2018-12-14T20:04:47.097363259+08:00" level=debug msg="Calling GET /v1.27/containers/json?filters=%7B%22label%22%3A%7B%22io.kubernetes.docker.type%3Dpodsandbox%22%3Atrue%7D%7D&limit=0"
Dec 14 20:04:47 dockerd[7926]: time="2018-12-14T20:04:47.098448324+08:00" level=debug msg="Calling GET /v1.27/containers/json?all=1&filters=%7B%22label%22%3A%7B%22io.kubernetes.docker.type%3Dcontainer%22%3Atrue%7D%2C%22status%22%3A%7B%22running%22%3Atrue%7D%7D&limit=0"
Dec 14 20:04:47 dockerd[7926]: 

dockerd一直在請求 list containers 接口,但是沒有響應。打印堆棧信息

$ kill -SIGUSR1 $(pidof dockerd)

生成的調試信息可以在以下目錄找到:

...goroutine stacks written to /var/run/docker/goroutine-stacks-2018-12-02T193336z.log
...daemon datastructure dump written to /var/run/docker/daemon-data-2018-12-02T193336z.log

查看 goroutine-stacks-2018-12-02T193336z.log 文件內容,

goroutine 248 [running]:
github.com/docker/docker/pkg/signal.DumpStacks(0x18fe090, 0xf, 0x0, 0x0, 0x0, 0x0)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/pkg/signal/trap.go:82 +0xfc
github.com/docker/docker/daemon.(*Daemon).setupDumpStackTrap.func1(0xc421462de0, 0x18fe090, 0xf, 0xc4203c8200)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/daemon/debugtrap_unix.go:19 +0xcb
created by github.com/docker/docker/daemon.(*Daemon).setupDumpStackTrap
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/daemon/debugtrap_unix.go:32 +0x10a
goroutine 1 [chan receive, 91274 minutes]:
main.(*DaemonCli).start(0xc42048a840, 0x0, 0x190f560, 0x17, 0xc420488400, 0xc42046c820, 0xc420257320, 0x0, 0x0)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/cmd/dockerd/daemon.go:326 +0x183e
main.runDaemon(0x0, 0x190f560, 0x17, 0xc420488400, 0xc42046c820, 0xc420257320, 0x10, 0x0)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/cmd/dockerd/docker.go:86 +0xb2
main.newDaemonCommand.func1(0xc42041f200, 0xc42045df00, 0x0, 0x10, 0x0, 0x0)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/cmd/dockerd/docker.go:42 +0x71
github.com/docker/docker/vendor/github.com/spf13/cobra.(*Command).execute(0xc42041f200, 0xc42000c130, 0x10, 0x11, 0xc42041f200, 0xc42000c130)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/vendor/github.com/spf13/cobra/command.go:646 +0x26d
github.com/docker/docker/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0xc42041f200, 0x16fc5e0, 0xc42046c801, 0xc420484810)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/vendor/github.com/spf13/cobra/command.go:742 +0x377
github.com/docker/docker/vendor/github.com/spf13/cobra.(*Command).Execute(0xc42041f200, 0xc420484810, 0xc420084058)
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/vendor/github.com/spf13/cobra/command.go:695 +0x2b
main.main()
 /root/rpmbuild/BUILD/docker-ce/.gopath/src/github.com/docker/docker/cmd/dockerd/docker.go:106 +0xe2
goroutine 17 [syscall, 91275 minutes, locked to thread]:
...

至此,我們可以確定,containerd 無響應導致的 docker ps 無響應,在堆棧中我們也可以看到調用 containerd 無響應是因爲加了lock.查看dmesg:通過 dmesg 查看系統異常信息,發現 cgroup 報 OOM 溢出錯誤。

[7993043.926831] Memory cgroup out of memory: Kill process 20357 (runc:[2:INIT]) score 970 or sacrifice child

查看了大部分機器的 dmesg 信息,發現都有 OOM 這個錯誤,至此我們懷疑是由於某個容器 OOM 導致的 containerd 無響應。模擬OOM。既然懷疑是容器 OOM 異常導致的 containerd 無響應,那我們乾脆自己創造現場模擬一下。首選我們創建一個 OOM 的部署,通過 nodeSelector 讓這個部署調度到指定 Node。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 labels:
 wayne-app: oomtest
 wayne-ns: test
 app: oomtest
 name: oomtest
spec:
 selector:
 matchLabels:
 app: oomtest
 template:
 metadata:
 labels:
 wayne-app: oomtest
 wayne-ns: test
 app: oomtest
 spec:
 nodeSelector:
 kubernetes.io/hostname: test-001
 containers:
 - resources:
 limits:
 memory: 0.2Gi
 cpu: '0.2'
 requests:
 memory: 0.2Gi
 cpu: '0.1'
 args:
 - '-m'
 - '10'
 - '--vm-bytes'
 - 128M
 - '--timeout'
 - 60s
 - '--vm-keep'
 image: progrium/stress
 name: stress

發現過了一會 test-001 這臺 Node 出現了 docker ps 無響應的情況,查看 dmesg 以及 containerd 的堆棧信息,發現和之前的 Node 出現的異常一致。至此,基本可以確定是某個容器 OOM 導致的 containerd hung 住。

原因分析

通過查找社區 Issues 及相關 PR,最後發現根本原因是 runc 的bug。使用 runc start 或 runc run 啓動容器時,stub process(runc [2:INIT])打開一個 fifo 進行寫入。 它的父 runc 進程 將打開相同的 fifo 閱讀。 通過這種方式,它們可以同步。如果 stub process 在錯誤的時間退出,那麼父 runc 進程 會永遠被阻塞。當兩個 runc 操作相互競爭時會發生這種情況:runc run / start 和 runc delete。 它也可能由於其他原因而發生, 例如 內核的 OOM killer 可以選擇殺死 stub process。

解決方案

通過解決 exec fifo 競爭來解決這個問題。 如果 在我們打開 fifo 之前 stub process 退出,那麼返回一個錯誤。

小結

containerd 官方已經在 v1.0.2 版本合併了這個修改。因此這個問題可以通過升級 Docker 版本解決。我們目前已經將部分機器升級到 Docker 18.06。 已升級的機器暫時未發現類似問題。相關issues: https://github.com/containerd/containerd/issues/1882、https://github.com/containerd/containerd/pull/2048、https://github.com/opencontainers/runc/pull/1698

二、Docker 在 Centos 系統下以 direct-lvm 模式運行, 無法啓動

Error starting daemon: error initializing graphdriver: devicemapper: Non existing device docker-thinpool
Dec 14 03:21:03 two-slave-41-135 systemd: docker.service: main process exited, code=exited, status=1/FAILURE
Dec 14 03:21:03 two-slave-41-135 systemd: Failed to start Docker Application Container Engine.
Dec 14 03:21:03 two-slave-41-135 systemd: Dependency failed for kubernetes Kubelet.
Dec 14 03:21:03 two-slave-41-135 systemd: Job kubelet.service/start failed with result 'dependency'.

根本原因

這個問題發生在使用 devicemapper 存儲驅動時Docker試圖重用之前使用 LVM thin pool。例如,嘗試更改節點上的 Docker 的數據目錄時會發生此問題。由於安全措施旨在防止 Docker 因配置問題而意外使用和覆蓋 LVM thin pool 中的數據,因此會發生此錯誤。

解決方案

要解決阻止Docker啓動的問題,必須刪除並重新創建邏輯卷,以便Docker將它們視爲新的thin pool。

警告:這些命令將清除Docker數據目錄中的所有現有鏡像和卷。 請在執行這些步驟之前備份所有重要數據。

1.停止 Docker

sudo systemctl stop docker.service

2.刪除 Dodcker 目錄

sudo rm -rf /var/lib/docker

3.刪除已經創建的 thin pool 邏輯卷

$ sudo lvremove docker/thinpool
Do you really want to remove active logical volume docker/thinpool? [y/n]: y
 Logical volume "thinpool" successfully removed

4.創建新的邏輯卷

lvcreate -L 500g --thin docker/thinpool --poolmetadatasize 256m

根據實際磁盤大小調整 thinpool 和 metadata 大小

Docker自動direct-lvm模式配置

如果您想要讓Docker自動爲您配置direct-lvm模式,請繼續執行以下步驟。1.編輯/etc/docker/daemon.json文件以將dm.directlvm_device_force = value從false更改爲true。 例如:

{
 "storage-driver": "devicemapper",
 "storage-opts": [
 "dm.directlvm_device_force=true"
 ]
}

2.除了刪除邏輯卷之外,還要刪除docker卷組:

$ sudo vgremove docker

3.啓動Dokcer

sudo systemctl start docker.service

三、總結

Docker 雖然是目前最常用的容器解決方案,但它仍舊有很多不足。

  • Docker 的隔離性比較弱,混布容易導致業務互相影響,可能因爲一個服務有問題就會影響其他服務甚至影響整個集羣。
  • Docker 自己存在一些 bug, 由於歷史原因,很多 bug 無法完全歸因於內核或者 Docker,需要 Docker 和內核配合修復。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章