在 Kubernetes 上以 Helm + GitOps 方式部署 BentoPDF:Ingress、Gateway API 与 SharedArrayBuffer 头部治理实战 在 Kubernetes 上以 Helm GitOps 方式部署 BentoPDFIngress、Gateway API 与 SharedArrayBuffer 头部治理实战【免费下载链接】bentopdfThe Privacy First PDF Toolkit项目地址: https://gitcode.com/gh_mirrors/be/bentopdfBentoPDF 是一个“隐私优先”Privacy First的纯前端 PDF 工具箱所有 PDF 处理都在用户浏览器本地完成。本文基于仓库中 docs/self-hosting/kubernetes.md 与仓库自带的 Helm Chartchart/系统讲解如何在 Kubernetes 集群中用 Helm 部署 BentoPDF、通过 Ingress / Gateway API 暴露服务并重点解决两类在边缘代理层极易踩坑的问题SharedArrayBuffer必需的安全响应头COOP/COEP被覆盖以及.mjs模块脚本被错误下发为application/octet-streamMIME 类型导致 Sign PDF / Form Filler 页面空白。读完本文你将掌握一套可复制、可验证的 Kubernetes 部署与排障流程。为什么在 Kubernetes 上部署一个“静态站点”仍有价值BentoPDF 的构建产物是一组纯静态资源HTML/JS/WASM由 nginx 提供托管本身无服务端业务逻辑。对个人站点而言Kubernetes 确实显得“杀鸡用牛刀”但如果你所在团队已经标准化了Helm GitOps如 Argo CD / Flux的工作流将 BentoPDF 纳入同一套交付管道可以免费获得声明式配置、版本化回滚、统一的服务发现Service、探针与安全上下文管理收益是明确的。从仓库的 chart/Chart.yaml 可以看到官方 Chart 描述为 “BentoPDF static frontend served by NGINX”当前 chart 版本1.0.7对应应用版本2.8.8。它只会生成一个 Deployment、一个 ClusterIP Service以及可选的 Ingress / Gateway HTTPRoute——结构非常简单适合直接并入已有的 GitOps 仓库。前置条件一个可用的 Kubernetes 集群任意发行版均可Helm v3一个 BentoPDF nginx 镜像例如ghcr.io/alam00000/bentopdf:tag容器内监听8080端口。镜像端口这一点需要特别强调Chart 的 values.yaml 中containerPort: 8080与service.port: 80是两个不同的值——Pod 容器监听 8080而 Service 对外暴露 80 并targetPort: http转发到容器端口见 service.yaml。官方 nginx 镜像的默认监听端口就是 8080见仓库根目录 nginx.conf 中的listen 8080;这也是文档中port-forward使用8080:8080的原因。用 Helm 部署 BentoPDF方式一从本仓库安装本地 Chart将仓库克隆到本地后Chart 位于chart/目录kubectl create namespace bentopdf helm upgrade --install bentopdf /path/to/bentopdf/chart \ --namespace bentopdf \ --set image.repositoryghcr.io/alam00000/bentopdf \ --set image.taglatest--namespace bentopdf指定部署命名空间image.repository/image.tag覆盖 values.yaml 中的默认镜像默认仓库是ghcr.io/alam00000/bentopdf-simpletag 为空时会回退到appVersion见 deployment.yaml 中image.tag | default .Chart.AppVersion的写法。方式二从 GHCR 安装 OCI Chart如果 Chart 已作为 OCI 制品发布到 GHCRexport GHCR_USERNAMEgithub-org-or-user helm upgrade --install bentopdf oci://ghcr.io/$GHCR_USERNAME/charts/bentopdf \ --namespace bentopdf \ --create-namespace \ --version 0.1.0 \ --set image.repositoryghcr.io/alam00000/bentopdf \ --set image.taglatest与方式一相比这里用--create-namespace自动创建命名空间并用--version锁定 Chart 版本号适合在 CI 或 GitOps 流水线中声明式执行。两种方式殊途同归最终都会创建一个名为bentopdf的 Deployment默认replicaCount: 1。Chart 默认值与值得关注的基线配置values.yaml 中除镜像外还有几项与生产部署强相关的默认值探针livenessProbe与readinessProbe均以 HTTP GET/探测端口httpKubernetes 会据此做就绪/存活管理安全上下文podSecurityContext默认runAsNonRoot: true、runAsUser/runAsGroup/fsGroup: 101容器securityContext默认allowPrivilegeEscalation: false、readOnlyRootFilesystem: true并drop: [ALL]、启用seccompProfile: RuntimeDefault。这与官方镜像使用非特权 nginxUID 101的定位一致可对照 docs/self-hosting/docker.md 中关于nginx-unprivileged的说明只读根文件系统由于readOnlyRootFilesystem: truedeployment.yaml 为 nginx 挂载了三个emptyDir卷/etc/nginx/tmp、/var/cache/nginx、/var/run用于写入 pid、缓存与临时文件。暴露服务Port-Forward、Ingress 与 Gateway APIPort-Forward快速验证部署完成后最快捷的验证方式是端口转发直接把本地 8080 映射到 Deploymentkubectl -n bentopdf port-forward deploy/bentopdf 8080:8080随后访问http://localhost:8080即可。此方式仅适合开发与临时验证不承载生产流量。Ingress可选在 values.yaml 中开启 Ingress 并配置域名以 nginx-ingress 为例ingress: enabled: true className: nginx hosts: - host: pdf.example.com paths: - path: / pathType: Prefix模板 ingress.yaml 会生成标准networking.k8s.io/v1Ingress 资源className写入ingressClassNamehosts[].paths渲染为 rule后端指向service.port即 Service 的 80 端口。如需 TLS可在ingress.tls中声明hosts与secretName。Gateway API可选该 Chart 同时支持 Gateway API 的GatewayHTTPRoute两种资源。以 Cloudflare Gateway API operator 为例gateway: enabled: true name: bento-tunnel namespace: bentopdf gatewayClassName: cloudflare httpRoute: enabled: true parentRefs: - name: bento-tunnel namespace: bentopdf sectionName: http hostnames: - pdfs.example.com模板层面的两个细节值得注意见 gateway.yaml 与 httproute.yamlgateway.enabledtrue时gatewayClassName为必填模板使用required函数强制校验缺省会直接渲染失败httpRoute.parentRefs可省略为空时模板自动引用同命名空间下的{gateway.name 或 release-name}-gateway、sectionName: http的默认父网关。默认listeners监听 HTTP 80 端口并允许同命名空间的 Route 接入allowedRoutes.namespaces.from: Same。让 LibreOffice 工具正常工作COOP/COEP 头部的“边缘”治理为什么这两个响应头如此关键BentoPDF 的 Word / Excel / PowerPoint 转换工具基于 LibreOffice WASM 运行时该运行时在浏览器中依赖SharedArrayBuffer而启用它需要页面满足跨源隔离cross-origin isolation条件。因此官方 nginx 镜像在响应中注入了两个安全头见 nginx.conf 中include /etc/nginx/security-headers.conf;Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp[!IMPORTANT] 在 Kubernetes 场景下Ingress / Gateway 控制器本身也是反向代理。BentoPDF 的 nginx 虽然正确设置了这些头部但绝大多数控制器会原样透传上游响应头——前提是没有任何边缘策略覆盖或剥离它们。如果控制台出现SharedArrayBuffer is not defined或 Office 转换工具白屏优先怀疑头部被边缘层改写。验证头部是否被保留对公网端点发起 HEAD 请求并过滤这两个头curl -I https://pdf.example.com/ | egrep -i cross-origin-opener-policy|cross-origin-embedder-policy期望输出Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp若缺失或被替换为其他值说明控制器/边缘策略动了手脚——典型元凶是“安全头中间件”统一设置了不同的 COOP/COEP 值。修复方案一在 nginx-ingress 上强制追加如果控制器不透传就在 Ingress 层用 annotation 补上nginx-ingress 支持configuration-snippetingress: enabled: true className: nginx annotations: nginx.ingress.kubernetes.io/configuration-snippet: | add_header Cross-Origin-Opener-Policy same-origin always; add_header Cross-Origin-Embedder-Policy require-corp always;always参数确保即使在 4xx/5xx 响应上也会附加头部。修复方案二用 Gateway API 的 ResponseHeaderModifier 过滤器Gateway API 提供了标准的ResponseHeaderModifier过滤器可直接挂在httpRoute.rules[*].filters上httpRoute: enabled: true hostnames: [pdf.example.com] parentRefs: - name: bento-tunnel namespace: misc sectionName: http rules: - matches: - path: { type: PathPrefix, value: / } filters: - type: ResponseHeaderModifier responseHeaderModifier: set: - name: Cross-Origin-Opener-Policy value: same-origin - name: Cross-Origin-Embedder-Policy value: require-corp从 httproute.yaml 的实现看rules[].filters会被toYaml原样渲染进 HTTPRoute因此上面的配置可以完整落到集群资源上。注意具体过滤器是否生效取决于 Gateway 控制器实现如果过滤器被静默忽略就退回方案一在边缘/控制器层追加头部。修复.mjsMIME 类型错误Sign PDF / Form Filler 白屏现象与根因浏览器控制台出现Failed to load module script: ... non-JavaScript MIME type application/octet-stream说明.mjs请求返回的Content-Type被前置控制器替换成了application/octet-stream。而 BentoPDF 镜像内的 nginx 已经为.mjs配置了正确的 MIME 类型——在 nginx.conf 的types块中显式声明了application/javascript mjs;。问题不在上游而在控制器剥离/覆盖了上游的Content-Type。修复方案一nginx-ingress 的 configuration-snippetingress: annotations: nginx.ingress.kubernetes.io/configuration-snippet: | location ~* \.mjs$ { types {} default_type application/javascript; }types {}清空继承的 MIME 映射default_type application/javascript让该 location 下的.mjs统一按 JavaScript 返回。修复方案二Gateway API 的 ResponseHeaderModifier同样可用ResponseHeaderModifier过滤器为.mjs路径设置Content-Type: application/javascript配置结构与前面 COOP/COEP 的例子一致仅需把set项换成Content-Type。验证打开 DevTools → Network 面板重新加载页面并定位之前失败的.mjs请求确认响应头现在是content-type: application/javascript即可。此问题一旦修复Sign PDF 与 Form Filler 页面中的 PDF.js 模块脚本即可正常加载。通过 ConfigMap 运行时禁用指定工具不需要重建镜像即可通过挂载config.json在运行时禁用若干工具——这对应仓库中的运行时配置机制前端通过fetch(config.json)动态加载见 src/js/utils/disabled-tools.ts。先创建 ConfigMapapiVersion: v1 kind: ConfigMap metadata: name: bentopdf-config namespace: bentopdf data: config.json: | { disabledTools: [edit-pdf, sign-pdf, encrypt-pdf] }再将其以subPath方式挂载到 nginx 的服务目录spec: containers: - name: bentopdf volumeMounts: - name: config mountPath: /usr/share/nginx/html/config.json subPath: config.json readOnly: true volumes: - name: config configMap: name: bentopdf-config工具 ID 的确定方法与生效范围工具 ID 就是页面 URL 去掉.html后的部分——打开任意工具地址栏路径的最后一段即为 ID例如edit-pdf、merge-pdf、compress-pdf。完整工具 ID 清单可参考 docs/self-hosting/docker.md 中的 “Disabling Specific Tools” 一节。从 src/js/utils/disabled-tools.ts 的源码可以看到三个与运维相关的细节ID 归一化代码内置了RENAMED_TOOL_IDS映射decrypt-pdf → unlock-pdf、encrypt-pdf → protect-pdf、pdf-to-docx → pdf-to-word旧 ID 会被自动归一化到新 ID因此按文档示例写encrypt-pdf也能正确命中protect-pdf配置合并构建期编译进 bundle 的__DISABLED_TOOLS__与运行时config.json的disabledTools会合并到同一个Set中对应 docs/self-hosting/docker.md 中“两种方式可组合、取并集”的说明生效范围被禁用的工具会从首页、搜索、快捷键、工作流构建器中隐藏并且直接访问 URL 也会被拦截——isCurrentPageDisabled()会结合当前路径判断并展示“工具不可用”页面。运行时配置通过cache: no-cache拉取避免浏览器缓存导致配置不生效。配置类型定义见 src/js/types/config-types.tsAppConfig.disabledTools?: string[]。小结与自检清单把 BentoPDF 部署上 Kubernetes 并稳定对外服务核心动作可以收敛为一份检查清单部署用本地 Chart 或 GHCR OCI Chart 完成helm upgrade --install确认镜像监听 8080、Service 端口映射正确暴露按需选择 Ingress 或 Gateway API注意gatewayClassName必填、parentRefs可省略的模板行为头部验证对公网端点执行curl -I ... | egrep -i cross-origin-opener-policy|cross-origin-embedder-policy确认same-origin与require-corp原样透传MIME 验证DevTools Network 中确认.mjs响应为application/javascript避免 Sign PDF / Form Filler 白屏工具治理需要合规裁剪时用 ConfigMap 挂载config.json的disabledTools免重建镜像即生效。每一步都有仓库内的实现作为依据Chart 模板在 chart/templates/nginx 头部与 MIME 配置在 nginx.conf运行时禁用逻辑在 src/js/utils/disabled-tools.ts。按上述清单操作即可让 BentoPDF 稳定运行在标准化的 Helm GitOps 交付体系内。【免费下载链接】bentopdfThe Privacy First PDF Toolkit项目地址: https://gitcode.com/gh_mirrors/be/bentopdf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考