离线部署
离线环境无法在缺少文件时临时访问公网补齐。一个镜像之外,还可能需要容器运行时、配置、证书和数据;一份源码之外,还需要构建工具、插件、传递依赖以及特定平台的二进制包。
准备工作的起点是目标机器将执行哪些操作。只运行已构建应用,可以交付 JAR、WAR、静态目录或镜像;若要求现场重新编译,还必须交付完整构建输入。两种任务应分开准备,避免把开发机缓存打包后误认为部署资料已经齐全。
根据目标环境确定交付内容
无公网、内网仓库与完全隔离
| 环境 | 仍可利用的服务 | 准备重点 |
|---|---|---|
| 禁止公网出口 | 内部 DNS、仓库、数据库、证书服务 | 将依赖地址改为内部服务,并配置访问权限与 CA |
| 允许受控代理 | 指定代理或白名单地址 | 明确哪些工具、运行阶段能使用代理 |
| 物理或逻辑隔离 | 经批准的介质和本地服务 | 完整交付包、独立校验、可执行安装与恢复材料 |
不能因为目标能访问公司数据库,就推断它能访问公共 Maven 仓库;也不能因为 SSH 能连接,就推断应用运行账号能读取配置。网络与身份要按具体进程检查。
在目标 Linux 上收集下面的信息。命令为只读检查,不安装软件;部分工具不存在时记录缺项,继续完成其他检查。
cat /etc/os-release
uname -m
uname -r
getconf GNU_LIBC_VERSION
id
df -h / /var
df -i / /var
findmnt -T /var
command -v java
command -v docker
command -v nodegetconf GNU_LIBC_VERSION 适合 glibc 系统,musl 环境应改查对应运行库信息。对于已安装的运行时,再执行 java -version、docker version、docker compose version 或 node -v,把版本与项目要求逐项对照。
还应确认 CPU 指令集、容器平台、可用内存、文件系统、挂载权限、服务账号、内部 DNS、时间同步、证书链和实际业务端口。涉及原生库时,操作系统名称与 CPU 架构仍不足以证明 ABI 兼容。目标是 ARM64,就应构建和演练 ARM64 的产物,而不是在 x86 开发机上运行成功后直接转运。
交付对象各自缺什么
| 交付对象 | 一起准备 | 目标侧验证 |
|---|---|---|
| Java JAR/WAR | 兼容 Java、容器及外部配置 | 真实启动、应用接口与外部依赖 |
| 前端静态目录 | 全部资源、服务器配置、接口地址 | 断公网后的浏览器页面、字体、延迟模块 |
| 容器镜像 | 目标平台层、Engine/Compose、挂载数据与配置 | 不拉取镜像即可创建并请求应用 |
| 源码 | 编译器、构建工具、锁文件、依赖、插件 | 全新工作目录中断网完成完整构建 |
| 数据服务 | 数据目录方案、初始化或迁移、备份恢复工具 | 使用实际备份恢复并核对数据 |
容器镜像包含应用文件层,但不包含宿主机内核、Docker Engine,也不包含挂载卷中的数据库内容。JAR 可能带齐 Java 库,却仍依赖 JNI、本地命令、字体或远程服务。检查应沿应用真实启动和业务请求进行。
多台服务器重复交付时,内部 APT/RPM、Maven/npm 仓库和镜像仓库通常更便于升级与追踪。仓库本身仍需可信导入、认证、备份和漏洞更新机制;依赖地址统一后,应保留精确版本和来源,避免内部同名制品覆盖。
把现成应用交付到无外部网络的容器
联网准备端生成交付包
使用 Spring Boot 部署运维中的示例,先构建 local/deploy-app:1.0.0。它实现 /api/greeting 和 readiness,不依赖数据库或外部服务。再下载离线运行配置并解压到 offline-lab。这个小包只提供 Compose 和两份非秘密配置,镜像由自己的构建结果导出。
下面在 Linux Bash 中执行,当前用户需有 Docker 权限。准备端与目标端均应安装匹配的 Engine 与 Compose;它们尚未安装时,先按下一节准备系统软件,不能指望应用镜像安装 Docker。
cd offline-lab
docker image inspect local/deploy-app:1.0.0 \
--format '{{.Os}}/{{.Architecture}} {{.Id}}'
mkdir -p images
docker image save --output images/app.tar local/deploy-app:1.0.0
docker image inspect local/deploy-app:1.0.0 --format '{{.Id}}' > image-id.txt
docker image inspect local/deploy-app:1.0.0 \
--format '{{.Os}}/{{.Architecture}}' > image-platform.txt
sha256sum images/app.tar compose.yaml release-r1.env release-r2.env \
image-id.txt image-platform.txt > SHA256SUMS
sha256sum --check SHA256SUMS所有条目应显示 OK。目录中保留以下内容,传输时完整复制,不漏掉回退配置:
offline-lab/
├── images/app.tar
├── compose.yaml
├── release-r1.env
├── release-r2.env
├── image-id.txt
├── image-platform.txt
└── SHA256SUMSdocker image save 保存所选镜像的层和标签,供 load 恢复;只会导出本机已有的平台内容。save 官方说明列出了多平台选择条件。docker export 导出的是容器文件系统,不能替代这里的镜像交付方式,更不会备份应用数据卷。
image-id.txt 记录该交付镜像的本地内容身份,另一台机器自行构建时可能不同。比较时使用同一个交付包导出与导入的 ID。重新构建得到的摘要没有必要与另一份独立构建相同,本地镜像 ID 与 registry manifest digest 也属于不同对象。
交付时还应附带制品来源、构建工具版本、目标平台、启动说明、应用版本、依赖清单和许可信息。配置模板不放真实密码;秘密通过受控渠道另行供应。校验清单检测文件变化,发布者身份应通过独立可信的签名或审批渠道确认。
目标端先校验,再导入
目标机器不需要连接镜像仓库。取得交付包后,在它的目录中执行:
(
set -eu
sha256sum --check SHA256SUMS
docker image load --input images/app.tar
test "$(docker image inspect local/deploy-app:1.0.0 --format '{{.Id}}')" \
= "$(cat image-id.txt)"
test "$(docker image inspect local/deploy-app:1.0.0 --format '{{.Os}}/{{.Architecture}}')" \
= "$(cat image-platform.txt)"
)任一摘要失败时停止导入,重新获取经过确认的文件,不在目标端重新生成 SHA256SUMS 把差异抹掉。上面的比较还要结合目标平台检查:包内镜像记录一致,但目标 CPU 不兼容时仍无法正常运行。load 文档解释了导入及平台选项。
归档来自外部时,应先检查文件列表、路径与链接类型,在专用暂存目录中以普通用户解压,避免归档中的绝对路径或父目录跳转覆盖系统文件。执行安装脚本、导入镜像和安装软件前都应确认其来源与批准范围。
运行阶段不允许补下载
配置将应用设为 network_mode: none,并使用已存在的镜像:
image: local/deploy-app:1.0.0
pull_policy: never
network_mode: none
environment:
SERVER_ADDRESS: 127.0.0.1
APP_MESSAGE: ${APP_MESSAGE:?select a release env file}这是完整 Compose 文件中的关键片段。实际文件还包含只读根目录、可写临时目录、内存和进程限制、日志轮转及健康检查。两个 env 文件分别指定 offline-r1 与 offline-r2。
docker compose --env-file release-r1.env up -d --wait --pull never --no-build
docker compose --env-file release-r1.env exec -T app \
curl -q --noproxy '*' --fail-with-body --max-time 5 \
http://127.0.0.1:8080/api/greeting
docker inspect offline-lab-app-1 --format '{{.HostConfig.NetworkMode}}'预期响应包含 "message":"offline-r1"、"version":"1.0.0",网络模式为 none。--pull never 禁止缺少镜像时访问仓库,--no-build 禁止临时构建,选项见 Compose up 文档。
none 网络只保留容器回环接口,因此请求从容器内部执行,不提供宿主机访问端口。Docker none 网络说明描述了这种隔离方式。这个实验验证应用自身无需外部网络即可运行,并没有隔离宿主 Docker daemon 的所有网络,也没有验证数据库、DNS 或内部服务连接。
真正的内网应用应使用经过批准的内部网络,逐项连接实际依赖。浏览器还需检查是否偷偷加载公网字体、脚本、地图、验证码或授权服务;服务端启动成功覆盖不到这些客户端行为。
用失败对照检验交付材料
在交付包的临时副本中修改 release-r2.env 后再次执行 sha256sum --check SHA256SUMS,应返回非零并指出该文件不匹配。重新复制原文件后再次校验全部通过,才继续运行。
不指定必填配置时,Compose 应在创建容器前失败:
env -u APP_MESSAGE docker compose --env-file /dev/null config --quiet这个负例预期非零,并提示需要选择 release env 文件。它只用于确认缺失配置会被发现,不能作为正常启动步骤。
还可使用一个确认本机不存在的实验镜像名验证禁止拉取:
docker run --rm --pull never local/offline-lab-deliberately-missing:1预期报告本地镜像不存在,退出非零。没有必要通过关闭整台主机网络来验证这个条件。
应用配置可以这样切换并恢复:
docker compose --env-file release-r2.env up -d --wait --pull never --no-build
docker compose --env-file release-r2.env exec -T app \
curl -q --noproxy '*' --fail-with-body http://127.0.0.1:8080/api/greeting
docker compose --env-file release-r1.env up -d --wait --pull never --no-build
docker compose --env-file release-r1.env exec -T app \
curl -q --noproxy '*' --fail-with-body http://127.0.0.1:8080/api/greeting响应依次出现 offline-r2、offline-r1。这里使用同一镜像,只验证运行配置切换与恢复;二进制升级时,应连同新旧镜像及其校验资料一并准备。
系统软件与现场构建需要额外准备
RPM 系统:连同依赖一起下载
使用 DNF 4 的发行版,可以在与目标同发行版、版本、架构和仓库配置的准备机中下载软件。以下以 nginx 为例,需要准备机已安装 dnf-plugins-core,并且使用目标允许的仓库和模块流:
mkdir -p rpm-bundle
dnf download --resolve --alldeps --destdir rpm-bundle nginx
rpm -K rpm-bundle/*.rpm--resolve 下载所需依赖,--alldeps 让已经安装在准备机上的依赖也进入下载集合。少了后者,开发机恰好装过的库可能在干净目标上缺失。DNF download 插件文档给出了两者区别。
包签名中的密钥必须来自受信任的发行版或组织渠道,不能遇到 NOKEY 就关闭校验。目标机接收并校验文件后,先查看离线安装事务:
cd rpm-bundle
sudo dnf --disablerepo='*' install --assumeno ./*.rpm确认没有意外删除、跨发行版替换或大范围降级,再在批准的维护窗口执行:
sudo dnf --disablerepo='*' install ./*.rpm
rpm -q nginx
nginx -v禁用网络仓库后,缺少依赖应直接报错。不要用 rpm --nodeps 绕过它,也不要从另一个系统复制 .so 到系统库目录。DNF 4 与 DNF 5 的插件和选项应按实际版本核对,其他事务行为见 DNF 命令参考。
可长期维护的 RPM 内网仓库还需要仓库元数据、模块信息和签名。只收集一批 RPM 更适合固定环境的单次交付,不宜冒充能持续解决所有依赖的完整镜像源。
Debian 系统:由目标机生成依赖请求
APT 的依赖选择受到目标已安装状态和软件源索引影响。apt-get download nginx 只下载指定包,通常不能作为完整离线安装材料。
Debian 的 apt-offline 可以在目标端生成请求,在联网准备端下载,再把数据导回目标。两端应先通过可信安装介质或批准的软件包安装好 apt-offline;它自身及其依赖也是准备内容。以下采用 Debian 的 apt-offline 流程,目标软件源应固定发行版代号,避免在准备期间随 stable 切换。
先同步索引,尤其适用于新安装且没有可用软件索引的目标:
# 离线目标:生成索引请求
sudo apt-offline set update.sig --update
# 联网准备端:取得 update.sig 后下载
apt-offline get update.sig --bundle update.zip
# 离线目标:取得 update.zip 后导入
sudo apt-offline install update.zip然后为目标需要的软件生成请求:
# 离线目标
sudo apt-offline set nginx.sig --install-packages nginx
# 联网准备端
apt-offline get nginx.sig --bundle nginx.zip
# 离线目标
sudo apt-offline install nginx.zip
sudo apt-get --no-download install nginx
dpkg-query -W nginxapt-offline install 导入 APT 所需数据和缓存,最后仍由 apt-get 执行软件安装事务。--no-download 使缺失包无法通过网络临时补齐;缺文件时返回准备流程,不跳过认证或依赖检查。apt-offline 手册和 apt-get 手册分别说明这两个阶段。
软件包安装脚本可能立即启动服务。正式机器上应先确认默认监听地址、账号、数据目录及维护窗口,再安装并调整配置。这里只给出发行版操作路径,具体发行版镜像上的安装事务仍需在对应测试主机演练。
Maven:验证完整构建,不只预下载依赖
如果目标只运行 JAR,优先交付已测试的制品和运行时。必须现场构建时,需要固定源码、JDK、Maven、构建配置、镜像仓库和本地仓库内容。Maven Wrapper 还可能下载 Maven 分发包,也应提前准备。
在与目标工具链一致的联网准备环境中,使用独立仓库完成实际构建,再以离线模式重复执行:
# 项目根目录,普通构建用户;本目录可写。
mvn -B -Dmaven.repo.local="$PWD/.offline-m2" clean verify
mvn -o -B -Dmaven.repo.local="$PWD/.offline-m2" clean verify两次必须使用相同 profile、模块和参数。项目如果还有代码生成、前端构建、集成测试或原生插件,应执行交付要求的完整任务集合,而非只跑 compile。预下载依赖的目标可以辅助准备,最终仍以真实离线构建检验是否齐全。
Maven 的 -o 约束 Maven 的仓库访问,定义见 Maven CLI。插件或测试代码仍可能自己访问网络;要证明完整流程不依赖公网,应在没有公网出口的等价环境中再执行。离线测试需要数据库时,应提前准备本地测试服务,不能通过跳过测试伪造相同验证结果。
不要直接交付整个个人 .m2。它可能混有未使用制品、失败下载记录、私有 settings 和凭据。内部仓库的 URL、服务器 ID 与 Maven 解析记录也会影响离线查找,应保持经过验证的配置并单独保护秘密。
npm:缓存、平台包与安装脚本一起验证
沿用前端部署中的工程,在 Linux 普通用户目录准备持久缓存:
cd frontend-lab
docker run --rm --user "$(id -u):$(id -g)" \
--mount "type=bind,source=$PWD,target=/app" --workdir /app \
node:24.20.0-bookworm-slim sh -ec '
npm ci --cache /app/.npm-cache
APP_RELEASE=offline npm run build
'
docker run --rm --network none --user "$(id -u):$(id -g)" \
--mount "type=bind,source=$PWD,target=/app" --workdir /app \
node:24.20.0-bookworm-slim sh -ec '
npm ci --offline --cache /app/.npm-cache
APP_RELEASE=offline npm run build
'第二次 npm ci 会重新安装依赖;安装与构建都在只有回环网络的容器中执行。预期两次均成功生成 site/releases/offline。Node 镜像已预先存在,实际转运时需要连同它准备,不能让目标第一次运行再拉取。
--prefer-offline 仍可能补下载,严格离线应使用 --offline,其定义见 npm 配置。原生依赖、可选平台包和安装脚本要在目标 OS、CPU、libc 与 Node 版本组合下准备;不要把 Windows 的 node_modules 直接复制给 Linux。
某些安装脚本会另行下载浏览器、编译工具或二进制文件,npm 缓存未必包含它们。禁用脚本后安装成功,也不能证明真实应用可用;必须确认这些脚本是否必要,并在断网条件下完成实际构建和运行。缓存目录及 .npmrc 对外提供前,应检查认证信息和私有仓库地址。
固定软件版本,也要固定数据变化
数据库初始化、升级迁移与数据恢复是三种操作。新库建表可以采用明确的初始化脚本;已运行数据库应使用带版本和执行状态的迁移机制。CREATE TABLE IF NOT EXISTS 只能避免某类重复创建,无法确认已有表的字段、约束和索引都符合新版本。
每次变更前确认当前 schema 版本、备份可恢复性、锁影响和新旧应用兼容性。恢复旧应用时可能无需恢复数据,也可能必须停写后恢复;后一种方式会影响备份之后产生的业务记录,需要明确恢复点与补偿方式,不能在安装脚本里默默执行。
文件上传目录、对象存储和消息状态也不在应用镜像中。交付清单应说明哪些是初始数据、哪些必须从现有环境迁移,以及由哪个账号写入。不要把生产数据和真实凭据顺手放进公开示例包。
升级维护与故障处理
安装记录应能回答当前实际运行什么
受限环境里的安装脚本可能在中途失败。重新执行前,应读取包管理器数据库、当前镜像身份、配置摘要、服务状态和数据库迁移记录,定位已完成的步骤。单独放一个 .done 文件只说明某次脚本写过它,不能证明当前系统仍与那次运行一致。
可重复执行的安装应把“准备文件”“修改系统”“启动服务”“验证业务”分开。准备阶段可以在新目录进行;修改前保留匹配的旧配置,出现异常时停止后续步骤。需要人工选择的数据迁移或回退,不应由宽泛的错误捕获自动执行。
安装结束至少取得实际进程版本、镜像或制品身份、加载的配置版本、监听地址、健康结果和一条关键业务请求结果。对于前端,增加真实浏览器断公网访问;对于数据服务,增加备份恢复和权限验证。记录可以保存在内部交付单中,秘密和客户数据不进入公开文档。
按失败阶段查找缺项
| 失败阶段 | 常见原因 | 检查与下一步 |
|---|---|---|
| 包校验失败 | 转运损坏、版本混装、文件被修改 | 比对批准的清单与签名,重新取得文件 |
| 系统包安装失败 | 缺依赖、仓库版本不匹配、模块流不同 | 保存完整事务错误,在等价准备机重新解析 |
| 镜像无法运行 | 平台错误、镜像缺失、运行时不兼容 | 比对平台和 Engine 版本,禁止自动拉取后再试 |
| 离线构建失败 | 插件、平台包或脚本下载未准备 | 从第一个缺失坐标或 URL 找到所属构建阶段 |
| 应用无法启动 | 外部配置、权限、动态库、证书或数据库不可用 | 用运行账号检查文件,查看首次启动异常 |
| 页面局部失效 | 公网字体、CDN、Worker 或延迟模块 | 用 F12 保留失败 URL,改为可达且被授权的资源 |
| HTTPS 失败 | 时间错误、名称不匹配、CA 缺失 | 校正时间与证书链,不使用跳过验证作为修复 |
| 健康但业务失败 | 健康接口未覆盖实际依赖或权限 | 请求真实接口并核对数据读写与下游日志 |
内网连接排查可沿网络与端口、域名与 DNS、HTTPS 与证书继续。使用代理时分别检查包管理器、Docker daemon、构建器和应用进程,某个终端里的代理变量不会自动配置所有服务。
如果目标需要访问内部 HTTPS 服务,应配置可信 CA 与正确域名。关闭校验、写死 IP 或临时使用 HTTP 可能让一次安装继续,却会改变安全性质和后续证书更新方式。授权服务、许可证检查、时间同步及证书吊销检查,也可能在首次启动之后才触发外部访问,应按实际产品行为纳入演练。
离线系统仍需要持续更新
离线限制减少了某些网络入口,也延长了补丁送达时间。应明确谁跟踪 JDK、容器基础镜像、系统库及应用依赖的安全公告,如何把新版本送入受限环境,以及谁负责验证与批准安装。
每次更新重新生成受控交付包和校验资料,保留仍可恢复的旧版本,不在原包上混入临时文件。内部镜像源的元数据、CA、签名密钥和许可证都有维护要求;应用升级成功后,也应继续观察错误率、资源使用和数据变化。
离线运行实验结束后,执行:
docker compose --env-file release-r1.env down这会移除本实验容器;导出的镜像归档、本地镜像和配置仍保留。正式环境中的卷、数据库和系统包应按各自恢复方案管理,不附加批量清理命令。
权威资料与规范地址
按具体配置、协议或操作查阅完整定义:
镜像交付与隔离运行
系统软件包
应用构建依赖
| 查阅内容 | 官方地址 |
|---|---|
| Maven CLI | https://maven.apache.org/ref/3.9.12/maven-embedder/cli.html |
| npm 配置 | https://docs.npmjs.com/cli/v11/using-npm/config/#offline |
