kkFileView Docker部署与附件在线预览实践

发布时间:2026/10/23 3:38:56
kkFileView Docker部署与附件在线预览实践 1. 先想清楚这套预览服务到底解决哪一段问题业务方想要的效果往往很简单用户在浏览器里点一下附件文件就能直接打开查看不用先下载、不用装任何客户端、手机上看也不变形。真正落地的时候这个朴素需求会牵扯出一大堆细节格式兼容、字体、并发、缓存、安全、授权链接的时效性每一项都能让人返工。我这几年做过的几个后台系统里附件预览都是同一个套路用 Docker 部署 kkFileView把 Office、PDF、图片、压缩包、音视频等格式的判断和渲染统一交给它业务系统只负责拼一条预览链接丢给前端 iframe。这篇就按我实际落地的顺序把部署、配置、对接到排障完整过一遍从没碰过这个工具的人照着做能跑通已经跑通的人也能从里面捡到几个少踩坑的细节。kkFileView 是国产开源项目Java 写的本质是一个格式转译中间层它接收一个文件 URL根据扩展名决定走哪条渲染链路——Office 系列交给内置的 LibreOffice 转成图片或者 PDFPDF 走 pdf.js 之类的渲染文本、代码、图片直接展示压缩包列目录音视频用播放器。对业务系统来说它就是一组 HTTP 接口最核心的那个叫onlinePreview。用 Docker 部署的好处是它依赖的 LibreOffice、字体、Java 运行时全被打进镜像了本机上不用折腾那套装了 LibreOffice 却发现中文字体全变方框的经典事故。1.1 业务侧的真实诉求长什么样先把需求拆开看因为不同诉求会直接决定你选哪种方案。第一类是内部系统附件预览用户在工单、合同、报销单里上传的文件需要同部门的人点开看一眼这类场景对样式还原度要求中等对响应速度要求高因为附件的平均大小往往只有几百 KB。第二类是对外文档展示比如产品手册、报告、公示材料这类对排版还原和移动端适配要求极高一个表格错位就可能被投诉。第三类是批量浏览与检索比如档案系统要在列表里给每一行都提供缩略图或首屏预览这种场景的性能压力跟单文件预览完全不是一个量级。这三种诉求对应到 kkFileView 上的配置策略是不一样的。第一类适合 Office 转图片、低分辨率、强缓存第二类适合转 PDF 加高清渲染甚至考虑前端直接嵌 PDF第三类必须做缓存预热和并发控制否则用户一翻页后端 LibreOffice 进程就被打满。我在项目启动阶段一般会先问清楚平均文件大小、峰值并发、是否对外、是否要求水印这四个问题答案基本就决定了后面的参数表。1.2 三条技术路线的取舍在选 kkFileView 之前业内常见的做法无非三条我把它们的实际体验列成表格方便你对照自己的场景判断。方案代表做法优点实际踩到的坑纯前端渲染前端用各类 JS 库直接解析 docx、xlsx、pdf不占服务端资源部署简单复杂排版还原差老版 doc、ppt 基本无解字体依赖客户端服务端转译kkFileView、Office 转 PDF 服务、自建 LibreOffice 调用格式覆盖广样式还原稳定客户端零负担吃内存转换有冷启动开销需要缓存和队列客户端/插件要求用户装阅读器或用本地 Office 打开还原度最高移动端和外部用户直接不可用运维成本高绝大多数 To B 后台最后都会落到第二条路上原因很现实你没法要求外部用户装东西也没法保证他们的浏览器能解析十年前的老 doc。而在服务端转译这一类里自己拿 LibreOffice 裸调是最容易翻车的——进程管理、超时、僵尸进程、并发隔离每一样都要写不少代码。kkFileView 把这些脏活包了一层虽然它自己也有坑但起点高得多。1.3 能力清单和边界我得把边界说清楚免得你上线后被业务方追问。kkFileView 的强项是办公文档和常见文本格式Office 三件套、PDF、OFD、图片、纯文本、代码文件、压缩包目录、音视频覆盖得相当全。它的弱项主要在两块一是高度复杂的排版比如带复杂公式的 docx、多层嵌套图表、特殊艺术字转出来可能有细微偏差这是 LibreOffice 渲染引擎本身的限制不是配置能解决的二是交互式内容宏、表单控件、动画转成图片或 PDF 之后就只剩静态画面了。还有一个容易被忽略的点它做的是预览不是编辑。有些业务方会问能不能在页面上改两笔再保存答案是另请高明这类需求要走在线编辑方案跟预览是两条完全不同的技术栈。我一般会在需求确认阶段就把这句话说死省得后期扯皮。2. 部署方案设计单容器还是拆开确定要用之后下一步是设计部署形态。kkFileView 官方镜像是一个全家桶Java 应用加 LibreOffice 加转换进程管理全在一个容器里这是它最省事也最推荐的用法。我见过有人想把它拆成应用容器 独立的 Office 转换服务理论上可行因为它的转换层本质是 JODConverter 去连本地或远程的 Office 进程端口但拆开之后版本匹配、字体同步、连接池管理全要自己维护收益很小。除非你有非常明确的隔离诉求否则别拆。2.1 镜像里到底装了什么理解镜像内容对排障极其重要。这个镜像大致包含四层东西Java 运行时和应用 jar 包、内置的 LibreOffice负责 Office 转换、应用配置文件目录、以及启动脚本。启动脚本会先把 Office 的监听进程拉起来再启动 Java 应用所以你在docker logs里会先看到 Office 相关的输出然后才是 Spring Boot 的启动横幅。如果日志里只有 Java 启动而没有 Office 进程那 Office 转换必然是失败的这是排查Office 文件转不出来的第一现场。配置文件在容器内的固定路径下通常形如/opt/kkFileView-x.x.x/config/application.properties。这个路径里的版本号会随镜像版本变化所以别硬编码记第一次跑起来之后docker exec进去看一眼目录结构最稳妥。与之相邻的还有日志目录后面排障全靠它。2.2 资源估算别拿 1C1G 的机器硬上这是新手最常低估的地方。Java 应用本身占 300 到 500 MBLibreOffice 每个转换进程在打开一个中等复杂度文档时会瞬时吃到 200 到 400 MB官方镜像默认会起两个 Office 监听端口做并行转换。所以一个能稳定跑起来的容器内存基线我给到 2 GB推荐 4 GBCPU 两核起步因为文档转换是 CPU 密集型操作单核机器上一个 30 页的 ppt 转图能让你等半分钟。磁盘也要留够。转换产物默认会落盘做缓存一个 20 页的 docx 转成图片可能是几十 MB缓存保留期如果设成一个月日活几百的系统轻松堆到几十 GB。我一般给缓存目录单独挂一块盘或者单独一个卷配合定时清理策略把磁盘占用的上限锁死。2.3 目录与挂载规划我习惯在宿主机上建一个统一目录把所有需要持久化的东西都放在里面容器随时可以删了重建。典型结构是这样/data/kkfileview/ ├── config/application.properties # 自定义配置 ├── cache/ # 转换产物缓存 ├── logs/ # 应用日志 └── fonts/ # 追加字体可选这里有个必须提醒的坑只挂载单个配置文件不要挂载整个 config 目录。因为镜像里的 config 目录除了主配置可能还有日志配置、模板文件等一堆东西整目录挂载会用一个空目录把它们全遮住启动直接报错而且报错信息往往指向一个跟配置毫无关系的地方非常迷惑。我第一次踩这个坑的时候对着找不到某个模板的报错查了快两个小时。2.4 网络与端口对外只需要暴露应用端口默认 8012。容器内部还有 Office 转换进程监听的端口一般是 2001、2002 这类这些不需要也不应该映射到宿主机它们只在容器内部通信。我见过把内部端口也映射出去的做法除了增加攻击面没有任何好处。如果预览服务要接在业务域名下面建议走统一网关或者 Nginx 反代让业务系统和预览服务同源这样前端 iframe 嵌入时不会遇到跨域和 X-Frame-Options 的麻烦。具体配置我在第 5 节展开。3. 上手部署从拉到跑通第一条预览链接前面铺垫够了直接开干。我下面用的版本是 4.4.0不同小版本的配置键名会有出入凡是涉及具体键名的地方都以你镜像里那份application.properties的实际内容为准这一点务必记住。3.1 拉镜像与首次启动先拉镜像然后跑一个最简容器什么配置都不改目的只有一个确认这套东西在你机器上能起来。docker pull keking/kkfileview:4.4.0 docker run -d \ --name kkfileview \ -p 8012:8012 \ --restart unless-stopped \ keking/kkfileview:4.4.0起来之后看日志docker logs -f --tail 200 kkfileview等到日志里出现应用启动完成的字样浏览器打开http://服务器IP:8012/会看到一个自带的测试页面可以直接上传一个 docx 或 pdf 试试。这个页面是官方提供的最快验证手段能省掉你自己拼链接的时间。如果这里就能正常预览说明基础链路Java、LibreOffice、字体、渲染全通了剩下的都是配置和对接问题。3.2 Compose 版本我更推荐这个命令行跑一次没问题但生产环境我更愿意用 compose 管起来配置和挂载都写在文件里换机器时复制一份就能重建。这是我实际在用的模板services: kkfileview: image: keking/kkfileview:4.4.0 container_name: kkfileview restart: unless-stopped ports: - 8012:8012 volumes: - ./config/application.properties:/opt/kkFileView-4.4.0/config/application.properties - ./cache:/opt/kkFileView-4.4.0/file - ./logs:/opt/kkFileView-4.4.0/log environment: - TZAsia/Shanghai - LANGzh_CN.UTF-8 deploy: resources: limits: memory: 4g healthcheck: test: [CMD-SHELL, curl -fs http://localhost:8012/ /dev/null || exit 1] interval: 30s timeout: 5s retries: 3 start_period: 120s几个细节值得说。TZ和LANG一定要设不设的话日志时间对不上中文文件名在极端情况下也可能出问题。start_period给到 120 秒因为 LibreOffice 拉起和 Java 初始化都需要时间健康检查设得太急会导致容器反复被判定不健康。内存限制我按 4G 设这既是保护容器也是保护宿主机——万一某个文档把 LibreOffice 的内存吃爆至少不会把整台机器拖死。挂载缓存目录那行路径要跟你容器内实际情况对齐。如果你不确定先跑 3.1 那个裸容器docker exec -it kkfileview ls /opt看一眼真实目录名再回来改别照抄。3.3 验证三类文件各测一遍只测一个 docx 是不够的。我上线前的验证清单固定是这个顺序一个简单 docx验证 Office 转译链路。一个带中文、带表格的 xlsx验证字体和排版。一个几十页的 pdf验证大文件和分页渲染。一个文件名带空格和中文的图片验证 URL 编码。第 4 条特别关键因为预览 URL 里的文件地址要经过编码空格、、、中文这些字符处理不当就会 404 或者预览空白。这个坑我在第 5 节会专门给代码。3.4 关于配置不生效这件事改完配置要重启容器这一点大家都知道。但有个更隐蔽的问题配置文件挂载成功了内容也改了重启后却没生效。原因通常是格式问题——properties 文件里键值之间的等号两侧别随便加空格不要用 Tab注释用#中文值在某些版本里需要转义或者干脆避免。另一个常见原因是有些配置项在启动时被硬编码覆盖了这种情况你改配置没用得通过环境变量或者换镜像版本解决。所以我的习惯是改完配置先docker exec进去cat一遍确认容器里看到的就是我改的那份再重启验证。4. 配置项拆解把默认值改成能上生产的值默认配置是能跑起来级别的直接上生产会出问题。这一节挑我必改的几组配置说每组都说清楚为什么改、改成什么、改了会带来什么副作用。4.1 缓存与清理不配这个迟早磁盘满默认会缓存转换结果这是好事第二次打开同一个文件几乎秒开。但默认的缓存保留期往往偏长日积月累就是磁盘告警。我一般把保留期设成 7 到 15 天具体看业务如果同一批文件会被反复查看就设长一点如果是上传就看一次的场景三天足够。同时必须打开定时清理。清理任务建议放在凌晨低峰期Cron 表达式类似0 0 3 * * ?每天三点跑一次。这里有个实测经验清理任务跑的时候会扫目录如果缓存目录里文件数量到了百万级扫描本身就会拖慢磁盘 IO所以缓存目录一定要和其他服务分开别把日志、数据库文件放在同一块盘上。注意清缓存只能治标。如果你的业务存在大量一次性文件预览完就再也不会看更彻底的做法是业务侧在上传时就生成预览并把结果保存下来或者干脆用对象存储的生命周期策略去删源文件从源头减少缓存量。4.2 Office 转图还是转 PDF这是影响体验最大的一个开关。转图片的意思是每页渲染成一张图前端翻页看转 PDF 是整篇合成一个 PDF交给 PDF 渲染器展示。转图片的优势是移动端体验稳定缩放、双指放大都是原生图片行为不容易出兼容问题缺点是页数多的时候请求数多且文字不可选中、不可搜索。转 PDF 的优势是文件小、可搜索、可复制文字适合文档类内容缺点是对渲染器依赖更强超大 PDF 在低端手机上可能卡顿。我的选择逻辑是内部系统、附件为主、移动端访问多选转图片对外文档、报告类、需要复制文字选 PDF。如果业务两种都有可以按文件类型分流或者干脆两个都试一遍让业务方拍板——这个决定权最好别自己扛。4.3 字体和水印中文乱码的根因中文显示成方框几乎是每个用 LibreOffice 做转换的人都遇到过的。原因是容器里没装中文字体LibreOffice 找不到字形就画方块。镜像一般会带一部分字体但不会覆盖所有中文字体尤其是某些公文常用的宋体、仿宋、楷体。解决办法是把宿主机上合法的字体文件复制进容器的字体目录然后重建字体缓存、重启容器。我这边的做法是准备一个字体目录把需要的字体文件放进去用挂载的方式映射到容器内对应的字体路径再执行一次字体缓存刷新。这里必须提醒一句字体是有版权的商用字体要确认授权别为了排版好看随手把一堆来源不明的字体扔进生产环境这是实打实的法律风险。水印是另一个常被要求的配置。默认无水印。一旦打开预览页面会铺上一层文字用于内部资料防外传。水印的内容、字号、颜色、透明度、倾斜角度一般都能配。我的经验是透明度别调太高否则会盖住正文影响阅读也别太低太低截图之后看不见等于没加。4.4 并发、超时和 JVM 参数并发这块要理解它的模型转换是串行排队到 Office 进程上去执行的进程数决定了并行度。默认起两个进程意味着同一时刻最多两个文档在转换第三个请求要么排队要么失败取决于配置。小团队够用日活上千的系统建议加到四个但每加一个进程内存基线就要多留 300 到 500 MB这笔账要算清楚。超时时间建议按业务的最大文档来定。默认值对付普通文档没问题但一个 50 页带大量图片的 ppt转换可能要二三十秒超时设短了就会看到预览失败但日志里其实转换还在跑用户重试一次又起一个转换任务进程越堆越多。这个问题我在第 6 节会讲怎么善后。JVM 参数一般不用大动但如果你的容器内存限制设得比较紧建议显式指定堆大小比如限制 2G 内存的容器给 JVM 分 1G 堆剩下的留给 LibreOffice 和系统缓存。不指定的话 JVM 可能按宿主机总内存去推算容器一超限就被 OOM 干掉日志里连个像样的错误都没有非常难查。4.5 安全项这几个开关必须拧紧预览服务本质上是你给它一个 URL它去下载并解析这在安全上天然是个 SSRF 风险点。如果你的服务部署在内网攻击者构造一个指向内网管理后台或者元数据地址的 url 参数就能借你的服务去探测内网。所以用域名或主机白名单限制可预览的文件来源只允许业务系统的文件服务域名其他一律拒绝。较新版本提供了类似trust.host的白名单配置如果你的版本没有这个键那就在 Nginx 或者网关层做来源校验绝不能裸奔。用扩展名黑名单挡掉危险类型。html、htm、svg 这类能执行脚本的格式默认就不该让它在预览域名下渲染否则等于开了个 XSS 通道。预览服务不要和后台管理系统同域。最稳妥的是放在独立子域即便出问题也不会牵连主站 Cookie。对外网开放时在网关层加签名鉴权让 url 参数带时效签名避免别人拿你的服务当免费的文档转译接口刷。注意白名单配置改完一定要用内网地址实测一次确认真的被拦住了。我见过配了白名单但因为格式写错比如协议头大小写、带端口没带端口导致规则没生效的情况配置看着对实际没拦住。5. 反向代理与业务系统对接服务跑通、配置调好接下来是把链接拼出来给业务用。这一步的坑比部署还多因为涉及编码、域名、跨域。5.1 Nginx 反代配置我通常让预览服务挂在一个独立子域下Nginx 做反代。核心配置大概是这个样子server { listen 443 ssl; server_name preview.example.internal; client_max_body_size 200m; location / { proxy_pass http://127.0.0.1:8012; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 10s; proxy_send_timeout 300s; proxy_read_timeout 300s; proxy_buffering off; } }两个参数值得单独说明。proxy_read_timeout必须放大默认 60 秒大文档转换超过这个时间 Nginx 会先断开用户看到的是 504但后端其实还在转白白浪费资源。proxy_buffering off是为了让流式返回的响应不被缓冲对于 PDF 这种边生成边返回的内容关掉缓冲体验更顺。如果你要把预览服务放在业务域名的子路径下比如/preview/那就要同时改应用上下文路径和 Nginx 的转发规则两边必须一致否则静态资源路径会错。这一块最容易出问题我的建议是能用独立子域就别用子路径省一半的事。5.2 URL 拼装编码错一个字就 404这是对接环节最高频的故障点。预览接口的文件地址参数需要做编码处理具体规则各版本略有差异但通用的做法是先对文件完整 URL 做一次 Base64再做一次 URL 编码。Java 版本import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; public String buildPreviewUrl(String fileUrl, String previewHost) throws Exception { String base64 Base64.getEncoder() .encodeToString(fileUrl.getBytes(StandardCharsets.UTF_8)); // URLEncoder 会把空格编成 必须替换回 %20 String encoded URLEncoder.encode(base64, UTF-8).replace(, %20); return previewHost /onlinePreview?url encoded; }前端 JavaScript 版本function buildPreviewUrl(fileUrl, previewHost) { // 注意要正确处理中文先把字符串转成 UTF-8 字节 const bytes new TextEncoder().encode(fileUrl); let binary ; bytes.forEach(b binary String.fromCharCode(b)); const base64 btoa(binary); return previewHost /onlinePreview?url encodeURIComponent(base64); }Python 版本import base64 from urllib.parse import quote def build_preview_url(file_url: str, preview_host: str) - str: b64 base64.b64encode(file_url.encode(utf-8)).decode(ascii) return f{preview_host}/onlinePreview?url{quote(b64, safe)}三个版本都要注意同一件事Base64 之后再做 URL 编码顺序不能反。反过来做Base64 里的和/会被当成特殊字符服务端解出来就是乱码表现就是预览一片空白或者文件不存在。另外前端用btoa时如果直接传中文字符串会抛异常必须先转字节这是很多人卡住的地方。还有一个参数值得配上如果文件 URL 里没有扩展名比如对象存储的 key 是一串哈希kkFileView 就判断不出格式需要额外传一个文件名参数让它知道该走哪条渲染链路。这个参数在对接对象存储时几乎必用。5.3 嵌入方式与移动端前端嵌入最省事的是 iframe给一个固定宽高就行。但有两个细节要注意一是预览服务返回的响应头里如果有X-Frame-Options: DENYiframe 会直接白屏需要在 Nginx 里放开或者干脆不设二是移动端 iframe 的高度要用视口单位自适应写死像素高度在小屏上会只露半页。如果业务要求点开全屏看更稳的做法是新开一个页面直达预览链接而不是在弹窗里塞 iframe尤其是 iOS 上弹窗内的 iframe 滚动行为经常很别扭。这个取舍我一般是两个都做列表页给缩略预览详情页给全屏跳转。6. 排障实录那些日志里不会直接告诉你的答案这一节是我这几年攒下来的问题清单按现象查最快。6.1 常见问题速查表现象大概率原因处理方向页面空白控制台无报错url 参数编码错误Base64 与 URL 编码顺序反了用工具手动解一次看能否还原出正确地址提示文件不存在预览服务访问不到文件地址网络不通或无权限进容器 curl 一下文件 URL中文变方框容器缺中文字体挂载字体并刷新缓存后重启Office 转不出来其他格式正常容器内 Office 进程没起来或已崩溃看日志开头的 Office 启动输出大文件转半天然后失败超时时间太短调大反代和应用超时偶发 502转换把内存打满容器被 OOM看 docker inspect 的重启次数和退出码同一个文件第二次打开还是很慢缓存没生效或缓存目录没持久化确认缓存目录挂载正确、有写权限部分文件预览样式错乱字体缺失或 LibreOffice 版本渲染差异补字体极端情况下换镜像版本试6.2 中文变方框的完整处理路径这个问题的处理流程我整理成了固定动作。第一步进容器执行字体列表命令看有没有中文字体没有就说明是缺字体。第二步把字体文件放进容器的字体目录注意权限要可读。第三步刷新字体缓存这一步很多人会漏不刷新的话新放进去的字体不生效。第四步重启容器因为 LibreOffice 进程在启动时加载字体不重启看不到变化。第五步拿一个字体最全的 docx 复测。还有一个隐蔽情况字体装了但文档里用的是某个特定字重的变体比如细体、粗体而容器里只有常规体LibreOffice 会用常规体替代看起来能显示但不像原稿。这种情况只能把对应字重也补上或者接受替代效果。6.3 转换卡死和进程堆积这是最恶心的一类问题。表现是某几个文件一直转不出来CPU 占用高但不下降重启容器后短暂恢复过一阵又出现。根因通常是某个畸形文档让 LibreOffice 进程卡在了死循环或者弹窗等待状态比如文档里有需要确认的宏提示。排查手段是进容器看进程列表数一下 Office 进程有几个正常情况下应该等于配置的进程数多出来的就是僵尸。短期处理是重启容器长期处理有三个方向一是给转换加硬超时超时直接杀进程重建二是把这类问题文件单独识别出来在业务侧拒绝上传或者标记为不支持预览三是升级镜像版本LibreOffice 的健壮性在各版本之间差异挺大我遇到过某个版本对特定老 doc 处理有 bug换个版本就好了。6.4 大文件和并发打满一个 100 MB 的 ppt 能让整个服务卡住十几秒这期间其他请求全在排队。缓解办法有几个层次业务侧限制上传大小和页数这是最有效的预览侧提高并发进程数但受内存限制再加一层就是给预览请求做队列和限流超过阈值直接返回正在处理请稍后而不是让用户干等然后超时。我实际做的组合是业务侧限制单个附件 50 MB 以内、页数 200 页以内预览侧起三个转换进程网关层对预览接口做并发限流。这套组合下来日常使用基本感知不到排队。6.5 日志在哪看看什么应用日志和转换日志是分开的。应用日志告诉你请求进来了、参数是什么、走了哪条链路转换日志告诉你 LibreOffice 那边发生了什么。排障顺序应该是先看应用日志确认参数正确再看转换日志确认渲染阶段有没有异常。我见过不少情况是参数就错了却一直在转换日志里找原因方向完全跑偏。日志级别默认是信息级排查时可以临时调到调试级能看到更细的中间过程但记得事后调回来调试日志量很大磁盘吃不消。7. 上线之后从能用到扛得住跑通只是开始真正考验人的是上线之后。这一节说几个我在长期运行中总结出来的东西。7.1 缓存命中率比什么都重要预览服务的性能瓶颈几乎全在第一次转换。如果你的业务里同一个文件会被多人反复查看这在工单、审批类系统里非常普遍那缓存命中率就是核心指标。观察方法很简单看日志里同一个文件短时间内有没有重复触发转换。提高命中率的做法确保缓存目录持久化且容量足够缓存保留期按业务访问规律设定别设太短如果业务侧能在文件上传时就触发一次预转换比如上传成功后异步请求一次预览接口用户第一次点开就是秒开体验提升非常明显。这个预热动作成本很低收益很高我基本每个项目都会加。7.2 多副本部署的坑单机扛不住要扩副本的时候有个问题必须先想清楚缓存是本地磁盘的。两个副本各自缓存各自的用户第一次请求打到 A第二次打到 B还是要重新转一遍缓存等于白做。解决办法有两条一是让缓存落到共享存储上但共享存储的 IO 延迟会让转换变慢需要实测二是用支持集中式缓存的配置较新版本提供了对接 Redis 一类的选项具体以你镜像支持的配置为准或者干脆在网关层做会话粘性让同一个文件地址的请求固定落到同一个副本。我倾向于后者实现简单副作用小。另外多副本时转换进程数要按副本数重新算内存账别一个副本 4G 内存 × 4 个副本把宿主机撑爆。7.3 该监控哪些指标不用搞得很复杂四个指标够用容器内存使用率防止 OOM、转换平均耗时感知性能退化、预览请求错误率感知故障、缓存目录占用防止磁盘满。前三个用常规的容器监控就能拿到第四个可以写个简单的定时脚本上报。我还会额外盯一个Office 进程数量超过预期值就告警这能提前发现进程堆积比等到用户投诉要早得多。7.4 一个容易被忽略的稳定性细节容器重启策略一定要设成unless-stopped而不是always。区别在于你手动停机做维护的时候always会跟你对着干把容器又拉起来unless-stopped则尊重你的手动操作。这个细节在半夜做紧急维护的时候特别重要能省不少骂。另外一个实践是给容器设日志轮转Docker 默认的 json-file 日志驱动不做限制的话一个日志文件能涨到几十 GB直接把磁盘吃满而且这种故障表现得非常诡异——服务本身好好的但突然所有写入都失败。在 compose 里加上日志大小和文件数限制两行配置的事。最后分享一个我在实际项目里反复验证过的小技巧把预览服务和文件存储放在同一个内网网段。原因很实际预览服务每次转换都要去拉一遍源文件如果文件存储在外网或者跨机房光下载就占掉大半时间而且大文件下载超时会导致转换直接失败。同网段之后这部分延迟基本可以忽略实测下来转换耗时的波动小了很多。至于怎么让业务侧的文件地址在内网可达、外网又不可达那属于网络规划的事提前跟运维对一次比事后改架构省事得多。踩过几次坑之后我的感受是这类看起来只是部署一个开源工具的活儿真正花时间的从来不是部署本身而是编码细节、资源估算和故障兜底。工具帮你把最脏的那部分封装好了剩下的判断题还得自己做。