Deno 中不可变脚本如何帮助 Windmill.dev (YC S22) 构建生产级运维
这是一篇由 Ruben Fiszel(Windmill.dev 创始人兼 CEO)撰写的客座博客文章。
Windmill.dev 是一个 开源开发者平台,公司可以在此构建内部工作流和 UI,这些工作流和 UI 来自脚本。包括 PhotoRoom 在内的 300 多家公司,包括像 PhotoRoom 这样的企业客户,都将 Windmill 作为生产基础设施的核心部分,用于各种操作,例如 ETL 管道、将内部和外部 API 拼接在一起等等。
由于公司依赖 Windmill 来处理由脚本组成的关键任务工作流,因此我们的技术必须具有高性能、安全性和灵活性。
- 脚本的冷启动时间最短
- 安全运行不受信任的任意代码
- 一种简单的方式来共享和组合脚本
Windmill 可以运行 Python、Typescript、Bash 和 Go 代码。对于 TypeScript,我们选择了 Deno,因为它性能出色、安全可靠,并且能够创建自包含的脚本。
脚本作为一等公民
由于其灵活性和可控性,脚本是构建工作流和 UI 的最小“单元”。Windmill 工作流由各种脚本组成。
一个简单的单分支流的示例,该流在 HackerNews 消息包含提及且情绪分析完成时在 Slack 上发布。
上面的示例表明,工作流不仅由现成的脚本组成,还使用不同语言的脚本,这些脚本具有各种输入和资源。
由于脚本用于各种工作流,因此它们的共享性和一致性非常重要。此外,Windmill 是社区驱动的:脚本在 Hub 上共享,所有经过批准的脚本都集成到产品中。
有了这些要求,脚本必须可靠、安全和高效。只有 Deno 才能在一个中提供所有这些。
Deno 使脚本不可变
Windmill 要求每个脚本公开一个主函数并在同一个文件中声明其依赖项。主函数的参数被解析以推断触发此类脚本的有效负载的相应 JSON 模式。此模型非常适合 Deno。
Deno 的依赖项要求可以在使用它们的同一个文件中声明。此外,导入语句可以指定正在使用的精确版本,即使对于 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 毫秒的冷启动时间。
缓存的依赖项还会通过一个由 S3 支持的同步系统传播到集群的各个工作程序,因此任何引用缓存依赖项的 Deno 脚本都不需要下载它,从而可以获得性能优势。
执行 Deno 脚本的冷启动时间大约为 15 毫秒,这意味着大多数轻量级脚本的端到端运行时间为 30 毫秒。
下一步是什么?
我们很高兴在 Windmill 的生产环境中使用 Deno——它简化了开发,添加了一层安全性,并确保了我们企业客户的最佳性能。
未来,我们旨在通过更深入地集成 Deno 并将其在进程中运行,以匹配无服务器/AWS Lambda 的性能。
不要错过更新——在 Twitter 上关注我们.