最近接了个活,客户那边的服务器是完全断网的(内网隔离,出于安全考虑),又是windows电脑,所以考虑使用docker,平时都是联网直接 docker compose up 完事。头一次遇到要把整套 MySQL + 后端 + 前端服务打包带到一台”与世隔绝”的 Windows 机器上跑起来,中间踩了几个坑,记录一下完整流程,给同样场景的朋友们省点时间。

场景说明

项目是标准的三件套:

  • mysql —— 数据库
  • backend —— Python/Flask 后端,跑在 gunicorn 里
  • web —— Nginx 托管前端页面,反向代理 API

平时开发联网状态下,一条 docker compose up -d 就能起。但目标机器完全没有网络,连 aptpipnpm 的源都碰不到,更别说 docker pull 了。思路很简单:在有网的电脑上把所有东西准备好,打包成文件,U 盘拷过去,离线机上直接加载。

第一步:有网电脑上把镜像打好包

先确保代码是最新的,然后照常构建:

1
2
3
cd D:\你的项目目录
git pull
docker compose build

构建完了以后,把用到的几个镜像导出成一个 tar 包:

1
docker save -o project-images.tar mysql:8.0 project-backend project-web

这里有个小提醒:docker save 支持一次导出多个镜像到同一个 tar 里,不用一个个来。导出的文件通常几百 MB 到一两个 G,看你项目依赖装了多少东西。

第二步:把需要的文件都收集到一起

除了刚才导出的镜像包,U 盘里还得放这几样:

文件 用途
项目目录(含 docker-compose.yml、初始化 SQL 等) Compose 编排配置和数据库初始化脚本
project-images.tar 打包好的镜像
WSL2 安装包(.msi Docker Desktop 在 Windows 上跑的底层依赖
Docker Desktop 安装包(.exe 目标机上运行容器的工具

WSL2 安装包可以去 GitHub 的 WSL 发布页 下,注意看清楚机器架构,一般台式机/笔记本都是选 x64 那个。

如果目标机已经装过 WSL 和 Docker 了,后续再更新项目,就不用重新拷安装包了,只需要拷项目目录和新的镜像 tar。

第三步:离线机上装环境

这一步跟正常装 Docker Desktop 没什么区别,就是把在线下载的步骤换成了运行本地安装包:

  1. 双击装 WSL 的 .msi,装完重启一下电脑
  2. 装 Docker Desktop,安装过程中记得勾上 Use the WSL 2 based engine(不然容器跑不起来)
  3. 打开 Docker Desktop,确认它显示正常运行(左下角是绿色的)

如果项目里有挂载宿主机某个目录的配置(比如存图片、存文件的路径),记得提前把这个目录建好,不然容器起来的时候会因为挂载路径不存在报错。

第四步:导入镜像,启动项目

把项目目录和 tar 包拷到离线机上以后:

1
2
3
cd D:\项目目录
docker load -i project-images.tar
docker compose up -d --no-build

注意这里加 --no-build,因为离线机没法联网拉依赖,docker compose up 默认如果发现镜像不存在会尝试构建,这在离线环境下必然失败。加了这个参数就是明确告诉它:别 build,直接用我导入的镜像跑。

跑起来以后用这条命令看看容器状态:

1
docker compose ps

都是 Up 状态就说明没问题,浏览器打开对应地址验证一下页面能不能访问。

后续更新怎么办

项目难免要迭代,更新流程也不复杂。有网的机器上重新 build 一遍,重新 save 出新的 tar,拷过去以后:

1
2
docker load -i project-images.tar
docker compose up -d --no-build

docker load 会覆盖同名的旧镜像,up -d 会用新镜像重建容器。这里有两个坑要注意:

  • 不要手动删旧镜像再导入,一般没必要,load 本身会处理好覆盖逻辑
  • 千万不要docker compose down -v 去”清理一下”,这个 -v 会把数据卷一起删掉,数据库里的数据也会跟着没了。正常更新用不到删卷这个操作。

踩坑一:Docker Desktop 一直卡在 “Starting the Docker Engine”

这个坑基本每台离线机第一次装的时候都会遇到一次:WSL 装完、Docker Desktop 也装完了,打开之后界面一直转圈,提示 “Starting the Docker Engine”,等再久也不会变绿。

排查过一圈发现跟网络、镜像都没关系,纯粹是 WSL 这层的虚拟化环境没起来或者卡住了。解法很固定,照着下面顺序来一遍基本都能好:

  1. 先把 Docker Desktop 完全退出(右下角托盘图标右键 Quit Docker Desktop,别只是关窗口)

  2. 打开 PowerShell,执行:

    1
    wsl --shutdown
  3. 重启电脑(这一步不要跳过,单纯 wsl --shutdown 有时候不够彻底)

  4. 重启后重新打开 Docker Desktop,耐心等几分钟,让它把 WSL2 的虚拟机重新拉起来

如果重启电脑之后还是老样子,八成是 BIOS 里的虚拟化功能(Intel VT-x / AMD-V)没开,或者 Windows 的”虚拟机平台”、”适用于 Linux 的 Windows 子系统”这两个可选功能没勾上,去”启用或关闭 Windows 功能”里确认一下。

踩坑二:镜像莫名其妙很大

这个坑值得单独说一下。有一次发现后端镜像居然有 3 个多 G,用 docker history 镜像名 查了一下每一层的大小:

1
2
3
COPY . .                          1.04GB
RUN pip install ... 444MB
RUN apt-get install ... 365MB

COPY . . 这一层正常情况下应该就是项目代码,几 MB 到几十 MB 都算合理,但它跑出了 1 个多 G,明显不对劲。查下来发现是 .dockerignore 没配全,构建的时候把一些不该打进镜像的大文件也一起 COPY 进去了。

排查思路很简单:先看 .dockerignore 里排除了哪些目录,再实际去项目目录里翻一遍,看排除规则之外还有没有大文件残留(比如临时生成的日志、缓存的第三方素材、之前调试留下的大数据文件)。把这些也加进 .dockerignore,重新 build 一次,这一层直接从 1G 掉到几 MB。

这里给个通用建议:如果你的镜像体积让你觉得”不对劲”,第一反应应该是跑一下 docker history 镜像名,一层一层看是哪个步骤占的空间大,而不是瞎猜。定位到具体层之后,问题基本就很好查了。