Red Hat OpenShift 虚拟化中的Localnet UDN示例
使用 CUDN Localnet 网络将 OpenShift 虚拟化虚拟机连接到 IBM Cloud VPC 子网,以实现基于 VLAN 且命名空间隔离的流量传输。
本教程将演示如何在 IBM Cloud 上的 Red Hat OpenShift on IBM Cloud 中,利用虚拟私有云(VPC)子网、集群用户定义网络(CUDN)、本地网和命名空间来创建一个三层应用程序示例。 本示例演示了如何利用基于虚拟局域网(VLAN)的 localnet 网络,将运行在 Red Hat OpenShift 虚拟化平台上的虚拟服务器直接连接到 VPC 子网。 有关网络类型的更多信息,请参阅 《 Red Hat OpenShift 》中面向 vSphere® 管理员的“开放虚拟网络(OVN)”网络配置部分。
概述
该示例部署了一个三层应用程序,其中Web层、数据库层和应用层各自位于不同的网络段中。 每个层级都使用一个专用的 VPC 子网和一个具有唯一 VLAN ID 的本地网 CUDN。 Web 层和数据库层被放置在一个命名空间中(green ),而应用程序层则被放置在另一个独立的命名空间中(red ),以此演示基于命名空间的隔离机制。
网络、命名空间和虚拟服务器的详细信息
下表总结了示例网络拓扑。
| Localnet CUDN | 名称空间 | VLAN 标识 | VPC 子网 | 虚拟服务器 |
|---|---|---|---|---|
vlan20-prod |
green |
20 | 198.18.0.0/24 | plant-web00, plant-web01 |
vlan21-db |
green |
21 | 198.18.1.0/24 | plant-db00, plant-db01 |
vlan22-app |
red |
22 | 198.18.2.0/24 | plant-app00, plant-app01 |
最终设置示意图
指南中的每个代码块都可以复制到文件中,并通过 oc apply -f <file-name>.yml 进行应用。 在 PDF 和 Word 格式中,某些代码块可能会跨越页面边界,而某些较长的行可能会在这些格式中换行。
本指南在 IBM Cloud 的 CLI 命令中,将 ic 作为 ibmcloud 的简写形式。 请在您的终端中定义 alias ic=ibmcloud,或者在后续命令中将 ic 替换为 ibmcloud。
准备工作
请确保已准备好以下各项:
- 一个具有管理 VPC 和 Red Hat OpenShift 资源权限的 IBM Cloud 账户。
- IBM Cloud 命令行界面已安装。
- 已安装
container-service和vpc-infrastructure插件。
container-service 插件提供了 ic ks 命令,本指南使用这些命令将VLAN虚拟网络接口(VNI)连接到集群。 vpc-infrastructure 插件提供了 ic is 命令,本指南将使用这些命令来创建VPC子网、虚拟网络接口(VNI)以及其他基础设施。
如果您需要安装所需的 CLI 插件,请使用以下命令。
ibmcloud plugin install container-service
ibmcloud plugin install vpc-infrastructure
检查网络先决条件
如果您使用的是 Red Hat OpenShift on IBM Cloud,请在开始之前查阅 Red Hat OpenShift on IBM Cloud 中的 OVN UDN/CUDN 网络先决条件。Red Hat OpenShift 虚拟化服务默认已包含这些先决条件。
为绿色层创建命名空间和 CUDN Localnet 二级网络
Localnet 网络可以在同一个命名空间内与 Layer 2 主网络、Layer 2 辅助网络以及其他 Localnet 网络共存。 每个本地网络都需要一个 VLAN ID。 目前,每个VLAN只能使用一个子网。
VLAN 不会在工作节点之间创建 Layer 2 域。 VLAN 用于每个工作节点内部,而非 VPC 内部。
创建 green 命名空间以及 vlan20-prod 和 vlan21-db 网络。 使用 oc apply -f step1.yml 应用该清单。
---
apiVersion: v1
kind: Namespace
metadata:
name: green
---
apiVersion: k8s.ovn.org/v1
kind: ClusterUserDefinedNetwork
metadata:
name: "vlan20-prod"
spec:
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- "green"
network:
topology: Localnet
localnet:
role: "Secondary"
physicalNetworkName: "vpc-vlans"
ipam:
mode: Disabled
vlan:
mode: Access
access:
id: 20
---
apiVersion: k8s.ovn.org/v1
kind: ClusterUserDefinedNetwork
metadata:
name: "vlan21-db"
spec:
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- "green"
network:
topology: Localnet
localnet:
role: "Secondary"
physicalNetworkName: "vpc-vlans"
ipam:
mode: Disabled
vlan:
mode: Access
access:
id: 21
请牢记以下几点:
- 在
Localnet配置中未定义该子网。 - 请勿使用
VLAN 1。 - 请使用 2 到 500 之间的 VLAN 号。 VLAN 501 至 4094 目前处于受限状态。
为红色层创建另一个命名空间和 CUDN Localnet 二级网络
创建 red 命名空间和 vlan22-app 网络。 使用 oc apply -f step2.yml 应用该清单。
---
apiVersion: v1
kind: Namespace
metadata:
name: red
---
apiVersion: k8s.ovn.org/v1
kind: ClusterUserDefinedNetwork
metadata:
name: "vlan22-app"
spec:
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- "red"
network:
topology: Localnet
localnet:
role: "Secondary"
physicalNetworkName: "vpc-vlans"
ipam:
mode: Disabled
vlan:
mode: Access
access:
id: 22
在您的 VPC 账户中创建保留 IP 和 VNI
此步骤将在每个子网中创建一个保留 IP 地址,然后使用该保留 IP 地址创建 VNI,并为每个虚拟网络接口分配一个 MAC address (VNI )。
请牢记以下几点:
- 在此步骤中,
VLAN ID未被连接。 - 您必须查阅本步骤中要使用的安全组。 运行
ic is sgs以列出可用安全组。 - 您可以批量执行此步骤。
- 每个主机最多支持 256 个 VNI。 请将使用率控制在85%以下,以便进行故障转移和从其他主机迁移。
在每个子网中创建保留 IP 地址
首先,在每个子网中为您的虚拟服务器创建一个保留 IP 地址:
ic is subnet-reserved-ip-create zone1-vm-prod --address 198.18.0.20 --name zone1-web00 --auto-delete false
ic is subnet-reserved-ip-create zone1-vm-db --address 198.18.1.20 --name zone1-db00 --auto-delete false
ic is subnet-reserved-ip-create zone1-vm-app --address 198.18.2.20 --name zone1-app00 --auto-delete false
使用保留的 IP 地址创建 VNI
现在,使用上一步中预留的 IP 名称创建 VNI:
ic is virtual-network-interface-create --name zone1-web00 --allow-ip-spoofing false --auto-delete false --protocol-state-filtering-mode disabled --enable-infrastructure-nat true --subnet zone1-vm-prod --vpc my-vpc --rip zone1-web00 --sgs handclap-roundworm-clique-eccentric --resource-group-name Default
ic is virtual-network-interface-create --name zone1-db00 --allow-ip-spoofing false --auto-delete false --protocol-state-filtering-mode disabled --enable-infrastructure-nat true --subnet zone1-vm-db --vpc my-vpc --rip zone1-db00 --sgs handclap-roundworm-clique-eccentric --resource-group-name Default
ic is virtual-network-interface-create --name zone1-app00 --allow-ip-spoofing false --auto-delete false --protocol-state-filtering-mode disabled --enable-infrastructure-nat true --subnet zone1-vm-app --vpc my-vpc --rip zone1-app00 --sgs handclap-roundworm-clique-eccentric --resource-group-name Default
将 VLAN VNI 绑定到集群
获取上一步中创建的每个 VNI 的 ID,然后使用 IBM Cloud container-service 插件将 VNI 连接到集群。 您需要集群 ID,可通过运行 ic ks cluster ls 获取。
请牢记以下几点:
- 如果某个工作器的 VNI 配额已满,则关联的操作将失败。
- VNI 可以绑定到集群(也称为“
floating”),也可以绑定到特定的 worker。
查询 VNI ID。
ic is vni zone1-web00 | awk '$1 ~ /^ID/ {print $NF}'
ic is vni zone1-db00 | awk '$1 ~ /^ID/ {print $NF}'
ic is vni zone1-app00 | awk '$1 ~ /^ID/ {print $NF}'
将每个 VNI 与对应的 VLAN ID 关联到集群中。 请将集群 ID 和 VNI ID 替换为您环境中的实际值。
ic ks vni attach baremetal -c <CLUSTER_ID> --vni <VNI_ID_web00> --vlan 20
ic ks vni attach baremetal -c <CLUSTER_ID> --vni <VNI_ID_db00> --vlan 21
ic ks vni attach baremetal -c <CLUSTER_ID> --vni <VNI_ID_app00> --vlan 22
将虚拟服务器添加到命名空间中
以下清单是一个简单的 YAML 示例,用于创建一个虚拟服务器。 您可以针对每个命名空间和网络更新此清单。 对于您创建的每个 VNI,都需要一个 MAC address。 运行 ic is vnis 命令以列出这些文件,并更新模板中的 macAddress 字段,以便 DHCP 能正常工作。
使用以下命令查找每个 VNI 的 MAC address。
ic is vni zone1-web00 | awk '/^Mac Address/ {print $NF}'
ic is vni zone1-db00 | awk '/^Mac Address/ {print $NF}'
ic is vni zone1-app00 | awk '/^Mac Address/ {print $NF}'
每个 VNI 的示例输出。 您的值不同:
02:00:01:00:98:A0
02:00:02:00:98:A5
02:00:02:00:98:AA
在应用清单之前,请牢记以下几点:
- 在每个清单中更新密码。
- 您需要在“
ssh_authorized_keys”字段中输入一个真实的 SSH 公钥才能登录。 - 您无法使用密码登录 SSH。
- 将每个
macAddress值替换为在前面的命令中查询到的、与之匹配的VNI的 MAC地址。macAddress必须与VNI匹配,这样DHCP才能分配预期的IP地址。
使用 oc apply -f <file-name>.yml 应用以下每个清单。 请使用以下链接直接跳转至各清单。
- 位于
green命名空间中的虚拟服务器plant-web00,地址为vlan20-prod - 位于
green命名空间中的虚拟服务器plant-db00,地址为vlan21-db - 位于
red命名空间中的虚拟服务器plant-app00,地址为vlan22-app
位于 green 命名空间中的虚拟服务器 plant-web00,地址为 vlan20-prod
# Bare YAML to stand up a Red Hat OpenShift on IBM Cloud virtual machine
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: "plant-web00"
namespace: "green"
annotations:
description: "example vm plant-web00"
labels:
app: "plant-web00-green-server"
kubevirt.io/dynamic-credentials-support: 'true'
vm.kubevirt.io/template: "centos-stream9-server-small"
vm.kubevirt.io/template.namespace: openshift
vm.kubevirt.io/template.revision: '1'
vm.kubevirt.io/template.version: v0.34.0
spec:
dataVolumeTemplates:
- apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
creationTimestamp: null
name: "plant-web00-green-server"
spec:
sourceRef:
kind: DataSource
name: "centos-stream9"
namespace: openshift-virtualization-os-images
storage:
resources:
requests:
storage: 30Gi
runStrategy: RerunOnFailure
template:
metadata:
annotations:
vm.kubevirt.io/flavor: "small"
vm.kubevirt.io/os: "centos-stream9"
vm.kubevirt.io/workload: "server"
labels:
kubevirt.io/domain: example
kubevirt.io/size: "small"
spec:
domain:
cpu:
cores: 1
sockets: 1
threads: 1
devices:
disks:
- disk:
bus: virtio
name: rootdisk
- disk:
bus: virtio
name: cloudinitdisk
interfaces:
- model: virtio
name: default
state: up
bridge: {}
macAddress: 02:00:01:00:98:A0 # <- replace with the MAC of VNI zone1-web00
networkInterfaceMultiqueue: true
rng: {}
memory:
guest: "2Gi"
hostname: "plant-web00"
networks:
- name: default
multus:
networkName: vlan20-prod
terminationGracePeriodSeconds: 180
volumes:
- dataVolume:
name: "plant-web00-green-server"
name: rootdisk
- cloudInitNoCloud:
userData: |-
#cloud-config
user: admin
password: "aRandomPassword9292"
ssh_authorized_keys:
- "ssh-ed25519 AAAA....BBBB....CCCCC.....DDDD put-your-key-here@example.local"
chpasswd: { expire: False }
runcmd:
- [ dnf, install, -y, epel-release ]
- [ dnf, install, -y, iperf3, nmap-ncat, darkhttpd ]
name: cloudinitdisk
位于 green 命名空间中的虚拟服务器 plant-db00,地址为 vlan21-db
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: "plant-db00"
namespace: "green"
annotations:
description: "example vm plant-db00"
labels:
app: "plant-db00-green-server"
kubevirt.io/dynamic-credentials-support: 'true'
vm.kubevirt.io/template: "centos-stream9-server-small"
vm.kubevirt.io/template.namespace: openshift
vm.kubevirt.io/template.revision: '1'
vm.kubevirt.io/template.version: v0.34.0
spec:
dataVolumeTemplates:
- apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
creationTimestamp: null
name: "plant-db00-green-server"
spec:
sourceRef:
kind: DataSource
name: "centos-stream9"
namespace: openshift-virtualization-os-images
storage:
resources:
requests:
storage: 30Gi
runStrategy: RerunOnFailure
template:
metadata:
annotations:
vm.kubevirt.io/flavor: "small"
vm.kubevirt.io/os: "centos-stream9"
vm.kubevirt.io/workload: "server"
labels:
kubevirt.io/domain: example
kubevirt.io/size: "small"
spec:
domain:
cpu:
cores: 1
sockets: 1
threads: 1
devices:
disks:
- disk:
bus: virtio
name: rootdisk
- disk:
bus: virtio
name: cloudinitdisk
interfaces:
- model: virtio
name: default
state: up
bridge: {}
macAddress: 02:00:01:00:98:A5 # <- replace with the MAC of VNI zone1-db00
networkInterfaceMultiqueue: true
rng: {}
memory:
guest: "2Gi"
hostname: "plant-db00"
networks:
- name: default
multus:
networkName: vlan21-db
terminationGracePeriodSeconds: 180
volumes:
- dataVolume:
name: "plant-db00-green-server"
name: rootdisk
- cloudInitNoCloud:
userData: |-
#cloud-config
user: admin
password: "aRandomPassword9292"
ssh_authorized_keys:
- "ssh-ed25519 AAAA....BBBB....CCCCC.....DDDD put-your-key-here@example.local"
chpasswd: { expire: False }
runcmd:
- [ dnf, install, -y, epel-release ]
- [ dnf, install, -y, iperf3, nmap-ncat, darkhttpd ]
name: cloudinitdisk
位于 red 命名空间中的虚拟服务器 plant-app00,地址为 vlan22-app
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: "plant-app00"
namespace: "red"
annotations:
description: "example vm plant-app00"
labels:
app: "plant-app00-red-server"
kubevirt.io/dynamic-credentials-support: 'true'
vm.kubevirt.io/template: "centos-stream9-server-small"
vm.kubevirt.io/template.namespace: openshift
vm.kubevirt.io/template.revision: '1'
vm.kubevirt.io/template.version: v0.34.0
spec:
dataVolumeTemplates:
- apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
creationTimestamp: null
name: "plant-app00-red-server"
spec:
sourceRef:
kind: DataSource
name: "centos-stream9"
namespace: openshift-virtualization-os-images
storage:
resources:
requests:
storage: 30Gi
runStrategy: RerunOnFailure
template:
metadata:
annotations:
vm.kubevirt.io/flavor: "small"
vm.kubevirt.io/os: "centos-stream9"
vm.kubevirt.io/workload: "server"
labels:
kubevirt.io/domain: example
kubevirt.io/size: "small"
spec:
domain:
cpu:
cores: 1
sockets: 1
threads: 1
devices:
disks:
- disk:
bus: virtio
name: rootdisk
- disk:
bus: virtio
name: cloudinitdisk
interfaces:
- model: virtio
name: default
state: up
bridge: {}
macAddress: 02:00:01:00:98:AA # <- replace with the MAC of VNI zone1-app00
networkInterfaceMultiqueue: true
rng: {}
memory:
guest: "2Gi"
hostname: "plant-app00"
networks:
- name: default
multus:
networkName: vlan22-app
terminationGracePeriodSeconds: 180
volumes:
- dataVolume:
name: "plant-app00-red-server"
name: rootdisk
- cloudInitNoCloud:
userData: |-
#cloud-config
user: admin
password: "aRandomPassword9292"
ssh_authorized_keys:
- "ssh-ed25519 AAAA....BBBB....CCCCC.....DDDD put-your-key-here@example.local"
chpasswd: { expire: False }
runcmd:
- [ dnf, install, -y, epel-release ]
- [ dnf, install, -y, iperf3, nmap-ncat, darkhttpd ]
name: cloudinitdisk
测试网络设置
IBM 建议您在进行这些测试时,使用与集群位于同一 VPC 中的 Linux® 中转主机。 从跳转主机出发,使用 SSH 通过您在虚拟服务器设置中指定的公钥连接到每台虚拟服务器。
配置 SSH 代理转发,或者使用托管在跳转主机上的新密钥。 请勿在不同系统之间复制您的私钥。
在一个终端会话中,使用 SSH 连接到 Web 层虚拟服务器:
ssh admin@198.18.0.20
在另一个终端会话中,使用 SSH 连接到数据库层虚拟服务器:
ssh admin@198.18.1.20
在另一个终端会话中,使用 SSH 连接到应用层虚拟服务器:
ssh admin@198.18.2.20
测试
运行以下测试以验证网络配置。
-
在每台虚拟服务器上启动一个简单的 HTTP 服务器。
- 在
198.18.0.20上,运行darkhttpd /usr/share/doc/bash --daemon。 - 在
198.18.1.20上,运行darkhttpd /usr/share/doc/bash --daemon。 - 在
198.18.2.20上,运行darkhttpd /usr/share/doc/bash --daemon。
- 在
-
在
198.18.0.20上,运行curl -v http://198.18.1.20:8080。 -
在
198.18.1.20上,运行curl -v http://198.18.0.20:8080。 -
在 jump 虚拟服务器上,运行
curl -v http://198.18.2.20:8080。 -
在
198.18.1.20上,启动一个iperf3服务器:iperf3 -s -p 9090。 -
在
198.18.0.20上,连接到green命名空间中的iperf3服务器:iperf3 -c 198.18.1.20 -p 9090。 -
从
198.18.1.20向198.18.0.20发送 ping 请求,反之亦然。 -
使用
198.18.2.20以及 jump 虚拟服务器的 IP 地址,重新运行上述任务。
应用多网络策略并重新运行测试
为 vlan20-prod 网络应用一项多网络策略,仅允许通过 TCP 的 8080 端口进行访问。 将以下清单保存为名为 my-policy.yml 的文件。 然后,运行 oc apply -f my-policy.yml 以应用该配置。
---
apiVersion: k8s.ovn.org/v1alpha1
kind: MultiNetworkPolicy
metadata:
name: allow-8080-tcp
namespace: green
annotations:
k8s.v1.cni.cncf.io/policy-for: vlan20-prod
spec:
podSelector: {}
ingress:
- ports:
- protocol: TCP
port: 8080
egress:
- ports:
- protocol: TCP
port: 8080
policyTypes:
- Ingress
- Egress
在 vlan20-prod 上针对 plant-web00 重新运行 测试。 TCP 8080 端口仍然可以正常工作,但其他流量(例如 ping 以及 9090 端口的 iperf3 测试)在该网络连接上已无法正常工作。
运行 oc delete -f my-policy.yml 以删除该策略并恢复连接。 测试再次通过了。