云服务器Docker启动失败原因?
- 来源:纵横数据
- 作者:中横科技
- 时间:2026/5/22 17:13:33
- 类别:新闻资讯
说实话,每次在云服务器上执行“systemctl start docker”之后,心里多少还是有点悬着的。因为Docker这东西,启动失败的原因实在是太多了。有时候是配置文件写错了,有时候是内核版本不兼容,还有时候是之前的残留文件在捣乱。
我接触Docker已经有几年了,从最早的1.几版本一直用到现在,在云服务器上经历过的启动失败少说也有几十次。今天这篇文章,我想把这些年遇到的Docker启动失败的案例和原因,老老实实地记录下来,希望能帮你少走一些弯路。
内核版本太低:最基础的硬性门槛
先讲一个让我印象特别深刻的案例。有一回,我在一台比较老的云服务器上安装Docker,安装过程很顺利,没有任何报错。但是到了启动这一步,死活起不来。执行“systemctl status docker”一看,服务状态是failed,日志里有一行错误写着“failed to start daemon: Devices cgroup isn't mounted”。
当时我花了好一阵子才弄明白,这台云服务器的内核版本是3.10,虽然Docker官方文档说3.10以上就能跑,但实际上很多功能都依赖较新的内核特性。而且这台机器的cgroup文件系统挂载方式跟新内核不太一样,导致Docker无法正确检测和控制容器的资源使用。
修复这个问题,最直接的办法就是升级内核。但云服务器升级内核不像物理机那么简单,得看云厂商是否提供了新版内核的镜像。有些老旧的云主机实例类型根本就不支持新内核,这时候就只能换一台较新规格的云服务器了。后来我养成了一个习惯,在云服务器上装Docker之前,先执行“uname -r”看一眼内核版本,低于4.0的版本我都会格外小心,优先考虑用官方脚本安装Docker,因为官方脚本会自动检测内核兼容性并给出提示。
还有一个跟内核相关的问题是缺少必要的内核模块。Docker依赖overlay2存储驱动,这需要内核加载overlay模块。有些云服务器为了安全考虑,默认禁用了这个模块。用“lsmod | grep overlay”可以检查模块是否加载,如果没有,就需要手动加载或者修改系统配置让它在启动时自动加载。
配置文件语法错误:一个多余的逗号引发的血案
Docker守护进程的配置文件通常放在“/etc/docker/daemon.json”里。这个文件要是写错了,Docker基本上就起不来了,而且报错信息有时候还不那么直观。
有一次我在配置Docker的镜像加速器,手动编辑了daemon.json文件。保存之后重启Docker,发现启动失败了。执行“systemctl status docker”看到的错误信息很长,核心意思是说配置文件解析失败。我用“cat daemon.json”看了一下文件内容,发现自己在最后一个配置项后面多加了一个逗号。JSON格式规定最后一个元素后面不能有逗号,就是这个多余的符号,让Docker无法解析配置文件,干脆拒绝启动。
从那以后,我每次修改daemon.json文件之后,都会先用“docker info”或者“dockerd —config-file /etc/docker/daemon.json —validate”来验证一下配置文件的语法是否正确。如果没有docker命令可用,也可以用一个简单的Python命令来验证JSON格式,“python3 -m json.tool daemon.json”会把文件内容重新格式化一遍,如果语法有错会直接报出来。
另外要注意的是,daemon.json文件里的配置项名字一定要写对。比如“storage-driver”写成“storage_driver”,Docker不会自动纠正,而是直接报错说这个配置项不认识。大小写也是敏感的,“RegistryMirrors”和“registry-mirrors”完全不是一回事。
存储驱动冲突:老数据拖累了新服务
这个问题发生在Docker版本升级之后比较多见。Docker支持多种存储驱动,比如aufs、overlay、overlay2、devicemapper。不同版本默认使用的存储驱动不一样,如果之前已经存储了一些镜像和容器,升级之后新的Docker版本尝试用不同的存储驱动去读取旧的数据,就会发生冲突。
我遇到过一台云服务器,从Docker 1.12升级到18.09之后,Docker就启动不了了。日志里提示“overlay2 is not supported with existing graph driver”。原因很简单,旧版本用的是overlay驱动,新版本默认用overlay2,两种驱动不兼容。解决方法是清理掉旧的Docker数据目录,通常是“/var/lib/docker”,但这个操作会删除所有已有的镜像、容器和卷,所以在做之前一定要确认这些数据可以丢弃或者已经备份过了。
如果你不想丢数据,也可以在daemon.json里强制指定使用旧的存储驱动,比如写“storage-driver”:“overlay”。这样新版本的Docker就会沿用旧的驱动,继续使用已有的数据。不过这不是长久之计,因为旧驱动迟早会被淘汰,更好的做法是迁移数据或者重新构建环境。
还有一个场景是云服务器上的磁盘分区挂载选项不对。overlay2存储驱动要求“/var/lib/docker”所在的文件系统支持d_type特性。有些文件系统或者某些挂载方式下d_type是禁用的,导致overlay2无法正常工作。这个问题在旧版本的云服务器上比较常见,解决方法是修改“/etc/fstab”里的挂载选项,添加“user_xattr”参数,然后重新挂载。
端口冲突:Docker要用的端口被占了
Docker守护进程默认监听一个Unix socket,这是本地通信用的。但如果配置了远程访问,比如让Docker监听2375端口,那么端口冲突的问题就来了。已经有一个程序占用了2375端口,Docker启动的时候就没办法绑定到这个端口上。
我有一个同事曾经遇到这个问题。他在云服务器上调试一个应用,手动启动了某个服务占用了2375端口,后来忘了关。重启Docker的时候一直报“bind: address already in use”。排查了半小时才发现是这个原因。用“netstat -tlnp | grep 2375”看到了占用端口的进程,关掉之后Docker就能正常启动了。
更常见的情况是,Docker安装之后默认不监听任何TCP端口,所以端口冲突本身不算高频问题。但如果你的云服务器上装了其他容器运行时,比如containerd或者cri-o,它们可能会跟Docker抢端口或者抢资源。尤其要注意的是,Docker和containerd的关系比较特殊,新版本的Docker内部会启动一个containerd进程,如果系统里单独装了一个containerd服务,两者可能会冲突。
系统资源不足:内存或磁盘不够用
Docker启动的时候需要分配一定的系统资源。虽然Docker本身占用的内存不大,但启动过程中要初始化存储驱动,加载已有的镜像元数据,这些操作都需要内存和磁盘空间。如果云服务器的内存耗尽或者磁盘满了,Docker就会启动失败。
我遇到过一台云服务器,磁盘使用率已经达到了百分之九十八。“/var/lib/docker”这个目录虽然没有多少数据,但Docker启动的时候要在里面创建一些临时文件和锁文件,由于磁盘空间不足,写文件失败,Docker就报错退出了。清理了一些旧的日志文件和临时文件,腾出几百兆空间之后,Docker就可以正常启动了。
内存不足的问题相对少见一些,因为Docker守护进程本身的内存占用一般不超过几百兆。但如果云服务器本身就只有512MB内存,而且上面还跑着其他服务,那Docker启动失败的可能性就很大了。用“free -h”看看可用内存,如果剩余内存太少,要么停掉一些不必要的服务,要么升级云主机的配置。
SELinux或者AppArmor的干扰:安全模块成了拦路虎
云服务器上默认开启SELinux或者AppArmor的情况不是很普遍,但确实有一些发行版会默认启用这些安全模块。它们会在内核层面限制进程的权限,Docker的某些操作如果触犯了安全策略,就会被拦截,导致启动失败或者功能异常。
有个案例是这样的,一台CentOS云服务器上装了Docker,启动没有报错,但是创建容器的时候一直提示权限不足。检查了用户权限,当前用户在docker组里,按理说应该没问题。后来看了SELinux的审计日志,“/var/log/audit/audit.log”里记录了很多被denied的操作。临时把SELinux设置成permissive模式,“setenforce 0”,再创建容器就成功了。这说明问题出在SELinux的策略上。
如果你不想关掉SELinux,可以安装container-selinux这个包,它提供了Docker运行需要的SELinux策略。安装之后重启Docker,问题通常就能解决。如果实在搞不定,关掉SELinux也是一个选择,毕竟大部分云服务器上这个功能并不是必需的。
对于使用AppArmor的Ubuntu云服务器,情况类似。AppArmor默认有一个docker的profile,如果这个profile有问题或者被修改过,Docker的某些功能就会受限。检查“/var/log/syslog”里AppArmor相关的条目,确认是不是被拦截了。
Docker服务文件损坏:systemd层面的问题
在大多数Linux发行版上,Docker是通过systemd来管理的。Docker的service文件如果被误删或者损坏,systemd就无法正确启动Docker进程。这种情况虽然不常见,但一旦发生,表现就是执行任何systemctl命令都没有明显的报错,但Docker就是起不来。
我帮朋友处理过一次这个问题。他的云服务器上不知道什么时候被人删掉了“/lib/systemd/system/docker.service”文件,导致systemctl start docker的时候虽然返回了0,但实际上并没有启动任何Docker进程。用“ps aux | grep dockerd”一看,进程根本不存在。重新安装了Docker包之后,service文件被恢复,问题就解决了。
如果你怀疑是service文件的问题,可以用“systemctl cat docker”查看当前systemd使用的service文件内容。如果文件不存在或者内容明显不对,重新安装Docker是最省事的办法。对于Debian系的系统,“apt install —reinstall docker-ce”就可以,RedHat系用“yum reinstall docker-ce”。
挂载点问题:NFS或者FUSE文件系统上的数据目录
有些用户会把Docker的数据目录“/var/lib/docker”放在NFS网络存储或者FUSE文件系统上,比如用fuse挂载的云存储桶。这么做虽然能让镜像和容器数据跨主机共享,但风险很大。因为Docker的存储驱动对这些文件系统的支持并不完善,经常出现文件锁问题或者元数据操作失败。
真实发生过的一个案例是,某团队把云服务器的“/var/lib/docker”目录通过NFS挂载到了一个共享存储上,想着多台云主机共享镜像仓库。结果Docker启动的时候一直卡住,日志里提示“failed to open database file”。原因是NFS的锁机制跟Docker内嵌的boltdb数据库不兼容,数据库文件无法被正确锁定。最后放弃了NFS方案,改用本地磁盘加镜像仓库的方式解决了问题。
如果你确实需要把Docker数据目录放在非本地文件系统上,建议使用overlay驱动而不是overlay2,并且要提前做好充分的测试。但总的来说,我个人不推荐这种做法,稳定性和性能都很难保证。
代理配置错误:拉取镜像的连带问题
严格来说,代理配置错误不会导致Docker守护进程启动失败,但会导致启动之后无法拉取镜像,从使用者的角度看,跟启动失败差不多。Docker守护进程和Docker客户端都有各自的代理配置方式,很多人搞混了,结果就是docker命令能执行,但docker pull一直超时。
有一个案例是这样的,云服务器在公司的内网环境里,需要配置HTTP代理才能访问外网。用户在“/etc/systemd/system/docker.service.d/http-proxy.conf”里配置了环境变量,但是没有执行“systemctl daemon-reload”和“systemctl restart docker”,导致配置没有生效。docker pull的时候一直卡在连接registry的阶段。重新加载systemd配置并重启Docker之后,镜像就能正常拉取了。
如果你在云服务器上配置了代理,记得Docker守护进程的代理配置是需要重启才能生效的。而且不同版本的Docker对代理的支持不完全一样,最好查阅一下官方文档确认配置方法。
总结
写到这里,回头看看这些年踩过的Docker启动失败的坑,我发现一个规律:大部分问题的根源,都在于对Docker依赖的环境没有完全搞清楚。
Docker不是一个孤立的软件,它依赖内核版本、依赖存储驱动、依赖cgroup、依赖systemd、依赖配置文件格式、还依赖各种安全模块的正常工作。任何一个环节出了问题,Docker都可能启动失败。而解决问题的关键,就是学会看日志。Docker的日志通常可以通过“journalctl -u docker”或者“systemctl status docker”看到,有时候完整的日志信息会被截断,加上“-n 50”或者“-f”参数能看到更多内容。
另外一点很重要,云服务器上的Docker环境跟本地开发环境不太一样。云服务器的系统通常经过云厂商的定制,内核参数、文件系统挂载选项、安全模块配置都可能有自己的特点。这些特点本身不是问题,但如果你不清楚它们的存在,就会在排障的时候走很多弯路。
我的经验是,遇到Docker启动失败,先别急着百度或者搜索错误码。按照这个顺序来排查:先看内核版本是否符合要求,再看磁盘空间和内存是否充足,接着检查daemon.json配置文件语法是否正确,然后查看Docker的日志输出。如果这些都没问题,再考虑存储驱动冲突、端口占用、SELinux干扰这些相对深层的原因。
Docker启动失败这件事,说大不大,说小也不小。大部分问题在有了正确的排查思路之后,都能在十几分钟内解决。




使用微信扫一扫
扫一扫关注官方微信 

