拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker容器开机自启设置:restart策略与docker update实操指南

Docker容器开机自启设置:restart策略与docker update实操指南

我平时收到最多的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 --restart=always 容器名或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_PASSWORD=your_password \ --restart unless-stopped \ mysql:8.0

3.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-stopped

docker-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-stopped:

docker update --restart=unless-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 --restart=unless-stopped $c fi done

注意这个操作会改动所有当前为no的容器,包括那些你可能故意设定为no的临时容器,执行前建议先docker ps -a把容器列表过一遍,确认哪些不需要改。

4.3 关于on-failure的最大重启次数

on-failure策略允许指定最多重启次数,比如:

docker update --restart=on-failure:5 my_container

意思是容器非正常退出时最多自动重启5次,5次之后就不再尝试了。这个策略适合那些可能存在偶发崩溃、但你又不希望它无限重启空转的场景。常见的是脚本任务型容器,跑完自己退出,出错重试几次还有机会成功,超过上限就不再折腾。

少写的提醒:docker update --restart=on-failure和--restart=on-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 Desktop(Windows/Mac),情况会不一样。Docker Desktop是一个独立的桌面应用,需要登录桌面环境后才会启动它的虚拟机,然后拉起Docker引擎。那容器还能自动启动吗?

答案是可以的,前提是Docker Desktop本身开机启动了,并且容器配置了restart策略。Docker Desktop在设置里有一项“Start Docker Desktop when you sign in to your computer”,需要勾选上。否则就算容器配置了always,Docker 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改成no:
docker update --restart=no 容器名

很多人第一次遇到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的时候被覆盖回去。

返回列表