webernetes:在浏览器中运行 Kubernetes

Lobsters Hottest 工具

摘要

Webernetes 是 ngrok 开发的一个开源库,使用 TypeScript 在浏览器中模拟 Kubernetes 集群,无需后端基础设施即可实现交互式 Kubernetes 内容。

<p>将 Kubernetes 移植到 TypeScript。演示网站地址:<a href="https://webernetes-demo.ngrok.app/" rel="ugc">https://webernetes-demo.ngrok.app/</a></p> <p><a href="https://lobste.rs/s/n2zqph/webernetes_kubernetes_browser">评论</a></p>
查看原文
查看缓存全文

缓存时间: 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-1node-2node-3),目前还不可配置。我希望将来支持任意添加和删除节点。

命名空间

支持,包括通过命名空间控制器删除命名空间内资源的特殊处理(与处理其他所有资源删除的垃圾回收控制器分开)。

Pod

已支持基本功能:Pod 可以包含 Container,这些容器可以监听 HTTP 流量端口。Pod 获得一个名称、一个 IP 地址,它们可以通过 DNS 名称或 IP 地址与其他 Pod 通信。它们可以接受环境变量。它们会接受探针检查。

尚不支持的内容:

  • Init 容器或临时容器
  • gRPC 探针
  • 卷挂载
  • 任何亲和性规则
  • 资源
  • 可能还有很多其他东西,但这些都是我想到的主要缺项。

Service

支持 ClusterIPNodePort 类型的 Service,尚不支持 LoadBalancerExternalName。Pod 可以通过 Service DNS 名称通信,请求将使用轮询方式负载均衡到 Service 中的 Pod。

不支持 UDP。如果你仔细想想,TCP 也不完全支持——我没有模拟到网络栈那么深的层次。内容只能通过 HTTP 和 DNS 相互通信,我不预期将来需要或想要改变这一点。因此,IP 族之间的区别也没有被建模。

EndpointSlices

一个有趣的 Service 实现细节,在开始这个项目之前我完全不知道它存在。它们被创建来跟踪作为 Service 一部分的一组 Pod。通常它们每 100 个 Pod 分片一次,但我没有这样做,纯粹是为了简单。它们存在,工作方式符合预期,但目前没有分片。

Events

大部分支持下,我试图确保我们触发与 Kubernetes 相同的事件。我没有进行任何事件聚合,可能并非所有字段都存在且正确,但带有消息的事件确实会被触发并可被检查。

ReplicaSets

支持,通常由 Deployment 创建。ReplicaSet 控制器也已就位,并且与上游 Kubernetes ReplicaSet 控制器基本一致。

Deployments

支持,包括 RollingUpdateRecreate 策略。Deployment 控制器已就位,并且与上游 Kubernetes Deployment 控制器基本一致。

开发

此仓库使用 mise (https://mise.jdx.dev/) 来固定工具链(Node、pnpm、ast-grepripgrep),以便在不同机器上可重现。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 installpnpm install --frozen-lockfile
  • mise run setup — 全新克隆后准备仓库(运行 install)。
  • mise run relock — 刷新 mise.lock 以匹配 .nvmrcpackage.json#packageManager
  • mise run doctor — 验证活动工具是否与提交的固定版本匹配。

要升级固定的版本,编辑 .nvmrcpackage.json#packageManagermise.toml 并运行 mise run relock

包脚本(pnpm testpnpm buildpnpm vibe-check 等)保持不变,安装依赖项后照常运行。

相似文章

浏览器标签中的类Linux内核 - 深入解析BrowserPod架构

Lobsters Hottest

深入解析BrowserPod架构,这是一个基于WebAssembly内核的浏览器内沙箱,完全在客户端运行兼容Linux的应用程序。本文涵盖内核设计、磁盘和网络子系统,以及其在浏览器中运行诸如Claude Code等工具的能力。