图片转换器配置指南
图片转换器是 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 按以下优先级生成:
- AWS_PUBLIC_BASE_URL:已设置时原样使用,例如
https://bucket.nyc3.digitaloceanspaces.com。 - AWS_ENDPOINT_URL:推导路径形式
{endpoint}/{bucket},例如https://storage.yandexcloud.net/my-bucket。 - 默认: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 |
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 虚拟主机桶形式。