cloudflare/workerd

GitHub: cloudflare/workerd

Cloudflare 开源的 JavaScript/WebAssembly 边缘运行时,支持本地开发测试和自托管部署 Workers 应用。

Stars: 7558 | Forks: 547

# 👷 `workerd`, Cloudflare 的 JavaScript/Wasm 运行时 ![Banner](/docs/assets/banner.png) `workerd`(发音为 "worker-dee")是一个 JavaScript / Wasm 服务器运行时,基于驱动 [Cloudflare Workers](https://workers.dev) 的相同代码构建。 你可以将其用于: * **作为应用服务器**,用于自托管为 Cloudflare Workers 设计的应用程序。 * **作为开发工具**,用于在本地开发和测试此类代码。 * **作为可编程 HTTP 代理**(正向或反向),用于高效地拦截、修改和 路由网络请求。 ## 简介 ### 设计原则 * **服务器优先:** 专为服务器设计,而非 CLI 或 GUI。 * **基于标准:** 内置 API 基于网络平台标准,例如 `fetch()`。 * **纳米服务:** 将你的应用程序拆分为像微服务一样解耦且独立部署的组件,但具有本地函数调用的性能。当一个纳米服务调用另一个时,被调用者在同一线程和进程中运行。 * **同构部署:** 与其将不同的微服务部署到集群中的不同机器,不如将所有纳米服务部署到集群中的每台机器,从而使负载均衡更加容易。 * **能力绑定:** `workerd` 配置使用能力而非全局命名空间来连接纳米服务与彼此及外部资源。结果是代码更具可组合性——并且对 SSRF 攻击免疫。 * **始终向后兼容:** 将 `workerd` 更新到新版本绝不会破坏你的 JavaScript 代码。`workerd` 的版本号只是一个日期,对应于该版本支持的最大["兼容性日期"](https://developers.cloudflare.com/workers/platform/compatibility-dates/)。你始终可以将 worker 配置为过去的日期,`workerd` 将模拟该日期存在的 API。 [阅读博文以了解更多关于这些原则的信息。](https://blog.cloudflare.com/workerd-open-source-workers-runtime/) ### 警告:这是测试版。正在进行中 尽管 `workerd` 的大部分代码已在 Cloudflare Workers 中使用多年,但 `workerd` 的配置格式和顶级服务器代码是全新的。我们在生产环境中运行它的经验还不多。因此,会有一些粗糙的边缘,甚至可能是一些荒谬的错误。部署到生产环境需自担风险(但请告诉我们出了什么问题!)。 在 `workerd` 脱离测试版之前,配置格式可能会以不向后兼容的方式更改,但在此之后应保持稳定。 一些注意事项: * **常规错误日志记录** 比较别扭。传统上,我们将错误日志分为“应用程序错误”(例如 Worker 从 JavaScript 抛出异常)和“内部错误”(Workers 团队应解决的实现中的错误)。然后我们将这些错误发送到完全不同的地方。在 `workerd` 世界中,服务器管理员希望同时看到这两者,因此日志记录变得完全不同,目前有点混乱。目前,使用 `--verbose` 标志运行 `workerd` 可能会有所帮助,这会导致应用程序错误以与内部错误相同的方式写入标准错误(但也可能会产生更多噪音)。我们将努力使开箱即用的体验更好。 * **二进制包** 仅通过 npm 提供,而不作为发行版包提供。这对于使用 Miniflare 进行测试很有效,但对于实际上根本不使用 Node 的生产服务器来说比较别扭。 * **多线程** 尚未实现。`workerd` 在单线程事件循环中运行。目前,为了利用多个核心,我们建议运行多个 `workerd` 实例并在它们之间平衡负载。我们可能很快会为此添加一些内置功能。 * **性能调优** 尚未完成,这里有唾手可得的成果。`workerd` 目前的性能尚可,但并不 spectacular。实验表明,通过调整编译器优化标志和内存分配器,我们大致可以在 "hello world" 负载测试中将性能提高一倍。 * **Durable Objects** 目前始终在使用本地磁盘存储的请求它们的同一台机器上运行。这对于测试和适合单台机器的小型服务来说已经足够了。然而,在可扩展的生产环境中,你可能希望 Durable Objects 分布在多台机器上,并始终为同一对象选择同一台机器。 * **参数化 Worker** 尚未实现。这是配置模式中指定的一项新功能,在 Cloudflare 上没有任何先例。 * **测试** 对于大多数 API 来说明显缺失。这是因为我们过去五年使用的测试工具与代码库的内部版本紧密相关。理想情况下,我们需要将这些测试转换为新的 `workerd test` 格式并将它们移动到此 repo;这是一项正在进行的工作。目前,我们将依靠内部测试来发现错误。我们理解这对于试图测试其更改的外部贡献者来说并不理想。 * **文档** 增长迅速,但绝对仍在进行中。 ### 警告:`workerd` 不是加固的沙箱 `workerd` 试图隔离每个 Worker,使其只能访问配置为访问的资源。然而,`workerd` 本身并不包含针对实现错误可能性的适当深度防御。当使用 `workerd` 运行可能的恶意代码时,你必须在其内运行适当的安全沙箱,例如虚拟机。特别是 Cloudflare Workers 托管服务[使用了许多额外的深度防御层](https://blog.cloudflare.com/mitigating-spectre-and-other-security-threats-the-cloudflare-workers-security-model/)。 话虽如此,如果你发现允许恶意代码脱离 `workerd` 的错误,请将其提交给 [Cloudflare 的漏洞赏金计划](https://hackerone.com/cloudflare?type=team) 以获得奖励。 ## 入门指南 ### 支持的平台 理论上,`workerd` 应该适用于 V8 支持的任何 POSIX 系统和 Windows。 实际上,`workerd` 在以下平台上经过测试: * Linux 和 macOS(x86-64 和 arm64 架构) * Windows(x86-64 架构) 在其他平台上,你可能需要进行一些调整才能使其工作。 ### 构建 `workerd` 要构建 `workerd`,你需要: * Bazel * 如果你使用 [Bazelisk](https://github.com/bazelbuild/bazelisk)(推荐),它将自动下载并使用正确版本的 Bazel 来构建 workerd。 * 在 Linux 上: * 我们使用 clang/LLVM 工具链构建 workerd 并支持版本 19 及更高版本。早期版本的 clang 可能仍然可以工作,但未得到官方支持。 * Clang 19+(例如 Debian Bookworm 上的 `clang-19` 包)。如果 clang 安装为 `clang-`,请在你的 PATH 中创建一个名为 `clang` 的符号链接,或者在 `bazel` 命令行中使用 `--action_env=CC=clang-` 来指定编译器名称。 * libc++ 19+(例如 `libc++-19-dev` 和 `libc++abi-19-dev` 包) * LLD 19+(例如 `lld-19` 包)。 * `python3`、`python3-distutils` 和 `tcl8.6` * 在 macOS 上: * Xcode 16.3 安装(在 macOS 15 及更高版本上可用)。尚未测试仅使用 Xcode Command Line Tools 进行构建,但也应该可以工作。 * Homebrew 安装的 `tcl-tk` 包(提供 Tcl 8.6) * 在 Windows 上: * 从 Microsoft Store 安装 [App Installer](https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget) 以获取 `winget` 包管理器,然后从管理员提示符运行 [install-deps.bat](tools/windows/install-deps.bat) 以安装 bazelisk、LLVM 以及在 Windows 上构建 workerd 所需的其他依赖项。 * 将 `startup --output_user_root=C:/tmp` 添加到用户目录中的 `.bazelrc` 文件。 * 在命令行开发时,首先在你的 shell 中运行 [bazel-env.bat](tools/windows/bazel-env.bat) 以选择工具和 Windows SDK 版本,然后再运行 bazel。 然后,你可以在命令行使用以下命令构建 `workerd`: ``` bazel build //src/workerd/server:workerd ``` 你可以传递 `--config=release` 以在发布模式下编译: ``` bazel build //src/workerd/server:workerd --config=release ``` 你也可以按照 [docs/vscode.md](docs/vscode.md) 中的说明使用 Visual Studio Code 进行构建。 编译后的二进制文件将位于 `bazel-bin/src/workerd/server/workerd`。 如果你在安装某些依赖项(如 clang 或 libc++)之前运行了 Bazel 构建,然后安装了依赖项,则必须重新同步本地缓存的工具链,或者清理 Bazel 的缓存,否则可能会遇到奇怪的错误: ``` bazel fetch --configure --force ``` 如果失败,你可以尝试: ``` bazel clean --expunge ``` 缓存现在将被清理,你可以尝试再次构建。 如果你安装了相当新的 clang 包,你可以构建一个性能更高的 workerd 发布 版本: ``` bazel build --config=thin-lto //src/workerd/server:workerd ``` ### 代码覆盖率(仅限 Linux) 代码覆盖率仅在 Linux 上受支持。要生成代码覆盖率报告,你需要安装 LLVM 覆盖率工具(`llvm-profdata` 和 `llvm-cov`): ``` sudo apt-get install llvm ``` 如果你的发行版安装了带版本号的二进制文件(例如 `llvm-profdata-19`),请创建符号链接: ``` sudo ln -sf /usr/bin/llvm-profdata-19 /usr/local/bin/llvm-profdata sudo ln -sf /usr/bin/llvm-cov-19 /usr/local/bin/llvm-cov ``` 然后使用以下命令运行覆盖率: ``` bazel coverage //... ``` 或者使用 just 命令,它还会生成 HTML 报告: ``` just coverage ``` ### 配置 `workerd` `workerd` 使用以 Cap'n Proto 文本格式编写的配置文件进行配置。 一个简单的 "Hello World!" 配置文件可能如下所示: ``` using Workerd = import "/workerd/workerd.capnp"; const config :Workerd.Config = ( services = [ (name = "main", worker = .mainWorker), ], sockets = [ # Serve HTTP on port 8080. ( name = "http", address = "*:8080", http = (), service = "main" ), ] ); const mainWorker :Workerd.Worker = ( serviceWorkerScript = embed "hello.js", compatibilityDate = "2023-02-28", # Learn more about compatibility dates at: # https://developers.cloudflare.com/workers/platform/compatibility-dates/ ); ``` 其中 `hello.js` 包含: ``` addEventListener("fetch", event => { event.respondWith(new Response("Hello World")); }); ``` [完整的参考文档由 workerd.capnp 中的注释提供。](src/workerd/server/workerd.capnp) [这里还有一个示例配置文件库。](samples) (TODO:提供更详细的教程。) ### 运行 `workerd` 要提供你的配置服务,请执行: `workerd serve my-config.capnp` 有关命令行用法的更多详细信息,请使用 `workerd --help`。 预构建的二进制文件通过 `npm` 分发。运行 `npx workerd ...` 以使用这些二进制文件。如果你运行的是预构建的二进制文件,则需要确保你的系统安装了正确的依赖项: * 在 Linux 上: * glibc 2.35 或更高版本(例如 Ubuntu 22.04、Debian Bookworm 上已包含) * 在 macOS 上: * macOS 13.5 或更高版本 * Xcode 命令行工具,可以使用 `xcode-select --install` 安装 * x86_64 CPU 至少具有 SSE4.2 和 CLMUL ISA 扩展,或 arm64 CPU 具有 CRC 扩展(在 armv8.1-a 下默认启用)。这些扩展受所有最近的 x86 和 arm64 CPU 支持。 ### 使用 `wrangler` 进行本地 Worker 开发 你可以使用 [Wrangler](https://developers.cloudflare.com/workers/wrangler/)(v3.0 或更高版本)通过 `workerd` 在本地开发 Cloudflare Workers。首先,运行以下命令以配置 Miniflare 使用此构建的 `workerd`。 ``` export MINIFLARE_WORKERD_PATH="/bazel-bin/src/workerd/server/workerd" ``` 然后,运行: `wrangler dev` ### 在生产环境中服务 `workerd` 旨在对其运行方式不加限制。 在生产环境中管理 `workerd` 的一个好方法是使用 `systemd`。特别有用的是 `systemd` 代表 `workerd` 打开特权套接字的能力,同时以非特权用户账户运行服务本身。为了帮助实现这一点,`workerd` 支持使用 `--socket-fd` 标志从父进程继承套接字。 这是一个系统服务文件示例,假设你的配置定义了两个名为 `http` 和 `https` 的套接字: ``` # /etc/systemd/system/workerd.service [Unit] Description=workerd runtime After=local-fs.target remote-fs.target network-online.target Requires=local-fs.target remote-fs.target workerd.socket Wants=network-online.target [Service] Type=exec ExecStart=/usr/bin/workerd serve /etc/workerd/config.capnp --socket-fd http=3 --socket-fd https=4 Sockets=workerd.socket # 如果 workerd 崩溃,则重启它。 Restart=always # 在非特权用户账户下运行。 User=nobody Group=nogroup # 加固措施:不允许 workerd 运行 suid-root 程序。 NoNewPrivileges=true [Install] WantedBy=multi-user.target ``` 以及相应的套接字文件: ``` # /etc/systemd/system/workerd.socket [Unit] Description=sockets for workerd PartOf=workerd.service [Socket] ListenStream=0.0.0.0:80 ListenStream=0.0.0.0:443 [Install] WantedBy=sockets.target ``` (TODO:完全解释如何让 systemd 识别这些文件并启动服务。)
标签:AI工具, HTTP代理, JavaScript运行时, Nanoservices, PE 加载器, self-hosting, Serverless, SOC Prime, SSRF防护, Wasm, WebAssembly, 反向代理, 安全, 应用服务器, 开发工具, 开源, 数据可视化, 本地测试, 标准Web API, 正向代理, 网络请求处理, 超时处理, 边缘计算