我平时收到最多的Docker私信除了“镜像拉不下来”之外就是“为什么我的容器开机不见了”。这里用一篇完整的实操笔记拆解Docker容器自启动设置覆盖创建时指定、运行后修改、不同策略选型以及Docker服务本身的开机自启设置最后把常见的几个误区也一起梳理掉。1. 容器自启动设置到底解决什么问题1.1 先说一个最常见的场景你花半小时部署好了一个Nginx容器或者MySQL容器测试一切正常第二天上班打开电脑发现服务访问不了了docker ps一看容器列表空空如也。或者更隐蔽的情况服务器因为断电重启了你人不在现场等回来的时候才发现业务挂了。这就是容器的自启动策略没有配置好。Docker容器默认是“一次性”的它依赖Docker守护进程daemon来管理生命周期。守护进程启动时并不会主动把之前所有容器都拉起来除非你明确告诉它“这个容器需要跟随Docker启动而启动”。我遇到过不少刚接触容器的人以为容器像注册成Windows服务一样装完就自动开机运行了。这个理解是错的。容器默认扮演的角色更像一个“手动启动的进程”关掉机器就是真的关了而且重启后不会自己回来。1.2 自启动和“开机启动Docker”是两件事展开之前先把概念厘清这里有两层“自启动”Docker守护进程dockerd本身的开机自启动。单个容器在守护进程启动之后是否自动恢复运行。两层缺一不可。守护进程没起来容器怎么配置都白搭。守护进程起来了但如果容器没有配置restart策略它也不会自动恢复。很多人的误区在于只配置了其中一层然后怀疑另一层出了问题。以Linux上通过systemd安装的Docker为例Docker服务默认是enabled状态也就是守护进程会开机自启。但容器呢如果你是用docker run创建后没有加任何参数那就是no策略属于“死也不会自动起”的状态。2. 核心原理restart策略是唯一下发通道2.1 restart策略的四种语义Docker官方提供四个restart策略值分别对应不同的自动拉起逻辑这是整个自启动设置的核心。no默认值。不管容器退出还是Docker重启都不会自动启动这个容器。always只要守护进程启动容器就跟着启动。如果容器异常退出Docker会尝试重启它不管当初是正常退出还是挂掉了。on-failure[:max-retries]仅当容器以非零状态码退出时才重启可以限制最多重启次数。unless-stopped类似always但有一个关键差异——如果用户在Docker停止之前手动stop了容器那么下次Docker启动时不会自动拉起该容器。这几种策略在docker run和docker update里都好使。你可能会问为什么会有unless-stopped这么个看起来“不彻底”的策略实际上它非常实用。举个例子你在维护一个容器临时把它停掉重启Docker可能不是你的本意用always的话重启Docker会强行拉起它。最后一个策略能保证“我手动停掉就不要自作主张再拉起来”符合运维直觉。2.2 修改已存在容器docker update是唯一正解很多人不知道有docker update这个子命令总以为得删了容器再重新run一个才能改策略。其实完全不需要。直接在容器运行状态下执行一条命令就完事docker update --restartalways 容器名或ID一条命令改完即时生效不用重启容器也不用重启Docker命令行会有容器名返回没有多余信息状态码是0就表示成功。实测下来它不中断容器里正在跑的进程正在处理的请求也不会断这个特性在生产环境特别有用。再强调一次docker update修改的是容器配置里的重启策略不是原地修改镜像也不会重建容器容器ID保持不变之前挂载的数据卷、网络映射都不受影响。3. 从零开始创建容器时就指定好自启动3.1 docker run时直接指定restart策略新部署容器时别图省事省略--restart参数。以部署一个Nginx容器为例docker run -d --name web_server --restart always -p 8080:80 nginx这里--restart always的语义就是只要dockerd活着就把这个容器拉起来。后面无论它是自己崩溃了还是你手动重启了Docker服务它都会尽量跑起来。这个策略最适合那些需要在宿主机启动后立刻提供服务的应用。MySQL容器也类似。有人用docker run部署MySQL时只做了端口映射和数据目录挂载没写restart策略结果服务器重启后数据库没有自动启动。MySQL这种有状态服务我建议用--restart unless-stopped因为有些时候你需要手动停止数据库做维护不希望重启Docker后又强制启动它。docker run -d --name mysql8 \ -p 3306:3306 \ -v /data/mysql:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDyour_password \ --restart unless-stopped \ mysql:8.03.2 docker-compose里的restart字段如果你习惯用docker-compose编排那就在service定义里加restart字段。注意这里的字段名是restart对应的是Docker的restart策略不是Docker Compose的restart有些老版本叫restart_policy写法不太一样。version: 3.8 services: nginx: image: nginx container_name: web_server ports: - 8080:80 restart: unless-stoppeddocker-compose up -d应用之后docker inspect能看到容器的RestartPolicy配置已经生效。需要提醒的是docker-compose写的restart在通过docker compose up创建容器后等价于docker run --restart的效果之后也可以用docker update去改但改动不会同步回docker-compose.yml文件下次up -d的时候配置会被覆盖回去。这是一个很容易踩的坑。4. 现实案例如何修改已运行容器的自启动4.1 查看当前策略动手之前先看看当前容器到底是什么策略尤其是接手别人部署的容器时这一步能帮你避免误判。docker inspect -f {{.Name}} - {{.HostConfig.RestartPolicy.Name}} $(docker ps -aq)这条命令会遍历所有容器包括已停止的把容器名和RestartPolicy打印出来。输出大概长这样/nginx-web - unless-stopped /mysql8 - no /redis-server - always如果看到no且你又希望它自启那就是执行docker update的时机了。这个命令比挨个docker inspect看JSON方便太多我后面的运维操作基本都用它来做第一轮体检。4.2 实测docker update全流程现在把目标指向一个当前restart策略为no的容器修改为unless-stoppeddocker update --restartunless-stopped my_container执行完没有任何废话输出其实成功了。稳妥一点就再查一遍确认docker inspect -f {{.HostConfig.RestartPolicy.Name}} my_container返回unless-stopped说明已经改好。整个过程不需要重启容器不需要停止再启动更不需要删除重建运行中的服务完全不受影响。这点对生产环境的价值很大你可以趁业务流量小的时候把一批容器的策略批量统一改掉不用中断任何服务。批量操作也很轻松。想把所有restart策略为no的容器全部改成unless-stopped一条shell循环就能解决for c in $(docker ps -aq); do cur$(docker inspect -f {{.HostConfig.RestartPolicy.Name}} $c) if [ $cur no ]; then docker update --restartunless-stopped $c fi done注意这个操作会改动所有当前为no的容器包括那些你可能故意设定为no的临时容器执行前建议先docker ps -a把容器列表过一遍确认哪些不需要改。4.3 关于on-failure的最大重启次数on-failure策略允许指定最多重启次数比如docker update --restarton-failure:5 my_container意思是容器非正常退出时最多自动重启5次5次之后就不再尝试了。这个策略适合那些可能存在偶发崩溃、但你又不希望它无限重启空转的场景。常见的是脚本任务型容器跑完自己退出出错重试几次还有机会成功超过上限就不再折腾。少写的提醒docker update --restarton-failure和--restarton-failure:0是有区别的前者没有限制次数后者等于no的效果。看到0的时候别以为是“无限次”的意思。5. Docker服务自身开机自启的坑与解法5.1 Linux systemd环境下的Docker服务自启前面说过容器自启动需要依赖Docker守护进程先起来。所以需要确认Docker服务本身设置了开机自启动。systemd管理下查看状态systemctl status docker输出里如果看到Loaded: loaded (/etc/systemd/system/docker.service; enabled; ...)说明已经设置为开机自启正常无需额外操作。如果显示disabled则执行systemctl enable docker注意这个命令本身不会启动Docker只创建开机软链如果当前没启动还要额外执行systemctl start docker。很多人执行完enable以为服务已经跑起来了发现docker ps报错其实是忘了start。5.2 Docker Desktop场景要注意什么如果你不是在纯Linux服务器上而是用Docker DesktopWindows/Mac情况会不一样。Docker Desktop是一个独立的桌面应用需要登录桌面环境后才会启动它的虚拟机然后拉起Docker引擎。那容器还能自动启动吗答案是可以的前提是Docker Desktop本身开机启动了并且容器配置了restart策略。Docker Desktop在设置里有一项“Start Docker Desktop when you sign in to your computer”需要勾选上。否则就算容器配置了alwaysDocker Desktop没开一切白搭。这里有个好消息即使Docker Desktop没有启动已经保存下来的容器配置不会丢失只是不会主动拉起来。等下次手动打开Docker Desktop符合restart策略的容器会自动恢复。这个场景和服务器上纯dockerd还是有一点区别的服务器上Docker服务是系统服务级存在开机不需要登录桌面就能拉起Docker Desktop则必须要用户登录系统机器重启后如果你不登录容器不会启动。5.3 守护进程启动顺序问题还有一个容易忽略的顺序依赖问题。如果容器里跑了需要网络的业务而Docker服务启动时网络还没就绪容器可能会启动失败然后一直处于重启循环。这个情况在systemd里其实可以通过依赖配置解决但Docker官方没有内置很完美的方案。常见的缓解手段是给容器加restart策略让它在守护进程启动后不断重试直到成功。always和unless-stopped策略自带“失败了自动再拉起”的兜底机制只要应用本身不是致命错误最终通常都能进入正常运行状态。6. 常见问题与排查实录6.1 配置了always还是没自启动如果你确认restart策略已经改成always但重启机器后容器还是没起来按下面顺序排查第一步确认Docker服务是否真的开机自启了执行systemctl is-enabled docker看到disabled就说明服务本身都没起来。第二步看Docker日志journalctl -u docker -b重点看最后几十行有没有报错常见的是磁盘空间不足、iptables规则加载失败、存储驱动问题。这些都可能导致守护进程启动失败。第三步检查容器具体报错docker ps -a docker logs 容器名有些容器虽然restart策略是always但启动后发现配置有问题比如端口被占用、数据卷挂载失败会反复重启然后停下来。6.2 容器启动后一直在重启循环状态一直在Restarting或者Exited后又起这未必是restart策略的锅更多时候是应用本身起不来Docker反复重启只是“执行策略”。典型的例子是Java应用内存配置过高容器被系统OOM杀掉Docker又根据always策略把它拉起来然后又被杀循环往复。遇到这种问题别急着改restart策略先看日志定位应用起不来的原因。查看内存相关docker stats观察内存占用是否逼近宿主机上限。同时用dmesg查看系统有没有OOM kill记录dmesg | grep -i oom这个现象和restart策略有关系但根因往往在应用侧把内存调小或者优化启动逻辑才能治本。6.3 手动停止后又被自动拉起来用docker stop把容器停了过一会儿发现它又跑起来了。其实这不是Bug是你的restart策略设置为always。always的策略语义里有一条不管容器是怎么退出的守护进程发现它没在运行就会尝试拉起。遇到这种情况有两个处理方式改用unless-stopped这样手动stop后重启Docker服务它不会自动拉起。但如果只是当前服务重启容器还是会恢复运行。明确不要自动启动就把restart改成nodocker update --restartno 容器名很多人第一次遇到docker stop失效会觉得Docker出毛病了其实它只是在忠实执行你配置的“跑步机策略”。6.4 修改自启动后原端口冲突docker update只修改restart策略不涉及端口调整但如果同宿主机上有两个容器都映射了相同端口Docker服务启动时后启动的那个会失败并进入重启循环。这种边界情况和restart策略本身无关但会让容器反复尝试启动看起来像是自启动配置出了问题。排查时先把第二个容器停掉docker stop 后启动的容器名 docker logs 后启动的容器名看日志里有没有address already in use。本质上是你部署规划的问题和自启动策略没有关系但故障表现很有迷惑性。机器重启前应用都是好的重启后其中某个容器起不来很容易误判成restart策略没生效。6.5 我的建议新老容器统一策略纯粹从运维管理角度给两个建议第一新部署的容器默认就把--restart unless-stopped写进启动命令里。它既能自动拉起又能尊重你手动停止的决定比较符合日常运维习惯。第二接手老项目时先用docker inspect批量扫一遍所有容器的RestartPolicy把策略为no且需要常驻的容器统一改掉。很多环境出问题都是因为有些容器不知道什么时候被谁改成了no或者根本没配置过。之前遇到一个很有意思的情况某台服务器上有十几个容器一部分是别人手动docker run创建的一部分是docker-compose创建的。后来有人手动更新过其中一个的restart策略但不知道过时配置会被compose覆盖导致某个容器重启后没起来。排查了半天发现docker-compose.yml里的restart字段并没有同步更新。这个坑提醒我们用compose管理的容器直接改docker update前先看一眼编排文件不然compose一下把你刚改的策略又覆盖回去了。7. 最后说点实在的体会容器自启动设置这个事看着简单其实是个挺典型的“差一步就出问题”的环节。我见过太多业务因为一台服务器重启就中断半天的案例事后一查容器没配置自启动。真正稳的做法往往是组合拳systemctl enable docker保持Docker服务开机自启容器统一配置unless-stopped再配合docker inspect定期巡检已有的容器策略。改成docker update --restart只会影响当前这个容器不会触碰其他容器和编排配置是生产环境里最推荐的操作路径。如果你用的是Docker Compose管理改完记得把restart字段也同步到compose.yml里免得下次up的时候被覆盖回去。