在构建裸机 Kubernetes 集群时,我们常常会遇到这么一个问题:除了使用 NodePort 之外,我们要如何向网络公开 Kubernetes Service?如果使用 NodePort 服务类型,它需要分配一个要打开的端口,并且要让防火墙规则连接到这些端口,但这种行为对基础架构并不安全,尤其是在将服务器暴露于外部网络时。

其实还有一种简洁的方法可以将 Kubernetes Service 公开到网络上,并使用其原始端口号。如果我们想将 Kubernetes 集群中的 MySQL 服务通过 3306 端口,而不是 32767 端口暴露给外界,我们就可以用到这种方法:使用 Kubernetes 外部 IP service 类型。

什么是外部 IP Service

从 Kubernetes 官网文档,我们可以看看外部 IP 的相关描述:

如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes Service 会被暴露给这些 externalIPs。通过外部 IP(作为目的 IP 地址)进入到集群,传到 Service 的端口上的流量,将会被路由到 Service 的 Endpoint 上。externalIPs 不会被 Kubernetes 管理,它属于集群管理员的职责范畴。

这里最重要的一点就是确保使用哪个 IP 来访问 Kubernetes 集群。使用外部 IP Service 类型,我们可以将 Service 绑定到连接集群的 IP。另外还有一点重要的是要知道 Kubernetes 网络与 Overlay 网络一起工作。这意味着,我们只要到达了集群中的任何节点(主节点或工作节点),就可以访问集群中的所有节点。

在上图中,节点 1 和节点 2 都有一个 IP 地址。节点 1 上的 IP 地址 1.2.3.4 绑定到 Pod 驻留在节点 2 中的 httpd 服务,而 IP 地址 1.2.3.6 绑定到 nginx 服务,因为 Pod 驻留在节点 1 中。curl IP 地址 1.2.3.4 时,我们应该可以看到来自 httpd 服务的响应,而 curl IP 地址 1.2.3.5 时,响应会来自 nginx 服务。

为什么不使用 Ingress?

即使使用 Ingress 将服务公开给外部,Ingress 还是为 L7 路由构建的,这意味着它支持 HTTP( 80 端口)或 HTTPS(443 端口)流量,但不支持其他端口。Ingress 只能充当基于主机的路由,或类似于 Web Server 世界中的虚拟主机。

外部 IP 的优缺点

外部 IP 的优点:

  • 我们可以完全控制所使用的 IP,使用属于自己 ASN 的 IP,而不是云提供商的 ASN。

外部 IP 的缺点:

  • 我们这些简单的设置不会具有高可用性,这意味着如果节点死亡,该服务将不再可用,我们要手动修复问题。
  • 管理 IP 需要手动。IP 不是动态配置的,因此需要人工操作。

如何使用外部 IP Service

这里的图与上文节点图类似,但 IP 地址和主机名不同。这可能不是一个很好的现实示例,但是在验证设置时,我们可以很容易地区分 IP 地址和主机。在实际示例中,我们可以在一个外部 IP 上公开 MySQL DB,在另一个外部 IP 上公开 Kafka 集群。

本文配置了 2 个 VM。k3s-external-ip-master 是我们的 Kubernetes 主节点,IP 为 1.2.4.120。k3s-external-ip-worker 是 Kubernetes 工作节点,IP 为 1.2.4.114。

步骤 1:设置 Kubernetes 集群

我们现在主节点上安装 K3,再让另一个节点加入集群:

然后现在应该可以看到类似下面的内容:

步骤 2:创建 Kubernetes deployment

我们先创建 nginx deployment 和 httpd deployments:

现在应该看到:

步骤 3:将 deployment 公开为外部 IP 类型

我们先看看 Nginx deployment:

公开 httpd deployment: