Penpot 导出 PDF 报错 —— 问题排查与解决记录

Penpot 导出 PDF 报错 ------ 问题排查与解决记录

日期:2026-08-24 | 服务器:192.168.36.136 | Penpot 版本:2.17.1


一、环境信息

项目 说明
服务器 192.168.36.136(Ubuntu 24.04,VMware 虚拟机)
硬件资源 2 核 CPU / 1.9GB 内存(低配,是本问题的关键背景)
部署方式 Docker Compose,目录 /data/penpot
组件版本 backend / frontend / exporter:latest(实际 2.17.1);MCP:2.17.1
容器清单 penpot-backend、penpot-frontend、penpot-exporter、penpot-postgres、penpot-redis、penpot-assets(MinIO)、penpot-mcp(共 7 个)
访问地址 http://192.168.36.136:9001

二、问题现象

前端访问 http://192.168.36.136:9001,在画布中导出 PDF 报错,导出失败。


三、排查过程

3.1 第一步:确认服务整体状态

bash 复制代码
systemctl --failed --no-pager          # 无失败单元
docker ps -a                           # 7 个容器全部 Up,postgres healthy
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9001/   # 200

结论:服务本身都在运行,问题出在导出链路内部。

3.2 第二步:定位第一个错误 ------ S3 存储配置(旧格式)

后端日志(penpot-backend):

复制代码
E app.http.errors - hint="internal error"
clojure.lang.ExceptionInfo: backend ':s3' not configured (impl.clj:212)
    app.rpc.management.exporter$sm$upload_tempfile.invokeStatic(exporter.clj:45)

W app.storage - hint="using deprecated configuration, please read 2.11 release notes"

exporter 日志(penpot-exporter):

复制代码
ERR [app.handlers.export-frames] hint="unexpected error on frames exportation"
  Data: {:type :internal, :code :unable-to-upload-resource, :response-status 500}

关键判断 :PDF 实际渲染成功 了(pdfunite 已合并 5 个分页),只是最后一步把导出结果上传到对象存储时失败(500 unable-to-upload-resource)。

根因 1 :存储配置使用的是 Penpot 2.11 之前的旧变量名/取值,新版后端无法识别:

旧格式(2.11 前) 新格式(2.11+)
PENPOT_ASSETS_STORAGE_BACKEND: "assets-s3" PENPOT_OBJECTS_STORAGE_BACKEND: "s3"
PENPOT_ASSETS_S3_BUCKET PENPOT_OBJECTS_STORAGE_S3_BUCKET
PENPOT_ASSETS_S3_REGION PENPOT_OBJECTS_STORAGE_S3_REGION
PENPOT_ASSETS_S3_ENDPOINT PENPOT_OBJECTS_STORAGE_S3_ENDPOINT
PENPOT_ASSETS_S3_ACCESS_KEY_ID AWS_ACCESS_KEY_ID
PENPOT_ASSETS_S3_SECRET_ACCESS_KEY AWS_SECRET_ACCESS_KEY

另外,MinIO(penpot-assets)中根本没有 assets bucket (/data 下只有 .minio.sys)。

修复 1 (/data/penpot/docker-compose.yaml):

bash 复制代码
cd /data/penpot
cp docker-compose.yaml docker-compose.yaml.bak   # 备份

# 将 6 项存储变量批量替换为新格式
sudo sed -i \
 -e 's/PENPOT_ASSETS_STORAGE_BACKEND: "assets-s3"/PENPOT_OBJECTS_STORAGE_BACKEND: "s3"/' \
 -e 's/PENPOT_ASSETS_S3_BUCKET/PENPOT_OBJECTS_STORAGE_S3_BUCKET/' \
 -e 's/PENPOT_ASSETS_S3_REGION/PENPOT_OBJECTS_STORAGE_S3_REGION/' \
 -e 's/PENPOT_ASSETS_S3_ENDPOINT/PENPOT_OBJECTS_STORAGE_S3_ENDPOINT/' \
 -e 's/PENPOT_ASSETS_S3_ACCESS_KEY_ID/AWS_ACCESS_KEY_ID/' \
 -e 's/PENPOT_ASSETS_S3_SECRET_ACCESS_KEY/AWS_SECRET_ACCESS_KEY/' \
 docker-compose.yaml

# 创建 MinIO bucket(用 mc 客户端容器)
sudo docker run --rm --network penpot_penpot --entrypoint sh minio/mc -c \
  'mc alias set local http://penpot-assets:9000 penpot penpot123 && mc mb --ignore-existing local/assets'

# 重启后端应用新配置
sudo docker compose up -d

验证 1 :后端重启后日志不再出现 backend ':s3' not configured 与弃用警告,存储问题解决。

3.3 第三步:定位第二个错误 ------ exporter 渲染超时(10 秒硬编码)

存储修复后仍然失败,此时错误变了:

复制代码
ERR [app.handlers.export-frames] hint="unexpected error on frames exportation"
  locator.waitFor: Timeout 10000ms exceeded.
  Call log:
    - waiting for locator('#screenshot-...') to be visible
ResourceRequest timed out

排查手段与关键结论:

  1. 用 exporter 容器内的 Playwright 手动加载渲染页(复现渲染过程):

    • 发现 /js/config-render.js 返回 404 (镜像中缺失,只有 config.js)------经检查 render.js 并不引用它,非致命,仅告警;
    • 发现 API 返回 401 ------ 这是测试脚本未注入认证 cookie 导致的误判 (真实 exporter 会注入 auth-token cookie)。
  2. 看前端 nginx 访问日志(真实 exporter 的请求):

    • get-page、get-font-variants 均返回 200(认证正常,数据正常下发);
    • 但单个对象从打开 render.html 到出结果需 8~15 秒 ,超过 exporter 等待上限 10 秒,最后一个请求以 499(客户端超时关闭)结束。
  3. 反编译 exporter 源码 (/opt/penpot/exporter/app.js,ClojureScript 打包产物):

    • app.browser.wait_for 超时默认值 10000 硬编码,无环境变量可配置;
    • 渲染等待 #screenshot-<id> 元素出现时只传节点、未传超时 → 使用默认 10 秒。

根因 2 :服务器仅 2 核 CPU / 1.9GB 内存,Chromium + WASM 渲染单对象需要 8~15 秒以上(内存吃紧时 swap 压力大),超过 exporter 硬编码的 10 秒超时 → 每次导出必失败。

修复 2(把渲染等待超时 10s → 60s,并持久化):

bash 复制代码
# 1) 修改 exporter 容器内的 app.js(精确替换唯一匹配点)
sudo docker exec penpot-penpot-exporter-1 sh -c \
  "sed -i 's/cljs\$cst\$1307\$timeout, 10000/cljs\$cst\$1307\$timeout, 60000/' /opt/penpot/exporter/app.js"

# 2) 拷出到宿主机做持久挂载
sudo mkdir -p /data/penpot/exporter-overrides
sudo docker cp penpot-penpot-exporter-1:/opt/penpot/exporter/app.js /data/penpot/exporter-overrides/app.js

# 3) docker-compose.yaml 中 penpot-exporter 服务增加挂载
#    volumes:
#      - ./exporter-overrides/app.js:/opt/penpot/exporter/app.js:ro

# 4) 重启 exporter
sudo docker compose up -d penpot-exporter

# 5) 验证挂载与超时值
grep -c 'timeout, 60000' /data/penpot/exporter-overrides/app.js   # 1
docker inspect penpot-penpot-exporter-1 --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}'

说明:挂载覆盖使修改在容器重建后仍然生效;备份位于容器内 app.js.bak 与宿主机 docker-compose.yaml.bak / .bak2。


四、最终验证结果

时间 事件
07:42:52 → 07:43:17 渲染对象 1(cb97a269)成功,耗时约 25s
07:43:17 → 07:43:34 渲染对象 2 成功,约 17s
07:43:34 → 07:43:51 渲染对象 3 成功
07:43:51 → 07:44:06 渲染对象 4 成功
07:44:06 → 07:44:18 渲染对象 5 成功
07:44:18 pdfunite 合并 5 个分页 PDF 成功,导出完成 ✅

后端与 exporter 日志全程无 error,MinIO assets bucket 已就绪,导出功能恢复正常。


五、根因总结

# 根因 影响 修复
1 存储配置使用 Penpot 2.11 前旧格式(PENPOT_ASSETS_STORAGE_BACKEND=assets-s3),且 MinIO 缺少 assets bucket 渲染成功但上传失败(500 unable-to-upload-resource) 升级为新格式变量 + 创建 bucket + 重启后端
2 服务器低配(2 核 / 1.9GB),WASM 渲染单对象需 8~15s+,超过 exporter 硬编码 10s 超时 渲染被中断,导出超时失败 将 exporter 渲染等待超时改为 60s 并挂载持久化

六、后续建议

  1. 强烈建议给虚拟机增加内存至 4GB+(当前 1.9GB,swap 占用 700MB+,是渲染慢的根源;加内存后 10s 超时大概率够用,可考虑恢复默认)。
  2. 若未使用 MCP 功能,可停用 penpot-mcp 容器,释放少量资源。
  3. 升级镜像或重新部署后,需重新检查 exporter-overrides/app.js 挂载与超时值是否仍然有效。
  4. 保留的备份文件:/data/penpot/docker-compose.yaml.bak、docker-compose.yaml.bak2、容器内 /opt/penpot/exporter/app.js.bak。

七、关键命令速查

bash 复制代码
# 查看所有服务/容器
docker ps -a
docker stats --no-stream

# 后端 / exporter 日志
docker logs --tail 100 penpot-penpot-backend-1
docker logs --tail 100 penpot-penpot-exporter-1
docker logs --since 10m penpot-penpot-frontend-1 | grep -E 'get-page|401|499'

# 检查 MinIO bucket
docker run --rm --network penpot_penpot --entrypoint sh minio/mc -c \
  'mc alias set local http://penpot-assets:9000 penpot penpot123 && mc ls local'

# 验证 exporter 超时值
grep -c 'timeout, 60000' /data/penpot/exporter-overrides/app.js
相关推荐
web打印社区19 小时前
C-Lodop 提示未准备好或 WebSocket 没准备好:先让本机服务起来
javascript·网络·websocket·网络协议·pdf·html
qq_3691736321 小时前
一句话将 Word、PPT、PDF 发布成链接
人工智能·pdf·word·powerpoint·效率工具·ai 办公
泡海椒1 天前
数据占比展示:jquick-pdf饼图动态数据渲染PDF教程
数据库·oracle·pdf
Dovis(誓平步青云)2 天前
多个链接不等于多份证据,新闻核验看板怎样合并来源
java·服务器·前端·javascript·人工智能·pdf·电脑
web打印社区2 天前
Windows 网页静默打印设置步骤:客户端、防火墙与联调清单
开发语言·前端·javascript·chrome·pdf·ecmascript
开开心心就好2 天前
图片白底怎么去掉?抠图工具抠完背景透明
java·服务器·开发语言·pdf·ocr·散列表·启发式算法
web打印社区2 天前
JS 静默打印怎么做:纯前端为什么不行,以及最小可跑通写法
开发语言·前端·javascript·websocket·网络协议·http·pdf
web打印社区2 天前
HTML5 静默打印:前端页面怎么调用打印机且尽量不弹窗
前端·javascript·vue.js·pdf·html·html5
晚来的棠棠2 天前
27考研数学超全资料PDF
考研·pdf
正经教主2 天前
【FDE系列】阶段3:Day 63:文档解析 — 把真实 PDF 手册变成可用文本
人工智能·pdf·fde