Oxide 上的 Kubernetes:客户需求如何塑造我们的集成

Hacker News Top 产品

摘要

Oxide 的解决方案软件工程师描述了客户反馈如何塑造他们为 Oxide 云平台构建的 Kubernetes 集成,包括对 Rancher、Omni 和 Cluster API 的支持。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/08/13 15:22

# Oxide 上的 Kubernetes:客户需求如何塑造我们的集成 | Oxide Computer Company 2024 年底,客户和潜在客户都迫切希望在 Oxide 上运行 Kubernetes,但我们没有任何受支持的集成来帮助他们实现这一点。 Kubernetes 和 Oxide 是天作之合。Kubernetes 通过标准扩展点来定义其期望的基础设施行为,而 Oxide 则通过 API 暴露实现这些行为所需的基础原语。集成的基础已经具备。缺少的是软件,以及对于客户究竟需要哪些集成的理解。 这就是我加入 Oxide 担任第一位解决方案软件工程师时的状况,[1 (https://oxide.computer/blog/kubernetes-on-oxide#_footnotedef_1)] 我的工作重点是构建软件来解决客户问题。我的第一个任务就是让在 Oxide 上部署和运维 Kubernetes 变得更加容易。 在我入职的第一周,我收到了两份帮助我上手的资源: 1. 客户提交的用于 Rancher 节点驱动程序的 pull request (https://github.com/oxidecomputer/rancher-machine-driver-oxide/pull/1) 2. RFD 493 初始 Kubernetes 集成 (https://rfd.shared.oxide.computer/rfd/0493) 的早期草案 这两份资源开启的工作,最终在反馈循环的推动下发展成了一项团队努力。我们没有在抽象层面设计集成,而是跟随客户从供应集群到运维工作负载过程中遇到的问题一步步推进。 这篇文章按照 Kubernetes 生命周期的各个阶段来讲述这些问题的演进,而不是严格按时间顺序。不同的供应工作流让我们接触到了 Rancher、Omni 和 Cluster API。运行集群需要基础设施调和,暴露应用暴露了网络空白,而有状态工作负载则暴露了存储限制。在每个阶段,客户的工作流都暴露出下一个空白,从而塑造了我们构建的集成,以及未来仍需推进的平台工作。 ## 如何在 Oxide 上供应 Kubernetes 集群? (https://oxide.computer/blog/kubernetes-on-oxide#_how_do_i_provision_a_kubernetes_cluster_on_oxide) 我们解决的第一个空白是供应。我们的近期目标是解除那位提交了 Rancher 节点驱动程序 pull request 的客户的阻塞。通过处理他们的用例,我们也能亲身体验在 Oxide 上创建 Kubernetes 集群,并帮助我们找出下一个要解决的问题。 没有任何一种供应方式能适配所有客户的工作流,因此我们最终发布了三种集成。 ### Rancher 节点驱动程序 (https://oxide.computer/blog/kubernetes-on-oxide#_rancher_node_driver) 在维护客户提交的集成之前,我们需要理解它所支持的工作流。我以前从未使用过 Rancher,也没有接触过节点驱动程序,因此审查这个贡献意味着要同时学习两者。 Rancher 节点驱动程序是一个可执行插件,它教 Rancher 如何在特定基础设施平台上创建和管理虚拟机。Oxide Rancher 节点驱动程序 (https://github.com/oxidecomputer/rancher-machine-driver-oxide) 将这些操作转换为 Oxide API 请求。一旦安装到 Rancher 中,它就能让客户将 Oxide 实例作为节点供应到 Rancher 管理的 Kubernetes 集群中。 测试确认客户实现可以正常工作。我合并了 pull request,添加了 CI/CD 和文档改进,并发布了初始版本。Oxide 正式拥有了第一个 Kubernetes 集成——而且已经有客户在生产环境中成功使用它了! 如果你是一家 Rancher 用户,希望在 Oxide 上运行 Kubernetes,请参阅我们的 Rancher 指南 (https://docs.oxide.computer/guides/integrations/rancher) 开始使用。 ### Omni 基础设施提供商 (https://oxide.computer/blog/kubernetes-on-oxide#_omni_infrastructure_provider) 客户表达了希望使用 Sidero Labs 的 Omni 来供应运行 Talos Linux 的 Kubernetes 集群的兴趣。Omni 通过基础设施提供商连接到各种基础设施平台,这些提供商是创建 Talos Linux 实例并向 Omni 注册它们的程序。 距离 KubeCon North America 2025 还有几个月,我们看到了与 Sidero Labs 合作构建并展示 Oxide Omni 基础设施提供商的机会。我们只有七周时间来完成它,才能赶上我们的 Oxide+Sidero 活动。[2 (https://oxide.computer/blog/kubernetes-on-oxide#_footnotedef_2)] 针对第二个供应平台进行构建,也将检验 Oxide API 在不同客户工作流下的表现。 集成工作揭示了 Omni 和 Talos Linux 中的多个问题。我将这些问题反馈给 Sidero Labs,见 siderolabs/omni#1633 (https://github.com/siderolabs/omni/discussions/1633),他们的团队非常愿意与我们合作——这是对 RFD 68 合作即共同价值观 (https://rfd.shared.oxide.computer/rfd/0068) 的一个很好的印证。 最令人难忘的问题是 siderolabs/talos#11948 (https://github.com/siderolabs/talos/issues/11948)。Oxide 使用 FAT12 文件系统来提供 cloud-init user-data,而不是 ISO 9660,但 Talos 的文件系统探测只尝试从 NoCloud 配置磁盘读取 ISO 9660 超级块。当读取失败时,探测就停止了,而不是尝试其他格式,例如 VFAT 或 MS-DOS。结果,Talos 从未读取包含加入 Omni 所需配置的 Oxide user-data。这个修复无法在 KubeCon 前发布,因此我们只能用一个相当有趣的变通方案。 > 目前的变通方法是在 user-data 中填充注释,以增加其大小,使其恰好使用 ISO 9660 超级块。 KubeCon 到来了,我们举办了一场 Oxide+Sidero (https://www.eventbrite.com/e/oxidesidero-at-kubecon-north-america-2025-tickets-1538869282449) 活动,展示了用于 Omni 的 Oxide 基础设施提供商 (https://github.com/oxidecomputer/omni-infra-provider-oxide)。客户现在可以使用这个基础设施提供商,将运行 Talos Linux 的 Oxide 实例作为节点供应到 Omni 管理的 Kubernetes 集群中。 如果你是一家 Omni 或 Talos Linux 用户,希望在 Oxide 上运行 Kubernetes,请参阅我们的 Omni 指南 (https://docs.oxide.computer/guides/integrations/omni) 开始使用。 ### Cluster API 提供商 (https://oxide.computer/blog/kubernetes-on-oxide#_cluster_api_provider) 我们最初编写 RFD 493 初始 Kubernetes 集成 (https://rfd.shared.oxide.computer/rfd/0493) 时,就知道我们想要为 Kubernetes Cluster API (CAPI) 构建一个基础设施提供商。Cluster API 提供了我们前两个集成所没有的东西——一个上游的、提供商可扩展的 API,用于管理集群,而无需依赖 Rancher 或 Omni 这样的第三方平台。 CAPI 允许运维人员通过 Kubernetes 自定义资源以声明式方式创建、扩缩、升级和删除 Kubernetes 集群。基础设施提供商负责处理平台特定的工作,例如创建和删除虚拟机。构建这样一个提供商是一项巨大的投入。当时,客户需求和工程能力还不足以支撑这项投入,因此该项目被推迟了。 后来,这两方面都发生了变化。客户开始要求 CAPI 提供商,解决方案软件工程团队也发展壮大了。我的队友 Josh 和 Brandon 接手了这项工作,并发布了 Cluster API Provider Oxide (CAPOx) (https://github.com/oxidecomputer/cluster-api-provider-oxide),让客户拥有了一种 Kubernetes 原生的方式来在 Oxide 上供应集群。 Cluster API 工作流还使用到我们的其他几个集成,让我们得以自举[3 (https://oxide.computer/blog/kubernetes-on-oxide#_footnotedef_3)]端到端的集群工作流。Kubernetes Image Builder (https://github.com/kubernetes-sigs/image-builder) 使用我们的 Packer 插件 (https://github.com/oxidecomputer/packer-plugin-oxide) 来创建可供 CAPI 使用的 Oxide VM 镜像,CAPOx 在供应实例时会使用这些镜像。使用 CAPOx 供应的集群还会使用单独安装的 Oxide cloud controller manager (CCM) (https://github.com/oxidecomputer/oxide-cloud-controller-manager) 来在运行时将 Kubernetes 与 Oxide 集成。 如果你想使用 Cluster API 在 Oxide 上供应 Kubernetes 集群,请参阅我们的 Cluster API 指南 (https://docs.oxide.computer/guides/integrations/cluster-api) 开始使用。 ## Kubernetes 如何跟踪 Oxide 实例? (https://oxide.computer/blog/kubernetes-on-oxide#_how_does_kubernetes_track_oxide_instances) 供应集成负责创建和管理 Oxide 实例,但它们并不会将这些实例与 Kubernetes 的 `Node` 对象进行调和。没有这种调和,集群就无法可靠地判断一个不可达的 Kubernetes 节点是暂时不可用,还是其背后的 Oxide 实例已经被删除了。 我们需要一个在每个集群中运行、能够与 Oxide API 通信、并持续将 Oxide 基础设施与 Kubernetes 状态进行调和的组件。Kubernetes 为此提供了一个标准扩展点:cloud controller manager (CCM) (https://kubernetes.io/docs/concepts/architecture/cloud-controller/)。CCM 允许特定基础设施的控制器将 Kubernetes 资源与基础设施提供商的 API 集成,而无需在 Kubernetes 本身中添加提供商特定代码。 我们构建了 Oxide cloud controller manager (https://github.com/oxidecomputer/oxide-cloud-controller-manager) 来将 Kubernetes 与 Oxide 连接起来。它的节点控制器保持 Kubernetes `Node` 对象与其背后的 Oxide 实例同步,记录实例 ID 和网络地址等详细信息,并报告每个实例是正在运行、已关闭还是不再存在。Kubernetes 使用这些信息来初始化节点,并在其背后的实例被删除时安全地移除这些节点。 CCM 不创建实例,也不供应集群。这仍然是 Rancher 节点驱动程序 (https://oxide.computer/blog/kubernetes-on-oxide#_rancher_node_driver)、Omni 基础设施提供商 (https://oxide.computer/blog/kubernetes-on-oxide#_omni_infrastructure_provider) 和 CAPOx (https://oxide.computer/blog/kubernetes-on-oxide#_cluster_api_provider) 等供应集成的工作。相反,它提供了一个跨这些供应工作流共享的运行时集成。 重要的是,构建 CCM 让我们在每个集群内部获得了一个持久的扩展点。随着 Oxide 的演进,我们可以在 CCM 中添加新的基础设施感知控制器,而不是更新每个供应集成。 有了这个运行时扩展点,我们就可以解决 Kubernetes 体验的另一个层面:暴露应用。CCM 架构还定义了一个用于 Kubernetes `LoadBalancer` 服务的 service 控制器,这为我们解决下一个客户问题提供了位置。 ## 如何使用 `LoadBalancer` 服务? (https://oxide.computer/blog/kubernetes-on-oxide#_how_do_i_use_loadbalancer_services) 客户期望云集成 Kubernetes 所具备的能力之一,是支持类型为 `LoadBalancer` 的 `Service` 对象。当用户创建此类服务时,Kubernetes 会要求云提供商的 service 控制器供应必要的基础设施,并将地址发布到 `Service` 的 status 中。但这里有一个问题:Oxide 当时还没有原生的负载均衡器。 然而,Oxide 确实有浮动 IP。浮动 IP 是来自机架外部 IP 池的地址,可以附加到实例上,也可以从实例上分离,从而使这些实例可以从其 VPC 外部访问。使用浮动 IP 为解除 `LoadBalancer` 服务的阻塞提供了一条路径。浮动 IP 会将流量传送到单个 Kubernetes 节点,然后 Kubernetes `Service` 数据面可以将流量分发到相应的 Pod。 要使其工作,需要考虑 Oxide 浮动 IP 在实例上如何呈现。它在两个重要方面对 guest 是透明的。首先,Oxide 在将入站流量发送到实例之前,会将其目的地地址转换为实例的内部 IP。其次,实例没有配置浮动 IP 的网络接口。 最终的流量流向如下: 使用浮动 IP 的 `LoadBalancer` 服务的流量流向。 `` ┌────────────────────────────────────────────────────────────┐ │ Client │ │ Request to floating IP: 45.154.216.233:80 │ └────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────┐ │ Oxide networking │ │ Translates destination to internal IP: 172.30.0.5:80 │ └────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────┐ │ Kubernetes node │ │ Packet arrives at internal IP: 172.30.0.5:80 │ └────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────┐ │ Kubernetes Service dataplane │ │ Selects a Service endpoint │ └────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────────┐ │ Pod │ │ Receives traffic on its target port │ └────────────────────────────────────────────────────────────┘ `` 这种地址转换带来了一个微妙的集成问题。Kubernetes `Service` 数据面需要将节点的内部 IP 视为 `Service` 前端,因为当数据包到达 guest 时,这才是实际携带的目的地地址。因此,service 控制器会在 `status.loadBalancer.ingress` 中发布两个条目:[4 (https://oxide.computer/blog/kubernetes-on-oxide#_footnotedef_4)] 1. 附加的浮动 IP,模式为 `Proxy` 2. 节点的内部 IP,模式为 `VIP` status 条目如下所示: `` status: loadBalancer: ingress: - ip: 45.154.216.233 ipMode: Proxy - ip: 172.30.0.5 ipMode: VIP `` 因此,`kubectl` 的输出看起来会有点不寻常: `` $ kubectl get service nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx LoadBalancer 10.106.122.233 45.154.216.233,172.30.0.5 80:30605/TCP 37h `` 用户会在 `EXTERNAL-IP` 列同时看到浮动 IP 和节点的内部 IP,尽管只有浮动 IP 可以从外部访问。这是一个不完美的抽象,但它让我们在等待 Oxide 原生负载均衡器的同时,支持了一个常见的 Kubernetes 工作流。 该实现目前支持 `externalTrafficPolicy: Cluster`,[5 (https://oxide.computer/blog/kubernetes-on-oxide#_footnotedef_5)] 这允许被选中的节点将流量转发到集群中任意位置的 `Service` endpoint。如果该节点消失,CCM 会将浮动 IP 移动到另一个符合条件的节点,并更新 `Service` status 中的内部地址。 当 Oxide 引入原生的负载均衡服务时,我们可以更新 service 控制器来使用它,而无需更改 Kubernetes 接口。客户将继续创建相同的 `LoadBalancer` 服务,只有背后的基础设施会发生变化。 要在你的集群上安装 Oxide CCM,请参阅我们的 CCM 指南 (https://docs.oxide.computer/guides/integrations/cloud-controller-manager) 开始使用。 ## 如何在 Kubernetes 中使用 Oxide 存储? (https://oxide.computer/blog/kubernetes-on-oxide#_how_do_i_use_oxide_storage_in_kubernetes) 在集群供应完成、与 Oxide 调和、并且可以从 VPC 外部访问之后,有状态工作负载的存储就成了下一个需要解决的层面。Kubernetes 用户通过 `Per

相似文章

求职面试教会我的 Kubernetes 知识

Hacker News Top

一位求职者发现,小型初创公司采用 Kubernetes 并非出于技术可扩展性,而是为了组织层面的好处,如一致性、共享知识和可追溯性。这篇文章反思了 Kubernetes 对小型团队的非技术优势。