跳到主要内容

深入了解云引擎

note

这篇文档希望向有经验的开发者介绍云引擎背后的更多细节,如希望快速地开始使用云引擎,请查看 快速开始部署云引擎应用

云引擎适合什么样的应用

云引擎是一个基于容器技术实现的、进程级别的运行环境,开发者只需关注应用的进程本身,而不需要关注操作系统级别的环境。

在云引擎上,应用的进程可以专注在实现业务逻辑,通过标准化的接口来和云引擎交互:

  • 应用可以使用 Git 来托管代码,云引擎会从指定的仓库拉取代码
  • 应用通过依赖清单(如 pacakge.json)来描述应用对环境的需求,云引擎会自动地准备好这些环境
  • 应用从环境变量中读取配置,云引擎提供了管理环境变量的能力
  • 应用本身不包含数据或共享状态无状态、通过网络来访问数据,云引擎提供了托管的数据库和缓存服务
  • 应用可以在云引擎上完成构建过程,云引擎提供了可自定义的构建机制
  • 应用可以以多进程的方式运行,以便横向扩展
  • 应用本身是完整的、可运行的(而不需要嵌入一个宿主环境),通过网络对外提供 HTTP 服务
  • 应用只需将日志写入到标准输出,云引擎会将日志收集起来以便查看
note

云引擎很大程度上受到了 12-Factor 的影响,你可以在它的官网上了解到更多构建现代化、可移植、易于扩展和维护的后端服务的方法论。

构建

云引擎针对多种语言提供了构建支持,我们称之为「运行环境(Runtime)」,云引擎会根据项目代码的结构去判断项目的类型,例如根目录包含 package.json 就会被认为是 Node.js 项目。

在开发者使用 Git 或命令行工具将代码上传到云引擎后,云引擎就会开始一个「构建」的过程,云引擎会根据项目的运行环境安装依赖(如 npm install)、构建可执行文件(如 go build)、执行用户自定义的命令(可在 leanengine.yaml 中配置)。

构建过程最后会生成一个「版本」用于后续的部署,其中包含了应用的源代码、下载的依赖和构建出的可执行文件(部分运行环境),版本有一个类似 20210913-150821 的编号,在应用内是唯一的。

为了加速构建的过程,云引擎采用了「分层缓存(Layered Cache)」的缓存机制,即如果一个步骤需要执行的操作没有变化,那么就不实际执行,直接采用前一次执行的结果(缓存);但如果某个步骤需要执行的操作发生了变化,那么从此之后的所有步骤都需要重新执行。举例来说如果项目的依赖清单(如 package.json)没有发生变化,那么就不会重新执行依赖安装,而是采用前一次安装好的结果,整个构建过程就会快很多。

caution

构建缓存只是一种加速构建的机制,云引擎并不保证依赖没有变化就一定能使用缓存。

caution

如果在项目依赖的版本指定为某个范围,且两次部署之间依赖声明文件无改动,但该依赖发布了新版本(新版本仍在指定的范围之内),那么,由于缓存机制的存在,在缓存过期前,仍会安装之前的依赖版本,而非指定范围内的最新版本。 如果希望这种情况下,始终安装指定范围内的最新版本依赖,那么需要使用 --no-cache 禁用缓存。

实例和部署

构建好的版本可以被部署到「实例」上,实例对应着服务器上的一个容器(一组进程),实际提供计算能力,对外提供服务。

平滑部署

标准版云引擎在进行部署、重启或自动的实例迁移时,会逐个启动新的容器,在新的容器工作正常后,旧的容器仍会保持运行至少 30 秒的时间,以便将正在处理的请求执行完。通过这样的平滑部署机制,在云引擎上部署新版本或重启时,几乎不会有请求失败,应用的处理能力也不会有明显下降。

应用可以接收 SIGTERM 信号来在退出前进行自定义的清理操作,默认情况下在收到 SIGTERM 信号后会有至少 10 秒的时间可以用作退出前的清理操作。

休眠和唤醒

云引擎的体验版实例(包括赠送的生产环境或赠送的预备环境实例)在一段时间没有外部请求后会休眠(停止运行),在有外部请求时再启动,从休眠中启动可能需要十几秒的时间,同时体验版实例每天最多保持运行 18 个小时,超过 18 小时会被强制休眠,即使有外部请求也不会启动。

自动的实例迁移

除了开发者进行的部署或重启,因均衡宿主机负载或宿主机需下线维护等原因,系统有时也会自动地触发实例迁移操作,这时也会使用平滑部署机制,因此对应用的影响很小,但在日志中会打印实例重启的信息、CPU 和内存图表也可能会有波动,属正常现象。

云函数

云函数是一种经过高度封装的函数计算功能,实际功能由云引擎 SDK 提供,本质上是与用户程序运行在同一个进程、同一个 HTTP 端口上的 HTTP 服务。

Hook 的实现方式和云函数非常相似,区别在于使用了特殊的命名、调用者是内网的数据存储或即时通讯服务,同时 SDK 会对调用者的来源 IP 进行检查,确保来自内网。

一个应用下不同分组中的云函数和 Hook 都属于同一命名空间,这意味着不同的云函数可以由不同的分组提供、使用不同的语言进行开发。在部署应用到云引擎时,云引擎会和 SDK 通讯获取云函数的列表,得出云函数名字和分组的映射关系,后续负载均衡会根据这个映射关系将请求转发到正确的分组。