从 0 到 1:用华为云 FunctionGraph + API 网关搭建 Serverless 图片压缩服务

举报
yd_253000176 发表于 2026/10/21 09:55:04 2026/10/21
【摘要】 用华为云 FunctionGraph 函数工作流配合 API 网关,把图片压缩与水印做成按量付费的 Serverless 接口,完整记录架构设计、函数代码、网关配置和三次真实踩坑的排查过程。

一、为什么把图片处理搬到 Serverless

去年底接手一个社区内容平台,用户上传的原图动辄 3~5MB,列表页首屏要加载几十张,移动端体验很差。传统做法是买一台 ECS 常驻跑 ImageMagick,但这类业务波峰明显:白天集中在 10 点到 12 点、20 点到 23 点,夜里几乎为零。常驻实例意味着大部分时间在空转,峰值时又容易排队。

于是我把图片压缩和水印改成华为云 FunctionGraph 函数工作流加 API 网关的组合:调用才计费,不调用不计费,并发由平台管理。上线三个月,月成本从一台 4U8G ECS 降到不足 5 元。

二、整体架构

链路很直白:客户端 POST 图片到 API 网关的 /compress 路径,网关把请求透传给函数,函数用 Pillow 压缩、加水印,写回 OBS 并返回临时下载链接。整个过程无状态,天然可横向扩展。

准备三样东西:一个 OBS 桶放原图与结果、一个 FunctionGraph 函数(Python 3.9 运行时)、一个共享版 API 网关。三者放在同一区域,避免跨区流量费。

三、函数代码

函数入口保持极简,核心逻辑不到 60 行:

import base64, io, json
from PIL import Image

def handler(event, context):
body = json.loads(event.get("body") or "{}")
raw = base64.b64decode(body["image"])
img = Image.open(io.BytesIO(raw)).convert("RGB")
w, h = img.size
scale = min(1.0, 1280 / max(w, h))
if scale < 1.0:
img = img.resize((int(w * scale), int(h * scale)), Image.LANCZOS)
buf = io.BytesIO()
img.save(buf, "JPEG", quality=82, optimize=True)
out = base64.b64encode(buf.getvalue()).decode()
return {"statusCode": 200, "body": json.dumps({"image": out, "size": len(buf.getvalue())})}

Pillow 需要打成 zip 上传,务必在 Linux 或 Docker 里 pip install -t 打包,Windows 下打的包会因缺少 .so 直接报 ImportError。

四、API 网关怎么配

新建 API,方法 POST,后端类型选 FunctionGraph,选中刚发布的函数。请求体默认限制 2MB,图片走 base64 会膨胀三分之一,所以我把上限调到 8MB,并让前端先压到 2MB 以内再上送。

另外要打开支持 CORS,并在函数返回头里补上 Access-Control-Allow-Origin,否则浏览器预检永远过不去。

五、三个真实踩过的坑

坑一:冷启动 2.8 秒。首次调用明显卡顿。配置预留实例,同时把 Pillow 的 import 放到模块顶层而不是 handler 内部,让平台在初始化阶段完成加载,冷启动降到 400ms 左右。

坑二:中文水印变方框。运行环境没有中文字体,draw.text 渲染全是豆腐块。把字体文件打进 zip 并用绝对路径指定,问题消失。

坑三:网关返回 502。函数日志正常,网关报 502。原因是函数返回的 body 必须为字符串,我一开始返回了 dict,网关序列化失败。改成 json.dumps 后恢复。

六、成本与适用边界

按每天 3000 次调用、单次 128MB 乘 0.8 秒估算,每月函数计算费用不到 2 元,网关共享版按量计费另计,总体可控。注意 FunctionGraph 单次执行最长 15 分钟,超过 8MB 的大图或视频转码这类长任务,更适合走 OBS 事件触发的异步流程,而不是同步接口。

一句话总结:把「平时不用、峰值要命」的能力交给 Serverless,是这类平台最划算的一笔账。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。