webernetes:在浏览器中运行 Kubernetes
摘要
Webernetes 是 ngrok 开发的一个开源库,使用 TypeScript 在浏览器中模拟 Kubernetes 集群,无需后端基础设施即可实现交互式 Kubernetes 内容。
查看缓存全文
缓存时间: 2026/06/18 20:06
ngrok/webernetes
Source: https://github.com/ngrok/webernetes
Webernetes
在你的浏览器中运行的 Kubernetes。观看实际效果,请查看演示!(https://webernetes-demo.ngrok.app/)
等等,什么?
本项目是 Kubernetes 项目的一个子集的移植,目的是让集群可以在浏览器中启动,无需任何后端服务器组件。
但为什么呢?
在 ngrok (https://ngrok.com/),我们希望制作关于 Kubernetes 的视觉和交互式内容。我们不想创建和维护用于启动真实集群的基础设施,因此我们决定创建一个基于浏览器的模拟器。希望和梦想是,这将使我们(和你!)能够创建可以长期存在的交互式 Kubernetes 内容,因为维护负担要小得多。
请注意: 这是非常实验性的。API 可能会变化,对不同资源的支持级别也可能会变化。我基本上是边做边摸索。
它是如何工作的?
首先,将 webernetes 安装为依赖项:
npm install @ngrok/webernetes
然后定义一个要在集群中运行的镜像。
Webernetes 不会运行来自 Docker Hub 的真实镜像,这也不是我们的目标。
import { BaseImage, type ProcessContext } from "@ngrok/webernetes";
class MyImage extends BaseImage {
// imageName 和 imageVersion 变量构成了镜像标签,
// 你将在容器定义中使用它。这里我们有 my-image:1.0,
// 但 webernetes 也知道如果你只指定 my-image 或
// my-image:latest 该怎么做。
static readonly imageName = "my-image";
static readonly imageVersion = "1.0";
// 如果容器清单中没有指定其他命令,这个命令将作为下面的 argv 传入。
readonly defaultCommand = ["server"];
// exec 是你镜像的主要入口点。它将通过容器定义中传入的命令行参数调用。
override async exec(ctx: ProcessContext, argv: readonly string[]): Promise<number> {
if (argv[0] !== "server") {
// 基础镜像定义了许多核心工具(cat、false、printenv 等),
// 所以如果我们不认识该命令,就回退到基础镜像。
return await super.exec(ctx, argv);
}
// 在此容器的 8080 端口上绑定。
ctx.listenHttp(8080, async (request) => {
return {
statusCode: 200,
body: "hello, world\n",
};
});
// 对于长时间运行的进程来说是必需的,以便在集群关闭时可以取消。
// 如果我们返回退出码 0,则上面的监听器将被注销,因为此容器已退出。
return await ctx.waitUntilKilled();
}
}
然后我们创建一个集群并向其注册我们的镜像。
import { Cluster } from "@ngrok/webernetes";
const cluster = new Cluster();
cluster.registerImage(MyImage);
然后我们可以运行集群并在其中使用我们的镜像生成一个 Pod。
// 默认情况下,这会启动一个 3 节点集群。目前无法更改。
await cluster.init();
await cluster.apply([
{
apiVersion: "v1",
kind: "Pod",
metadata: {
name: "my-pod",
labels: { app: "my-pod" },
},
spec: {
containers: [
{
name: "my-container",
image: "my-image:1.0",
},
],
},
},
]);
要向 Pod 发送请求,你需要创建一个 Service 来与它通信。在这种情况下,NodePort 服务提供了最简单的路径。
await cluster.apply([
{
apiVersion: "v1",
kind: "Service",
metadata: { name: "my-service" },
spec: {
type: "NodePort",
ports: [
{
port: 80,
targetPort: 8080,
nodePort: 31000,
protocol: "TCP",
},
],
selector: { app: "my-pod" },
},
},
]);
const resp = await cluster.fetch("http://node-1:31000");
const text = await resp.text(); // hello, world
Pod 也可以通过 HTTP 相互通信。要查看一些可运行示例中的工作原理,请查看 examples/ 目录下的代码。要查看完整的视觉演示,请查看 demo/ 目录下的代码。
已实现和未实现的内容
到目前为止,我将范围限定在我需要的部分,以便制作我想要的第一个内容——关于探针的内容。我还得说明,我绝非对 Kubernetes 的每个细节都无所不知的专家,因此很可能我遗漏了一些东西,或者我还没有完全实现我认为自己已经实现的东西。
节点
Cluster启动一个 3 节点集群(node-1、node-2、node-3),目前还不可配置。我希望将来支持任意添加和删除节点。
命名空间
支持,包括通过命名空间控制器删除命名空间内资源的特殊处理(与处理其他所有资源删除的垃圾回收控制器分开)。
Pod
已支持基本功能:Pod 可以包含 Container,这些容器可以监听 HTTP 流量端口。Pod 获得一个名称、一个 IP 地址,它们可以通过 DNS 名称或 IP 地址与其他 Pod 通信。它们可以接受环境变量。它们会接受探针检查。
尚不支持的内容:
- Init 容器或临时容器
- gRPC 探针
- 卷挂载
- 任何亲和性规则
- 资源
- 可能还有很多其他东西,但这些都是我想到的主要缺项。
Service
支持 ClusterIP 和 NodePort 类型的 Service,尚不支持 LoadBalancer 和 ExternalName。Pod 可以通过 Service DNS 名称通信,请求将使用轮询方式负载均衡到 Service 中的 Pod。
不支持 UDP。如果你仔细想想,TCP 也不完全支持——我没有模拟到网络栈那么深的层次。内容只能通过 HTTP 和 DNS 相互通信,我不预期将来需要或想要改变这一点。因此,IP 族之间的区别也没有被建模。
EndpointSlices
一个有趣的 Service 实现细节,在开始这个项目之前我完全不知道它存在。它们被创建来跟踪作为 Service 一部分的一组 Pod。通常它们每 100 个 Pod 分片一次,但我没有这样做,纯粹是为了简单。它们存在,工作方式符合预期,但目前没有分片。
Events
大部分支持下,我试图确保我们触发与 Kubernetes 相同的事件。我没有进行任何事件聚合,可能并非所有字段都存在且正确,但带有消息的事件确实会被触发并可被检查。
ReplicaSets
支持,通常由 Deployment 创建。ReplicaSet 控制器也已就位,并且与上游 Kubernetes ReplicaSet 控制器基本一致。
Deployments
支持,包括 RollingUpdate 和 Recreate 策略。Deployment 控制器已就位,并且与上游 Kubernetes Deployment 控制器基本一致。
开发
此仓库使用 mise (https://mise.jdx.dev/) 来固定工具链(Node、pnpm、ast-grep、ripgrep),以便在不同机器上可重现。Node 从 .nvmrc 读取,pnpm 从 package.json#packageManager 单一来源。安装 mise (https://mise.jdx.dev/installing-mise.html) 后,从全新克隆开始:
mise install # 安装固定的工具(并写入 mise.lock)
mise run setup # 从 lockfile 安装工作区依赖项
可用的 mise 任务:
mise run install—pnpm install --frozen-lockfile。mise run setup— 全新克隆后准备仓库(运行install)。mise run relock— 刷新mise.lock以匹配.nvmrc和package.json#packageManager。mise run doctor— 验证活动工具是否与提交的固定版本匹配。
要升级固定的版本,编辑 .nvmrc、package.json#packageManager 或 mise.toml 并运行 mise run relock。
包脚本(pnpm test、pnpm build、pnpm vibe-check 等)保持不变,安装依赖项后照常运行。
相似文章
浏览器标签中的类Linux内核 - 深入解析BrowserPod架构
深入解析BrowserPod架构,这是一个基于WebAssembly内核的浏览器内沙箱,完全在客户端运行兼容Linux的应用程序。本文涵盖内核设计、磁盘和网络子系统,以及其在浏览器中运行诸如Claude Code等工具的能力。
@K8sFM:字节跳动开源高性能 Kubernetes 调度器 Gödel,回馈开源社区 Wa…
字节跳动已将其高性能 Kubernetes 调度器 Gödel 开源,贡献给开源社区。
@larsencc: https://x.com/larsencc/status/2053862900289470765
本文详解了开源 browser-use 库的生产架构,阐述了如何利用 AWS Lambda、SQS 和 S3 扩展浏览器代理,实现状态管理与重试机制。
自托管的开发沙箱与预览URL(Docker、Go、无K8s)
sandboxed 是一个开源引擎,能将单个 Linux 机器转变为一系列隔离的开发沙箱,配备编码代理和实时预览 URL,支持自托管且易于安装。
Show HN: Kage – 将任意网站快照为单个二进制文件,支持离线浏览
Kage 是一个 Go 工具,它通过无头 Chrome 渲染页面、快照 DOM 并剥离 JavaScript,将网站克隆为可离线访问的文件夹。它输出静态 HTML 文件,无需网络即可浏览。