Serverless Framework 二进制安装器解析:install.sh 安装、自动更新与版本解析机制
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
本文基于binary-installer子项目的官方说明文档 README 展开,完整覆盖 curl 一键安装、自定义 CA 证书、Windows 代码签名等运维要点,并结合 main.go、src/version.go 等 Go 源码深入剖析二进制安装器的启动流程、版本解析、24 小时节流缓存与回退策略。读完后你将掌握 Serverless Framework 二进制安装方式背后的完整工作机制,并知道如何通过环境变量控制其更新与网络信任行为。
二进制安装器是做什么的
binary-installer目录负责两类交付物:
- launcher 二进制:一个用 Go 编写的轻量可执行文件,安装后即为
serverless命令本身。它负责下载、缓存并启动真正的框架(sf-core),并按策略自动更新; - install.sh脚本:即官方提供的 curl 安装命令所拉取执行的脚本,用于下载并落盘 launcher 二进制、创建 PATH 入口。
通过 curl 安装
在终端中执行官方提供的安装命令:
curl -o- -L https://install.serverless.com | bash这条命令安装的是最新的 launcher 二进制,框架本身(sf-core)会在你第一次运行serverless时才下载,因此首次调用会有一个拉取最新 release 的过程。默认情况下serverless命令会每 24 小时检查一次新版本;如果需要强制检查并下载新版本,可以设置环境变量SERVERLESS_FRAMEWORK_FORCE_UPDATE=true,之后每次运行serverless都会重新解析并下载可用版本。
install.sh 脚本做了什么
从 install.sh 源码可以看到完整流程:
- 平台检测:基于
$OSTYPE识别linux/darwin,其他平台直接报错退出; - 架构检测:通过
uname -m将x86_64映射为amd64,将arm64/aarch64映射为arm64; - 下载二进制:从
https://install.serverless.com/installer-builds/serverless-<平台>-<架构>下载到$HOME/.serverless/bin/serverless(先写入.tmp再mv重命名),并chmod +x; - 创建
sls别名:执行ln -sf serverless $HOME/.serverless/bin/sls,因此sls与serverless等价可用; - 写入 PATH:按 shell 类型分别处理——fish 通过
fish_user_paths添加,zsh 追加到~/.zshrc,其他 shell 依次尝试~/.bashrc、~/.bash_profile、~/.bash_login、~/.profile,写入内容为export PATH="$HOME/.serverless/bin:$PATH",并用grep防止重复写入; - 最后提示重启 shell(Codespace 环境)或直接
exec $SHELL重新加载。
构建体系
Makefile 定义了构建入口:对darwin/linux/windows三平台 ×amd64/arm64双架构交叉编译(显式跳过不支持的windows/arm64组合),并构建两个变体:
- prod 变体(
build-prod):使用 install_base_url.go,安装基地址固定为生产环境https://install.serverless.com; - canary 变体(
build-canary,附加-tags=canary编译标签):使用 install_base_url_canary.go,当环境变量SLS_USE_CANARY为真时切换为开发环境https://install.serverless-dev.com,否则仍走生产地址。
从源码结构看,这两个文件通过//go:build构建标签互斥编译,是"同一份代码、两种发布渠道"的典型实现。
运行环境与要求
- Node.js >= 18 且
npm必须在 PATH 上。main.go 中doesNodeExistAndIsItAccessible()用exec.LookPath同时检查node和npm是否存在,isNodeUpToDate()解析node --version输出的主版本号并校验>= 18;不满足时分别提示 "Nodejs is not installed..." 或 "Your Nodejs version is too old, please upgrade to Node 18 or newer..."; - 网络可达性:需要能访问
https://install.serverless.com(稳定版 release 与版本索引)和https://install.serverless-dev.com(canary 渠道); - 架构限制:launcher 仅支持
amd64与arm64,且windows/arm64组合被显式拒绝(见 main.go 的getBinaryName(),架构校验失败会打印错误并os.Exit(1))。
核心流程:一次serverless调用的完整链路
以下流程对应 README 的 "How the Binary Installer Works",每一节都给出源码佐证。
1. 启动与环境准备(main.go)
main()函数(main.go)依次执行:
- 额外 CA 证书配置:当
SLS_DISABLE_EXTRA_CA_CERTS被设置且值不为"false"时,调用certs.ConfigureHTTPRootCAs()(见下文"自定义 CA 证书"一节); - 确保
~/.serverless/binaries存在:createServerlessDirectoryIfNotExists()以0755权限创建该目录; - 自更新处理:若第一个参数是
update(isUpdateCommand()),调用updateInstaller()下载新 launcher 并原地替换——实现上采用"三文件交换"策略:先下载到serverless.new,把当前可执行文件改名为serverless.old(并删除上一次的.old),再把.new改回正式文件名,且在新文件改名前显式Close(),以避免 Windows 上"文件被占用"错误(main.go); - 本地 v3 兼容性让位:
runLocalVersionIfAvailable()检查当前目录node_modules/serverless/bin/serverless.js与本地package.json,若项目以devDependencies声明了< v4.0.0的 serverless,则直接用node执行本地 v3 入口并透传全部参数、继承退出码——这是为了让全局安装的 v4 launcher 与仍在使用本地 v3 的项目共存(main.go); - 解析配置路径 → 解析版本 → 校验 Node → 启动框架:最终执行
node <releasePath>/package/dist/sf-core.js <原始参数>。若命令行含--debug,会额外注入NODE_OPTIONS=--enable-source-maps环境变量。
一个值得注意的实现细节:launcher 用signal.Notify注册了os.Interrupt并消费信号,注释说明目的是防止 Go 进程在子 Node 进程处理 CTRL+C 之前先退出,让信号透传给子进程处理。
2. 配置文件解析
resolveConfigFilePath()(main.go)先扫描--config/-c(含=形式)参数;未显式指定时扫描当前工作目录,匹配serverless、serverless-compose、serverless.containers、serverless.ai四个基础名 ×yml、yaml、js、ts、cjs、mjs、json七种扩展名。对显式路径还支持~展开、相对路径拼接 CWD,且当路径不存在或指向目录时回退到本地扫描结果。
3. frameworkVersion 的提取
src/parse.go 按扩展名分派解析策略:
- YAML(
.yml/.yaml):用yaml.v3反序列化后直接取顶层frameworkVersion字符串; - JSON(
.json):json.Unmarshal后取顶层frameworkVersion; - JS/TS(
.js/.ts):使用宽松正则frameworkVersion\s*:\s*['"]*(.+)['"]提取,属于启发式解析;若文件中出现frameworkVersion但正则匹配失败,会向 stderr 打印 "Could not parse frameworkVersion from file, defaulting to auto-update" 并按未指定版本处理(即自动使用最新受支持版本); - 未找到配置文件时返回空版本 +
ERROR_NOT_IN_FRAMEWORK_DIR,语义上表示"onboarding"场景,直接使用最新版本。
4. 版本解析:稳定渠道与 Canary 渠道
入口是 src/version.go 的GetFrameworkVersion():
Canary 渠道(frameworkVersion: canary或canary-<commit-short-sha>):
canary:请求https://install.serverless-dev.com/releases.json取version字段,下载https://install.serverless-dev.com/archives/canary-<version>.tgz;- 固定 canary:版本串直接来自配置,下载
.../archives/<version>.tgz。固定值会经过双重安全校验——validateCanaryVersion()要求匹配^canary-[A-Za-z0-9._-]+$且不含..,containedReleasePath()进一步确保解压目录是~/.serverless/releases的直接子目录,从根上杜绝路径穿越; - canary 请求不受 24 小时节流限制,仅在配置确实使用 canary 渠道时才发起。
稳定渠道:
- 拉取版本索引
https://install.serverless.com/versions.json(受节流控制,见下文); - 若配置未指定
frameworkVersion:取supportedVersions数组最后一项(最新版本),并在CI环境变量存在时打印提示——建议通过frameworkVersion: ~<version>关闭自动更新,保持 CI 构建可复现; - 若指定了约束(如
^4.0.0):findClosestMatch()使用Masterminds/semver将约束解析为 semver constraint,把受支持版本列表降序排序后取第一个满足约束的版本; - blocked 版本:若请求的精确版本命中
blockedVersions,会打印 WARNING 提示存在已知 bug 或安全问题、建议升级,但仍然尊重用户的显式请求并继续安装。
5. 下载与安装 release
downloadFrameworkVersion()(src/version.go):
- 目标目录为
~/.serverless/releases/<version>;当该目录不存在或强制更新时才发起下载(HTTP 客户端超时 5 分钟,可响应 Ctrl+C/SIGTERM 中断); - 流式解压
.tgz(gzip+tar),每个条目的落盘路径都会做前缀校验(filepath.Clean后必须位于 release 目录内),防止恶意归档写出目录外; - 依赖安装策略:检查解压出的
package/package.json是否存在非空dependencies——- 存在:在
package/下执行npm install --no-audit --no-fund --no-progress,成功时静默,失败时向 stderr 输出目录、命令、错误与完整输出,进程以非零码退出; - 不存在(新版"bundled archive"格式,依赖已内置):跳过 npm install,并调用
cleanupUnusedEsbuildBinaries()删除@esbuild下非当前平台的二进制目录(按goPlatformToEsbuildDir白名单精确匹配,如darwin-x64、linux-arm64、win32-x64),据源码注释说明约可节省 40MB 磁盘;
- 存在:在
- 成功后写入
metadata.json并打印✔ Installed Serverless Framework v<version>。
从源码看还有一层 README 未展开的兜底:若读取配置文件出现非预期错误,GetFrameworkVersion()会调用 src/local.go 的localReleaseFallback()——枚举~/.serverless/releases/下已存在的版本目录,按约束(缺省为*)挑最接近的本地已安装版本直接运行,避免网络或配置解析失败导致完全不可用。
6. Node 检查与框架启动
通过上述检查后,launcher 以node <releasePath>/package/dist/sf-core.js启动框架并原样透传 CLI 参数、stdio 与环境变量;子进程非零退出时 launcher 继承其退出码。
本地保存的文件
| 路径 | 内容与作用 |
|---|---|
~/.serverless/binaries/metadata.json | 结构为{ "version": string, "updateLastChecked": ISO8601 }。updateLastChecked用于节流版本索引的重新拉取;version仅信息性展示,不参与逻辑判断。实现见 src/metadata/metadata.go |
~/.serverless/binaries/versions.json | 版本索引缓存(supportedVersions、blockedVersions)。24 小时新鲜期内容解析时直接命中缓存;网络错误时作为离线回退 |
~/.serverless/releases/<version>/ | 解压后的框架 release 目录,含package/及已安装依赖;实际执行的入口是package/dist/sf-core.js |
~/.serverless/bin/serverless | launcher 二进制本体(由 install.sh 写入) |
HTTP 调用与节流策略
| 请求 | URL | 节流 | 说明 |
|---|---|---|---|
| 版本索引(稳定渠道) | https://install.serverless.com/versions.json | 最多每 24 小时一次,以metadata.json.updateLastChecked为键 | 成功拉取后写入versions.json缓存并刷新时间戳;拉取或解析失败时回退到已有缓存 |
| Canary 元数据 | https://install.serverless-dev.com/releases.json | 不节流 | 仅当使用 canary 渠道时请求 |
| 稳定版 release 包 | https://install.serverless.com/archives/serverless-<version>.tgz | 无 | 目标 release 目录缺失或强制更新时下载 |
| Canary release 包 | https://install.serverless-dev.com/archives/<canary-version>.tgz(最新 canary 为canary-<x>.tgz) | 无 | 同上 |
| 安装器自更新 | <安装主机>/installer-builds/serverless-<os>-<arch> | 无 | 仅serverless update时触发 |
24 小时节流的具体实现:metadata.go 的ReadVersionsFromCache()优先比较metadata.UpdateLastChecked + 24h与当前时间(而非文件 mtime),force=true时跳过缓存直连网络。
更新策略(何时会下载)
- 框架 release 仅在以下情况下载:
- 解析出的
~/.serverless/releases/<version>目录不存在; - 用户通过
serverless update或SERVERLESS_FRAMEWORK_FORCE_UPDATE=true显式强制更新。
- 解析出的
- 24 小时节流只约束刷新版本索引,不约束 release 安装本身——也就是说,即使索引是缓存命中的,只要目标版本目录不存在,仍会正常下载对应 release。
- 运行
serverless update成功后,若解析出的最新版本与当前版本不同,会额外打印黄色提示 "A new version, , has been released. Update yourframeworkVersionproperty to use it"。
环境变量一览
| 变量 | 作用 |
|---|---|
SERVERLESS_FRAMEWORK_FORCE_UPDATE | 设置后(任意值)触发一次全新的版本解析与 release 下载,即使匹配的 release 目录已存在 |
SLS_DISABLE_EXTRA_CA_CERTS | 值不为"false"时启用额外 CA 证书注入(即激活下述三个标准变量的处理) |
NODE_EXTRA_CA_CERTS | PEM 文件路径,可含多个根 CA |
SSL_CERT_FILE | PEM 文件路径,可含多个根 CA |
SSL_CERT_DIR | 目录列表,按 OS 路径分隔符分隔(Unix 为:,Windows 为;),目录下应包含 PEM 编码的 CA 文件 |
CI | 存在(且不为0,见 version.go 的IsCIEnvironment())时:安装器不再显示 spinner 动画,且未指定frameworkVersion时打印"建议固定版本以禁用自动更新"的提示 |
SLS_USE_CANARY | 仅 canary 编译变体中生效,为真时自更新从install.serverless-dev.com拉取 |
自定义 CA 证书
在存在私有 CA 或 TLS 拦截代理的环境中,NODE_EXTRA_CA_CERTS、SSL_CERT_FILE、SSL_CERT_DIR可以让安装器与框架下载额外信任指定 CA。实现位于 src/certs/certs.go:ConfigureHTTPRootCAs()以系统证书池为基底,把环境变量指向的 PEM 文件逐个追加(pool.AppendCertsFromPEM),SSL_CERT_DIR下会遍历普通文件并解析符号链接,最后将包含全部根 CA 的池替换进http.DefaultTransport.TLSClientConfig.RootCAs,从而让默认 HTTP 客户端(也就是索引拉取、archive 下载)全部生效。注意该行为需要SLS_DISABLE_EXTRA_CA_CERTS为非"false"值才会激活(从变量名与 main.go 的调用条件看,它承担了"开关"角色,具体命名语义可参考 README 的表述)。不可读的文件会被静默跳过。
Windows 代码签名
Windows 二进制serverless-windows-amd64使用通过 Azure Artifact Signing 签发的Serverless Inc证书进行 Authenticode 签名,并附 RFC 3161 时间戳。在受管环境中,可以按发布者规则(WDAC/AppLocker)加白名单,而不必依赖文件哈希。
验证方式:
Get-AuthenticodeSignature .\serverless-windows-amd64 | Format-List Status, SignerCertificate # 期望输出:Status: Valid, Signer: CN=Serverless Inc, ...或使用 Windows SDK:
signtool verify /pa serverless-windows-amd64需要注意:installer-builds/下的二进制在每次 launcher 发布时原地覆盖,因此按文件哈希做 pin 的校验会在每次发布后失效。如果杀软误报,应先检查 Authenticode 签名,再向厂商提交误报申诉。
签名本身走的是无凭据托管流程:发布工作流(README 中指明为.github/workflows/release-binary-installer.yml)通过id-token: write权限获取 GitHub OIDC token,由 Azure 侧的联邦身份凭据换取签名权限;该 Azure 身份仅持有Artifact Signing Certificate Profile Signer角色。仓库需配置 secretsAZURE_TENANT_ID、AZURE_CLIENT_ID、AZURE_SUBSCRIPTION_ID,以及 variablesAZURE_TRUSTED_SIGNING_ENDPOINT、AZURE_TRUSTED_SIGNING_ACCOUNT、AZURE_TRUSTED_SIGNING_CERT_PROFILE。
错误处理与回退
- 拉取版本索引失败 → 若存在则回退到缓存的
versions.json; - 版本索引解析失败 → 同样回退缓存副本;
- 请求 canary 元数据失败或 JSON 畸形 → 命令直接失败并给出清晰错误信息(canary 无缓存可回退);
- release 安装期间
npm install失败 → 合并输出与退出码打到 stderr,进程非零退出; - 从源码结构看还有两处增强:下载/解压中途被 Ctrl+C 或 SIGTERM 打断时,未完成的 release 目录会被
defer清理并提示重新运行serverless update;tar 条目与 canary 版本号均做了路径穿越防护(见 version.go 的containedReleasePath与解压循环内的前缀校验)。
支持的配置格式与解析规则小结
frameworkVersion可写在 YAML(serverless.yml)、JSON(serverless.json)中,也可从serverless.js/serverless.ts/serverless.cjs用正则启发式提取;- 约束(如
^4.0.0、~4.15.0)按 semver 与versions.json中的受支持列表匹配,取满足约束的最高版本; - 精确命中的 blocked 版本会给出告警但仍按用户请求安装;
frameworkVersion: canary进入 canary 渠道,canary-<commit-short-sha>则直接固定到某次 canary 构建。
单元测试方面,src/version_test.go、src/parse_test.go、src/metadata/metadata_test.go 等文件对版本匹配、解析与缓存逻辑提供了验证,可通过make test(即go test ./...)在本地运行。
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考