内网 Windows 服务器上要做文档在线预览和编辑业务系统里点开一个 Word 文件直接在浏览器里打开、改完自动存回这种需求这几年出现得越来越密集。真到动手的时候你会发现卡住人的往往不是代码而是环境这台机器没有外网装的还是 Windows Server中间件、字体、镜像一个都下不来。我手上这个项目就是这种情况标题里说的Onlyoffice Docker Windows 离线安装本质上讲的就是在完全断网的 Windows 宿主上把一套文档服务跑起来并让业务系统调通。这篇内容适合两类人看一类是正在做技术选型、要评估这条路走不走得通的架构角色另一类是明天就要动手、需要一个能照着抄的部署流程的运维和开发。下面我按实际操作的顺序把镜像搬运、参数配置、字体处理、集成调试和排错链路完整拆一遍中间那些文档里不会写、但一定会踩到的细节我会重点标出来。1. 断网 Windows 主机上跑文档服务先把这三件事想明白1.1 为什么这套东西非要套一层容器而不是直接在 Windows 上装很多人第一反应是Windows 不是有现成的安装包吗双击下一步不就行了。真装过就知道问题在哪。这套文档服务在 Linux 下是一组协同工作的组件——文档转换服务、编辑服务、缓存服务、反向代理彼此之间靠本地 socket 和端口通信安装包本质上是一堆 shell 脚本在编排这些组件。Windows 版虽然提供了安装程序但它对系统版本、补丁、运行库版本非常敏感而且组件版本和主程序版本是绑死的一旦业务方要求升级或者回退你得整体卸载重装中间产生的日志、缓存、证书目录全在系统盘里散着回滚的时候基本靠人工清理。容器化之后这些组件被打包进同一个镜像内部通信方式固定宿主机只需要提供一个能跑容器的环境。升级就是换一个镜像标签回滚就是把旧标签再启一次业务数据和日志通过挂载卷留在宿主机上跟容器生命周期解耦。我自己的判断标准很简单这个中间件未来一年会不会升级两次以上会就上容器。文档服务这种跟着浏览器兼容性走的组件升级频率一定不会低。不过容器化也带来一个隐性成本宿主机上必须先有一个能跑容器的运行时环境。在能联网的机器上这不叫事在断网机器上这件事本身就是一个独立的小项目后面第 2 章会重点讲。1.2 离线这条路真正卡人的地方从来不是镜像本身新手做离线部署脑子里想的是我把镜像导出再导入不就完了。实际操作下来镜像反而是最容易搞定的一环——几百兆到两三个 G 的 tar 包用移动硬盘拷过去就行。真正耗时间的是下面这几类东西卡点类别具体表现影响程度运行时底座缺失宿主没装容器运行时或者 WSL2 内核更新包没带直接卡死无法推进字体资源缺失容器内只有极少数西文字体中文文档排版错乱、方框乱码功能性缺陷用户第一时间投诉密钥与配置漂移密钥随机生成、容器重建后变化调用方签名全部失效集成阶段最难定位的问题网络可达性错位浏览器能访问、容器访问不到业务服务或者反过来表现为白屏、保存失败日志里看不出原因时间同步偏差内网机器时钟不准签名有效期校验失败偶发极难复现这几类问题有一个共同特点它们都不在安装文档里。安装文档假设你有一个干净、联网、时间准确的 Linux 环境。而内网 Windows 环境每一条都不满足。我习惯在动手之前先列一张物料清单把需要提前在内网外准备的东西一次性列全避免来回跑。清单大概长这样容器运行时安装包、WSL2 内核更新包如果走 WSL2 后端、文档服务镜像 tar 包、常用中文字体文件ttf/otf、可选的字符校验工具、一个能离线验签的小脚本。这些东西加起来不到 5 G但少一样就得再跑一趟。提前确认清楚内网这台机器是谁在管、能不能改防火墙规则、能不能重启。这三件事没确认就开始部署后面大概率会在半路被拦下来。1.3 版本怎么选别追最新也别停在太老的版本选版本这件事我踩过两次坑。第一次是追最新镜像拉下来发现某个内部接口的参数结构变了业务侧调用报错回头翻变更说明才发现是主版本升级带来的不兼容。第二次是图省事用了很早的版本结果新版浏览器的某些特性支持不好表格里的公式渲染出问题被使用方追着问。我的经验是选一个次新的大版本锁定到具体的补丁号比如锁定到x.y.z这样的三段版本不要用latest也不要用只有两段的浮动标签。理由很实际——三段号意味着镜像内容不会变你在测试环境验证过的东西搬到生产环境行为完全一致而latest每次拉取都可能是新的离线环境下更麻烦因为你根本不知道手里这个 tar 包到底是哪个版本。另外要提醒一句这类文档服务的社区版本通常对同时在线编辑的人数有上限超过之后会提示连接数不足。选型阶段一定要把峰值并发算清楚别等到上线当天才发现人数不够。计算方式不是公司有多少人而是同一分钟内真正打开同一个文档的人数这个数字通常比想象的小很多。2. 断网机器上的容器底座Windows 侧的准备动作2.1 容器运行时的离线安装与后端选择Windows 上跑容器有两条路一是装带图形界面的桌面版运行时二是直接在 WSL2 里装引擎。前者省心后者干净。我这次选了前者原因很朴素——运维同事需要图形界面看一眼容器状态命令行对他们是负担。离线安装的第一个前提是安装包本身要提前下好。这类安装程序体积在几百兆很多版本还会在安装过程中在线下载组件所以一定要找那种完整离线包或者提前把组件缓存准备好。装之前先把 Windows 功能打开dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart执行完必须重启。重启后还要装 WSL2 内核更新包这个东西是独立的一个 msi 文件很多人就是在这一步翻车的——安装程序装完了一启动容器就报内核版本过低然后开始怀疑人生。提前把它放进物料清单能省下半天。安装完成后在设置里确认两件事后端是不是 WSL2以及资源分配给了多少。默认配置在开发机上够用在服务器上往往偏小。虚拟化功能在 BIOS 里叫 VT-x 或 AMD-V也要确认已开启这个在任务管理器的性能页签里能直接看到。如果这台机器上还跑着别的虚拟化软件可能会和 Hyper-V 层冲突表现是容器启动时报虚拟化平台不可用。这种情况要么换后端要么把冲突的软件停掉。我遇到过一台机器上装了三种虚拟化方案的最后是彻底清理掉两个才跑通。2.2 磁盘、内存和 WSL2 的资源上限这一步很少有人认真做但它直接决定了后续使用体验。WSL2 后端默认会吃掉宿主一半左右的内存而且不会主动释放。文档服务在转换大文件的时候内存占用会冲得很高转换完内存也不一定马上归还。表现出来就是跑了一周之后宿主机的可用内存越来越少最后整个系统变卡。解决办法是在用户目录下建一个配置文件路径是C:\Users\用户名\.wslconfig内容是[wsl2] memory16GB processors8 swap4GB localhostForwardingtrue这里的内存上限按宿主物理内存的 50%~60% 给别给满要给 Windows 自己留出空间。改完之后执行wsl --shutdown让它生效注意这会停掉所有容器选个业务低峰期做。磁盘方面容器镜像和卷默认落在系统盘的用户目录下。文档服务镜像解压后体积不小加上转换过程中的临时文件系统盘吃紧是常态。我的做法是把镜像存储位置迁到数据盘或者在装的时候就把数据目录指到空间大的分区。迁移镜像存储位置在图形界面设置里可以直接改改完记得把旧目录清理掉不然空间不会立刻释放。临时文件和缓存目录也要盯一下。文档转换会产生大量中间文件如果这个目录和历史日志共用一块盘长期跑下去容易把盘塞满届时表现是转换服务莫名失败但日志里没有明显报错。2.3 把镜像搬进内网导出、传输、加载的完整链路镜像搬运的流程是固定的三段外网机器拉取并导出介质传输内网机器加载。每一段都有细节。第一段在外网机器上拉取并导出docker pull onlyoffice/documentserver:8.1.3 docker save -o onlyoffice-ds-8.1.3.tar onlyoffice/documentserver:8.1.3导出的 tar 包体积通常在 2 G 上下。这里第一个坑是文件系统限制如果你的移动硬盘是 FAT32 格式单文件超过 4 G 就写不进去虽然这个包可能刚好卡在边缘但加上别的物料很容易超。要么格式化成 exFAT 或 NTFS要么用分卷压缩拆开。第二段传输没什么好说的但建议同时记下镜像的 ID 和摘要值docker inspect --format{{.Id}} onlyoffice/documentserver:8.1.3把这个值记在部署文档里。第三段加载到内网机器后再用同样的命令比对一次确认搬运过程中没有损坏。这一步看起来很形式主义但我确实遇到过一次 tar 包在拷贝过程中断掉、加载时报了看不懂的解压错误排查了两个小时才发现是介质问题。加载命令docker load -i onlyoffice-ds-8.1.3.tar加载完之后docker images里能看到带版本标签的镜像就说明底座这部分已经完成了。如果是桌面版运行时路径要用 Windows 风格比如docker save -o D:\pkg\onlyoffice-ds-8.1.3.tar onlyoffice/documentserver:8.1.3。Windows 下命令行工具对路径的反斜杠处理有时会出问题实在不行就切到 PowerShell 并用正斜杠。3. 启动参数逐条拆解端口、卷、密钥和字体3.1 一条完整的启动命令以及每个参数在管什么镜像进了内网接下来是把它跑起来。我习惯先用一条完整的命令跑通再考虑要不要转成编排文件。命令大概是这样docker run -d --name onlyoffice-ds \ -p 8080:80 \ -e JWT_ENABLEDtrue \ -e JWT_SECRET这里放一串足够长的随机字符 \ -e JWT_HEADERAuthorization \ --restartalways \ -v D:/oo/data:/var/www/onlyoffice/Data \ -v D:/oo/logs:/var/log/onlyoffice \ -v D:/oo/lib:/var/lib/onlyoffice \ -v D:/oo/fonts:/usr/share/fonts/truetype/custom \ onlyoffice/documentserver:8.1.3逐条说清楚它们在管什么-p 8080:80容器内部的服务监听 80映射到宿主的 8080。选 8080 是因为 Windows 上 80 端口经常被别的 Web 服务占着用 8080 能避开大部分冲突。这个映射关系要记牢后面所有地址里都是 8080。-e JWT_ENABLEDtrue开启令牌校验。关掉它确实能让调试变简单但意味着任何知道地址的人都能调用你的文档服务内网也不该这么干。-e JWT_SECRET...签名密钥。这是整篇文章最重要的一行后面 3.3 单独展开。--restartalways开机自启和异常退出自动拉起。服务器重启之后没人手动去敲命令这一行必须加。四个挂载卷分别对应数据、日志、缓存和自定义字体。Windows 路径用正斜杠写反斜杠在某些版本上会被当成转义符。启动后等三十秒左右访问http://localhost:8080/healthcheck返回true就说明服务起来了。这个健康检查接口比直接打开首页靠谱首页在服务刚起、字体还没生成完的时候可能会短暂返回异常。3.2 中文字体缺失导致的排版事故以及怎么补这个问题第一次遇到的时候我完全没往字体上想——文档能打开内容也对但中文全部显示成方块或者字体变成了某种不认识的宋体替代品行距和分页全乱。用户看到的第一反应是你这系统不行。原因很直接容器镜像为了控制体积只打包了少量基础西文字体没有中文字体。浏览器端渲染时虽然可能用本机字体兜底但服务端转换生成的预览图和最终文档渲染是在容器里完成的容器里没有字体它就渲染不出来。修复办法是把字体文件放进挂载目录然后触发字体缓存重建。字体文件从哪来从一台装了完整字体库的 Windows 机器上把C:\Windows\Fonts目录下的常用中文字体宋体、黑体、楷体、仿宋以及对应的粗体变体拷出来。注意字体是有授权的商用场景要确认许可范围别随手从网上下个来路不明的包。拷贝到D:/oo/fonts之后执行docker exec -it onlyoffice-ds /usr/bin/documentserver-generate-allfonts.sh这个脚本会扫描字体目录并重建缓存跑完需要一两分钟期间服务会短暂不可用。每次新增或替换字体都要重跑一次只放文件不跑脚本是无效的这一点很多教程里没写导致有人放完字体发现没变化就放弃了。字体数量也要控制。我见过有人把整个字体库几千个文件全塞进去结果容器启动时间从十几秒涨到好几分钟内存也上去了。实际业务里用到的中文字体不会超过十种挑常用的放就够了。3.3 密钥这一行决定了后面所有联调能不能通令牌校验这个机制的原理不复杂调用方用密钥对一份配置做签名服务端用同一个密钥验签验过了才认这个请求。密钥不一致一律拒绝。听着简单但它是联调阶段最高频的故障源。第一个坑是密钥长度不够。太短的密钥在某些版本上会有告警而且安全性也谈不上。我的习惯是生成一串 32 位以上的随机字符字母数字混合别用有意义的单词。生成方式随便命令行里openssl rand -hex 32或者用一个密码管理器生成都行。第二个坑更隐蔽不显式设置密钥的时候服务启动会自己随机生成一个然后写进容器内部的一个配置文件里。这个文件不在挂载卷范围内。也就是说你把容器删掉重建密钥就变了而调用方还在用旧密钥签名结果全部 403。表现出来的现象是昨天还好好的今天就打不开了。所以我的规矩是密钥必须显式写在启动参数里同时记进配置管理文档。不要依赖容器自动生成也不要指望从日志里找回来——日志里那串可能只是部分字符。第三个坑是时间。令牌里带有效期宿主机时钟偏差超过容差验签就会失败。内网机器尤其容易出这个问题因为经常没有配置可靠的时间同步源。部署完先看一眼docker exec onlyoffice-ds date和宿主机的系统时间是否一致差个几十秒以内问题不大差几分钟就一定会出事。调用方签名的代码大概是这样以 Node.js 为例const jwt require(jsonwebtoken); const SECRET process.env.OO_JWT_SECRET; const config { documentType: word, document: { fileType: docx, key: doc- Date.now() - Math.random().toString(36).slice(2, 8), title: 示例文档.docx, url: http://业务服务地址:9000/files/demo.docx }, editorConfig: { callbackUrl: http://业务服务地址:9000/api/oo/callback, lang: zh-CN, mode: edit } }; const token jwt.sign(config, SECRET, { algorithm: HS256 });注意document.key的生成规则。这个 key 不是文件名它是协同编辑的分组标识key 相同的请求会被认为是同一份文档进同一个编辑会话。如果两个不同用户该看到不同版本的文档却用了同一个 key就会互相看到对方的内容反过来同一份文档每次打开都用新 key协同编辑就失效了两个人各改各的最后只有一个人保存成功。我的做法是用文件唯一标识 内容版本号/修改时间组合成 key这样内容变了 key 就变内容没变 key 就不变。3.4 四个挂载卷各自的职责以及升级时怎么保住数据卷的规划看起来是个小事但它决定了以后升级和迁移顺不顺利。四个目录的分工容器内路径存的是什么丢了会怎样/var/www/onlyoffice/Data配置、证书等持久数据配置丢失需要重新初始化/var/log/onlyoffice各组件日志排查问题时没材料/var/lib/onlyoffice文档缓存与转换中间产物缓存重建短期性能下降/usr/share/fonts/truetype/custom自定义字体中文排版立刻出问题升级流程就变得很清晰停掉旧容器、删掉旧容器卷不动、加载新镜像、用同样的挂载参数重新启动。因为配置和数据都在卷里新容器起来直接复用业务侧完全无感。这里有个必须强调的禁忌不要用提交容器的方式做版本管理。有人图省事在容器里改了点东西然后用提交命令把它固化成一个新镜像。这样做出来的镜像没有构建记录过三个月没人说得清里面改了什么也没法重建。所有需要固化的改动要么写进启动参数要么写进挂载的配置文件。还有一点Windows 上挂载 Windows 路径到容器里文件读写性能相比容器内部路径会明显下降尤其是字体这种小文件密集的目录。如果发现字体生成脚本跑得特别慢可以考虑把字体目录改用命名卷用docker cp把文件拷进去。这是用空间换时间接受与否看你的耐心。4. 让业务系统真正调起来集成侧三个绕不开的坑4.1 回调地址必须是浏览器能访问到的那个地址这是我认为整个集成过程中最容易出错的地方原因是它有两个方向的可达性要求而这两个方向的要求经常互相矛盾。方向一浏览器要能拿到编辑器脚本、能加载文档。所以你在页面里引用的脚本地址必须是用户浏览器所在网络能够访问到的地址。方向二文档服务容器要能把用户编辑完的内容回写到你的业务服务。所以配置里那个回调地址必须是容器内部能够访问到的地址。这两个地址经常不是同一个。比如业务服务部署在同一台 Windows 宿主上监听 9000 端口。浏览器访问用http://192.168.1.50:9000这没问题。但容器里访问这个 IP 可能不通因为容器在虚拟网络里有自己的网段访问宿主机的局域网 IP 有时会被拦。Windows 上给了一个约定好的域名host.docker.internal容器内用它就能访问到宿主机。于是就出现了这种组合// 浏览器侧看到的文档地址用宿主机的局域网 IP document.url http://业务服务IP:9000/files/demo.docx // 容器侧回写地址用容器能解析的宿主机别名 editorConfig.callbackUrl http://host.docker.internal:9000/api/oo/callback如果业务服务不在同一台机器上回调地址就写那台机器的实际地址但要先验证容器网络到那边的连通性。验证方式很简单docker exec -it onlyoffice-ds curl -I http://目标地址:端口/返回任何 HTTP 响应都算通返回Connection refused或者超时就是不通。这个验证一定要在写代码之前做否则后面调试白屏问题时你会一直在前端找原因其实是网络不通。4.2 编辑器白屏、脚本 404 和混合内容拦截白屏是集成阶段最常见的问题没有之一。它有好几种完全不同的成因得分层排查。第一层脚本地址对不对。编辑器不是一个 npm 包它是从文档服务那边动态加载的。地址形如http://文档服务地址:8080/web-apps/apps/api/documents/api.js。直接在浏览器地址栏里访问这个地址应该能看到一段 JS 内容。看不到说明地址拼错了或者端口不对。第二层容器到宿主的端口映射是否生效。在宿主机上访问http://localhost:8080/healthcheck通了不等于局域网其他机器能访问。Windows 防火墙默认会拦入站连接需要显式放行New-NetFirewallRule -DisplayName OO-8080 -Direction Inbound -LocalPort 8080 -Protocol TCP -Action Allow加完规则从另一台机器上用浏览器访问一次确认能通。这一步能排除掉大量我本地明明能开的误判。第三层也是最阴的一层混合内容拦截。业务系统的页面如果是 https 的而文档服务是 http 的浏览器会直接把 http 的脚本拦掉控制台里报的是混合内容错误但页面上就是一片白。这个问题的解法只有两个给文档服务配上证书走 https或者把业务页面降到 http。前者正规后者省事但不推荐。排查的时候按 F12 打开控制台先看 Network 面板有没有红色请求再看 Console 里有没有报错。只要脚本请求根本没发出去那基本就是地址或拦截问题如果请求发了但返回 404那是路径问题如果返回 200 但页面还是白那要看返回的内容是不是一个错误页。4.3 保存回写回调接口要处理好的几种状态用户编辑完点保存文档服务会向你的回调地址发一个 POST 请求请求体里带一个状态码和一个文档地址。状态码的含义大致分几类值为 1 或者 4 表示内容有变更可以下载值为 2 表示可以关掉编辑会话了值为 3 或者 7 表示保存出错了值为 6 表示强制保存。处理逻辑的关键点有三个。第一状态 2 的时候不要急着下载文件。这时候用户可能只是关掉了页面没改动直接去拉文件要么拿到旧内容要么地址还没准备好。我的写法是只在状态为 1 或 4 的时候去拉文件其他状态只做记录。第二拉文件的请求也要带令牌。很多人以为只有前端打开编辑器时需要签名其实回调里给的那个文档地址同样需要。如果服务端开了令牌校验你不带就下载不了表现是回调收到了、状态也对但文件拉不下来日志里一堆 403。第三幂等。网络抖动、超时重试都可能让同一个回调被推多次。你的接口要能识别重复请求最简单的办法是用文档 key 加状态做去重处理过的直接返回成功不重复落盘。回调接口处理完之后必须返回一个 JSON 响应内容大致是{error:0}告诉服务端我收到了别再推了。如果返回别的服务端会认为你没处理成功继续重试日志里会看到同一个回调反复出现。5. 内网环境下的排查链路从现象倒推原因5.1 容器反复重启按这个顺序看症状是docker ps里容器状态一直在Restarting和Up之间跳。这时候别急着去看业务日志先看容器为什么退出。第一步看容器日志的尾部docker logs --tail 100 onlyoffice-ds大概率能看到具体的错误原因。常见的几种端口被占用报address already in use、挂载路径不合法Windows 路径写错、盘符不存在、内存不足被系统杀掉。第二步端口冲突要单独查。netstat -ano | findstr :8080能看到占用进程的 PID再用任务管理器对一下是哪个程序。Windows 上 8080 被占用的概率不低尤其是装过一些开发工具之后。换一个端口比杀掉别人的进程安全。第三步进容器看内部组件状态。如果能进得去说明主进程活着只是某个子服务挂了docker exec -it onlyoffice-ds supervisorctl status正常应该看到几个组件都是RUNNING。如果有FATAL或者BACKOFF的说明那个组件起不来通常是配置问题或者依赖缺失。这时候去看对应组件的日志路径一般在挂载的日志目录下。第四步如果容器连进都进不去说明主进程在启动阶段就挂了基本可以锁定在挂载和资源两类原因上。把挂载参数减到最少再试能跑就一个个加回来用二分法定位。5.2 打开文档提示下载失败问题通常在这一层这个错误信息很误导人它听起来像是文件不存在实际上大部分情况和文件本身没关系。最常见的原因是文档服务容器访问不到文件地址。前面说过前端拿到的文档地址和容器实际去拉取的地址是同一个如果这个地址是容器网络访问不到的比如写了个内网域名但容器内 DNS 解析不了或者用了localhost容器拉不到文件就报下载失败。排查方式是直接在容器里 curl 那个地址docker exec -it onlyoffice-ds curl -I http://文件地址返回 200 就说明网络和权限都没问题问题在别处返回 404 说明地址拼错或者服务端路由不对返回 401/403 说明鉴权没过超时说明网络不通。另一个常见原因是文件格式不被支持。这类服务支持的格式是有限集合冷门格式或者加密过的文档会直接拒绝。测试阶段建议先用最普通的 docx 试跑通了再试业务里实际用到的格式。还有一个很隐蔽的原因同一份文档用了相同的 key 但内容变了。服务端会把 key 当作内容标识去缓存key 没变它就返回缓存里的旧内容如果旧内容对应的文件已经被你删了或者挪了就会报错。这种情况下的表现是偶尔能开偶尔不能很难复现。所以前面说的 key 生成规则一定要按内容版本走。5.3 内存、时钟和那些只在长时间运行后才暴露的问题有几类问题在部署当天完全正常跑上一两周才开始冒头这类问题最难处理因为发生的时候往往没人盯着。内存是最典型的一个。文档转换是个吃内存的活大文件、复杂表格、带大量图片的文档都会把占用推高。WSL2 后端还有个特点分配出去的内存不轻易还给 Windows。所以你会看到容器本身内存占用不高但宿主机的可用内存持续下降。解决办法就是前面说的.wslconfig设上限再加一个定时重启容器的策略——比如每周固定时间docker restart几秒钟的事能避免大部分慢性问题。时钟偏差是第二个。内网机器如果没配时间同步一个月漂几分钟很正常。签名校验、缓存过期、会话有效期这些逻辑全依赖时间漂多了就会出各种奇怪现象。我的做法是在部署文档里明确写上确认时间同步服务已启用并且在运维巡检项里加一条每周检查时间偏差。日志占盘是第三个。文档服务的日志默认不做轮转日积月累能占掉几个 G。挂载到宿主机之后可以用宿主机的计划任务定期清理或者在启动参数里限制日志级别。这个是纯粹的运维活但部署阶段就要规划好别等到盘满了再去救。5.4 版本升级与回滚留好后路再动手升级这件事我给自己定的规矩是任何时候都要能在十分钟内回到上一个可用版本。具体做法是保留上一版镜像的 tar 包和完整启动命令。升级的时候不是原地覆盖而是新起一个容器跑在另一个端口上验证通过后改端口映射切换旧容器先停掉但不删观察一两天没问题再清理。这样如果新版本有问题把旧容器启起来就恢复了。升级前要确认的事新版本对配置参数有没有变更有些参数在新版里被废弃或改名不处理会启动失败、挂载卷的目录结构有没有变极少但发生过、业务侧调用的接口有没有不兼容改动主版本升级要特别留意。这三件事查清楚升级过程基本不会有意外。回滚之后还有一件事要做把新版本产生的数据清掉。如果新版本已经写入了缓存目录回滚到旧版时旧版可能读不懂那些缓存表现为文档打不开。所以切换前先备份缓存目录回滚时一并还原这是完整的一套动作。6. 我个人在几次部署里攒下的几点体会同样是搭一套文档服务第一次做和第五次做差别不在于命令记得多熟而在于知道哪些地方必须先确认、哪些现象意味着什么。分享几个我觉得最有价值的经验。先把网络可达性画成一张图。图上至少要有四个点用户浏览器、业务服务、文档服务容器、文件存储位置。每两个点之间标上能不能通、用什么地址通。这张图画出来前面说的回调地址问题、白屏问题、下载失败问题有一大半在动手写代码之前就能规避。我现在每次开新项目都会先画这张图花十分钟省两天。所有配置显式化别依赖默认值。密钥、端口、路径、时区能写死的全部写死并记进文档。默认值只在你第一次跑通的时候好用等容器重建一次默认值就变成未知值了。这是我在密钥那件事上栽过跟头之后最大的改变。给字体和时区单独留检查项。这两件事的故障表现都很像业务 bug——字体问题表现为排版错乱时区问题表现为偶发失败——排查的时候容易往代码上想。部署完成后先拿一份带中文、带表格、带图片的文档完整走一遍编辑保存流程能挡掉很大一部分问题。别把社区版当企业版用。并发人数、功能范围、技术支持这三项在选型阶段就要和业务方对齐清楚。我见过项目上线后才发现并发超限的回头改方案的成本比一开始就选对高得多。最后补一个小技巧内网机器上的部署过程最好全程录屏或者用脚本记录每一步命令。离线环境最大的痛点是那次是怎么弄好的没人记得等三个月后再来一台机器要部署有记录和没记录完全是两种工作量。把命令整理成一个能重复执行的脚本参数抽成变量下次换个环境改几个值就能跑这才是必备两个字的实际含义。