首页
/ Serverless Framework `dev` 命令实战指南:基于 AWS IoT Core 的 Lambda 本地实时联调与开发模式

Serverless Framework `dev` 命令实战指南:基于 AWS IoT Core 的 Lambda 本地实时联调与开发模式

2026-09-08 19:38:08作者:霍妲思

本指南围绕 Serverless Framework 的 serverless dev 命令展开。它以部署在 AWS Lambda 上的真实基础设施为运行底座,通过 AWS IoT Core 建立的 WebSocket 通道把线上函数收到的每一次真实事件实时转发到本地代码中执行——从而让开发者在不做模拟器、不反复部署的前提下获得"改代码即生效"的开发体验。阅读本文后,你将掌握 serverless dev 的全部选项用法、其 IoT 事件回传机制的底层原理、Node.js/TypeScript 本地执行的实现方式,以及 Sandboxes(AWS Lambda MicroVMs)容器级本地开发模式的启动方法,并能独立定位开发会话中常见的故障。

serverless dev 能解决什么问题

在传统的 Serverless 开发流程里,每修改一次函数代码都要执行一次 serverless deploy 才能真正触发一次调试,云函数异步事件(例如 S3 上传、SQS 消息、EventBridge 规则触发)很难在本地完整模拟。serverless dev 改变了这一循环:

  • 它在指定的 stage 与 region 上激活一个长驻的 CLI 开发会话
  • 你的函数仍以真实代码部署在 AWS Lambda 上接收真实事件,但事件会被"截获"并通过 WebSocket 转发到本机;
  • 本机在近乎真实的环境变量、IAM 权限与上下文条件下执行你的函数代码,日志直接打印在终端,结果与错误实时回传 Lambda;
  • 本机源码一旦变化,热重载即时生效,无需反复部署,也不需要任何本地模拟/仿真

不过需要再次强调原文档中的告诫:虽然技术上可行,官方不建议在 prod stage 上激活开发会话。

快速开始:激活一个开发会话

在服务目录下直接运行:

serverless dev

命令执行后会经历一次"插桩 + 部署"过程:CLI 先为你的服务打上 Dev Mode 所需的运行时垫片(shim),执行一次部署把垫片发布到 AWS,随后 CLI 保持长连接并输出 Connected (Ctrl+C to cancel),此时终端开始实时转发线上事件、日志与响应。按下 Ctrl+C 可退出会话(详见下文"退出与清理"小节)。

该命令要求服务依赖(serviceDependencyMode: 'required'),并且属于带 AWS 扩展的主命令(hasAwsExtension: true),其命令生命周期定义位于 commands-schema.jsdev 顶层插件入口在 lib/plugins/dev.js,而核心实现全部位于 packages/serverless/lib/plugins/aws/dev/index.js

选项(Options)

原文档列出的常用选项与命令 schema(commands-schema.js)中实际支持的选项合并整理如下:

选项 简写 类型 说明
--stage -s string 要在哪个 stage 激活开发会话,例如 devlocal 或个人命名 stage
--region -r string 在指定 stage 中于哪个区域激活会话,例如 us-east-2
--aws-profile string 使用的 AWS 命名 Profile
--detailed boolean 在终端完整展示每一次调用的请求事件与响应体(默认仅打印一行摘要)
--mode -m string 开发模式类型:functions(默认)、agents(AgentCore 运行时)、sandboxes(AWS Lambda MicroVMs)。未指定时按服务配置自动检测
--agent -a string agents 模式下要运行的 Agent 名称,默认取第一个 runtime Agent
--sandbox string sandboxes 模式下要运行的 Sandbox 名称(本地 Docker 容器方式)
--assume-role boolean sandbox 模式下默认以沙箱部署的执行角色运行本地容器;传 --no-assume-role 则改用你的本地 AWS 凭据
--port -p string 本地 dev 端口:agents 模式对应 AgentCore 容器端口(默认 8080),sandboxes 模式对应本地 MicroVMs 控制面 API 端口(默认 9100
--on-exit string 退出开发会话时的行为,合法值仅 remove(Ctrl+C 退出时询问是否移除已部署的栈)

其中 --mode 的合法取值在插件层通过 validateModeOption() 强制校验,非法值会以 INVALID_DEV_MODE_OPTION 错误码拒绝;--on-exit 也类似,非 remove 的值会被 INVALID_DEV_ON_EXIT_OPTION 拒绝,见 index.jsindex.js

注意: 会话区域必须处于 AWS IoT Core 支持的区域范围内。插件内 validateRegion() 维护了一份受支持区域清单(涵盖 us-east-1/2us-west-1/2eu-*ap-*sa-east-1 等),若所选区域不在其中会以 UNSUPPORTED_REGION 错误直接中止,详见 index.js。这也解释了为何 dev 无法在 IoT Core 不可用的区域使用。

支持的语言运行时

目前该命令只对 Node.js(JavaScript 与 TypeScript) 提供开箱即用的支持:

  • Node.js (JS & TS) —— 已支持
  • Python —— 即将支持
  • Go —— 即将支持
  • Ruby —— 即将支持
  • Java —— 即将支持

这一限制有明确的源码依据:在会话准备阶段,插件会扫描服务内所有函数,一旦发现 runtime 不以 nodejs 开头便抛出 DEV_MODE_UNSUPPORTED_RUNTIME 错误并给出说明,见 index.js。垫片部署端只接受 AWS Lambda 当前可用的 Node 运行时列表 nodejs18.x / nodejs20.x / nodejs22.x / nodejs24.x;本地执行端(LocalLambda)的 Node wrapper 则额外兼容 nodejs14.x/16.x 并支持 .js/.mjs/.cjs/.ts/.mts/.cts 扩展名,见 local-lambda/index.js

工作原理:IoT Core 通道与 shim 插桩

serverless dev 并不使用 API Gateway 之类的 WebSocket 方案,而是利用每个 AWS 账号默认即具备、无需部署任何额外基础设施的 AWS IoT Core 终端节点建立安全通道。整条链路在代码中清晰可分,主要分为四步:

1. 为函数注入访问 IoT 的 IAM 权限

CLI 会为服务中所有 Lambda 函数补充一条 IAM 声明,使函数具备向 IoT Core 发布/订阅的能力:

Effect: 'Allow',
Action: ['iot:*'],
Resource: '*'

实际实现中,插件在保留你原有 IAM 配置的前提下(兼容旧版 provider.iamRoleStatements 与新版 provider.iam.role.statements 两种写法),把上述声明追加进去,并额外兼容 serverless-iam-roles-per-function 插件的逐函数 IAM 写法,见 index.js。函数自身的 IAM 配置会在会话开始时被深拷贝备份,供会话结束后恢复。

2. 将函数 handler 替换为 shim 并注入环境变量

插件会先把 shim.js 用 esbuild 打包、压缩,并以 index.js 为入口写进一个 zip 文件,覆盖设置到服务级与函数级的 package.artifact 上——也就是说,本次部署上传的"函数代码"其实就是 shim(详见 pack()index.js)。随后每个函数的配置被原地改写:

  • handler 被替换为 index.handler(原来的 handler 暂存到 originalHandler 供构建插件使用);
  • runtime 被调整为与你本地 Node 版本匹配、且 AWS Lambda 支持的版本(本机为 v18/20/22/24 时原样映射,否则回退 nodejs20.x);
  • 注入四组关键环境变量,告知 shim 应该连接哪里、服务属于谁:
SLS_IOT_ENDPOINT = <账号专属 IoT Data-ATS 终端地址>
SLS_SERVICE      = <服务名>
SLS_STAGE        = <会话 stage>
SLS_FUNCTION     = <函数名>

其中 IoT 终端地址通过 Iot.describeEndpoint({ endpointType: 'iot:Data-ATS' }) 获取,同一账号下各区域唯一,且默认可用,见 index.js

3. 以"无业务代码"的方式部署

完成配置改写后,插件通过内部 spawn('deploy') 触发一次标准部署——但这次部署的内容只有 shim,不含你的业务代码。所有函数因此变成"事件转发器"。部署结束后,内存中的服务配置会被立刻还原(restore() 恢复原始 handler、runtime、environment 与 IAM),并清理 package.artifact,保证后续本地 dev-build 不会因残留产物而跳过构建,见 index.js

4. 建立 WebSocket 长连接并订阅事件主题

CLI 使用 MQTT over WSS 连接 IoT 终端,按 sls/{region}/{service}/{stage}/{functionName} 的命名规则为每个函数订阅 requesterror 主题,并每秒向 sls/.../_heartbeat 发布一次心跳(QoS 1)以维持连接与探活,见 index.js

线上 shim 与本地 CLI 的职责对称:

  • shim(云端):收到 Lambda 调用后,从 context 提取非函数字段(awsRequestIdfunctionNamefunctionVersionmemoryLimitInMBlogGroupNamelogStreamName 等),连同完整 event 与环境变量发布到 …/{function}/request 主题;随后阻塞等待 …/{function}/response 主题上的结果,并把它作为 handler 的返回值交还给 Lambda 运行时;若超过约 2 秒未收到本机心跳,则抛出"Dev Mode Disconnected"错误提示重新执行 serverless dev,见 shim.jsshim.js
  • CLI(本地):订阅到 request 消息后解析出 functionName,进入本地执行流程,再把结果发布回 …/{function}/response

事件主题中还做了事件来源识别——本地终端对 API Gateway v1/v2、EventBridge、S3、SQS、SNS 事件会打印出可读的一行摘要(如 → λ handler ── aws:sqs:queueName:msgId),配合 --detailed 可查看完整事件体,见 index.js

本地执行环境:Child Process + Runtime Wrapper

当开发会话处于活跃状态,收到函数调用事件后,CLI 会创建子进程尽可能还原线上 Lambda 的执行环境:

  • 环境变量:透传线上函数环境(shim 剔除 PATHNODE_PATHLD_LIBRARY_PATHPWDSHLVL 等宿主无关变量后原样发送);
  • IAM 权限:真实函数携带的临时 AWS 凭据随环境变量一并生效,本地代码对 AWS 的调用走真实 IAM;
  • 上下文与事件:包括 awsRequestId、函数名、内存限制等真实 context 元数据以及原始事件;
  • 超时:以函数配置的 timeout(缺省 6 秒)为依据计算 getRemainingTimeInMillis(),本地执行若超过该时限会输出超时告警,见 index.js

本地执行交由 local-lambda/index.js 中的 LocalLambda 类完成:它以 cwd 指向服务目录,spawn 一个 node 子进程执行 runtime-wrappers/node.js,并把 { handlerFileAbsolutePath, handlerName, event, partialContext } 序列化后作为参数传入。wrapper 内会:

  • 动态 import() 用户的 handler 文件(同时兼容 CommonJS 与 ESM);
  • context 补充 succeed / fail / done / getRemainingTimeInMillis 等函数——这些函数无法通过 WebSocket 序列化,因此必须在本地补齐;
  • 拦截 console.log/infostdout/stderr,把对象参数以彩色、无限深度的 util.inspect 格式输出,使本地日志可读性接近真实 Lambda;
  • 执行完毕后把 { response, error } 写入系统临时目录下以子进程 PID 命名的 sls_<pid>.json,由父进程读取并删除,见 runtime-wrappers/node.js 相关实现。你可以在本机源码树中对照 runtime-wrappers/node.js 阅读完整的上下文注入与结果回传逻辑。

执行结果与错误随后封装回 JSON,通过 …/{function}/response 主题回传云端 shim;本地抛出的错误会携带原有的 name / message / stack 在远端重新构造,从而保持错误形态一致。需要注意的是,事件与响应都经由 MQTT 传输,受约 125 KB 载荷上限约束(两端分别以 MQTT_PAYLOAD_LIMIT = 125 * 1024 定义),超限时本地会打印 PayloadTooLargeError 提示,见 index.jsshim.js

退出与清理:还原线上代码

开发会话中的函数运行的是 shim 而非你的真实代码,因此关闭会话后若不还原,线上函数将无法正常工作。退出时 CLI 会明确提醒:请立即执行一次常规部署来移除 Dev Mode 插桩、恢复原始代码:

serverless deploy

若你在启动时传了 --on-exit=remove,则 Ctrl+C 退出时会先弹出确认询问是否移除当前 stage/region 的服务栈(确认后内部 spawn('remove') 拆除资源),见 index.js。无论选择哪种退出方式,把"结束后部署还原"纳入你的工作流都是安全使用 Dev Mode 的前提。

实践示例

在原文档基础上,以下命令覆盖了最常见的几种用法。

在默认 dev stage 快速激活会话:

serverless dev

dev stage、us-east-2 区域激活会话:

serverless dev --stage dev --region us-east-2

在个人专属 stage 上激活(便于与他人隔离):

serverless dev --stage austen

维护一个专门的 local stage:

serverless dev --stage local

原文档指出,维护一个专用的本地 stage 非常有益:每次执行命令时不必反复改动基础设施,就能快速进入会话。若希望每次调用都查看完整事件与响应体,可追加 --detailed

Sandboxes(AWS Lambda MicroVMs)的本地开发

对于 sandboxes(Lambda MicroVMs)serverless dev 走的是与上述函数级 IoT 会话完全不同的机制:它把 Sandbox 以 Docker 容器方式跑在本地。CLI 会构建 Sandbox 的 Dockerfile,启动一个本地、与 AWS SDK 兼容的 Lambda MicroVMs 控制面;这个控制面按需把 MicroVM 实例作为 Docker 容器拉起、转发它们的日志,并在文件变化时热重载镜像。

serverless dev --sandbox <name>             # 仅定义了一个 sandbox 时可省略 --sandbox
serverless dev --mode sandboxes             # 等价写法;当服务只含 sandboxes 时会被自动检测
serverless dev --sandbox <name> --port 9300 # 本地控制面端点端口(默认 9100)

几点关键约束与行为(可对照原文档以及 sandboxes.md 的 Local development 章节阅读):

  • 需要本机存在可用的 Docker daemon,且 Sandbox 的 artifact 必须是本地目录(内含 Dockerfile)——artifacts3:// zip 的 Sandbox 无法用 dev 本地运行;
  • 默认情况下,本地容器以 Sandbox 已部署的执行角色运行,因此代码里的 AWS 调用走真实 IAM;传入 --no-assume-role 可跳过角色扮演,改用你本地环境的 AWS 凭据;
  • 控制面端点地址跨会话保持稳定(默认 http://127.0.0.1:9100),可用 --port 调整;它可被任何使用 LambdaMicrovmsClient 的编排代码当作 AWS 端点调用。

模式选择(functions / agents / sandboxes)支持自动检测:插件会依据服务中是否定义了函数、runtime Agent、Sandbox 来推断,也可用 --mode/--sandbox/--agent 显式指定,相关判断逻辑见 index.js。Sandbox 模式的实现位于 packages/serverless/lib/plugins/aws/sandboxes/dev

故障排查(Troubleshooting)

TypeScript 无法工作

Dev Mode 底层调用本地函数时依赖 ts-node 来执行 TypeScript,因此服务中必须存在合法的 tsconfig.json。请确认配置文件有效后重试。本地 wrapper 的动态 import() 支持 .ts/.mts/.cts 扩展名文件,但解析与转译依赖项目自身的 TypeScript 工具链配置。

VPC 内的 Lambda 无法工作

serverless dev 无法直接支持运行在 VPC 内的 Lambda 函数,原因在于函数需要能访问 AWS IoT Core 终端节点。两种解决思路:

  1. 临时移除函数上的 VPC 配置再启动开发会话;
  2. 调整 VPC 网络,使其可通过 interface VPC endpoints(终端节点)与 AWS IoT Core 建立连通,具体可参考 AWS 官方文档中"通过 interface VPC endpoints 使用 AWS IoT Core"一节。

区域或载荷受限

  • 若所选 region 不在 AWS IoT Core 支持清单内,命令会以 UNSUPPORTED_REGION 报错——请切换到列表中的区域;
  • 若单个事件或响应超过约 125 KB,会收到 PayloadTooLargeError:Dev Mode 的 MQTT 通道不承载大载荷,请改为常规部署后直接 invoke 验证大载荷场景。

修改了基础设施配置

开发会话的配置热重载只针对函数代码。若你在会话期间修改了 serverless.yml 中的基础设施定义,终端会提示"如已做基础设施变更,请重启 serverless dev"——请退出后重新运行命令使其生效,见 index.js

深入阅读

本文涉及的机制均可在仓库源码与测试中继续追踪:

一句话总结:serverless dev 把"真实云事件 + 本地代码执行"组合为近乎零部署成本的联调闭环——对纯 Node.js/TypeScript 服务,它是替代本地仿真与高频部署的高效方案;对容器型 Sandbox 负载,则应切换 --sandbox 的 Docker 本地运行模式。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391