事件简介:
周末出现了一次偶然的停电事故,供电恢复后,按照惯例重启服务器,并重新运行各项服务。然而,一台Ubuntu 16.04服务器的Docker容器无法启动了……
1. 启动容器报错
# docker start nginx_test
Error response from daemon: cgroups: cgroup mountpoint does not exist: unknown
Error: failed to start containers: nginx_test
错误信息指出:cgroup的挂载点不存在!
2. 手动挂载cgroup
从网络查询可知,很多用户都遇到过该错误,一个临时的解决办法是执行以下两条命令:
# sudo mkdir /sys/fs/cgroup/systemd
# sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
由于这仅仅是一个临时处理办法,系统重启后失效。因此,重启系统后,需要再次执行以上命令,才能正常启动容器,目前暂时没有找到可行的持久化解决方案。
参考资料:https://github.com/docker/for-linux/issues/219
3. 再次尝试启动容器,出现新的报错
# docker start nginx_test
Error response from daemon: OCI runtime create failed: container with id exists: 6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8: unknown
Error: failed to start containers: nginx_test
以上错误信息较容易理解:由于运行时已经存在,创建运行时失败!
4. 删除错误的容器运行时信息
以上错误是我们第一次启动容器失败造成的:容器虽然启动失败,但已经产生了一些垃圾信息。
我们需要到容器运行时目录,列出运行时信息,删除启动失败的容器运行时ID文件夹:
# cd /run/docker/runtime-runc/moby
# ll
total 0
drwx------ 6 root root 120 7月 4 22:05 ./
drwx------ 3 root root 60 7月 4 22:02 ../
drwx--x--x 2 root root 80 7月 4 22:05 6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8/
# rm -rf 6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8/
5. 第三次启动容器,似乎启动成功
# docker start nginx_test
nginx_test
6. 确认容器运行状态
通过docker ps确认容器运行状态,发现容器并没有启动,实际情况是容器启动后又自动退出了。
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7. 查看容器日志
a. 找出日志路径。
# docker inspect nginx_test
[
{
"Id": "6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8",
"Created": "2022-07-04T13:59:30.590052488Z",
"Path": "nginx",
"Args": [
"-g",
"daemon off;"
],
"State": {
"Status": "exited",
"Running": false,
......
},
......
"LogPath": "/var/lib/docker/containers/6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8/6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8-json.log",
......
}
]
b. 查看日志
# tail -n 10 /var/lib/docker/containers/6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8/6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8-json.log
{"log":"2022/07/04 21:59:30 [emerg] 1#1: socket() 0.0.0.0:80 failed (13: Permission denied)\n","stream":"stderr","time":"2022-07-04T13:59:30.980187208Z"}
{"log":"nginx: [emerg] socket() 0.0.0.0:80 failed (13: Permission denied)\n","stream":"stderr","time":"2022-07-04T13:59:30.980199187Z"}
{"log":"2022/07/04 22:08:04 [emerg] 1#1: socket() 0.0.0.0:80 failed (13: Permission denied)\n","stream":"stderr","time":"2022-07-04T14:08:04.4140586Z"}
{"log":"nginx: [emerg] socket() 0.0.0.0:80 failed (13: Permission denied)\n","stream":"stderr","time":"2022-07-04T14:08:04.414142635Z"}
错误信息表现为权限问题,尝试启动其它容器,结果类似,皆因为权限问题而启动失败。
8. 关闭apparmor
根据网络资料,docker的权限问题可能与系统安全应用程序apparmor有关,一些用户关闭或者卸载apparmor后问题解决,这一点特别涉及的是Deepin操作系统。
当前的操作系统Ubuntu与Deepin一样基于Debian,于是同样尝试了关闭apparmor的效果:
# systemctl stop apparmor
结果证明,问题依旧。
随后还确认了SELinux状态,发现SELinux并没有开启。
9. 开启docker容器的特权模式
在普通权限模式下,docker容器中的root用户并不具备宿主机的root权限。为了让容器中的root用户成为真正的root用户,可以通过--privileged参数来运行容器。
docker run --privileged
由于容器已经存在,不想重新创建容器,此时可以直接修改容器运行配置。
注:
修改容器运行配置需要关闭docker。
不关闭docker,仅仅关闭容器,修改容器运行配置无效,并且修改后的配置还可能会被docker回写覆盖。
a. 关闭docker
# systemctl stop docker
b. 到达容器运行配置目录,找到配置文件hostconfig.json
# cd /var/lib/docker/containers/
# ll
total 40
drwx------ 10 root root 4096 7月 4 21:59 ./
drwx--x--x 14 root root 4096 7月 4 22:02 ../
drwx------ 4 root root 4096 7月 4 22:08 6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8/
# cd 6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8/
8# ll
total 44
drwx------ 4 root root 4096 7月 4 22:08 ./
drwx------ 10 root root 4096 7月 4 21:59 ../
-rw-r----- 1 root root 578 7月 4 22:08 6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8-json.log
drwx------ 2 root root 4096 7月 4 21:59 checkpoints/
-rw------- 1 root root 3150 7月 4 22:08 config.v2.json
-rw-r--r-- 1 root root 1558 7月 4 22:08 hostconfig.json
-rw-r--r-- 1 root root 13 7月 4 22:08 hostname
-rw-r--r-- 1 root root 174 7月 4 22:08 hosts
drwx------ 2 root root 4096 7月 4 21:59 mounts/
-rw-r--r-- 1 root root 42 7月 4 22:08 resolv.conf
-rw-r--r-- 1 root root 71 7月 4 22:08 resolv.conf.hash
c. 编辑容器运行配置文件hostconfig.json,将privileged的值由false改为true
# vi hostconfig.json
d. 启动docker
# systemctl start docker
e. 确认容器的特区模式为开启状态
# docker inspect nginx_test
[
{
"Id": "6d3477dce19d76a2c22f21e5140a290a5dc16aa23c3ef0cf45600ad2a7b806b8",
"Created": "2022-07-04T13:59:30.590052488Z",
"Path": "nginx",
"Args": [
"-g",
"daemon off;"
],
"State": {
"Status": "exited",
"Running": false,
......
},
......
"HostConfig": {
......
"Privileged": true,
......
}
}
]
f. 启动容器
注:
这时启动容器也不一定能够一次启动成功。实践中,容器启用特权模式后,需要第二次启动容器才能启动成功。
# docker start nginx_test
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# docker start nginx_test
nginx_test
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6d3477dce19d nginx "nginx -g 'daemon of…" 23 minutes ago Up 2 seconds 80/tcp, 0.0.0.0:443->443/tcp nginx_test
总结
为何停电后,原本正常运行的容器无法启动了?问题虽然得以解决,但本次docker事故仍然是未解之谜:
1. 为何docker运行容器时突然出现cgroup无法自动挂载的现象。
2. 为何原本不需要特权模式的容器在停电事件后需要开启特权模式才能正常运行;虽然有说法使用小于1024的端口需要root权限,但是在停电之前,没有以特权模式运行的容器可以正常使用小于1024的端口。