Docker 基础体系 · 第 63/70 篇。示例以现代 Docker Engine、BuildKit 与 Compose 规范为基础,并明确 Linux 容器边界。

Docker Live Restore 与重启恢复:Daemon 故障、容器存活和限制

Docker 中有三个经常被混为一谈的问题:

  1. dockerd 进程重启或暂时不可用时,已经运行的容器是否继续运行?
  2. 宿主机重启后,容器是否能够自动恢复?
  3. Docker 服务恢复后,容器、网络、日志和应用状态是否都已经恢复?

这三个问题分别对应 Live Restore(实时恢复)容器重启策略以及应用自身的持久化与恢复能力。它们解决的故障层级不同,不能互相替代。

本文讨论现代 Docker Engine、BuildKit 与 Compose 规范下的 Linux 容器,重点解释 Docker Daemon 故障、容器进程存活、宿主机重启恢复和相关限制。


一、先区分 Docker Daemon、容器和宿主机

1. Docker Daemon 是控制面,不等于容器进程

Docker Daemon 通常指 dockerd,它负责:

  • 解析 Docker API 请求;
  • 创建、启动、停止和删除容器;
  • 管理镜像、网络、卷和插件;
  • 与 containerd、容器运行时以及 Linux 内核交互;
  • 提供 docker psdocker run 等命令背后的服务端能力。

容器本身不是一个独立的虚拟机。一个 Linux 容器通常只是宿主机上的一组进程,它们通过以下 Linux 机制隔离:

  • PID namespace;
  • mount namespace;
  • network namespace;
  • user namespace;
  • cgroup;
  • capabilities、seccomp 和其他安全限制。

在现代 Docker Engine 中,典型进程关系可以简化为:

flowchart TD
    CLI["docker CLI / API 客户端"]
    D["dockerd"]
    CT["containerd"]
    SHIM["containerd-shim"]
    RUNC["runc"]
    P["容器进程"]
    K["Linux Kernel"]
    
    CLI --> D
    D --> CT
    CT --> SHIM
    SHIM --> RUNC
    RUNC --> P
    P --> K
    SHIM --> K

runc 通常负责创建容器,完成后退出;容器长期运行时,containerd-shim 会承担与容器进程相关的管理和 I/O 连接职责。具体进程划分会随 Docker Engine、containerd 和运行时版本变化,但关键事实是:

dockerd 是 Docker 的控制面,而容器进程最终由 Linux 内核调度和维护。

因此,dockerd 崩溃不必然意味着容器进程已经退出;反过来,dockerd 正常运行也不能保证容器内应用没有崩溃。

2. docker ps 不是容器存活本身

执行:

docker ps

需要 Docker API 服务可用。如果 dockerd 正在重启,可能出现:

Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
Is the docker daemon running?

这只能说明 Docker 客户端暂时无法访问 Daemon,不能直接推出容器已经停止。

判断容器内进程是否仍然存在,需要从宿主机进程、容器运行时或应用端口等多个角度验证。例如:

ps -ef | grep '[d]ocker-proxy'
ps -ef | grep '[c]ontainerd-shim'
ss -lntp
curl -fsS http://127.0.0.1:8080/health

其中每种检查回答的问题不同:

  • docker ps:Docker 控制面是否能看到容器;
  • containerd-shim:相关运行时管理进程是否仍存在;
  • ss:宿主机监听端口是否仍然存在;
  • 应用健康检查:应用是否真的能够提供服务。

二、什么是 Live Restore

1. 定义

Docker Live Restore 是 Docker Engine 的一个 Daemon 配置能力。启用后,当 dockerd 暂时不可用或重启时,已经运行的容器可以继续运行,而不必因为 Daemon 的退出而被主动停止。

配置方式是在 Docker Daemon 配置文件中加入:

{
  "live-restore": true
}

Linux 上通常使用:

/etc/docker/daemon.json

如果该文件已经存在,必须把配置合并到原有 JSON 中,不能直接覆盖原配置。例如:

{
  "data-root": "/var/lib/docker",
  "log-driver": "local",
  "live-restore": true
}

配置文件必须是合法 JSON:

  • 不能写注释;
  • 字符串使用双引号;
  • 最后一项不能多逗号;
  • 同一个选项不能同时在命令行和配置文件中定义出冲突值。

可以先验证配置:

sudo dockerd --validate --config-file=/etc/docker/daemon.json

成功时,现代 Docker Engine 通常输出类似:

configuration OK

不同版本对验证输出的具体文字可能不同;关键是命令退出状态为成功,并且没有报告未知字段或冲突配置。

然后重启 Docker 服务使配置生效:

sudo systemctl restart docker

检查是否启用:

docker info --format '{{json .LiveRestoreEnabled}}'

典型输出:

true

也可以直接查看:

docker info

并查找:

Live Restore Enabled: true

2. Live Restore 的核心条件

可以把 Live Restore 的目标抽象为:

D(t0)=不可用C(t0)=运行中K(t)=宿主机内核持续运行D(t_0) = \text{不可用} \quad\land\quad C(t_0) = \text{运行中} \quad\land\quad K(t) = \text{宿主机内核持续运行}

则在满足运行时和配置限制时,期望:

C(t1)=继续运行C(t_1) = \text{继续运行}

其中:

  • DD 表示 Docker Daemon;
  • CC 表示容器进程;
  • KK 表示宿主机 Linux 内核;
  • t0t_0 是 Daemon 故障时间;
  • t1t_1 是 Daemon 恢复或观察时间。

这个条件的直觉是:Live Restore 保留的是已经存在的容器进程及其运行时状态,不是把容器状态保存成一个可以跨宿主机启动的快照。

因此,以下情况不能仅靠 Live Restore 解决:

  • 宿主机重启;
  • 宿主机断电;
  • Linux 内核崩溃;
  • 容器进程自身崩溃;
  • 需要重新创建容器;
  • 需要恢复未持久化到卷或外部存储的数据。

三、Daemon 重启时,容器为什么可以继续运行

1. 没有 Live Restore 时的常见路径

如果没有启用 Live Restore,Docker Daemon 停止时,Docker 可能会停止它管理的容器。抽象流程如下:

sequenceDiagram
    participant A as 运维人员
    participant D as dockerd
    participant R as containerd/shim
    participant C as 容器进程

    A->>D: systemctl restart docker
    D->>R: 停止或断开管理关系
    R->>C: 停止容器
    D-->>A: Daemon 重新启动
    D->>R: 重新扫描容器
    Note over C: 原容器已经退出

这里不能简单理解为“Daemon 是容器的父进程,所以 Daemon 一死容器必死”。真实行为还受到 containerd、shim、systemd 服务单元和 Docker Engine 版本影响。但在未启用 Live Restore 时,Docker 默认可以在 Daemon 停止期间停止容器,因此不应依赖容器继续运行。

2. 启用 Live Restore 后的路径

启用 Live Restore 后,Daemon 重启期间的目标路径变为:

sequenceDiagram
    participant A as 运维人员
    participant D as dockerd
    participant R as containerd/shim
    participant C as 容器进程
    participant K as Linux 内核

    A->>D: systemctl restart docker
    D--xR: dockerd 暂时退出
    Note over R,C: shim 和容器进程保持运行
    C->>K: 继续运行、使用 namespace/cgroup
    D->>R: 新实例启动并重新连接
    R-->>D: 返回容器状态
    D-->>A: docker ps 恢复可用

关键点有两个:

  1. 容器进程在 Daemon 暂时不可用期间仍存在
  2. Daemon 恢复后重新接管和展示这些容器

Live Restore 不是把容器迁移到另一个节点,也不是在内存中创建容器副本。容器始终运行在原来的宿主机上。

3. 一个可验证的实验

先启动一个持续写入时间的容器:

docker run -d \
  --name live-restore-demo \
  --restart unless-stopped \
  busybox \
  sh -c 'i=0; while true; do date -Iseconds; i=$((i+1)); sleep 2; done'

查看容器状态:

docker ps --filter name=live-restore-demo

然后重启 Docker:

sudo systemctl restart docker

几秒后再次查看:

docker ps --filter name=live-restore-demo
docker logs --tail 10 live-restore-demo

启用并正确工作的 Live Restore 环境中,预期现象是:

  • Docker 客户端在 Daemon 重启的短时间内可能无法连接;
  • Daemon 恢复后,容器仍显示为 Up
  • 容器内的时间输出没有因为 Daemon 重启而重新从零开始;
  • 容器 ID 不变。

可以进一步记录容器启动时间:

docker inspect -f \
  'status={{.State.Status}} started={{.State.StartedAt}} restartCount={{.RestartCount}}' \
  live-restore-demo

如果只是 Daemon 重启而容器进程未退出,StartedAt 通常不会被更新为一次新的容器启动时间,RestartCount 也不应因为这次 Daemon 重启而增加。

这个实验验证的是“Daemon 重启期间容器是否存活”,不是应用高可用。若应用本身在此期间崩溃,Live Restore 不会阻止它退出。


四、Live Restore 不等于宿主机重启恢复

1. 宿主机重启会销毁运行时进程

宿主机重启时,Linux 内核会停止所有用户空间进程,包括:

  • dockerd
  • containerd;
  • containerd-shim;
  • 容器中的应用进程。

因此,Live Restore 的前提:

K(t)=持续运行K(t) = \text{持续运行}

在宿主机重启时不成立。容器内进程不会跨越内核重启继续存在。

宿主机启动后,Docker Daemon 会从 Docker 数据目录读取容器元数据,例如:

  • 容器配置;
  • 镜像引用;
  • 挂载信息;
  • 重启策略;
  • 网络配置;
  • 容器状态记录。

随后,Docker 根据容器的重启策略决定是否重新启动容器。

2. 重启策略的形式化理解

设容器发生退出事件,退出码为 ee,并且 Docker Daemon 在运行。重启策略可以粗略表示为:

no

R(e,m)=0R(e, m) = 0

无论容器如何退出,都不自动重启。no 是默认策略。

on-failure[:N]

R(e,m)={1,e0m<N0,e=0 或已达到次数上限R(e, m) = \begin{cases} 1, & e \ne 0 \land m < N \\ 0, & e = 0 \text{ 或已达到次数上限} \end{cases}

其中:

  • ee 是容器退出码;
  • mm 是已发生的失败重启次数;
  • NN 是可选的最大重启次数。

例如:

docker run -d \
  --name retry-demo \
  --restart on-failure:5 \
  alpine \
  sh -c 'exit 1'

该容器异常退出后最多尝试有限次数。它不表示宿主机启动时一定恢复一个此前被手动停止的容器,也不表示应用已经健康。

always

容器退出后自动启动;Daemon 启动时通常也会启动该容器。手动停止容器后,Daemon 重启可能再次启动它。

unless-stopped

行为接近 always,但如果管理员明确停止容器,Docker 会记录这一意图,Daemon 后续启动时通常不会自动启动该容器,直到再次手动启动。

创建示例:

docker run -d \
  --name web \
  --restart unless-stopped \
  nginx:alpine

为已有容器修改策略:

docker update --restart unless-stopped web

检查策略:

docker inspect -f '{{json .HostConfig.RestartPolicy}}' web

可能得到:

{"Name":"unless-stopped","MaximumRetryCount":0}

3. Compose 中的重启策略

Compose 文件可以写:

services:
  web:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "8080:80"

启动:

docker compose up -d

这里的 restart 是 Compose 管理的容器重启策略。

不要把它与以下字段混淆:

deploy:
  restart_policy:
    condition: on-failure

deploy.restart_policy 主要用于 Swarm 服务部署语义;在普通的 docker compose up 场景中,不能把它当作普通 Compose 容器的等价替代。目标环境不同,生效字段也不同。

4. 重启策略的边界

重启策略只处理“容器进程退出后的再次启动”,不保证:

  • 容器内数据没有丢失;
  • 应用已经完成初始化;
  • 依赖数据库已经可用;
  • 网络路由已经恢复;
  • 消息不会重复消费;
  • 端口已经可以接受请求;
  • 多个服务按照业务依赖顺序恢复。

例如,以下容器即使不断重启,也不能形成有效服务:

docker run -d \
  --name api \
  --restart always \
  my-api:latest

如果 my-api 启动后立即因为数据库连接失败而退出,Docker 只会不断重复同一个失败过程。

HEALTHCHECK 也不会自动重启一个仍然运行但健康检查失败的容器。健康检查的结果主要用于状态观察和编排决策;若需要根据健康状态执行重启,必须由外部控制器、编排系统或运维逻辑明确实现。


五、Daemon 故障、容器存活和请求可用性不是同一件事

1. 运行中的容器可能继续响应请求

假设一个容器中的 HTTP 服务已经监听 0.0.0.0:8080,宿主机通过端口映射提供服务:

docker run -d \
  --name http-demo \
  --restart unless-stopped \
  -p 8080:80 \
  nginx:alpine

dockerd 暂时重启时,已有的容器网络 namespace、应用监听 socket 以及部分宿主机网络规则可能继续存在。因此,现有连接或新连接在某些环境下仍可能成功。

但这不是一个可以无条件扩展的保证。结果取决于:

  • 容器是否仍然运行;
  • 宿主机网络规则是否仍然存在;
  • 使用的网络驱动;
  • 连接是否依赖 Docker 内置 DNS;
  • 日志驱动是否需要 Daemon;
  • 外部负载均衡器如何判断后端健康;
  • 应用是否依赖 Daemon API。

因此,正确的验证不是只执行:

systemctl is-active docker

而是同时测试业务路径:

curl -v http://127.0.0.1:8080/

如果 HTTP 请求在 Daemon 重启期间成功,这说明业务数据路径仍可用;如果失败,则还需要确定失败点是容器进程、网络、端口映射还是应用自身。

2. 新操作在 Daemon 不可用时不能完成

Live Restore 保留的是已有容器,而不是 Docker API 的可用性。Daemon 故障期间通常不能可靠执行:

docker run ...
docker exec ...
docker cp ...
docker inspect ...
docker logs ...
docker stop ...
docker rm ...

客户端可能直接报无法连接 Daemon;即使某些操作在恢复后可以完成,也不应假设故障期间的控制请求会被排队并自动执行。

这产生一个重要差异:

操作或能力 Daemon 暂时不可用时
已有容器中的进程继续运行 Live Restore 目标能力
创建新容器 不可用
删除容器 不可用
docker exec 进入容器 通常不可用
通过 Docker API 读取状态 不可用
应用自行访问外部数据库 取决于网络和依赖
宿主机直接访问已监听端口 可能可用,必须验证

Live Restore 解决的是运行面短暂保持,不是管理面持续可用


六、Live Restore 的限制

1. 宿主机内核必须持续工作

Live Restore 不能跨越:

  • 关机;
  • 重启;
  • 断电;
  • 内核崩溃;
  • 宿主机硬件故障;
  • 虚拟机被底层平台强制销毁。

如果业务要求节点故障后仍可用,需要多个节点、负载均衡、编排调度或外部故障转移,而不是只依赖单机 Live Restore。

2. 容器自身退出时,Live Restore 不会“复活”它

例如:

docker run -d \
  --name crash-demo \
  --restart no \
  busybox \
  sh -c 'sleep 5; exit 1'

5 秒后容器会退出。此时即使 dockerd 完全正常,Live Restore 也没有任何作用,因为它只处理 Daemon 与运行中容器之间的关系,不负责把已退出的应用变成健康应用。

如果需要自动重启,应使用合适的 --restart 策略,但仍要通过日志和退出码调查根因。

3. 配置变更不会自动重建运行中的容器

Live Restore 期间保留的是原容器状态。修改 /etc/docker/daemon.json 后重启 Daemon,不会自动把已有容器按照所有新配置重新创建。

例如修改:

{
  "log-driver": "local"
}

不会自动把旧容器已经建立的日志后端转换为新的日志配置。许多容器级配置是在创建容器时确定的,通常需要:

docker compose up -d --force-recreate

或删除后重新创建容器,才能使新配置真正作用于容器。

重新创建有数据和连接风险:

  • 容器 ID 会变化;
  • 容器临时可写层会丢失;
  • 未挂载到卷的数据会丢失;
  • 短暂停止可能导致业务中断;
  • 依赖容器 IP 的配置可能失效。

4. 日志驱动存在兼容边界

容器输出的处理路径不一定完全独立于 Docker Daemon。Live Restore 对日志驱动有兼容限制,官方文档明确建议使用受支持的 json-filelocal 日志驱动验证该能力;使用其他日志驱动时,Daemon 不可用期间的日志行为可能不同,不能假设容器和日志采集都能无缝继续。

检查当前默认日志驱动:

docker info --format '{{.LoggingDriver}}'

检查某个容器:

docker inspect -f '{{.HostConfig.LogConfig.Type}}' live-restore-demo

即使容器继续运行,也要考虑:

  • 日志是否暂存在运行时缓冲区;
  • 日志是否会阻塞应用;
  • Daemon 恢复后是否能继续读取;
  • 远程日志后端是否独立可用;
  • 容器日志是否因为磁盘满而导致应用异常。

生产环境不应仅用“容器还在运行”推断“日志链路正常”。

5. Swarm 服务不是普通单机容器

Live Restore 针对 Docker Engine 管理的容器。Swarm 服务还包含:

  • 服务期望状态;
  • 调度器;
  • 节点状态;
  • overlay network;
  • service task;
  • manager quorum。

不能把单机容器的 Live Restore 直接等同于 Swarm 服务高可用。Swarm 的服务恢复依赖 Swarm 控制面和调度机制,必须分别验证 manager、worker、task 和网络行为。

尤其要注意:

“单机容器在 Daemon 重启时继续运行”不等于“编排系统在节点故障时完成跨节点接管”。

6. Live Restore 不是 checkpoint、快照或迁移

Live Restore 不保存一个可以在另一台机器恢复的容器内存快照。它不能直接完成:

  • 把运行中的容器迁移到另一台宿主机;
  • 恢复容器内存中的连接和线程;
  • 在不同内核或不同架构上恢复进程;
  • 从备份恢复容器的瞬时运行状态。

容器镜像、容器配置和卷数据可以被备份或重新部署,但这与进程级状态恢复是不同问题。


七、Daemon 重启与宿主机重启的故障路径对比

故障 Daemon 是否可用 已运行容器 新建容器 是否跨越宿主机重启
dockerd 短暂重启,未启用 Live Restore 暂时不可用 可能被停止 不可用 不适用
dockerd 短暂重启,启用 Live Restore 暂时不可用 目标是继续运行 不可用 不适用
Daemon 崩溃后恢复 暂时不可用 取决于 Live Restore、运行时和故障路径 恢复后可用 不适用
宿主机正常重启 重启期间不可用 所有进程退出 启动后可用 需要重启策略
宿主机断电或内核崩溃 不可用 所有进程丢失 恢复后可用 需要持久化和自动启动
宿主机永久损坏 永久不可用 丢失 需迁移或重建 需要多节点或备份

这个表中的“容器继续运行”只说明进程生命周期,不说明服务一定能够对外提供正确结果。


八、Docker 服务恢复时到底发生了什么

以配置了 unless-stopped 的容器为例,宿主机重启后的典型流程如下:

stateDiagram-v2
    [*] --> Running
    Running --> DaemonUnavailable: dockerd 崩溃或重启
    DaemonUnavailable --> Running: Live Restore 且宿主机未重启
    DaemonUnavailable --> ProcessLost: 宿主机重启/内核故障
    ProcessLost --> DaemonStarting: 宿主机启动
    DaemonStarting --> RestartEvaluated: dockerd 读取元数据
    RestartEvaluated --> Running: 重启策略允许启动
    RestartEvaluated --> Stopped: 无重启策略或曾被明确停止
    Running --> Exited: 应用进程退出
    Exited --> Running: 重启策略触发
    Exited --> Stopped: 重启策略不触发或达到上限

宿主机重启后的恢复至少涉及四层:

  1. systemd 是否启动 Docker 服务
  2. Docker 是否能够读取 data-root
  3. 容器的重启策略是否允许启动
  4. 应用依赖的卷、网络、DNS 和外部服务是否已就绪

例如,Docker 数据目录放在单独磁盘:

{
  "data-root": "/srv/docker"
}

如果 /srv/docker 对应的文件系统尚未挂载,Docker 可能启动失败,或者更危险地使用了错误的空目录。此时重启策略没有机会发挥作用,因为负责解释重启策略的 Daemon 本身没有正确启动。

因此,Daemon 配置中的 data-root、日志路径、代理、镜像源、TLS 证书和 systemd 挂载依赖,都属于恢复链路的一部分。


九、一个更完整的恢复测试

不能只测试 systemctl restart docker。建议分别验证 Daemon 重启和宿主机重启。

1. 准备测试容器

docker run -d \
  --name recovery-demo \
  --restart unless-stopped \
  -p 18080:80 \
  nginx:alpine

检查容器和策略:

docker inspect recovery-demo \
  --format 'status={{.State.Status}} restart={{.HostConfig.RestartPolicy.Name}} started={{.State.StartedAt}}'

测试服务:

curl -fsS http://127.0.0.1:18080/ >/dev/null
echo $?

返回 0 表示这一次 HTTP 请求成功,不代表完整恢复流程已经验证。

2. 验证 Daemon 重启

sudo systemctl restart docker

恢复后执行:

docker inspect recovery-demo \
  --format 'status={{.State.Status}} restartCount={{.RestartCount}} started={{.State.StartedAt}}'

curl -fsS http://127.0.0.1:18080/ >/dev/null

应分别关注:

  • 容器是否仍为 running
  • restartCount 是否意外增加;
  • 应用端口是否仍可访问;
  • Daemon 日志中是否有 containerd、shim、网络或日志驱动错误。

查看 Daemon 日志:

sudo journalctl -u docker --since '-10 minutes' --no-pager

查看容器日志:

docker logs --tail 50 recovery-demo

3. 验证宿主机重启

宿主机重启是有风险的操作,必须先确认:

  • 具备带外管理或控制台;
  • SSH 恢复路径已验证;
  • 业务可以承受测试窗口;
  • 卷和关键数据已有备份;
  • 不会误把生产节点从集群中永久移除。

重启:

sudo reboot

恢复后先检查服务:

systemctl is-active docker
docker info
docker ps

再检查容器:

docker inspect recovery-demo \
  --format 'status={{.State.Status}} restartCount={{.RestartCount}} started={{.State.StartedAt}}'

curl -fsS http://127.0.0.1:18080/ >/dev/null

如果容器未恢复,应按顺序排查:

sudo systemctl status docker --no-pager
sudo journalctl -u docker -b --no-pager
docker inspect recovery-demo
findmnt /srv/docker
df -h

这里的 -b 表示查看本次启动周期的 systemd 日志。排查重点包括:

  • Docker 是否启动失败;
  • data-root 是否挂载;
  • 磁盘是否满;
  • 镜像和容器元数据是否可读;
  • 端口是否被其他进程占用;
  • restart policy 是否为 no
  • 容器是否因为应用错误启动后立即退出。

十、常见误解与失败表现

误解一:启用 Live Restore 后,docker 命令仍然应该可用

错误。Live Restore 保持的是容器运行,不是 Docker API 可用。

Daemon 重启期间:

docker ps

可能失败,但容器进程仍可能继续处理现有业务流量。

误解二:有 --restart always,就不需要 Live Restore

两者触发时机不同:

  • Live Restore:Daemon 暂时不可用时,尽量不停止已经运行的容器;
  • restart policy:容器退出后,Daemon 负责重新启动容器。

没有 Live Restore 时,Daemon 重启可能先导致容器退出,再由 Daemon 恢复后尝试重启。这个过程会产生服务中断。restart policy 不能消除这段中断。

误解三:Live Restore 可以防止容器崩溃

错误。容器中的 PID 1 退出,容器就会退出。Live Restore 不会修复:

  • 空指针崩溃;
  • OOM;
  • 配置错误;
  • 依赖服务不可用;
  • 应用主动退出;
  • 容器被内核或管理员杀死。

误解四:容器恢复就等于数据恢复

错误。容器临时可写层不是可靠的数据持久化位置。需要保留的数据应放在:

  • Docker volume;
  • 绑定挂载的持久化文件系统;
  • 外部数据库;
  • 对象存储;
  • 其他明确设计的持久化系统。

例如:

docker run -d \
  --name db \
  --restart unless-stopped \
  -v db-data:/var/lib/postgresql/data \
  postgres:16

即使容器因为宿主机重启而被重新创建或重新启动,卷中的数据库文件仍有机会保留。但“文件保留”不等于“数据库事务一定一致”,数据库仍然需要自己的崩溃恢复和备份机制。

误解五:depends_on 可以保证恢复顺序和服务可用

Compose 的 depends_on 可以影响容器创建或启动顺序;即使结合健康检查,也不能自动解决所有恢复问题,例如:

  • 数据库端口已经打开,但还没有完成恢复;
  • 应用启动后需要执行迁移;
  • 外部服务尚未可用;
  • 多个副本同时恢复造成资源竞争。

应用必须对依赖失败、连接重试和幂等初始化负责。


十一、Daemon 升级时的取舍

Live Restore 常被用于降低 Docker Engine 重启或升级期间的容器中断。升级前仍需区分两个风险:

  1. Daemon 进程暂时不可用;
  2. 新版本 Docker、containerd、运行时或存储驱动与现有状态不兼容。

Live Restore 只能帮助处理第一类风险,不能消除第二类风险。

升级前至少验证:

docker version
docker info
docker ps -a
docker system df

并记录:

  • Docker Engine 版本;
  • containerd 和 runc 版本;
  • 存储驱动;
  • cgroup 模式;
  • 日志驱动;
  • data-root
  • 运行中的容器及其镜像;
  • Compose 文件和环境变量;
  • 卷清单;
  • Daemon 配置和 TLS 证书。

升级后需要验证:

docker version
docker info
docker ps
docker inspect <container>
docker logs <container>

并进行真实业务检查,而不是只确认 docker info 成功。

如果升级涉及存储驱动、cgroup、网络插件、日志插件或 Docker API 兼容性,Live Restore 不能作为“可以跳过灰度和回滚”的理由。正在运行的旧容器虽然可能继续存活,但新 Daemon 是否能够正确重新连接、展示和管理这些容器,仍取决于版本兼容性和运行时状态。


十二、生产环境中的合理组合

对于单台 Linux Docker 主机,可以把恢复能力拆成四个层次:

层次一:Daemon 短暂故障

启用:

{
  "live-restore": true
}

目标是减少 dockerd 重启造成的容器中断。

层次二:容器进程退出

为适合自动恢复的服务设置:

services:
  api:
    image: example/api:1.2.3
    restart: unless-stopped

同时保证应用能够:

  • 正确处理 SIGTERM;
  • 在依赖暂时不可用时重试;
  • 启动过程幂等;
  • 不把临时文件当作持久数据;
  • 通过健康检查暴露真实状态。

层次三:宿主机重启

确保:

  • Docker systemd 服务启用;
  • data-root 所在文件系统先挂载;
  • 容器具有正确的 restart policy;
  • 卷和外部依赖可用;
  • 启动后的端口和健康检查经过验证。

例如:

sudo systemctl enable docker

具体发行版可能还需要检查 containerd、网络服务或挂载单元的启动依赖。

层次四:宿主机故障

Live Restore 和 restart policy 都不能提供跨节点故障转移。此时需要:

  • 多节点部署;
  • 负载均衡;
  • 编排系统;
  • 数据复制;
  • 备份和恢复演练;
  • 明确的 RTO 与 RPO。

其中:

  • RTO 是恢复服务所允许的最大时间;
  • RPO 是可以接受的数据丢失时间窗口。

单机 Live Restore 主要改善 Daemon 级别的 RTO,不能把单机变成高可用集群。


十三、排查顺序

当“Daemon 重启后容器似乎不工作”时,不要直接执行 docker restart,否则可能覆盖原始故障证据。可以按以下顺序判断:

1. 先确认 Daemon

systemctl is-active docker
docker info
sudo journalctl -u docker -b --no-pager

2. 再确认宿主机进程和端口

ps -ef | grep '[c]ontainerd-shim'
ss -lntp

如果 Docker API 不可用,宿主机级别的进程和端口检查尤其重要。

3. Daemon 恢复后检查容器状态

docker ps -a
docker inspect <container>

重点看:

docker inspect <container> \
  --format '
status={{.State.Status}}
exitCode={{.State.ExitCode}}
oomKilled={{.State.OOMKilled}}
error={{.State.Error}}
started={{.State.StartedAt}}
finished={{.State.FinishedAt}}
restartCount={{.RestartCount}}
restartPolicy={{.HostConfig.RestartPolicy.Name}}
'

4. 判断是容器退出还是应用不健康

  • Status=exited:容器进程已退出;
  • Status=running 但请求失败:检查应用、网络、端口和依赖;
  • OOMKilled=true:检查内存限制和宿主机压力;
  • restartCount 持续增加:可能是启动失败循环;
  • Daemon 日志有日志驱动错误:检查日志路径和驱动兼容性;
  • 容器运行但 DNS 失败:检查 Docker 网络和应用的 DNS 依赖。

5. 最后决定恢复动作

只有在保留证据后,才考虑:

docker logs <container>
docker restart <container>
docker compose up -d

如果需要重新创建:

docker compose up -d --force-recreate

必须先确认容器临时层中没有唯一数据,并确认重新创建不会改变关键网络、卷或证书配置。


结语

Docker Live Restore 的准确含义是:

当 Docker Daemon 暂时不可用时,尽量让已经运行的 Linux 容器进程继续存活,并在 Daemon 恢复后重新建立管理关系。

它的边界同样明确:

  • 它不能跨越宿主机重启;
  • 它不能防止应用自身退出;
  • 它不能替代容器 restart policy;
  • 它不能保证日志、DNS、网络和外部依赖都不受影响;
  • 它不能提供跨节点故障转移;
  • 它不能恢复未持久化的数据;
  • 它不能替代 Docker Engine 升级验证和回滚方案。

因此,完整的恢复设计应当把问题拆开处理:用 Live Restore 缩短 Daemon 故障影响,用重启策略恢复退出或宿主机重启后的容器,用卷和外部存储保护数据,再用多节点和编排机制处理宿主机级故障。只有分别验证这些层次,才能知道一次“Docker 重启后服务仍然可用”究竟依赖了什么,以及在什么故障边界下会失效。


系列导航与关联阅读

官方资料

本文依据 Docker、OCI 与 CNCF 官方文档重新梳理;正文与生产检查清单由 WR BLOG 编写。