ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Serverless 实战:用函数计算部署 Puppeteer 截图服务

Serverless 实战:用函数计算部署 Puppeteer 截图服务 简介这份PPT资源面向希望快速上手Serverless架构的开发者与运维人员以“快速开发一个分布式Puppeteer网页截图服务”为主线系统讲解函数计算的核心概念与落地方法。内容涵盖函数计算介绍、Web应用迁移函数计算的实操体验以及将Puppeteer网页截图服务部署到函数计算平台的完整流程并延伸至使用Rendertron搭建Headless Chrome渲染解决方案帮助读者理解无服务器、弹性伸缩、高可用与低成本等特性在真实项目中的价值。资源包共1个文件为pptx演示文稿大小约3.42MB结构清晰、图文并茂适合作为技术分享或自学课件。目前已有152人学习读者可从中获得从概念到部署的完整知识链路掌握yarn dev、fun deploy等命令的使用思路并了解分布式截图与网页渲染服务的实现要点适合具备一定Web开发基础、想切入Serverless领域的工程师参考。1. 从一台永远在线的截图服务器说起这份 PPT 到底能帮你省下什么很多团队第一次做网页截图都是先开一台 2 核 4G 的云主机装 Chrome、装 Puppeteer、写个 Express 接口然后祈祷它别在半夜 OOM。流量小的时候机器闲着烧钱流量一上来 Chrome 进程排队排到超时运维还得盯着磁盘和内存。这份《Serverless 技术开发实战.pptx》讲的正是另一条路把 Puppeteer 截图服务拆成函数交给函数计算托管用fun deploy一条命令推上去按调用次数计费没人访问时成本趋近于零。它适合正在做自动化测试、SEO 预渲染、报表导出或者数据抓取的工程师也适合想搞明白 Serverless 到底能不能扛住真实业务的人。PPT 本身是课程讲义核心内容围绕函数计算介绍、Web 应用迁移体验、Puppeteer 截图服务部署三块展开还附了 Rendertron 搭建 Headless Chrome 渲染方案的延伸阅读。下面我按自己拆项目的习惯把它还原成一份能照着复现的实战笔记。2. 函数计算与 Puppeteer 的选型逻辑为什么不是 ECS 加 Docker2.1 函数计算到底替你管了什么先把概念对齐。函数计算Function Compute是一种事件驱动的计算服务你上传的是一段代码而不是一台机器。平台负责运行环境、扩缩容、负载均衡和故障恢复你只为代码实际执行的那几百毫秒到几秒付费。PPT 里列了四个关键词无需管理基础设施、实时弹性伸缩、高可用、低成本。这四个词不是宣传语落到 Puppeteer 场景里各有具体含义。无需管理基础设施意味着你不用再维护 Chrome 的依赖库。Puppeteer 在 Linux 上跑起来需要一堆共享库字体、libnss、libatk 缺一个就启动失败自己装机器时这些坑要踩一遍。函数计算提供官方或社区维护的运行时镜像Chrome 依赖已经打包好你只需要把业务代码和 node_modules 一起传上去。实时弹性伸缩对应的是截图请求的突发性。一个内容平台做分享卡片可能白天每分钟几十次、凌晨几乎为零也可能一条推送带来瞬时几百并发。固定机器要么浪费要么被打爆函数计算按并发自动拉起实例请求结束实例回收。高可用和低成本是一体两面。多可用区部署由平台保证你不用自己搭主从计费按实际执行时间和调用次数空闲不花钱。对截图这种典型的短时、突发、无状态任务这个模型比常驻服务更贴合。2.2 Puppeteer 在函数环境里的三个约束选型讲完得说清楚 Puppeteer 放进函数计算后有哪些硬约束不然部署上去也是白搭。第一是冷启动。函数实例首次被调用或长时间空闲后重建时需要加载运行时、启动 Chrome这个过程可能几百毫秒到几秒。截图本身可能只要 1 秒冷启动却占了大头。常见做法是设置预留实例或者接受首屏稍慢、后续请求走热实例。第二是执行时长上限。函数计算对单次调用有超时限制具体数值以你所用平台的配置为准。截图任务要设合理的timeout页面加载慢的站点要提前判断不能让函数一直挂着。第三是无状态。函数实例随时可能被回收本地磁盘写入不保证保留。截图结果要么直接返回二进制流要么上传到对象存储再返回 URL不能指望存在本地。2.3 从 Web 应用到函数的迁移路径PPT 里给了一条迁移体验路径命令是yarn dev和fun deploy。这套流程对应的是把本地 Web 应用改造成函数入口再用工具链部署。我一般会这样组织目录# 项目结构示例 puppeteer-shot/ ├── index.js # 函数入口导出 handler ├── package.json # 依赖声明含 puppeteer-core 或 puppeteer ├── template.yml # 函数计算资源描述文件 └── node_modules/ # 本地安装的依赖部署时一并上传yarn dev用于本地调试模拟函数被事件触发时的行为fun deploy读取template.yml把代码和配置打包上传到函数计算。这里的关键是入口函数签名要符合平台约定比如// index.js const puppeteer require(puppeteer-core); exports.handler async (event, context) { // 从事件中解析目标 URL 和截图参数 const url event.url || https://example.com; const width event.width || 1280; const height event.height || 800; // 启动无头 Chromeargs 针对容器环境做适配 const browser await puppeteer.launch({ headless: true, args: [--no-sandbox, --disable-setuid-sandbox, --disable-dev-shm-usage] }); try { const page await browser.newPage(); await page.setViewport({ width, height }); await page.goto(url, { waitUntil: networkidle2, timeout: 15000 }); const buffer await page.screenshot({ type: png }); return { statusCode: 200, headers: { Content-Type: image/png }, body: buffer.toString(base64), isBase64Encoded: true }; } finally { await browser.close(); // 必须关闭否则实例资源泄漏 } };这段代码里几个参数值得展开。--no-sandbox和--disable-setuid-sandbox是因为容器内通常没有完整的沙箱权限不加会直接启动失败。--disable-dev-shm-usage解决的是容器/dev/shm分区过小导致 Chrome 崩溃的问题这个坑在本地开发时往往遇不到一上云就翻车。waitUntil: networkidle2表示等网络基本空闲再截图适合大多数页面但对有长轮询或持续动画的站点可能永远等不到这时要换成domcontentloaded加固定延时。timeout要小于函数本身的超时时间留出余量。template.yml是部署的描述文件常见字段包括服务名、函数名、运行时、入口、内存和超时# template.yml ROSTemplateFormatVersion: 2015-09-01 Transform: Aliyun::Serverless-2018-04-03 Resources: screenshot-service: Type: Aliyun::Serverless::Service Properties: Description: Puppeteer 截图服务 screenshot-func: Type: Aliyun::Serverless::Function Properties: Handler: index.handler Runtime: nodejs14 MemorySize: 1024 Timeout: 30 CodeUri: ./内存给 1024MB 是因为 Chrome 渲染吃内存给太小容易在复杂页面上被杀。超时 30 秒是上限保护实际业务里截图超过 15 秒基本可以判定页面有问题。CodeUri指向当前目录fun deploy会把整个目录打包所以node_modules要在本地装好且注意不要把无关的大文件带进去。3. 部署 Puppeteer 截图服务从本地跑通到线上调用3.1 本地验证与依赖处理在fun deploy之前先在本地把函数逻辑跑通。最省事的办法是写一个本地调用脚本直接 require 入口文件并传入模拟事件// local-test.js const { handler } require(./index); (async () { const event { url: https://example.com, width: 1200, height: 900 }; const context {}; const result await handler(event, context); // 把 base64 结果写到本地文件肉眼确认截图是否正确 const fs require(fs); fs.writeFileSync(shot.png, Buffer.from(result.body, base64)); console.log(截图已保存状态码:, result.statusCode); })();跑node local-test.js打开生成的shot.png检查。这一步能提前暴露 Chrome 启动参数、页面加载策略、视口设置的问题比部署上去再看日志快得多。依赖方面如果函数计算环境已内置 Chromium可以用puppeteer-core减小包体积如果需要自带浏览器就用完整puppeteer但要注意包大小和下载源。国内环境安装时常见做法是设置PUPPETEER_DOWNLOAD_HOST或使用镜像源避免下载超时。3.2 fun deploy 的完整流程与参数本地验证通过后执行部署。典型流程是# 安装函数计算工具链 npm install -g alicloud/fun # 配置账号信息按提示输入 AccessKey 等 fun config # 本地调试模拟事件触发 fun local invoke screenshot-func # 部署到云端 fun deployfun config会引导你填写账号、区域和默认服务名这些信息保存在本地配置文件里后续命令直接读取。fun local invoke用于在本地模拟函数执行适合调试入口逻辑。fun deploy读取template.yml创建或更新服务和函数输出部署结果和调用入口。部署完成后平台会给出一个 HTTP 触发地址或调用方式。如果配置了 HTTP 触发器可以直接用 curl 测试# 通过 HTTP 触发器调用截图函数 curl -X POST https://your-trigger-url \ -H Content-Type: application/json \ -d {url:https://example.com,width:1280,height:800} \ --output result.png如果返回的是 base64 编码的 body需要先解码再保存。这一步常见问题是触发器鉴权配置有的平台默认开启签名校验直接 curl 会返回 403需要在控制台调整触发器的认证方式或带上签名头。3.3 截图服务的参数调优与结果处理服务跑起来之后真正影响体验的是参数。下面这张表是我在实际项目里反复调过的几个关键项参数作用常见取值注意点waitUntil页面加载完成的判定条件networkidle2/domcontentloaded长轮询页面用前者会超时timeout页面加载超时15000ms要小于函数超时viewport视口宽高1280x800移动端截图要设 deviceScaleFactorfullPage是否整页截图true / false整页截图内存占用高type图片格式png / jpegjpeg 体积小适合分享卡片MemorySize函数内存1024MB 起复杂页面给 2048MB结果处理上如果截图要长期保存建议在函数里直接上传到对象存储返回一个带时效的 URL而不是把 base64 塞在响应里。base64 会让响应体积膨胀约三分之一大图容易触发响应大小限制。上传对象存储的代码大致是// 在截图成功后上传到对象存储 const OSS require(ali-oss); const client new OSS({ region: your-region, accessKeyId: ..., accessKeySecret: ..., bucket: your-bucket }); const objectName shots/${Date.now()}.png; await client.put(objectName, buffer); return { statusCode: 200, body: JSON.stringify({ url: client.signatureUrl(objectName, { expires: 3600 }) }) };AccessKey 不要硬编码在代码里用函数计算的环境变量配置代码里通过process.env读取。这是血泪经验硬编码的密钥一旦随代码包泄露后果比截图失败严重得多。4. 避坑与排查截图服务上线后最容易翻车的五件事4.1 现象函数调用返回超时日志显示 Chrome 启动失败原因容器环境缺少 Chrome 运行所需的共享库或者启动参数没加--no-sandbox。本地开发机通常有完整桌面环境不会暴露这个问题。解决确认使用的运行时镜像是否包含 Chromium 依赖在puppeteer.launch的args里加上--no-sandbox、--disable-setuid-sandbox、--disable-dev-shm-usage、--disable-gpu。如果仍然失败在函数里打印browser.process().stderr或捕获启动异常看具体缺哪个库。4.2 现象截图成功但图片是白屏或只有部分内容原因页面是前端渲染waitUntil设成了domcontentloadedDOM 就绪时数据还没请求回来或者页面有懒加载视口外的图片没触发加载。解决改用networkidle2并适当增加超时对懒加载页面截图前执行page.evaluate滚动到底部再滚回顶部触发所有图片加载必要时在goto之后加page.waitForSelector等待关键元素出现。4.3 现象并发上来后大量请求失败日志报内存不足原因每个函数实例启动一个 ChromeChrome 本身吃内存复杂页面渲染时内存峰值更高。函数内存配置偏小或者实例内没有及时关闭 browser。解决把MemorySize调到 2048MB 或更高确保每次调用结束后browser.close()在finally里执行如果单实例并发处理多个请求考虑每个请求独立启动 browser 还是复用复用要注意页面隔离和资源清理。4.4 现象部署成功但 HTTP 调用返回 403 或签名错误原因HTTP 触发器默认开启了鉴权或者请求方法、Content-Type 与触发器配置不匹配。解决在控制台检查触发器的认证方式测试阶段可以改为匿名调用生产环境建议保留签名并让调用方正确计算签名。同时确认触发器支持的请求方法包含 POSTContent-Type 为application/json。4.5 现象本地fun local invoke正常线上fun deploy后行为不一致原因本地和线上的运行时版本、环境变量、依赖版本不一致或者node_modules里包含了本地平台特有的二进制文件。解决在template.yml里明确指定运行时版本环境变量通过配置文件或控制台设置不要依赖本地 shell依赖安装时使用与线上一致的操作系统架构必要时在 CI 环境重新安装依赖再部署。5. 进阶用 Rendertron 思路做 SEO 预渲染与定时截图PPT 里提到了 Rendertron这是一个基于 Headless Chrome 的渲染服务常用来解决 SPA 应用的 SEO 问题。它的思路和 Puppeteer 截图服务同源但目标不同截图是给人看图片Rendertron 是给爬虫看渲染后的 HTML。把这两条线合起来能做出更完整的方案。一个具体技巧是在函数里根据请求头区分用途。爬虫请求返回渲染后的 HTML普通请求返回截图。代码骨架如下exports.handler async (event, context) { const url event.url; const userAgent (event.headers event.headers[user-agent]) || ; const isBot /bot|spider|crawler/i.test(userAgent); const browser await puppeteer.launch({ headless: true, args: [--no-sandbox, --disable-dev-shm-usage] }); try { const page await browser.newPage(); await page.goto(url, { waitUntil: networkidle2, timeout: 15000 }); if (isBot) { // 爬虫返回渲染后的完整 HTML const html await page.content(); return { statusCode: 200, headers: { Content-Type: text/html }, body: html }; } else { // 普通用户返回截图 const buffer await page.screenshot({ type: jpeg, quality: 80 }); return { statusCode: 200, headers: { Content-Type: image/jpeg }, body: buffer.toString(base64), isBase64Encoded: true }; } } finally { await browser.close(); } };这个模式的好处是一套函数同时服务两类需求省去单独维护渲染服务的成本。验证方法也简单用 curl 带上User-Agent: Googlebot请求看返回的是 HTML 还是图片再用普通 UA 请求确认返回图片。两种结果都符合预期说明分流逻辑生效。另一个进阶用法是定时截图。函数计算支持定时触发器可以配置 cron 表达式让函数每天固定时间对指定页面截图并上传对象存储用于监控页面变化或生成日报。配置方式是在template.yml里给函数加一个定时触发器Triggers: - Name: daily-shot Type: Timer Properties: CronExpression: 0 0 8 * * * # 每天 8 点触发 Enable: true Payload: {url:https://example.com,width:1280,height:800}Cron 表达式各平台格式略有差异配置前确认字段顺序。Payload 是触发时传给函数的事件内容函数里按普通事件解析即可。定时截图要注意幂等和失败重试如果某天截图失败最好有告警或补跑机制别让监控断档。从那以后我每次上线截图服务都会先用fun local invoke跑通本地逻辑再部署到测试环境用真实 URL 压一遍并发最后才切生产。这套流程帮我挡掉过 Chrome 启动失败、内存不足、触发器鉴权好几个坑。希望帮到你。本文还有配套的精品资源点击获取
返回列表