Deno 中不可变的脚本如何帮助 Windmill.dev (YC S22) 构建生产级运维
这是一篇客座博客,由 Ruben Fiszel 撰写,他是 Windmill.dev 的创始人兼 CEO。
Windmill.dev 是一个开源开发者平台,企业可以使用脚本构建内部工作流程和 UI。超过 300 家公司,包括像 PhotoRoom 这样的企业客户,都将 Windmill 作为其生产基础设施的核心部分,用于各种操作,例如 ETL 管道、将内部和外部 API 整合在一起等等。
由于企业依赖 Windmill 来处理由脚本组成的关键任务工作流程,因此我们的技术必须是高性能、安全且灵活的
- 脚本的最小冷启动时间
- 安全地运行不受信任的任意代码
- 一种分享和组合脚本的简单方法
Windmill 可以运行 Python、Typescript、Bash 和 Go 代码。对于 TypeScript,我们选择 Deno 是因为其性能、安全性以及创建自包含脚本的能力。
脚本作为一等公民
由于脚本的灵活性和可控性,脚本是构建工作流程和 UI 的最小“单元”。 Windmill 工作流程由各种脚本组成。
一个简单的单分支流程示例,用于在 HackerNews 消息包含提及并进行情感分析时发布到 Slack。
上面的示例表明,工作流程不仅由现成的脚本组成,还使用不同语言的脚本,具有各种输入和资源。
由于脚本用于各种工作流程,因此它们的可共享性和一致性非常重要。此外,Windmill 是社区驱动的:脚本在 Hub 上共享,所有批准的脚本都集成到产品中。
根据这些要求,脚本必须是可靠、安全且高性能的。只有 Deno 能在一个平台上提供所有这些。
Deno 使脚本不可变
Windmill 期望每个脚本都公开一个 main 函数,并在同一文件中声明它们的依赖项。解析 main 函数的参数以推断有效负载的相应 JSON 模式,从而触发此类脚本。这种模型与 Deno 非常契合。
Deno 的依赖项要求可以在使用它们的同一文件中声明。此外,import 语句可以指定正在使用的精确版本,即使是 npm 导入也是如此
import mysql from "npm:mysql2@^2.3.3/promise";
import * as wmill from "https://deno.land/x/[email protected]/mod.ts";
当指定依赖项版本时,我们可以保证脚本执行的可重现性,而与环境无关。最重要的是,共享 Deno 脚本就像共享单个文件一样容易——无需单独的依赖项清单,例如 package.json。
一旦创建或更新脚本,脚本的新版本就会与不可变的永久哈希关联,然后与相关元数据一起存储在 Postgres 中。
借助 Deno,Windmill 上的脚本是不可变的,并且是一等公民——整个社区可共享、可组合的构建块。
使用 Deno 安全地运行任意代码
Windmill 在多租户环境中运行用户生成的代码,因此每个脚本的执行都必须隔离(对于自托管安装,为方便起见,可以禁用这些安全措施)。Deno 的 选择性许可模型 使我们能够精细地控制每次执行的访问权限。
对于 Windmill 中使用其他语言的脚本,沙箱化是通过 NSJail 实现的,但这会带来很大的性能开销,并且使用和配置起来很复杂。Deno 由于其默认安全特性,在多租户环境中的性能方面具有优势。
Deno 的最小冷启动
借助 Deno 与 V8 和 依赖项的不可变缓存 的集成,我们已经能够实现 15 毫秒的 Deno 冷启动时间
缓存的依赖项也通过 S3 支持的同步系统在集群的工作进程之间传播,因此任何引用缓存依赖项的 Deno 脚本都不需要下载它,从而获得性能优势。
执行 Deno 脚本的冷启动时间约为 15 毫秒,这意味着大多数轻量级脚本的端到端运行时间为 30 毫秒。
下一步是什么?
我们很高兴在 Windmill 的生产环境中使用 Deno——它简化了开发,增加了一层安全性,并确保了我们企业客户的最佳性能。
未来,我们的目标是通过更深入地集成 Deno 并在进程中运行它,来匹配无服务器/AWS lambda 的性能。
不要错过更新——在 Twitter 上关注我们。