OpenShift 虚拟化: 配置集群内虚拟机网络通信
1 | 作者:李晓辉 |
简单回顾Kubernetes SDN
Kubernetes 会给每个容器(Pod)自动分配一个在同一个子网里的 IP 地址。这就意味着,即使这些容器运行在不同的节点上,或者属于不同的 Kubernetes 命名空间,它们也能够轻松地互相通信。这就好像是给每个容器都发了一张“通行证”,让它们可以在集群的网络里自由穿梭。
Kubernetes 是通过一个叫软件定义网络(SDN)的东西来实现这个功能的。SDN 就像是一个智能的网络管理员,能够通过编程的方式来控制网络流量和网络资源。这有点像 VMware 的 vSwitch 软件,不过更灵活、更强大。
Red Hat OpenShift 使用了一个叫 Cluster Network Operator(CNO)的东西来管理这个 SDN。CNO 就像是一个指挥官,它会调用符合容器网络接口(CNI)规范的插件来配置容器网络接口。简单来说,CNO 就是那个“幕后英雄”,它确保所有的网络配置都能正确无误地完成。
Red Hat OpenShift 提供了几种插件提供商,比如 OVN-Kubernetes 和 Kuryr。默认情况下,安装时会选择 OVN-Kubernetes 提供商,它会在每个节点上运行 Open vSwitch(OVS)插件。这个插件就像是网络的“交通警察”,确保数据包能够顺利地到达目的地。
用service 暴露虚拟机和Pod
接下来,我们来看看怎么通过服务(Services)来暴露容器和虚拟机。下面这个图展示了 Kubernetes 的 SDN 是如何把所有的容器(Pods)连接到一个共享网络里的。这就像是把所有的容器都放在同一个虚拟网络派对里,它们可以轻松地互相发送消息,不管它们在集群的哪个角落。
容器在集群里可是个“流浪汉”,它们会不断地被创建和销毁。比如,当你更新应用版本的时候,Kubernetes 会把旧的容器干掉,然后创建新的容器来运行新版本。再比如,当某个节点需要维护的时候,Kubernetes 会把那个节点上的容器都销毁,然后在其他节点上重新创建新的容器。这就像是在玩“ musical chairs”(抢椅子游戏),容器们不断地在节点之间跳来跳去。
因为 Kubernetes 给每个容器都分配了一个独一无二的 IP 地址,所以要想找到这些容器,可不是件容易的事。这就像是在一个不断变化的迷宫里找朋友,每次都得重新找一遍。
别担心,Kubernetes 的服务(Services)就是来解决这个问题的。服务就像是一个“固定联络点”,它给其他容器提供了一个单一且固定的 IP 地址,不管那些容器跑到哪里去了。虚拟机(VMs)也是在容器里运行的,所以它们也可以用服务来互相通信。
服务是怎么做到的呢?它用了一种叫做“标签”(labels)的东西。你可以给容器贴上标签,然后服务就会用这些标签来找到对应的容器。这就像是给容器们贴上了一个“名字标签”,服务就可以通过这个标签来找到它们。当容器被创建或者销毁的时候,服务会自动更新这些“名字标签”,并且把流量均匀地分配到各个容器上。这就像是有一个超级智能的交通指挥官,确保所有的流量都能顺利到达目的地。
Kubernetes 用了一个子网来管理容器,另一个子网来管理服务。这就像是有两个不同的“邮局”,一个负责容器的邮件,另一个负责服务的邮件。当你通过服务的 IP 地址发送请求的时候,Kubernetes 会自动把请求转发到正确的容器上。这就像是你只需要把信寄到“邮局”,然后“邮局”会帮你把信送到正确的人手里。
想象一下,你有一个应用,它有三个容器在运行 API。这些容器可能不在同一个节点上。你可以创建一个服务,比如叫 service1
,它会在这些容器之间平衡负载。你还可以创建另一个服务,比如叫 service2
,它可以把请求转发到一个虚拟机上。这就像是你有一个超级智能的助手,它会帮你把请求送到正确的地方,不管那个地方在哪里。
嘿,朋友们!今天咱们继续聊 Kubernetes 的那些事儿,这次重点说说怎么查看集群的网络配置。
查看集群的网络配置
在安装 Kubernetes 集群的时候,你可以配置每个网络的地址范围。这个地址范围就像是给每个网络分配了一块“地盘”,所有的容器和服务都会在这个“地盘”里分配 IP 地址。
如果你想查看你的集群正在使用哪些地址范围,可以运行一个简单的命令:
1 | oc get network/cluster -o yaml |
假设你运行了上面的命令,输出可能看起来像这样:
1 | apiVersion: config.openshift.io/v1 # API 版本号,指定 Kubernetes API 的版本 |
Kubernetes 能提供好几种服务,让虚拟机(VM)能被集群外的设备访问。具体来说,Kubernetes 支持以下几种服务类型:
- ClusterIP 类型的服务:它会给服务分配一个内部 IP 地址,不过这个地址只能在集群内部用,外边的设备想访问可不行。
- NodePort 类型的服务:它会在集群的每个节点上开放一个端口,外部设备可以通过节点的 IP 地址加上这个端口来访问服务。
- LoadBalancer 类型的服务:它会给服务分配一个外部可访问的 IP 地址,外部设备可以直接通过这个 IP 地址来访问服务。
- ExternalName 类型的服务:它会将服务映射到一个外部的域名,外部设备可以通过这个域名来访问服务。
再看看 Red Hat OpenShift,它也很牛,提供了一种叫路由(route)的机制,能让服务在集群外被访问。这路由机制的详细情况,咱们以后再慢慢唠。
通过 DNS 记录来访问服务
首先,Kubernetes 内部有个 DNS 服务,它能响应集群内部应用程序的查询请求,帮它们找到服务的 IP 地址。这个 DNS 服务是由 DNS Operator 来管理的,它会部署并运行一个 DNS 服务器,这个服务器会自动监控服务,创建和更新 DNS 记录。DNS Operator 管理的域名是 svc.cluster.local,它会按照 servicename.namespace.svc.cluster.local 的格式来创建记录。而且,它还会自动在 Pod 里创建 /etc/resolv.conf 配置文件,这样 Pod 就能直接进行域名解析,不用再做其他配置了。
比如说,你想获取 prod2 命名空间里 backend 服务的 IP 地址,就可以通过查询 backend.prod2.svc.cluster.local 这个记录来实现,就像这样:
1 | [lxh@host ~]# getent hosts backend.prod2.svc.cluster.local |
为虚拟机(VM)创建服务
接下来,说说怎么为虚拟机(VM)创建服务。在 Kubernetes 里,虚拟机是运行在 virt-launcher Pod 里面的。这些 Pod 会在 Pod SDN 上获得一个 IP 地址,你可以用这个地址来创建一个 Kubernetes 服务,通过服务 SDN 上的一个固定 IP 地址来访问虚拟机,这个服务的 IP 地址和虚拟机的 IP 地址是不一样的。
virt-launcher 进程在 Pod 里运行,它会运行一个动态主机配置协议(DHCP)服务器,给虚拟机分配 IP 地址和 DNS 配置。virt-launcher Pod 会把进入的流量重定向到虚拟机,并且把出去的流量路由到目的地。
你可以创建一个 Kubernetes 服务,把虚拟机暴露在集群内部。一旦虚拟机被暴露,集群里的 Pod 和其他虚拟机就能访问虚拟机上运行的应用程序了。
最后,说说怎么为服务准备标签。如果你想暴露一个虚拟机,就得给 VirtualMachine 资源添加一个标签,然后创建一个服务,这个服务的标签选择器要和你添加到虚拟机上的标签一致。
如果你想通过 Web 控制台给虚拟机添加标签,就去 Virtualization → VirtualMachines,选中虚拟机,然后去 YAML 标签那里操作。
要是你想给虚拟机加标签,一定要在 .spec.template.metadata.labels
这个路径下的标签部分加哦。这样操作能确保标签被正确设置到 virt-launcher Pod 上。为啥要强调这个呢?因为虚拟机的资源配置里有不少标签部分,但只有这个位置的标签才是 Kubernetes 用来识别服务相关 Pod 的关键。其他地方的标签,虽然也有用,但和这个服务识别没啥关系。
要是你想在命令行里给虚拟机加标签,那就用 oc edit vm vmname
命令来编辑虚拟机,然后保存修改就行啦。
比如你想给名为 backendvm
的虚拟机加标签,操作就像这样:
1 | [lxh@host ~]$ oc edit vm backendvm |
然后你会看到类似这样的虚拟机资源配置:
1 | apiVersion: kubevirt.io/v1 |
不过得注意,当你编辑虚拟机资源清单的时候,Kubernetes 不会自动把你的修改同步到 VirtualMachineInstance 和 virt-launcher Pod 资源上。要是想让这些修改生效,你可以重启虚拟机,这样 Kubernetes 就会根据最新的虚拟机资源清单重新创建 VirtualMachineInstance 和 virt-launcher Pod 资源。
要是你想从 OpenShift 的 Web 控制台重启虚拟机,就去 Virtualization → VirtualMachines,选中虚拟机,然后点击 Actions → Restart,或者直接用重启图标。
除了用 Web 控制台,你还能用 virtctl
客户端从命令行重启虚拟机呢。比如你想重启名为 backendvm
的虚拟机,就用下面这个命令:
1 | [lxh@host ~]$ virtctl restart backendvm |
重启之后,你可以用 oc get vm
命令看看虚拟机是不是已经运行起来了:
1 | [lxh@host ~]$ oc get vm |
要是你想手动给 virt-launcher Pod 设置 tier: backend
标签,直接在命令行里用 oc label pod
命令就行。先用 oc get pods
找到 Pod 的名字:
1 | [lxh@host ~]$ oc get pods |
然后给它加上标签:
1 | [lxh@host ~]$ oc label pod virt-launcher-backendvm-frxbj tier=backend |
要是你更习惯用 Web 控制台来操作,就去 Workloads → Pods,选中对应的 virt-launcher Pod,然后在 Details 标签页里手动设置 tier: backend
标签。
在 Labels 部分,点击 Edit 按钮,然后就能添加标签啦。
说到这儿,我得提醒你一句,虽然你可以给 virt-launcher Pod 设置标签,但你还是得在虚拟机资源级别定义相同的标签。为啥呢?因为当你重启虚拟机的时候,Kubernetes 会销毁 VMI 和 virt-launcher Pod 资源,然后根据虚拟机资源重新创建它们。要是你没有在虚拟机资源上关联标签,那么你之前给 virt-launcher Pod 设置的标签就会丢失。
所以,一定要记得在虚拟机资源上也设置好标签哦,这样即使重启虚拟机,标签也能一直保持有效。
要是你想从 Web 控制台创建服务,操作也很简单。直接去 Networking → Services,然后点击 Create Service。接下来,就可以用 YAML 编辑器来声明服务啦。
你创建好服务后,Web上就会显示IP地址了
你要是从命令行创建服务,可以用下面这个清单文件:
1 | apiVersion: v1 |
用 oc create -f service_file.yaml
命令来创建服务:
1 | [lxh@host ~]$ oc create -f service_file.yaml |
创建服务后,可以用 oc get svc
命令确认服务创建成功并查看分配的 IP 地址:
1 | [lxh@host ~]$ oc get svc |
virtctl
工具也提供了从命令行创建服务的方法。可以用 virtctl expose vmi vmi-name
命令来创建服务,该命令会使用 VM 的所有标签来创建服务的选择器:
1 | [lxh@host ~]$ virtctl expose vmi backendvm --name backend \ |
对于每个匹配选择器的 Pod,Kubernetes 会自动创建一个端点资源。一个 VMI 必须匹配选择器使用的所有标签,服务才会包含该 VMI。虽然使用 VM 的所有标签来创建服务很方便,但可能并不理想。如果 VMI 上的标签发生变化,那么该 VMI 就不再包含在服务中了。
要确认服务指向 VM,可以比较端点资源中的 IP 地址与 VirtualMachineInstance 资源中的 IP 地址:
1 | [lxh@host ~]$ oc get endpoints |
创建服务后,DNS Operator 会添加 backend.prod2.svc.cluster.local
记录,地址为 172.30.123.204
。然后,你可以通过 backend.prod2.svc.cluster.local
DNS 名称从另一个 Pod 或另一个 VM 访问 VM 上运行的应用程序。由于 DNS Operator 在 Pod 上部署的 /etc/resolv.conf
文件定义了 svc.cluster.local
和 prod2.svc.cluster.local
搜索域,你也可以使用 backend.prod2
或 backend
短名称来访问应用程序。
下面这个例子启动了一个临时测试 Pod,并对服务名称进行 DNS 查询:
1 | [lxh@host ~]$ oc run mytestnet -it --rm --image=rhel8/toolbox |
为虚拟机配置无头服务(headless)
服务为稳定 IP 地址提供 DNS 名称,还为连接到服务的所有 Pod 提供负载均衡。但是,如果客户端要直接连接到单个 Pod,可以使用无头服务。无头服务创建一个 DNS 条目,将 DNS 名称与 Pod 的 IP 地址绑定。无头服务允许客户端直接连接到它偏好的任意 Pod。不会分配集群 IP;kube-proxy 不处理服务;平台也不会进行负载均衡或代理。
要创建无头服务,将清单中的 spec.clusterIP
字段设置为 None
:
1 | apiVersion: v1 |