服务器开机/重启时配置restart:always的Docker容器启动报Exit 127
故障根因
容器返回Exit 127是Linux标准退出码,代表entrypoint指定的启动命令不存在、或命令依赖的关键文件缺失,和db、redis服务是否就绪没有关系,这也是之前配置服务健康检查不生效的原因。
手动执行docker-compose restart能正常启动,是因为此时宿主机所有磁盘分区、挂载点已经完成加载,容器绑定的宿主机目录可正常访问。开机自启时Docker服务启动早于/home分区(或存放代码的对应数据分区)的挂载流程,容器启动时绑定的三个/home/livebuzz/路径还不存在,容器内对应挂载点为空,entrypoint找不到要执行的项目文件直接退出,且因为容器进程没真正启动,所以执行docker-compose logs看不到任何应用日志。
排查验证步骤
- 对比启动时序:执行
systemd-analyze blame查看开机后各单元启动耗时,确认docker.service启动时间早于对应/home路径的挂载单元时间 - 开机后不手动操作容器,直接执行
docker inspect control-php查看Mounts字段,可看到绑定挂载的Source路径存在no such file or directory类的报错记录 - 开机后手动检查
/home/livebuzz/code/control等三个挂载源路径,确认在容器启动失败的时间点,这些路径还未被系统挂载、或为空目录
解决方案
方案1:调整Docker服务启动依赖(推荐)
让Docker服务等待所有相关挂载点就绪后再启动,从系统层面规避启动时序问题:
- 执行命令编辑Docker服务的systemd覆写配置:
systemctl edit docker.service - 在打开的编辑器中插入以下配置,指定Docker在/home挂载完成后再启动:
[Unit] After=network-online.target firewalld.service home.mount Wants=network-online.target home.mount如果代码存放在单独的数据盘(比如单独挂载到
/home/livebuzz),可以执行systemctl list-units --type=mount查询对应挂载点的单元名,替换配置里的home.mount即可。 - 保存退出后执行以下命令重载配置,下次开机即可验证效果:
systemctl daemon-reload
方案2:给容器增加挂载点等待逻辑
如果不方便调整系统服务启动顺序,可以修改control-php容器的启动逻辑,启动前先等待挂载目录就绪再执行业务启动命令:
- 编写自定义entrypoint脚本,循环检测项目核心文件是否存在,确认挂载完成后再执行原有启动命令:
#!/bin/sh # 最多等待30秒,检测项目入口文件是否存在 for wait_count in $(seq 1 30); do if [ -f /var/www/control/项目实际入口文件路径 ]; then break fi sleep 1 done # 执行原有镜像的启动命令 exec docker-php-entrypoint 原容器启动命令 - 在Dockerfile中把该脚本设置为新的entrypoint,重新构建镜像即可。
方案3:调整代码存放路径
把绑定挂载的代码、配置文件迁移到/opt、/srv这类随根分区早期挂载的路径下,避免晚挂载的目录导致容器启动时文件缺失,该方案需要修改docker-compose里的volumes路径配置,改动量较大,可按需选择。
内容的提问来源于stack exchange,提问作者user1469914
相关产品推荐
相关产品推荐

