图片转换器配置指南

图片转换器是 Node.js 微服务,将 SVG 场地背景渲染为优化的 WebP 图片。每个背景生成三个版本:完整分辨率、1000px 预览,以及带内联 base64 数据的 400px 模糊占位图,用于渐进加载。图片保存到 S3 兼容对象存储或本地文件系统。

技术栈:Node.js 22、Express 5、Puppeteer(无头 Chromium 渲染 SVG)、Sharp(图片处理)、Redis(转换队列)。

核心设置

设置 类型 默认值 说明
PORT integer 3000 Express 监听端口
NODE_ENV string production Node 环境
DEBUG boolean false 启用调试模式,将 LOG_LEVEL 设为 debug
LOG_LEVEL string info Winston 日志级别:error、warn、info、http、verbose、debug

文件处理设置

设置 类型 默认值 说明
UPLOAD_FOLDER string /tmp 上传临时目录,处理后清理
MAX_CONTENT_LENGTH integer 16777216 最大上传字节数,默认 16 MB

图片处理设置

设置 类型 默认值 说明
MAX_IMAGE_WIDTH integer 15000 最大输出宽度,像素
MAX_IMAGE_HEIGHT integer 15000 最大输出高度,像素
DEFAULT_WIDTH integer 440 基础转换端点默认宽度
DEFAULT_HEIGHT integer 246 基础转换端点默认高度
PNG_OPTIMIZATION boolean false 启用图片压缩
PNG_OPTIMIZATION_QUALITY string 0.6-0.8 WebP 输出质量范围

存储设置

设置 类型 默认值 说明
STORAGE_TYPE string s3 存储后端:s3 或 local
LOCAL_STORAGE_PATH string ./storage 本地存储的文件系统路径

AWS S3 配置

STORAGE_TYPE=s3 时需要以下设置。

设置 类型 默认值 说明
AWS_ACCESS_KEY_ID string (必填) S3 身份验证的 IAM 访问密钥
AWS_SECRET_ACCESS_KEY string (必填) S3 身份验证的 IAM 私密密钥
AWS_REGION string us-east-1 存储桶所在 AWS 区域
AWS_BUCKET_NAME string (必填) 图片存储桶名称
AWS_ENDPOINT_URL string MinIO、DigitalOcean Spaces、Garage 等兼容服务的自定义 S3 端点
AWS_PUBLIC_BASE_URL string 覆盖生成图片 URL 的公共基础地址,见下文

重要通过安全环境变量或 Kubernetes Secret 提供 AWS 凭据,不要提交到版本控制。

公共 URL 解析

上传图片的公共 URL 按以下优先级生成:

  1. AWS_PUBLIC_BASE_URL:已设置时原样使用,例如 https://bucket.nyc3.digitaloceanspaces.com。
  2. AWS_ENDPOINT_URL:推导路径形式 {endpoint}/{bucket},例如 https://storage.yandexcloud.net/my-bucket。
  3. 默认:AWS 虚拟主机形式 https://{bucket}.s3.{region}.amazonaws.com。

使用 DigitalOcean Spaces、MinIO、Garage、Yandex Cloud 等非 AWS 提供方的自托管客户,应设置 AWS_PUBLIC_BASE_URL,确保公共 URL 正确。

S3 存储桶策略

允许公开读取图片的策略:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}

CORS 配置

为避免浏览器处理跨域图片时画布被污染,请配置存储桶 CORS:

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET"],
    "AllowedOrigins": ["*"],
    "ExposeHeaders": []
  }
]

注意生产环境请将 AllowedOrigins 限制为具体域名,不要使用 *。

Redis 设置

REDIS_ENABLED=true 时(Helm 默认)转换进入 Redis 队列。每个请求记录为任务,GPU 转换器优先领取,CPU 等待 CPU_CLAIM_DELAY_MS 后领取;工作进程渲染期间失效的任务返回队列重试。false 时在请求内渲染,无需 Redis。Redis 已启用但无法连接时也在请求内渲染,GET /jobs/{id} 返回 503,并记录连接错误;Redis 恢复后自动开始排队。

设置 类型 默认值 说明
REDIS_ENABLED boolean false 在 Redis 排队,而非请求内渲染
REDIS_HOST string localhost Redis 主机名
REDIS_PORT integer 6379 Redis 端口
REDIS_PASSWORD string 需要身份验证时的 Redis 密码
CONVERTER_ROLE string cpu gpu 立即领取,cpu 先等待 CPU_CLAIM_DELAY_MS
CPU_CLAIM_DELAY_MS integer 5000 GPU 优先于 CPU 领取任务的等待时间
WORKER_ENABLED boolean true false 时 Pod 只接收提交、不执行渲染
JOB_TTL_SECONDS integer 3600 Redis 任务记录有效期
RESULT_TTL_SECONDS integer 900 Redis 渲染结果有效期
JOB_VISIBILITY_TIMEOUT_MS integer 300000 领取任务的渲染时限,超时返回队列
JOB_MAX_ATTEMPTS integer 3 任务标记失败前的尝试次数
WORKER_IDLE_POLL_MS integer 500 空闲工作进程轮询间隔
REAPER_INTERVAL_MS integer 30000 将失联领取任务放回队列的扫描间隔

身份验证

设置 类型 默认值 说明
API_KEY string 共享密钥。设置后,所有转换路由和 GET /jobs/{id} 要求 Authorization: Bearer ,缺少时返回 401;/health、/metrics 和本地素材路由仍开放。Helm 生成此值并连接到 Editor-service

API 端点

Method Path 说明
POST / 基础 SVG 转图片,直接返回文件;查询参数 width、height
POST /background/ 完整背景转换,返回 full、preview、blurred 三种 URL 的 JSON;参数 filename、debug
POST /thumbnail/ 缩略图转换,返回三种 URL 的 JSON;参数 filename、debug
POST /photo/ 区域照片转换,返回照片和缩略图 URL
GET /jobs/{id} 排队任务对象 { requestId, kind, status, attempts, enqueuedAt, finishedAt, result }。status 为 pending、running、succeeded 或 failed;完成前 result 为 null,之后为同步路由原本返回的 JSON。未知或过期 ID 返回 404,结果保留 RESULT_TTL_SECONDS;队列关闭返回 503
GET /health 服务健康检查,返回 { “status”: “healthy” }
GET /health/redis Redis 连接状态、Pod 转换 role,以及 pending 和 processing 队列深度
GET /metrics 文本格式 Prometheus 指标

POST /、/background/、/thumbnail/ 接受 multipart/form-data 中的 SVG 文件(image/svg+xml)。POST /photo/ 接受最多 10 MB 的 JPEG、PNG 或 WebP,返回 url 和 thumbUrl。

启用队列时,转换路由返回 202 Accepted 和任务 ID,通过 GET /jobs/{id} 获取结果,Editor-service 自动执行轮询。关闭队列时直接返回结果。

速率限制:每 IP 在 RATE_LIMIT_WINDOW_MS(默认 60000)内最多 RATE_LIMIT_MAX(默认 600)次转换请求,包括 /photo/。GET /jobs/{id} 独立按 JOBS_RATE_LIMIT_MAX 限制,默认每分钟 5000 次。/health 和 /metrics 不限流。

请求超时:5 分钟,即 300 秒。

输出格式

生成 WebP 图片,而非 PNG;支持 alpha 通道,文件大小减少 30–50%。

每个背景生成三个版本:

版本 宽度 质量 用途
full 原始 60–80% 完整分辨率背景
preview 1000px 60–80% 首次渲染的快速预览
blurred 400px 30–50% 带内联 base64 的渐进加载占位图

在配置的存储后端保存为 backgrounds/{filename}/{variant}.webp。

Docker

基于 node:22.14.0-alpine,安装 Chromium 供 Puppeteer 使用。端口 3000。每 30 秒 GET /health 检查,重试 3 次,启动宽限 5 秒。

Puppeteer 环境变量:

  • PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true,使用系统 Chromium。
  • PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser。

以非 root 用户 uid 10001 运行,使用 tini 作为 init,避免僵尸进程。

监控

Prometheus 指标

/metrics 提供:

  • http_request_duration_seconds 直方图,标签 method、route、status_code。
  • http_requests_total 计数器,使用相同标签。
  • 默认 Node.js 进程指标:内存、CPU、事件循环延迟和 GC。

面向图片处理优化的分桶为 0.01、0.05、0.1、0.25、0.5、1、2.5、5、10、30 秒。

健康端点

  • GET /health:服务运行时返回 200。
  • GET /health/redis:返回 Redis 连接、转换角色及 pending、processing 队列深度。

配置方式

优先级从高到低:环境变量、Kubernetes ConfigMap/Secrets、仅开发使用的 .env 文件。

Kubernetes 部署

通过 Helm values 和 Secret 管理配置:

converter:
  image:
    repository: registry.gitlab.com/seatmap.pro/seatmap/converter-service
    tag: latest
  env:
    PORT: '3000'
    LOG_LEVEL: 'info'
    STORAGE_TYPE: 's3'
    AWS_REGION: 'us-east-1'
    AWS_BUCKET_NAME: 'your-bucket'
    # AWS_PUBLIC_BASE_URL: "https://your-cdn.example.com"  # if using non-AWS S3

Secret 管理

使用以下方式安全管理 AWS 凭据和 Redis 密码:

  • Kubernetes Secrets,通过 seatmap-helm-secrets 注入。
  • External Secrets Operator。
  • Service Account 的 IAM 角色(IRSA)。

开发设置

本地 Docker Compose:

cd products/converter-service
cp .env.sample .env
# Edit .env with your settings (use STORAGE_TYPE=local for development)
docker compose up

启动 3000 端口的转换器和 6379 端口的 Redis。

不要将敏感凭据提交到版本控制。

故障排查

  • 上传失败:检查 MAX_CONTENT_LENGTH 和 Ingress 正文大小,大 SVG 可能需要提高默认 16 MB 限制。
  • S3 拒绝访问:验证 AWS 凭据和桶权限,确认 AWS_PUBLIC_BASE_URL 与实际桶 URL 形式一致。
  • 图片空白或损坏:确认 Chromium 位于 /usr/bin/chromium-browser,Docker 中 PUPPETEER_EXECUTABLE_PATH 正确。
  • 内存占用高:大 SVG 会使 Sharp 和 Puppeteer 消耗较多内存,容器至少 512 MB,生产推荐 2 GB。
  • Renderer 无法加载 WebP:检查桶的 CORS,渲染器需要跨域加载背景。
  • 公共 URL 错误:非 AWS 服务需显式设置 AWS_PUBLIC_BASE_URL,默认生成逻辑假设 AWS 虚拟主机桶形式。

输入功能、设置、端点或 SDK 方法。