Docker应用更新失败,怎样退回原来的版本

Compose 更新应用的核心是:准备新镜像、保留旧版本与配置、重建目标服务,再检查实际业务。只执行 restart 不会应用新镜像或新的 Compose 配置。下面用一个没有数据库的演练站,把 v1 更新为 v2,再退回 v1,先看清整个过程。

创建独立演练站

需要已按安装教程准备 Docker Engine、Compose 插件和 curl 的 Linux 服务器。确认本机 18081 未被占用,现有 Compose 项目没有 baole-release-demo。新建下方目录;如果目录或同名镜像已经存在,请另选实验名称,不覆盖已有项目。以下命令在同一个终端中依次执行,后续构建会继续使用首次记录的 BL_BASE 变量。

sudo ss -ltnp 'sport = :18081'
sudo docker compose ls
sudo docker image ls baole-release-demo
mkdir -m 700 "$HOME/baole-release-demo"
cd "$HOME/baole-release-demo"
sudo docker pull nginx:stable-alpine
BL_BASE=$(sudo docker image inspect --format '{{index .RepoDigests 0}}' nginx:stable-alpine)

cat > Dockerfile <<'DOCKERFILE'
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
COPY index.html /usr/share/nginx/html/index.html
DOCKERFILE

printf '<!doctype html><meta charset="UTF-8"><title>宝乐云发布演练</title><h1>宝乐云发布演练 v1</h1>\n' > index.html
sudo docker build --build-arg "BASE_IMAGE=$BL_BASE" -t baole-release-demo:v1 .
printf 'WEB_IMAGE=baole-release-demo:v1\n' > .env

把下面内容保存为当前目录的 compose.yaml。端口只绑定本机,因此不需要开放公网访问;浏览器预览可另用已有的受控测试入口。

name: baole-release-demo
services:
  web:
    image: ${WEB_IMAGE:?请设置WEB_IMAGE}
    restart: unless-stopped
    ports:
      - "127.0.0.1:18081:80"
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/"]
      interval: 5s
      timeout: 3s
      retries: 6
      start_period: 5s
sudo docker compose config --quiet
sudo docker compose up -d --wait --wait-timeout 60
curl -fsS http://127.0.0.1:18081/

响应正文应包含“宝乐云发布演练 v1”。同时检查 sudo docker compose ps,web 应处于 healthy。健康检查只是应用的一项检测,页面内容还需要实际比对。

保存当前版本,再构建v2

mkdir before-update
cp compose.yaml .env before-update/
sudo docker compose images > before-update/images.txt
sudo docker inspect --format '{{.Image}}' "$(sudo docker compose ps -q web)" \
  > before-update/web-image-id.txt

printf '<!doctype html><meta charset="UTF-8"><title>宝乐云发布演练</title><h1>宝乐云发布演练 v2</h1>\n' > index.html
sudo docker build --build-arg "BASE_IMAGE=$BL_BASE" -t baole-release-demo:v2 .
printf 'WEB_IMAGE=baole-release-demo:v2\n' > .env
sudo docker compose config --quiet

本例两个版本使用同一基础镜像摘要,差别只有页面内容,便于观察更新结果。实际业务应使用唯一版本标签或镜像摘要,保留原镜像;新版镜像可以提前构建或拉取,正式切换前不要清理旧镜像。

只重建目标服务并检查结果

sudo docker compose up -d --no-deps --wait --wait-timeout 60 web
sudo docker compose ps
sudo docker compose logs --tail=40 web
curl -fsS http://127.0.0.1:18081/

只有这次响应变为“宝乐云发布演练 v2”,才证明请求命中了新版本。--no-deps 表示不连带启动依赖服务;真实项目只有在依赖已运行、版本和配置仍兼容时才使用。单实例重建会有短暂中断,本例不承诺无感更新。

如果命令报错、服务不健康或页面不符合预期,先保存报错和新容器日志。--wait 失败不会自动替你回退;不要继续重建或删除数据来碰运气。

把演练站退回v1

cp before-update/compose.yaml compose.yaml
cp before-update/.env .env
sudo docker compose config --quiet
sudo docker compose up -d --no-deps --wait --wait-timeout 60 web
curl -fsS http://127.0.0.1:18081/
sudo docker inspect --format '{{.Image}}' "$(sudo docker compose ps -q web)"
cat before-update/web-image-id.txt

页面应再次显示 v1,当前镜像 ID 应与更新前保存的 ID 一致。如果本地 v1 标签曾被重新指向其他镜像,应先依据保存的镜像 ID 找回原镜像,而不是相信标签名字没变。

用于真实应用时,还要处理数据

应用情况 回退需要同时考虑什么
代码在镜像内,数据结构没有变化 恢复原镜像与匹配的配置,复测登录、读取、写入等实际功能。
配置或上传文件发生变化 确认原版本是否仍兼容这些文件;需要恢复时使用核验过的备份,并保留切换后新增数据。
数据库结构或数据格式已经迁移 不能只切回旧镜像;需要软件支持的逆向迁移,或相互匹配的应用与数据库恢复方案。
WordPress等把程序本身放进持久卷 镜像切换不等于网站代码回退;需分别管理网站核心、插件、主题和数据库版本。

因此,生产更新前要把镜像、Compose 配置、凭据来源、卷位置和数据备份一起记录,并先在隔离环境做一次更新与回退。容器退出问题可参考Docker退出与重启排查;演练结束后只停止本次 baole-release-demo 项目,不清理其他容器和卷。