Red Hat OpenShift 虛擬化技術的Localnet UDN範例

透過 CUDN Localnet 網路,將 OpenShift 虛擬化虛擬機器 (VM) 連接到 IBM Cloud VPC 子網,以實現基於 VLAN 且命名空間隔離的流量傳輸。

本教學將示範如何在 IBM Cloud 上的 Red Hat OpenShift on IBM Cloud 中,利用虛擬私有雲(VPC)子網、叢集使用者自訂網路(CUDN)、localnet 以及命名空間,建立一個三層式應用程式的範例。 此範例示範如何透過基於虛擬區域網路(VLAN)的 localnet 網路,將在 Red Hat OpenShift 虛擬化環境上運行的虛擬伺服器直接連接到 VPC 子網。 有關網路類型的更多資訊,請參閱《 Red Hat OpenShift 中的 Open Virtual Network(OVN)網路》一文(適用於 vSphere® 管理員 )。

概觀

此範例部署了一個三層式應用程式,其中 Web 層、資料庫層和應用程式層各自位於不同的網路區段中。 每個層級皆使用專用的 VPC 子網,以及具有唯一 VLAN ID 的 localnet CUDN。 Web 層與資料庫層被放置於同一個命名空間(green )中,而應用程式層則被放置於另一個獨立的命名空間(red )中,藉此展示基於命名空間的隔離機制。

網路、命名空間及虛擬伺服器的詳細資訊

下表概述了此範例網路佈局。

Localnet CUDN、命名空間及虛擬伺服器佈局範例
Localnet CUDN 名稱空間 VLAN ID 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

最終設置示意圖

Localnet 三層式應用程式架構
Localnet 三層式應用程式架構

本指南中的每個程式碼區塊均可複製到檔案中,並透過 oc apply -f .yml`` 執行。 在 PDF 和 Word 格式中,某些程式碼區塊可能會跨越頁面邊界,而某些較長的行可能會在這些格式中換行。

本指南在 IBM Cloud 的 CLI 指令中,將 ic 作為 ibmcloud 的簡寫。 請在您的命令列介面中設定 alias ic=ibmcloud ,或在後續指令中將 ic 替換為 ibmcloud 。

開始之前

請確保您已備妥以下項目:

  • 一個具備管理 VPC 和 Red Hat OpenShift 資源權限的 IBM Cloud 帳戶。
  • IBM Cloud CLI 已安裝。
  • 已安裝「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」),或綁定至特定的工作節點。

查詢 VNI 識別碼。

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

將虛擬伺服器新增至命名空間

以下 manifests 是一個 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 .yml`` 套用以下各份清單。 請使用以下連結直接跳轉至各份清單。

位於 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

測試

執行以下測試以驗證網路設定。

  1. 在每台虛擬伺服器上啟動一個簡單的 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。
  2. 在 198.18.0.20 上,執行 curl -v http://198.18.1.20:8080。

  3. 在 198.18.1.20 上,執行 curl -v http://198.18.0.20:8080。

  4. 在 jump 虛擬伺服器上,執行 curl -v http://198.18.2.20:8080。

  5. 從 198.18.1.20 開始,啟動一個 iperf3 伺服器:iperf3 -s -p 9090。

  6. 從 198.18.0.20 連線至 green 命名空間中的 iperf3 伺服器:iperf3 -c 198.18.1.20 -p 9090。

  7. 從 198.18.1.20 向 198.18.0.20 進行 Ping 測試,反之亦然。

  8. 請使用 198.18.2.20 以及跳轉虛擬伺服器的 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 以移除該政策並恢復連線。 測試再次通過。