1
2
3
4
5
作者:李晓辉

微信联系:lxh_chat

联系邮箱: 939958092@qq.com

今天咱们来唠唠怎么用 MetalLB 配置外部访问虚拟机,特别是那些非 HTTP 服务,因为有时候 ingress 和 routes 真的搞不定,像 SMTP、POP3、IMAP 这些文本协议,还有 SSH、LDAP、MySQL 这类二进制协议,它们都不太“听话”,没办法用常规的 ingress 和 routes 来暴露。

咱先说说 Kubernetes 服务,它靠一个选择器来找到匹配的 Pod,一旦有连接请求到服务 IP 地址和端口,就会把通信转发到匹配的 Pod 上。一个服务资源得包含这些内容:

  • 一个选择器,描述运行后端服务的 Pod。
  • 一个端口列表,这些端口就是 Pod 上提供服务的端口。

想把虚拟机提供的非 HTTP 服务暴露出去,可以用NodePort服务或者Loadbalancer服务。这两种方式都能让你直接从集群外访问虚拟机的服务端口。

NodePort服务
NodePort服务会在集群的所有节点上暴露服务端口。要是没指定端口,系统会默认从 30000 到 32767 这个范围里分配一个NodePort。不过,NodePort和目标虚拟机的端口大概率对不上号。

Loadbalancer服务
Loadbalancer服务就厉害了,它能提供一个固定的 IP 地址和一个服务端口,把通信转发给匹配选择器的 Pod。就算 virt-launcher Pod 搬家到别的节点,这个 IP 地址也不会变。这个 IP 地址是根据底层基础设施分配的,要么是云提供商给的,要么是 MetalLB 运营商给的。

配置NodePort服务资源

服务资源默认用的是 ClusterIP 类型,这种类型的服务只能在集群内部访问。要是把类型参数设置成 NodePort,OpenShift 就会在所有集群节点上打开相同的网络端口,然后把进来的流量重定向到你的 Pod 或虚拟机。默认情况下,OpenShift 会在 30000 到 32767 这个范围里分配一个端口。

这样一来,外部客户端就能通过集群节点的 IP 地址和分配的端口来访问你的服务了。

nodeport

先瞅瞅这个服务定义,它声明了一个NodePort类型的资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: database
namespace: prod2
spec:
type: NodePort
selector:
vmtype: linux
ports:
- protocol: TCP
port: 3306
targetPort: 3306
nodePort: 30336
  • type: NodePort:这就是告诉系统,咱要搞个NodePort服务,别搞成 ClusterIP 或者 LoadBalancer 啦。
  • port: 3306:在集群内部,这个服务会在 3306 端口上提供数据库应用,就跟普通的 ClusterIP 服务没啥两样。
  • nodePort: 30336:这个参数就是让 OpenShift 在所有集群节点上打开的端口号。你得确保这个端口在所有节点上都是空闲的,而且得在 30000 到 32767 这个范围内。要是你不指定,OpenShift 自己会挑一个可用的端口。

不过,Red Hat 建议咱尽量别用NodePort服务来处理 TCP 和 UDP 流量,为啥呢?

  • 你得把集群节点的 IP 地址暴露到集群外面,这可是个安全风险。
  • 你不能选默认范围(30000 到 32767)之外的端口。
  • 在云环境中,集群节点的 IP 地址可能不是固定的,云基础设施可能会根据负载动态添加或移除节点。
  • NodePort服务可能还需要额外的网络配置才能让外部访问到端口。

LoadBalancer服务

LoadBalancer服务就厉害多了,它可以在一个固定的 IP 地址上暴露虚拟机网络服务。跟NodePort服务不一样,LoadBalancer服务暴露的是一个单一的 IP 地址,这个地址不会因为虚拟机搬到别的节点而改变。

不过,LoadBalancer服务也不是万能的,它需要一些网络特性支持,这些特性在所有环境中都不一定有。比如,云提供商通常会提供自己的LoadBalancer服务,这些服务用的都是针对云提供商的特定功能。而且,负载均衡器资源可能还需要分配有限的资源,比如 IP 地址。

要是你在云提供商上运行 Kubernetes 集群,云控制器管理器就会用云提供商的 API 来配置LoadBalancer服务所需的云资源。但是,这些云特性在自托管集群里是没有的。在自托管集群里,你可以用像 MetalLB 这样的负载均衡器Operator来根据你的网络具体设置来配置LoadBalancer服务。

总之,NodePort服务和LoadBalancer服务各有各的用处,也各有各的局限,选哪种还得看你的具体需求和环境。

loadbalancer

嘿,朋友!今天咱们来唠唠怎么在不同环境下配置LoadBalancer服务,还有怎么用 MetalLB 和虚拟机(VM)来实现外部访问。这可是一门技术活儿,但别担心,我给你慢慢道来。

在云环境中配置LoadBalancer服务

如果你的 Kubernetes 集群是部署在云提供商的基础设施上的,那配置LoadBalancer服务就简单多了。Kubernetes 会自动在云提供商的基础设施上配置一个外部负载均衡器。具体来说:

  • 在 Amazon Web Services 上:Kubernetes 会在 Amazon Elastic Load Balancing 上创建一个负载均衡器。
  • 在 Microsoft Azure 上:Kubernetes 会在 Azure Load Balancer 服务上创建LoadBalancer服务。
  • 在 IBM Cloud 上:Kubernetes 会在 IBM Cloud LoadBalancer服务上创建LoadBalancer服务。

不过,得注意的是,每个 Kubernetes LoadBalancer服务都会在云提供商的基础设施上创建一个负载均衡器。你可以去云提供商那里看看每个负载均衡器的成本,这样就能估算出这类网络服务的总成本了。

在非云环境中配置LoadBalancer服务

要是你的集群是部署在裸金属服务器上,那LoadBalancer服务的行为就有点不一样了。在这种情况下,Kubernetes 会从集群管理员准备好的外部 IP 地址池中分配一个 IP 地址给服务。

集群管理员得把这些外部 IP 地址配置好,让网络能够把它们路由到集群节点上。当集群节点收到目标 IP 地址是服务外部 IP 的数据包时,就会把流量转发给跟服务关联的 Pod 或虚拟机。

MetalLB Operator

MetalLB 是 Kubernetes 的一个负载均衡器实现,专门用于那些不在云提供商上运行的集群,比如裸金属集群或者部署在私有环境中的虚拟化集群。MetalLB 有两种运行模式:二层(使用 ARP)和三层(使用 BGP)。这两种模式各有各的特点和要求:

  • 二层(ARP)模式:这是 MetalLB 默认的模式。它使用免费 ARP 数据包来宣传LoadBalancer服务的 IP 地址。对于 MetalLB 管理的 IP 地址的入站流量,会被转发到通过 ARP 宣传该 IP 的节点上。
  • 三层(BGP)模式:这种模式使用 BGP 来向其他 BGP 对等体宣传LoadBalancer服务的路由。MetalLB 需要管理员配置自治系统(AS)编号。当路由器收到LoadBalancer服务的流量时,它会选择一个宣传了负载均衡器 IP 地址的节点,然后把流量发送到那个节点,服务代理(CNI 网络插件)会把流量分发给所有匹配服务选择器的 Pod。

MetalLB 是一个可以通过Operator生命周期管理器安装的Operator。安装Operator后,你得通过它的自定义资源定义来配置 MetalLB。在大多数情况下,你得给 MetalLB 提供一个 IP 地址范围,让它能够控制分配给LoadBalancer服务的 IP 地址。在你的 OpenShift 集群中安装 MetalLB 之前,你得考虑网络需求。

你可以通过 web 控制台或者命令行来安装 MetalLB Operator。安装完Operator后,你得创建一个 IP 地址池,用来给服务分配 IP 地址。MetalLB Operator会在 metallb-system 命名空间中创建自定义资源。

  • MetalLB 自定义资源:这个自定义资源代表 MetalLB Operator部署的主要实例。
    1
    2
    3
    [lixiaohui@host ~]$ oc get metallb -n metallb-system
    NAME AGE
    metallb 7d
  • IPAddressPool 自定义资源:这个自定义资源包含了 MetalLB 可以分配给LoadBalancer服务的 IP 地址范围。你得根据集群部署环境的网络配置来设置这个范围。
    1
    2
    3
    [lixiaohui@host ~]$ oc get ipaddresspool -n metallb-system
    NAME AUTO ASSIGN ... ADDRESSES
    gls-metallb-ipaddresspool true ... ["192.168.50.20-192.168.50.40"]
  • L2Advertisement 自定义资源:这个自定义资源允许在二层通过免费 ARP 数据包宣传选定池提供的负载均衡器 IP 地址。
    1
    2
    3
    [lixiaohui@host ~]$ oc get l2advertisement -n metallb-system
    NAME IPADDRESSPOOLS ...
    gls-metallb-l2advertisement ["gls-metallb-ipaddresspool"] ...

使用LoadBalancer服务与虚拟机

虚拟机资源包含了虚拟机实例(VMI)和 virt-launcher Pod 的模板。kubevirt.io/domain=vm1 标签代表虚拟机的名字,当虚拟机启动时,这个标签会被传递给 VMI 和 virt-launcher Pod。

1
2
3
4
5
6
7
8
9
10
11
12
13
[lixiaohui@host ~]$ oc get vm/vm1 -o yaml | \
grep -C 5 'kubevirt.io/domain'
template:
metadata:
creationTimestamp: null
labels:
flavor.template.kubevirt.io/small: "true"
kubevirt.io/domain: vm1
kubevirt.io/size: small
os.template.kubevirt.io/rhel8.4: "true"
workload.template.kubevirt.io/server: "true"
spec:
architecture: amd64

当虚拟机启动时,VMI 和 virt-launcher Pod 资源会被创建。

1
2
3
4
5
6
7
8
9
10
11
[lixiaohui@host ~]$ oc get vm
NAME AGE STATUS READY
vm1 2m Running True

[lixiaohui@host ~]$ oc get vmi
NAME AGE PHASE IP NODENAME READY
vm1 1m Running 10.8.2.92 worker02 True

[lixiaohui@host ~]$ oc get pod
NAME READY STATUS RESTARTS AGE
virt-launcher-vm1-4htsh 1/1 Running 0 1m

你可以通过 kubevirt.io/domain 标签来过滤命名空间中的 Pod,找到跟虚拟机关联的 virt-launcher Pod。

1
2
3
[lixiaohui@host ~]$ POD=$(oc get pod -l kubevirt.io/domain=vm1 -o name)
[lixiaohui@host ~]$ echo ${POD}
pod/virt-launcher-vm1-4htsh

然后,你可以用 Pod 名称来创建LoadBalancer服务资源,暴露虚拟机的端口。这样,你就能从 OpenShift 集群外部直接访问服务端口了。

下面这个 oc expose 命令使用了 -o yaml--dry-run=client 参数,来创建LoadBalancer服务资源的 YAML 表示。输出被重定向到了 service.yaml 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[lixiaohui@host ~]$ oc expose ${POD} --name=vm1 \
--type=LoadBalancer --selector kubevirt.io/domain=vm1 --port=22 \
-o yaml --dry-run=client | tee service.yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
flavor.template.kubevirt.io/small: "true"
kubevirt.io: virt-launcher
kubevirt.io/created-by: 7aaf118c-f174-3eba-9ec5-680cd791a020
kubevirt.io/domain: vm1
kubevirt.io/nodeName: worker02
kubevirt.io/size: small
os.template.kubevirt.io/rhel8.4: "true"
vm.kubevirt.io/name: vm1
workload.template.kubevirt.io/server: "true"
name: vm1
spec:
ports:
- port: 22
protocol: TCP
targetPort: 22
selector:
kubevirt.io/domain: vm1
type: LoadBalancer
status:
loadBalancer: {}
  • 标签是从暴露的 virt-launcher Pod 复制过来的。
  • kubevirt.io/domain 标签代表虚拟机的名字。
  • 服务 IP 地址上的连接端口。
  • 目标端口是从端口参数复制过来的,因为 oc expose 命令中没有指定。
  • 服务选择器匹配 Pod 标签。
  • 服务类型被设置为负载均衡器。

注意:要创建 Kubernetes 服务,你必须使用带有 virt-launcher Pod 的 oc expose 命令。你不能使用 VM 或 VMI 资源来创建 Kubernetes 服务。

你可以使用 YAML 资源清单来创建LoadBalancer服务资源。

1
2
[student@workstation network-lb]$ oc apply -f service.yaml
service/vm1 created

MetalLB 会给负载均衡器服务分配一个外部 IP 地址。服务选择器通过 kubevirt.io/domain 标签匹配跟虚拟机关联的运行中的 virt-launcher Pod。端点有 virt-launcher Pod 的内部 IP 地址和端口。

1
2
3
4
5
6
7
[lixiaohui@host ~]$ oc get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
vm1 LoadBalancer 172.30.157.246 192.168.50.22 22:30826/TCP 10m

[lixiaohui@host ~]$ oc get endpoints
NAME ENDPOINTS AGE
vm1 10.8.2.94:22 10m

暴露的端口 22 是用于 SSH 服务的。端点匹配 virt-launcher Pod 的 IP 地址,目标端口 22 转发 SSH 连接。

你可以通过外部 IP 地址和端口号来验证服务是否暴露给了 OpenShift 集群外部的流量。下面这个命令验证是否可以在端口 22 上建立到负载均衡器 IP 地址的 TCP 连接。

1
2
3
4
[lixiaohui@host ~]$ nc -vz 192.168.50.22 22
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Connected to 192.168.50.22:22.
Ncat: 0 bytes sent, 0 bytes received in 0.04 seconds.

这条消息表明,到端口 22 的 TCP 连接是成功的。

你可以通过负载均衡器服务的外部 IP 地址,从 OpenShift 集群外部的机器上访问虚拟机的 SSH 服务。

1
2
3
4
[lixiaohui@host ~]$ ssh developer@192.168.50.22
Warning: Permanently added '192.168.50.22' (ED25519) to the list of known hosts.
...output omitted...
[developer@vm1 ~]$

使用负载均衡器服务暴露多个端口

在使用 oc expose 命令创建服务时,你只能传递一个 --port 参数。如果需要,你可以编辑 Kubernetes 负载均衡器服务资源清单,来添加更多端口。因为 --port 参数只添加一个端口,所以它不会给端口添加 name 参数。然而,当配置多个端口时,清单需要 name 参数。下面这个负载均衡器服务在一个邮件服务器虚拟机上暴露了多个端口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: v1
kind: Service
metadata:
labels:
kubevirt.io/domain: email-server
...output omitted...
name: email-server
spec:
ports:
- port: 25
name: smtp
protocol: TCP
targetPort: 25
- port: 587
name: submission
protocol: TCP
targetPort: 587
- port: 993
name: imaps
protocol: TCP
targetPort: 993
- port: 995
name: pop3s
protocol: TCP
targetPort: 995
selector:
kubevirt.io/domain: email-server
type: LoadBalancer
status:
loadBalancer: {}
  • kubevirt.io/domain 标签代表虚拟机的名字。
  • 暴露端口 25 用于 SMTP 流量。
  • 当服务定义中只有一个端口时,name 是可选的。但是,当向服务中添加多个端口时,name 是必需的。
  • 暴露端口 587 用于通过 TLS 的 SMTP 流量。
  • 暴露端口 993 用于通过 SSL 的 IMAP 流量。
  • 暴露端口 995 用于通过 SSL 的 POP3 流量。
  • 服务选择器匹配 Pod 标签。
  • 服务类型被设置为负载均衡器。

在对负载均衡器服务进行更改后,新的服务端口会被列出。

1
2
3
4
5
[lixiaohui@host ~]$ oc get services
NAME TYPE CLUSTER-IP EXTERNAL-IP
email-server LoadBalancer 172.30.77.133 192.168.50.30
PORT(S) AGE
25:30935/TCP,465:32038/TCP,587:30373/TCP,993:31431/TCP,995:31115/TCP ...

服务选择器匹配虚拟机 Pod,端点代表虚拟机上暴露的网络端口。

1
2
3
[lixiaohui@host ~]$ oc get endpoints
NAME ENDPOINTS AGE
email-server 10.9.2.18:25,10.9.2.18:465,10.9.2.18:587 + 2 more... ...

通过负载均衡器为虚拟机启用SSH

OpenShift web 控制台提供了一个功能,可以在负载均衡器上添加 SSH 服务。要验证这个功能是否可用,集群管理员可以进入 Virtualization → Overview。在 Settings 选项卡中,展开 General Settings 和 SSH configuration。确保 SSH over LoadBalancer 服务被设置为开启。

enabling-ssh-over-loadbalancer

在 Web 控制台中为虚拟机配置通过负载均衡器的 SSH 服务

  1. 进入虚拟机页面:首先,登录到你的 OpenShift Web 控制台。在控制台的左侧导航栏中,找到并点击 “Virtualization” 选项,然后在下拉菜单中选择 “VirtualMachines”。这会带你进入虚拟机的列表页面,展示你所有的虚拟机。
  2. 选择目标虚拟机:在虚拟机列表中,找到你想要配置 SSH 服务的虚拟机,然后点击它的名称。这会带你进入该虚拟机的详细信息页面,这里包含了虚拟机的各种配置信息和操作选项。
  3. 进入配置页面:在虚拟机的详细信息页面中,你会看到上方有几个不同的标签页。点击 “Configuration” 标签,进入配置页面。这个页面允许你对虚拟机的各种设置进行调整和配置。
  4. 选择 SSH 配置:在配置页面的左侧,你会看到一个 “SSH” 的选项卡。点击这个选项卡,进入 SSH 配置页面。在这里,你可以设置虚拟机的 SSH 服务类型。
  5. 设置 SSH 服务类型:在 SSH 配置页面中,找到 “SSH service type” 的选项。这个选项决定了虚拟机的 SSH 服务是如何暴露的。将它设置为 “SSH over LoadBalancer” 选项。这个选项会通过负载均衡器来暴露虚拟机的 SSH 服务,让你可以从集群外部安全地访问虚拟机的 SSH 服务。
  6. 获取 SSH 登录命令:当你选择了 “SSH over LoadBalancer” 选项后,页面会提供一个用于登录虚拟机的 SSH 命令。这个命令包含了通过负载均衡器访问虚拟机所需的所有信息,比如外部 IP 地址和端口号等。你可以直接复制这个命令,在你的终端中运行它,就可以通过 SSH 连接到虚拟机了。

注意事项

  • 确保负载均衡器服务已配置:在你通过 Web 控制台启用 “SSH over LoadBalancer” 之前,确保你的集群已经配置了负载均衡器服务,并且 MetalLB 或其他负载均衡器操作符已经正确安装和配置。否则,即使你在 Web 控制台中启用了这个选项,也可能无法正常工作。
  • 检查网络策略:如果你的集群有网络策略限制,确保这些策略允许从外部访问虚拟机的 SSH 端口。否则,即使负载均衡器服务配置正确,你可能也无法通过 SSH 连接到虚拟机。
  • 安全性考虑:通过负载均衡器暴露 SSH 服务虽然方便,但也增加了安全风险。确保你有适当的安全措施,比如使用强密码、密钥认证,以及限制可以访问 SSH 服务的 IP 地址范围等。

ssh-settings