把一套后端服务搬上 CloudBase 时,真正容易返工的不是代码写错,而是先动手改了入口、再发现运行方式本身不合适。云函数按请求拉起、有体积和时长约束,适合短请求、偶发流量;云托管跑的是容器,进程常驻,适合长连接、后台任务和带系统依赖的服务。判断顺序建议是:先数请求特征,再量依赖体积,再看运行时限制,最后把改造项折算成工时,而不是凭“无服务器更省事”的印象直接选。
选云函数还是云托管,先看三件事:请求是不是短且偶发、依赖包能不能压进函数的体积和冷启动容忍范围、运行环境有没有函数装不上的系统依赖或常驻进程。三项都过关,云函数改造量通常更小;只要有一项明显不匹配,就该优先评估云托管,避免上线后再迁移一遍入口、日志和发布流程。以下判断都建议在本地或测试环境先跑一遍再下决定。
列出服务的请求特征:偶发调用还是有长连接常驻
先从日志或网关侧统计三项指标,不要凭印象。峰值并发看的是同一秒内同时在处理的请求数;单次时长看的是 P95 而不是平均值;连接形态看的是是否存在 WebSocket、SSE 或客户端定时轮询。统计周期建议覆盖至少一个完整业务日,含一次高峰。命令示例(按你实际的日志字段替换):
# 从访问日志粗略统计每秒并发与耗时分布
awk '{print $4, $NF}' access.log | sort | uniq -c | sort -rn | head -20
# 统计单次请求耗时分布,字段名按实际日志调整
awk '{print $10}' access.log | sort -n | awk '{a[NR]=$1} END{print "p50", a[int(NR*0.5)], "p95", a[int(NR*0.95)]}'
判断方向可以这样对照:
- 偶发、单次几百毫秒到几秒的接口:和按需拉起的方式匹配度高,云函数可以先试。
- 持续高并发且对冷启动敏感:云函数需要预留实例或预热,成本和配置复杂度会上升,此时要对比云托管常驻实例的账单。
- 存在 WebSocket、SSE 或服务端定时任务:函数实例不保证长期存活,连接会被断开,这类服务通常应选云托管。
- 客户端高频轮询、每秒请求量稳定:本质是准常驻负载,用函数只是把固定负载拆成许多次调用,建议倾向容器。
验证方式是:在测试环境用压测工具复现峰值并发,观察函数侧是否出现大量冷启动、超时或连接被重置的日志。出现就说明形态不匹配,不必继续优化函数参数。
量一下依赖体积与首次启动时间
函数的体积约束和冷启动时间是最容易在后期卡住的两件事。先在本地把构建产物打出来,看压缩包大小和依赖树:
# 看构建产物目录占用,注意排除本地缓存和测试文件
du -sh dist/ node_modules/
# 找出体积最大的依赖,逐个判断能否换成轻量实现或移到外部服务
du -sh node_modules/* | sort -rh | head -20
# 压缩后大小更接近上传包体
zip -rq app.zip dist node_modules && ls -lh app.zip
启动耗时建议在本地模拟一次冷启动:清掉本地缓存和进程,用一个最小入口加载完整依赖并打印耗时。比如在入口文件顶部记录时间戳,在初始化完成后输出差值;Node 环境可以用 node -e "const t=Date.now();require('./入口文件');console.log(Date.now()-t)" 做粗测,Python 可以用 python -X importtime -c "import 你的模块"。这类数字只用于横向比较,不能直接等同于线上冷启动。
判断方向:如果压缩包接近函数体积上限,或者依赖加载明显拖慢首次响应,优先做两件事——把非必要的构建依赖、测试数据、文档移出包体;把图片处理、PDF 生成、大型模型文件这类重依赖拆到对象存储或独立服务。如果拆完仍然超限,或者业务无法接受首次请求的等待,迁移方向就是云托管,容器镜像的体积约束通常宽松得多,代价是要自己维护镜像和发布。
确认是否需要自定义运行时和系统依赖
这一节决定有没有“函数装不上的东西”。典型需要容器的场景包括:
- 自带可执行二进制:比如 ffmpeg、ImageMagick、某些数据库客户端,需要固定版本并随镜像一起分发。
- 依赖特定系统库或字体:比如中文字体渲染、libreoffice 转文档、需要 root 安装的 apt 包。
- 需要常驻进程:队列消费者、定时扫描任务、内存缓存、长轮询工作线程。
- 需要固定运行环境:语言版本、glibc 版本与函数提供的运行时不一致,且短期无法升级代码。
不要等到部署时才发现装不上。本地先用容器验证依赖能否跑通,镜像尽量贴近目标运行环境:
# 用基础镜像本地跑一遍,确认二进制和系统库可用
docker run `--rm` -v "$PWD":/app -w /app node:20-slim \
sh -c "apt-get update && apt-get install -y `--no-install-recommends` 你的系统包 \
&& node -e \"require('./入口文件')\""
验证标准很具体:入口能加载、依赖能 import、关键命令能在容器里执行成功、退出码为 0。如果本地容器里也要装一堆系统包才能跑,基本可以判断这类服务更适合云托管;反过来,如果依赖全是纯语言包、没有系统级安装,云函数通常能承接。
把两种方式的改造项写成对照清单
到这一步,把差异落到具体改动上,而不是凭感觉估工时。下面每项都写清楚改哪里,可以直接贴进任务清单:
- 入口改造:云函数需要把服务包装成单一入口,接收事件对象、返回结构化结果,原来靠 HTTP 框架中间件做的事要重新组织;云托管保留原有监听端口的启动方式,通常只需确认监听地址和端口来自环境变量。
- 静态资源处理:云函数不适合托管大量静态文件,通常要迁到对象存储或 CDN,并改前端引用路径;云托管可以继续由服务自己返回静态资源,但要注意镜像体积和缓存策略。
- 日志采集:云函数要求输出到标准输出,本地写文件的方式会丢日志,需要改日志配置;云托管同样建议标准输出,但容器内多进程日志要统一收口到 stdout/stderr。
- 环境变量注入:云函数在控制台或配置文件中按函数维度配置,本地调试需要用模拟方式注入;云托管按服务或版本配置,注意敏感信息走密钥管理而不是写进镜像。
- 发布流程:云函数是打包上传、按版本发布,适合小步快跑;云托管要先构建镜像、推送到镜像仓库、再更新服务,CI 里需要多一步构建和缓存处理。
把每项标注成“不用改 / 小改 / 重写”,再乘以你团队对这块的熟悉程度,就能得到大致工作量。判断的边界也要写清楚:如果入口改造和静态资源处理都是重写级别,云函数省下的运维收益很可能被改造工时吃掉;如果只有发布流程需要调整,云函数的改动面通常更可控。最终选择建议在测试环境各跑通一次最小链路——一次请求、一条日志、一次发布——再定,避免上线后才发现日志采不到或环境变量没注入。