云引擎 Node.js 运行环境
这篇文档是针对 Node.js 运行环境的深入介绍,如希望快速地开始使用云引擎,请查看 快速开始部署云引擎应用。
所有 Node.js 项目都必须在根目录包含一个 package.json 和启动脚本(如没有在控制台或 leanengine.yaml
内指定,请确保 package.json
内的 scripts.start
存在)才会被云引擎正确识别,云引擎也会从中读取项目对于环境的需求:
{
"name": "node-js-getting-started",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js -- "
},
"dependencies": {
"leancloud-storage": "^3.11.0",
"leanengine": "^3.3.2"
},
"devDependencies": {
"nodemon": "^1.18.7"
},
"engines": {
"node": "16.x"
}
}
如果你希望创建一个新的项目,推荐从我们的 Node.js 示例项目 开始。
启动命令
云引擎默认使用 npm start
来启动项目,你可以在 package.json
中修改 scripts.start
来使用不同的程序入口或向 node
添加额外的参数。
{
"scripts": {
"start": "node server.js"
}
}
在使用 命令行工具 本地调试时,lean up
会优先使用 npm run dev
来启动项目。
配置 Node.js 版本
在 package.json
的 engines.node
字段可以指定 Node.js 版本:
{
"engines": {
"node": "16.x"
}
}
你还可以设置为 *
表示总是使用最新(current)版本。
配置包管理器
云引擎目前支持以下包管理器:
云引擎会按照以下条件使用包管理器:
包管理器 | 条件 | 版本 |
---|---|---|
pnpm | 存在合法能被解析的 pnpm-lock.yaml | |
lockfileVersion: '6.0' 或更高 | 8 | |
lockfileVersion: 5.3 或更高 | 7 | |
其他 | 6 | |
Yarn 1 | 存在 yarn.lock | 1 |
Yarn 2+ | 不默认支持,需通过 Corepack 启用 | 2+ |
npm | 其他情况 | 使用 Node.js 默认提供的 npm |
实验性 Corepack 支持
由于 Corepack 还是实验性特性,云引擎不能保证对 Corepack 的支持是稳定的
通过给分组设置 ENABLE_EXPERIMENTAL_COREPACK
环境变量为任意非空字符串来启用实验性 Corepack 支持。
云引擎会通过调用 Corepack 读取 package.json
里的 packageManager
字段来自动识别、使用用户指定的包管理器,这也是目前唯一一种使用 Yarn 2+ 的方式。
假设有以下 package.json
:
{
"name": "example",
"packageManager": "yarn@4.0.2"
}
此时云引擎会自动调用 corepack prepare --activate
并识别包管理器为 Yarn 2+。
参考:Corepack
默认命令
云引擎默认运行的脚本会随着包管理器的变化而变化,如使用了 pnpm, npm ci
会变成 pnpm install --frozen-lockfile
。
云引擎只有在没有指定 installDevDependencies
为 true
且构建脚本为空(没有手动指定,package.json
里的 scripts.build
不存在)时才会省略 devDependencies 的安装。
阶段 | 包管理器 | 条件 | 命令 |
---|---|---|---|
install | npm | Node.js 10 以上且存在 package-lock.json 或 npm-shrinkwrap.json | npm ci |
npm install 或 npm install --omit=dev | |||
pnpm | pnpm install --frozen-lockfile 或 pnpm install --frozen-lockfile --prod | ||
Yarn 1 | yarn install 或 yarn install --production | ||
Yarn 2+ | yarn install |
请注意 Yarn 1 只会使用 yarn.lock
内解析的 URL 下载依赖且不会遵循用户设置的源,请选择合适的源,否则可能会增加构建时间。
安装依赖(package.json
)
云引擎会自动安装 package.json
中的依赖:
{
"dependencies": {
"leancloud-storage": "^3.11.0",
"leanengine": "^3.3.2"
},
"devDependencies": {
"nodemon": "^1.18.7"
}
}
在安装依赖的过程中,云引擎会正常触发 NPM 的生命周期脚本(Life Cycle Scripts),如 postinstall
、prepare
等。
因为云引擎会在云端安装依赖,所以命令行工具默认也不会上传 node_modules
目录;如果使用 Git 部署,也建议将 node_modules
目录添加到 .gitignore
中,使其不加入版本控制。
云引擎会上传 .yarn
文件夹,所以如果启用了 Yarn 2+ 的 PnP(Plug'n'Play)但不想使用 Zero-installs,请将 .yarn/cache
加入到 .gitignore
和 .leanignore
中
自定义构建过程
除了默认的构建过程和运行命令外,开发者还可以在 leanengine.yaml
中进一步地调整运行命令(run
)、依赖安装命令(install
)和构建命令(build
),覆盖默认的行为:
run: echo 'run another command'
install:
- {use: 'default'}
- echo 'install additional dependencies here'
build:
- echo 'overwrite default build command here'
详细的说明见 Reference: leanengine.yaml,下面是一些具体的例子:
为子项目安装依赖
install:
- use: default
- require:
- ./frontend/package.json
- ./frontend/package-lock.json
- cd frontend && npm ci
build:
- npm run build
- cd frontend && run build
这里我们在保留默认行为的同时,额外为 frontend
目录下的项目安装了依赖、运行了构建命令。
系统级依赖
在云引擎的线上环境中,开发者可以在 leanengine.yaml
中定义额外的系统级依赖:
systemDependencies:
- imagemagick
支持的完整列表见 Reference: leanengine.yaml。
构建日志
默认情况下构建过程中产生的日志不会显示到控制台,只有构建失败时,最后一个步骤的日志才会被显示在控制台上。
如需打印完整的构建日志以便调试,可以在部署时勾选「打印构建日志」或命令行工具添加参数 --options 'printBuildLogs=true'
。
健康检查
云引擎目前主要为 Web 应用优化,应用在启动后需要在环境变量 LEANCLOUD_APP_PORT
中指定的端口上提供 HTTP 服务,注意需要监听在 0.0.0.0
地址(所有接口)上,而不是一些框架默认的 127.0.0.1
。
在应用部署时,云引擎的管理程序会每隔一秒去检查应用是否启动成功,如果超过启动时间限制(默认 30 秒)仍未启动成功,即认为启动失败,部署会中止。在之后的运行过程中,也会有定期的健康检查来确保应用正常运行,如果健康检查失败,云引擎管理程序会自动重启你的应用。
健康检查会通过 HTTP 检查应用的首页(/
),如果返回 HTTP 2xx 的响应,就视作成功。
点击展开健康检查与云引擎 SDK 的关联
云引擎还会尝试检查由 SDK 处理的 /__engine/1/ping
,如果 SDK 接入正确,便不再要求首页(/
)返回 HTTP 2xx。
如果 云服务控制台 > 云引擎 > 管理部署 > 你的分组 > 设置 > 云函数模式 设置为「开启」或 leanengine.yaml
中 functionsMode
设置为 strict
,云引擎会检查 SDK 是否被正确地接入,否则会视作启动失败。
点击展开自定义启动时长(startupTimeout
)
启动时间限制默认为 30 秒,可设置范围为 15–120 秒,如需延长或缩短,可以在 leanengine.yaml
文件中设置:
startupTimeout: 60
云端环境
绑定自定义域名
云引擎需要设置域名才能访问。在 云服务控制台 > 云引擎 > 管理部署 > 你的分组 > 设置 > 访问域名 处可以绑定域名。
如果你绑定的域名以 stg-
开头(如 stg-api.example.com
),会自动关联到预备环境。
负载均衡和加速节点
所有对云引擎的 HTTP 或 HTTPS 请求都会经过负载均衡,负载均衡组件会处理 HTTPS 加密、重定向到 HTTPS、对响应进行压缩等一般性工作,因此云引擎上的程序不需要自己实现这些功能。同时负载均衡带来的一些限制,在云引擎程序内进行修改也无法越过,如:
/.well-known/acme-challenge/
开头的路径被用于自动管理证书,不会转发到云引擎程序。- 请求头(URL 和 header)每行最大 8K,总计最大 64K。
- 请求体积(上传文件体积)最大 100M。
- 连接或等待响应的超时时间为 60 秒。
获取客户端 IP 等信息
云引擎的负载均衡会在 HTTP header 中传递一些有关原始请求的信息:
X-Real-IP
: 请求的来源 IP。X-Forwarded-Proto
: 请求的来源协议(http
或https
)。Forwarded
: RFC 7239 规定的用于传递代理信息的头,包含 IP 和 协议。
在使用加速节点的情况下,以上的 HTTP header 中给出的实际上是加速节点的信息,而非原始请求信息。
在使用加速节点的情况下,还会有这些 HTTP header:
X-Forwarded-For
: 逗号隔开的多个 IP,其中第一个是原始请求 IP。
以上 HTTP 头中给出的信息并不可靠,云引擎无法确认其真实性,存在被伪造的可能。
在 Express 中:
app.get("/", function (req, res) {
console.log(req.headers["x-real-ip"]);
res.send(req.headers["x-real-ip"]);
});
中国大陆节点的云引擎应用会默认启用加速节点,如果确实需要准确的原始请求 IP,可以开通独立 IP 来绕过加速节点,更多关于加速节点与独立 IP 的区别见 域名绑定指南 § 云引擎域名。
重定向到 HTTPS
在绑定云引擎自定义域名时,可以选择「强制 HTTPS」,勾选后负载均衡组件会将 HTTP 的请求重定向到 HTTPS 的同一路径。
在使用加速节点的情况下,「强制 HTTPS」选项无法正确工作,仍需 在项目代码层面实现重定向。
加速节点缓存
如果你将自定义域名解析到加速节点(也包括云引擎的共享域名),那么加速节点会对请求进行缓存,加速节点会有一些默认的缓存规则。
默认会缓存的情况:
- 响应头中有
Last-Modified
(通常是静态资源,其中 HTML 最多缓存 60 秒)。
不会缓存的情况:
- 出错的响应(非 2xx)。
- 非幂等请求(如
POST
)。 - 响应头中没有
Last-Modified
(通常是动态接口)。
默认的缓存时长取决于文件类型和 Last-Modified
(越不常修改的文件缓存越久),你可以通过自行设置 Cache-Control
来覆盖默认的行为,边缘节点会尽可能遵守其中的要求,比如:
- 设置
Cache-Control: no-cache
来避免响应被缓存。 - 设置
Cache-Control: max-age=3600
来设置缓存时长(一小时)。
如果希望完全避免被缓存机制影响,可以开通独立 IP 来绕过加速节点,更多关于加速节点与独立 IP 的区别见 域名绑定指南 § 云引擎域名。
环境变量
云引擎平台默认提供下列环境变量供应用使用:
变量名 | 说明 |
---|---|
LEANCLOUD_APP_ID | 当前应用的 App ID 。 |
LEANCLOUD_APP_KEY | 当前应用的 App Key 。 |
LEANCLOUD_APP_MASTER_KEY | 当前应用的 Master Key 。 |
LEANCLOUD_APP_ENV | 当前的应用环境:开发环境没有该环境变量,或值为 development (通过命令行工具启动)。预备环境值为 stage 。生产环境值为 production 。 |
LEANCLOUD_APP_PORT | 当前应用开放给外网的端口,只有监听此端口,用户才可以访问到你的服务。 |
LEANCLOUD_API_SERVER | 访问存储服务时使用的地址。该值会因为所在数据中心等原因导致不一样,所以使用 REST API 请求存储服务或其他云服务时请使用此环境变量的值。 |
LEANCLOUD_APP_GROUP | 云引擎实例所在的组。当使用云引擎组管理功能时,该值为组的名称。 |
LEANCLOUD_REGION | 云引擎服务所在区域,值为 CN 或 US ,分别表示国内版和国际版。 |
LEANCLOUD_VERSION_TAG | 云引擎实例部署的版本号。 |
旧版云引擎使用的以 LC_
开头的环境变量(如 LC_APP_ID
)已经被弃用。为了保证代码兼容性,LC_
变量在一段时间内依然有效,但未来可能会完全失效。为了避免报错,建议使用 LEANCLOUD_
变量来替换。
你还可以在控制台上设置自定义的环境变量来存储配置信息。
日志
关于如何在控制台上查看日志,以及访问日志等更多内容,请看 云引擎平台功能 § 查看日志。
云引擎会收集应用打印到标准输出(stdout)和标准错误输出(stderr)的日志:
console.log("hello"); // stdout
console.error("some error"); // stderr
日志单行最大 4096 个字符,多余部分会被丢弃;日志收集速率最大 600 行每分钟,多余的部分会也被丢弃。
时区
云引擎使用北京时间(东八区)。
文件系统
你可以向 /home/leanengine
或 /tmp
目录写入临时文件,最多不能超过 1 GB。
云引擎每次部署都会产生一个新的容器,即使不部署系统偶尔也会进行一些自动调度,这意味着你 不能将本地文件系统当作持久的存储,只能用作临时存储。
如果你写入的文件体积较大,建议在使用后自动删除他们,否则如果占用磁盘空间超过 1 GB,继续写入文件可能会收到类似 Disk quota exceeded
的错误,这种情况下你可以重新部署一下,这样文件就会被清空了。
出入口 IP 地址
如果开发者希望在第三方服务平台(如微信开放平台)上配置 IP 白名单而需要获取云引擎的入口或出口 IP 地址,请进入 云服务控制台 > 云引擎 > 管理部署 > 云引擎分组 > 设置 > 出入口 IP 来自助查询。
中国大陆节点的云引擎应用会默认启用加速节点,取决于底层的供应商,入口 IP 将会非常频繁地变动。如果确实需要固定入口 IP,可以开通独立 IP。
我们会尽可能减少出入口 IP 的变化频率,但 IP 突然变换的可能性仍然存在。因此在遇到与出入口 IP 相关的问题,我们建议先进入控制台来核实一下 IP 列表是否有变化。
如需保持入口 IP 不变,建议为云引擎绑定独立 IP。
疑难问题
Node.js 项目的 devDependencies
没有安装?
云引擎会在部署时用 npm ci
为你安装项目依赖,包括 devDependencies
。
不过,如果项目的 Node.js 版本小于 10,或者项目目录下没有 lockfile,则会使用 npm install --production
安装依赖,相应地,devDependencies
中列出的依赖不会安装,可参考 默认命令。
如需安装 devDependencies
,请在项目的 leanengine.yaml
中指定 installDevDependencies: true
。
npm ERR! peer dep missing
错误怎么办?
部署时出现类似错误:
npm ERR! peer dep missing: graphql@^0.10.0 || ^0.11.0, required by express-graphql@0.6.11
说明有一部分 peer dependency 没有安装成功,因为 Node.js 版本小于 10 时,线上只会安装 dependencies 部分的依赖,所以请确保 dependencies 部分依赖所需要的所有依赖也都列在了 dependencies 部分(而不是 devDependencies)。
你可以在本地删除 node_modules,然后用 npm install --production
重新安装依赖来重现这个问题。
或者,你也可以考虑将项目升级到 Node.js 10 以上的版本。
路由超时设置
因为 Node.js 的异步调用容易因运行时错误或编码疏忽中断,为了减少在这种情况下对服务器内存的占用,也为了客户端能够更早地收到错误提示,所以需要添加这个设置,一旦发生超时,服务端会返回一个 HTTP 错误码给客户端。
使用 Express 框架实现自定义路由的时候,请求默认的超时时间为 15 秒,该值可以在 app.js
中进行调整:
// 设置默认超时时间
app.use(timeout("15s"));