从 LB Ingress 到 ZTM:集群服务暴露新思路

从 LB Ingress 到 ZTM:集群服务暴露新思路

上周六有幸参与了 KubeSphere 社区和 Higress 社区联合举办的「云原生 + AI Meetup 广州站」的活动。在会上,我分享了一篇关于「从 LB Ingress 到 ZTM:集群服务暴露新思路」的主题演讲。在这里,我将分享一下演讲的内容,同时将文章标题做了小调整。

集群服务暴露的需求

集群服务暴露的需求来自 Kubernetes 服务的虚拟化和网络隔离。众所周知,Kubernetes 的 Pod 是动态的,可能会频繁的删除、重建,重新调度到不同的节点,IP 地址也会随之变化。Kubernetes 使用 Service 来提供访问 Pod 的稳定接口,实现对服务的抽象。

Service 为 Pod 提供了一个稳定的 DNS 名称和虚拟 IP 地址,而不依赖于 Pod 的临时 IP。因此在集群内部的通信,通过 Service 的 ClusterIP 访问完全不存在问题。

不过 Service 的 ClusterIP 只能在集群内部访问,外部无法直接访问。Service DNS 名称的解析,只能在集群内部进行。这种网络隔离作为网络保护机制,确保 Pod 和 Service 的访问受限于集群内部。

然而,我们在实际应用中,往往需要将服务暴露到集群外部,以便外部用户访问。这时,我们就需要额外的组件来实现集群服务的暴露。尤其是在一些高级应用场景下,如多集群、多云等,更需要一种灵活、动态的方式来暴露集群服务。

集群服务的暴露方式

Kubernetes 对集群服务的抽象,提供了多种方式,设计外部访问的有 LoadBalancerNodePort,以及更高级的 Ingress。(Ingress 可能逐渐被 Gateway API 所取代,而二者实现上基本一致。我们下面讨论的 Ingress 泛指 Kubernetes 高级入口流量管理。)

这些方式的实现方式各有不同,各有优劣,适用于不同的场景。

LoadBalancer

LoadBalancer 是常见的暴露集群服务的方式之一。在云环境中通过云提供商的负载均衡器,可以将流量分发到集群内的多个 Pod 上;在 On-Prem 环境中,可以使用开源负载均衡器。咱们这里主要讨论开源负载均衡器。

kubectl get service
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx        LoadBalancer   10.43.177.15   192.168.2.1    80:32186/TCP   2m18s

在开源的服务均衡器中,有两类比较常见的实现。第一种的实现比较多,如 MetaLB、青云的 OpenELBkube-vip 以及 PureLB。这几种的实现方式基本类似,都会在集群的每个节点上运行一个控制器 Pod。这个控制器 Pod 会监听 LoadBalancer 类型 Service 的变化,然后为其配置 VIP,并将 VIP 绑定到节点的网卡上。最后这个 VIP 会通过 ARP(二层)或者 BGP(三层)协议通知外部网络。

如果到某个 VIP 存在多条路由,通常会使用等价多路径(ECMP)路由测量将流量分发到多个节点上,以实现负载均衡。

详细内容可以参考我过往的几篇文章:

第二种的实现方式相对简单不少,如 K3s 的 ServiceLB 也就是以前 Klipper,基于 iptables 和 HostNetwork。当监控到有 LoadBalancer 类型的 Service 创建时,ServiceLB 会为该 Service 创建一个 DS(DaemonSet),DS 在每个 Node 上创建代理容器,代理容器使用 hostNetwork 网络模式,hostPort 为 Service 的端口。

访问的入口是每个节点的 IP 地址,代理容器接收到流量后通过 iptables 转发到 Service 的 clusterIP,最终到达 Pod。

这种实现方式的优点是轻量级,不需要依赖云供应商的负载均衡器,成本低,简单易用,非常适合网络和资源受限的场景;缺点也明显,全节点监听端口,缺乏 TLS 终止等高级功能,转发需要节点网络高并发场景下成为瓶颈。

NodePort

如果说 LoadBalancer 是比较高级的方式,那么 NodePort 就是最简单直接的方式了。NodePort 是一种 Service 类型,用于在集群的每个节点上开放一个固定的端口(30000-32767),将该端口的流量转发到后端的 Pod。

针对每个 NodePort 类型的 Service,Kubernetes 会其分配一个端口。运行在每个节点上的 kube-proxy 会根据 Service 和 Pod 的信息,设置 iptables 规则。这样,当有流量到达节点的 NodePort 端口时,iptables 会先匹配到对应的 Service,然后将流量转发到后端的 Pod。

场景上,NodePort 适用于小型集群、测试环境等,不需要负载均衡,直接暴露节点 IP 和端口即可。缺点是无自动负载均衡,暴露节点 IP,安全性较低。

Ingress

Ingress 是 Kubernetes 内置的流量管理对象,定义了 HTTP 或 HTTPS 路由规则。Ingress 通过 Ingress Controller 实现,Ingress Controller 会根据 Ingress 资源的配置,动态的更新负载均衡器的配置,以实现流量的分发。慢慢地 Ingress 可能会被 Gateway API 所取代,后者有着更加灵活的扩展能力。但二者实现基本类似,都是有代理和控制器组成。

所以这里我们以大家最为熟悉的 Ingress 为例。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example
spec:
  rules:
  - http:
      paths:
      - backend:
          service:
            name: foo
            port:
              number: 8080
        path: /foo
      - backend:
          service:
            name: bar
            port:
              number: 8080
        path: /bar

Ingress 的定位是集群的单一入口,通过这个入口管理多个服务。通过基于域名、路径进行流量转发。与 LoadBalancerNodePort 不同,Ingress 工作在 OSI 模型的第七层,支持 HTTP、HTTPS 等应用层协议,提供更加高级的流量管理功能,如 TLS 卸载、限流、金丝雀发布、熔断、重定向等,还可以通过更加复杂的路由规则实现更加灵活的流量控制。

场景上,Ingress 适用于复杂路由规则、HTTPS 支持等场景。缺点是配置可能相对复杂,还需要依赖 Ingress Controller。更主要的是,Ingress 的代理本身仍然需要对外暴露,需要额外的 LoadBalancer 来实现。

介绍了这几种方式后,我们看下是否还有其他的方式来暴露集群服务。

零暴露面网络 ZTM

零暴露面网络 ZTM(Zero Trust Mesh) 是基于 HTTP/2 隧道的去中心化的、面向远程办公和 IoT 边缘网络等场景的开源网络基础设施软件。

ZTM 可以运行在任意的现存 IP 网络之上,包括但不限于局域网、互联网、容器网络等。ZTM 为保障应用安全提供了必要的网络基础,包括网络联通性、基于端口的访问控制、mTLS 加密的网络通道、基于证书的身份识别与访问控制、负载均衡等基础网络及安全能力。

 ZTM 可以多种设备上运行,支持 CPU 架构:x86、ARM、MIPS、RISC-V、LoongArch 等,以及操作系统:Linux、Windows、macOS、FreeBSD、Android 等。

ZTM 的架构

ZTM 的架构非常简单,

  • ZTM Agent:部署在需要接入零信任网络的设备上,包括个人计算机、服务器或边缘设备上,用于发起加密隧道,将设备的流量安全地转发到 ZTM Hub。
  • ZTM Hub:分布式部署的接入点,与每个 Agent 建立加密隧道,转发来自 Agent 的请求,实现多点接入和高可用性。

通过 ZTM Hub 的连通,Agent 之间可以在已有网络(局域网、互联网、容器网络等)之上组建一个安全的零信任网络,实现设备之间的安全通信。

这个网络在 ZTM 中的术语 Mesh。Mesh 是一个逻辑网络,由多个 Agent 组成,Agent 之间通过加密隧道连接。Agent 也可以加入多个 Mesh,实现与多个网络之间的互联。

ZTM 的特性

  • 零防火墙:全链路无需防火墙配置,管理更加简单。
  • 零端口:没有开放端口,轻松应对各种扫描。
  • 零运维:终端网络不需要虚拟网卡,不需要终端路由,不需要终端防火墙。
  • 零特权:Agent 运行的用户态,不需要特权配置,更加安全。
  • 零路由:基于服务发现的访问机制,无需复杂易错的路由配置。
  • 零信任:基于证书的身份识别,可信设备,全链路验证身份和访问控制。

ZTM App

zt-app 框架是一个基于 ZTM 的应用开发框架,提供了一套标准化的开发接口,使开发者能够更轻松地构建去中心化的应用。zt-app 框架的设计目标是“简单、易用、安全”,为开发者提供便捷的开发工具。

ZTM 是用 PipyJS 编写的,PipyJS 是一种专为 Pipy 设计的定制版 JavaScript 。开发人员可以使用 PipyJS 轻松地开发 ZTM App。

在 ZTM 中内置了几大关键 App:

  • zt-tunnel:在点对点之间建立安全的 TCP/UDP 隧道。
  • zt-proxy:SOCKS/HTTP 代理,从一个端点接收流量,另一个端点转发出去。
  • zt-script:在端点上远程执行脚本。
  • zt-terminal:远程访问端点上的 shell。

隧道 zt-tunnel

零信任隧道,消除了物理距离和网络隔离的限制,可以从任何地方访问设备。zt-tunnel 有两个核心概念:

  • 出口 Outbound:隧道的出口,位于目标设备的网络中。
  • 入口 Inbound:隧道的入口,位于访问方的网络中。

比如这个场景,我们有两个隔离的网络,通过 ZTM 打通组成了一个 ZTM 网络。在我们的办公网络中有两台设备:Windows 设备支持远程桌面访问;Linux 设备支持 SSH 访问。我们称这两个为远端设备

当我们需要从外面访问这两台设备时,我们只需要在办公网络的 ZTM Agent 上创建名为 tcp/rdptcp/ssh-vm 的两个出口,分别指向要访问的两台设备。

在访问侧的 ZTM Agent 上创建两个入口,分别指向上面创建的出口,并指定端口。当我们需要访问远端设备时,仅需访问本地 Agent 的地址和入口端口即可。

如果需要访问其他网络的设备,只需要其网络中运行 ZTM Agent 并连接到同一个 ZTM Hub 上,然后为设备添加出口和入口即可。

代理 zt-proxy

zt-tunnel 的方式访问设备时需要为每个服务都创建隧道的出口和入口,可能会比较繁琐。zt-proxy 可以简化这个过程,通过代理的方式访问设备。

zt-proxy 可以为访问方提供一个 HTTP 或 SOCKS 代理,在目标网络的 ZTM Agent 上,我们只需添加代理的目标地址(可以是 IP 网段,也可以是带有通配符的域名),完成。在访问方只需要目标的 IP 地址或者域名,通过代理即可访问目标设备了。

通过 IP 网段或者域名,可以实现设备的批量添加,非常适合多设备的场景。并且代理的方式,可以让我们像在同一个局域网中一样访问远程设备。

在了解过 ZTM 的基本概念和特性之后,我们可以看下如何使用 ZTM 来暴露集群服务。

使用 ZTM 暴露集群服务

可能有人已经想到了,我们可以使用 ZTM 来打通集群内外、甚至是多个集群的网络。

借助 ZTM Hub,我们可以在集群内外部署 ZTM Agent,打通与集群的连通性。这样即使在两个隔离的网络,我们也能通过 ZTM 的 HTTP/2 隧道到访问集群内的服务。

使用 zt-tunnel 暴露集群服务

在这个场景中,我们通过 ZTM 的 mesh 网络连通了集群内外的隔离网络。在集群内部,我们可以通过 zt-tunnel 为需要对外暴露的服务创建出口,然后在集群外部的 ZTM Agent 上创建入口,即可通过 ZTM 的隧道访问集群内的服务。

配置隧道的出入口后,我们可以通过 Agent 的地址以及配置的隧道入口的端口里访问集群内的服务 A 了,比如 curl http://192.168.1.30:8080

如果需要访问其他服务,只需要为其他服务创建出口和入口即可。

使用 zt-proxy 暴露集群服务

如果有大量的服务需要对外暴露,我们可以借助 zt-proxy 来简化这个过程。在连通 mesh 网络之后,在集群内部可以配置 zt-proxy 的目标为集群内服务的 DNS,如 *.svc.cluster.local,这样将集群内的所有服务作为代理的目标,也可以通过指定暴露某个命名空间下的服务。

然后通过集群外部 Agent 的代理来访问集群内部服务了,比如 curl -x http://192.168.1.30:8080 http://svc-a.svc:8080

通过控制暴露服务的 DNS,我们可以控制服务的暴露范围。

Demo

为了展示 ZTM 如何暴露集群服务,准备了一个 简单的 Demo

在这个 Demo 中通过 K3d 创建了两个网络完全隔离的集群,接着通过 ZTM 连通两个集群。然后,分别演示了如何通过 ZTM 的隧道和代理来进行跨集群的服务访问。

有兴趣的朋友可以自己动手试试。

方案对比

在介绍过集群服务暴露的方式和 ZTM 后,我们可以对比一下这几种方式。

特性 NodePort LoadBalancer ZTM
技术原理 iptables DNAT(BGP/ARP) HTTP/2 反向隧道
访问方式 节点 IP + 固定端口 云、开源负载均衡器 + 外部 IP 隧道、代理
网络依赖 节点需拥有固定或可访问的 IP 外部 IP 地址、BGP/ARP 网络环境 无需直接暴露网络
适用场景 小型集群或测试环境,快速暴露服务 公有云集群,需高可用和稳定外部访问服务 零暴露面安全场景,远程和跨集群访问
易用性 简单直接,配置少 自动化配置,依赖云平台或开源负载均衡器 需要部署 ZTM Agent 和 Hub

ZTM 更多应用场景

远程执行命令 zt-terminal

zt-tunnel 零信任终端,基于“设备 + 证书”身份认证和访问控制的远程命令行工具。获得授权后,一个设备可以在另一个设备上执行 shell 命令。

在远端的网络配置可以执行命令的用户。

ztm terminal config --add-user zt-user1

在本地网路中就可以访问指定设备上的 shell 了。

ztm terminal open ep-office

分布式文件共享 zt-cloud

在 mesh 网络中,可以通过 zt-cloud 来实现分布式文件共享。在某个 Agent 上发布的文件,可以通过 mesh 网络广播到其他的 Agent,实现文件的分发。

其他场景

这里列出来 ZTM 的一些其他应用场景,还有更多的场景等待大家的探索。

  • 内网穿透
  • 远程办公
  • 安全访问云资源
  • 远程调试
  • 多集群网络
  • 设备远程访问
  • 文件共享

总结

这次分享,我们介绍了集群服务暴露的需求,以及 Kubernetes 中常见的暴露方式。然后介绍了 ZTM 的基本概念和特性,以及如何使用 ZTM 来暴露集群服务。

希望通过本次的分享,大家可以了解到 ZTM 的基本概念和特性,以及如何使用 ZTM 来现集群服务的暴露。

(转载本站文章请注明作者和出处乱世浮生,请勿用于任何商业用途)

comments powered by Disqus