가상화 워크로드를 위한 기본 네트워크 분할

Red Hat Blog를 보다가 관심 가는 글이 보여서 AI번역+약간 교정해 보았습니다.
출처: https://developers.redhat.com/articles/2025/05/01/native-network-segmentation-virtualization-workloads

이 문서에서는 Red Hat OpenShift 에서 클러스터 기본 네트워크를 재정의하고 관리형 IPAM (IP Address Management)을 통해 레이어 2 격리 네트워크를 사용하여 워크로드를 연결하는 방법을 보여줍니다. 이러한 유형의 네트워크는 NAT (Network Address Translation) 없이 동서 통신을 제공하고 전체 수명 주기 동안 안정적인 IPAM 구성을 제공함으로써 쿠버네티스 플랫폼의 가상화 워크로드 과제를 해결합니다. 이를 위해 user-defined network (UDN) 기능을 소개합니다. 

동기 부여 

가상화 관점에서 OpenShift 사용자는 두 가지 유형으로 구분됩니다. 하나는 NAT 없이 동서 통신을 위한 격리된 네트워크(OpenStack Neutron 방식)를 원하는 기존 가상화 사용자이고, 다른 하나는 IPAM을 포함한 완벽하게 관리되는 네트워크 환경을 원하는 Kubernetes 사용자입니다. 이를 통해 사용자는 워크로드에 고정 주소를 할당하거나 네트워크에 DHCP(동적 호스트 구성 프로토콜) 서버를 배포할 필요가 없습니다. 

사용자 유형에 관계없이 두 가지 모두 동일한 목표를 공유합니다. 즉, 안정적인 IPAM 구성(IP 주소, 게이트웨이, DNS 구성)이 필요하고 네트워킹 솔루션은 베어 메탈과 클라우드 플랫폼 모두에서 작동해야 합니다. 

문제 

이 경우, 사용자의 목표 달성을 방해하는 것은 쿠버네티스 플랫폼 자체, 특히 쿠버네티스 네트워킹 모델입니다. 간단히 말해, 쿠버네티스 플랫폼은 지나치게 고집이 센 편입니다.  

플랫폼의 모든 워크로드를 연결하는  단일 네트워크를 제공합니다. 사용자가 워크로드 간 트래픽을 제한하려는 경우, NetworkPolicy를 프로비저닝하는 것이 유일한 방법입니다. 이 방법은 두 가지 이유로 비용이 많이 듭니다. 누군가 정책을 작성하고 유지 관리해야 하며, 네트워크에서 변경 사항이 발생할 때마다 정책을 조정해야 하기 때문입니다. 모든 것을 연결하는 단일 네트워크가 있기 때문에 이러한 작업이 빈번하게 발생합니다. 

설상가상으로, 쿠버네티스 네트워크 정책은 네트워크와 전송 OSI 계층(3계층과 4계층)에서 작동합니다. 즉, 쿠버네티스 자체적으로 계층 2 격리를 수행할 방법이 없다는 뜻입니다. 

목표

이제 이러한 격리된 2계층 네트워크를 통해 무엇을 해결하려는지, 그리고 그 이유를 이해했으므로 이에 대한 목표를 나열해 보겠습니다.

  • 워크로드/테넌트 격리: 서로 통신할 수 없는 서로 다른 격리된 네트워크에서 다양한 유형의 애플리케이션을 그룹화하는 기능입니다.
  • 중복되는 서브넷: 동일한 Pod 서브넷 범위로 클러스터에 여러 네트워크를 만들어 동일한 설정을 복사할 수 있습니다.
  • 네이티브 쿠버네티스 API 통합: 쿠버네티스 네트워크 API를 완벽하게 지원합니다. 즉, 서비스, 네트워크 정책, 관리 네트워크 정책을 UDN에서 사용할 수 있습니다.
  • OpenShift 네트워크 API와의 기본 통합: OpenShift 네트워크 API를 지원합니다. 현재 UDN에서 송신 IP를 사용할 수 있습니다. OpenShift 경로에 대한 지원을 추가할 계획입니다.
  • 안정적인 IPAM 구성: 워크로드는 수명 주기 동안 IP, 게이트웨이, DNS 구성이 안정적이어야 합니다.
  • 클라우드 플랫폼 지원: 패킷은 해당 패킷이 실행되는 노드의 IP 주소와 함께 클러스터 노드에서 나가야 합니다. 그렇지 않으면 클라우드 공급자가 해당 패킷을 삭제합니다.
  • 클러스터 기본 네트워크에서 사용 가능한 일부 Kubernetes 인프라 서비스에 대한 액세스: UDN에 연결된 워크로드에서  kube-dnskube-api에 대한 액세스가 가능하며 계속 사용 가능합니다.
  • 동서 트래픽에는 NAT가 없습니다.

요구 사항

  • OpenShift 클러스터, 버전 >= 4.18
  • Red Hat OpenShift  Virtualization과  MetalLB를 설치하고 구성했습니다(후자는 서비스 부분 에만 필요합니다). 자세한 내용은 OpenShift 문서를 참조하세요 .

사용 사례 

OpenShift UDN 기능을 통해 다루는 두 가지 주요 사용 사례는 네임스페이스 격리 와  격리된 클러스터 전체 네트워크입니다 . 이전 섹션에서 언급한 모든 목표는 두 사용 사례 모두에 적용된다는 점을 명심하세요  . 

네임스페이스 격리 

이 사용 사례에서 네임스페이스의 모든 워크로드는 서로 액세스할 수 있지만 클러스터의 나머지 부분과는 격리됩니다. 즉, 다른 UserDefinedNetwork의 워크로드는 해당 워크로드에 액세스하거나 접근할 수 없습니다. 클러스터 기본 네트워크에 연결된 워크로드도 마찬가지이며, UDN Pod에 연결할 수 없습니다(또는 그 반대의 경우도 마찬가지). 

그림 1에서 이 개념의 그래픽 표현을 확인할 수 있습니다. 녹색 네임스페이스의 워크로드와 파란색 네임스페이스의 워크로드 간 통신은 차단되어 있지만, 같은 네임스페이스의 다른 포드와는 자유롭게 통신할 수 있습니다. 

대체 텍스트
그림 1: 네임스페이스 범위의 UserDefinedNetworks에 대한 연결 다이어그램.

이 사용 사례를 달성하기 위해 OpenShift 4.18에서 사용자는 UserDefinedNetwork CR을 프로비저닝할 수 있습니다. 이는 네임스페이스의 워크로드가 기본 UDN을 통해 연결되고 다른 UDN(또는 클러스터의 기본 네트워크)의 워크로드가 액세스할 수 없음을 나타냅니다. 

네임스페이스의 워크로드는 클러스터를 이그레스하고 목표 섹션 에 나열된 쿠버네티스 기능과 통합될 수 있습니다  . 이를 위해 사용자는 먼저 네임스페이스를 생성하고, 네임스페이스의 워크로드는 클러스터를 이그레스하고 목표 섹션 에 나열된 쿠버네티스 기능과 통합될 수 있습니다  . 이를 위해 사용자는 먼저 네임스페이스를 생성하고, 레이블을 추가하여 기본 클러스터 네트워크를 UDN으로 재정의하려는 의사를 명시적으로 표시해야 합니다  k8s.ovn.org/primary-user-defined-network 레이블을 추가하여 기본 클러스터 네트워크를 UDN으로 재정의하려는 의사를 명시적으로 표시해야 합니다. 

참조 매니페스트는 다음 YAML을 참조하세요. 

---
apiVersion: v1
kind: Namespace
metadata:
  name: green
  labels:
	k8s.ovn.org/primary-user-defined-network: ""
---
apiVersion: k8s.ovn.org/v1
kind: UserDefinedNetwork
metadata:
  name: namespace-scoped
  namespace: green
spec:
  topology: Layer2
  layer2:
    role: Primary
    subnets:
      - 192.168.0.0/16
    ipam:
      lifecycle: Persistent

이제 UDN을 프로비저닝했으니, 가장 관련성 높은 기능을 보여주기 위해 UDN에 연결된 몇 가지 워크로드를 만들어 보겠습니다. 이 경우, 웹 서버 컨테이너 하나와 가상 머신(VM) 두 개를 생성합니다. 다음 YAML 매니페스트를 프로비저닝합니다.

---
apiVersion: v1
kind: Pod
metadata:
  name: webserver
  namespace: green
spec:
  containers:
  - args:
	- "netexec"
	- "--http-port"
	- "9000"
	image: registry.k8s.io/e2e-test-images/agnhost:2.45
	imagePullPolicy: IfNotPresent
	name: agnhost-container
  restartPolicy: Always
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  labels:
	kubevirt.io/vm: vm-a
  name: vm-a
  namespace: green
spec:
  runStrategy: Always
  template:
	metadata:
  	name: vm-a
  	namespace: green
	spec:
  	domain:
    	devices:
      	disks:
      	- disk:
          	bus: virtio
        	name: containerdisk
      	- disk:
          	bus: virtio
        	name: cloudinitdisk
      	interfaces:
      	- name: isolated-namespace
          binding:
          	name: l2bridge
      	rng: {}
    	resources:
      	requests:
        	memory: 2048M
  	networks:
  	- pod: {}
    	name: isolated-namespace
  	terminationGracePeriodSeconds: 0
  	volumes:
  	- containerDisk:
      	image: quay.io/kubevirt/fedora-with-test-tooling-container-disk:v1.4.0
    	name: containerdisk
  	- cloudInitNoCloud:
      	userData: |-
        	#cloud-config
        	password: fedora
        	chpasswd: { expire: False }
    	name: cloudinitdisk
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  labels:
	kubevirt.io/vm: vm-b
  name: vm-b
  namespace: green
spec:
  runStrategy: Always
  template:
	metadata:
  	name: vm-b
  	namespace: green
	spec:
  	domain:
    	devices:
      	disks:
      	- disk:
          	bus: virtio
        	name: containerdisk
      	- disk:
          	bus: virtio
        	name: cloudinitdisk
      	interfaces:
      	- name: isolated-namespace
          binding:
            name: l2bridge
      	rng: {}
    	resources:
      	requests:
        	memory: 2048M
  	networks:
  	- pod: {}
    	name: isolated-namespace
  	terminationGracePeriodSeconds: 0
  	volumes:
  	- containerDisk:
      	image: quay.io/kubevirt/fedora-with-test-tooling-container-disk:v1.4.0
    	name: containerdisk
  	- cloudInitNoCloud:
      	userData: |-
        	#cloud-config
        	password: fedora
        	chpasswd: { expire: False }
    	name: cloudinitdisk

동서 연결성 

세 가지 워크로드가 실행되면 VM 중 하나에서 웹 서버 포드의 HTTP 서버에 접속하여 동서 트래픽이 제대로 작동하는지 확인할 수 있습니다. 하지만 이를 위해서는 먼저 웹 서버 IP 주소를 파악해야 합니다. 이 작업은 다음 k8snetworkplumbing network-status 주석을 참고합니다. 

kubectl get pods -ngreen webserver -ojsonpath="{@.metadata.annotations.k8s\.v1\.cni\.cncf\.io\/network-status}" | jq
[
  {
	"name": "ovn-kubernetes",
	"interface": "eth0",
	"ips": [
  	"10.244.1.10"
	],
	"mac": "0a:58:0a:f4:01:0a",
	"dns": {}
  },
  {
	"name": "ovn-kubernetes",
	"interface": "ovn-udn1",
	"ips": [
  	"192.168.0.4"
	],
	"mac": "0a:58:cb:cb:00:04",
	"default": true,
	"dns": {}
  }
]
# login using fedora/fedora
virtctl console -ngreen vm-a
Successfully connected to vm-a console. The escape sequence is ^]
vm-a login: fedora
Password:
[fedora@vm-a ~]$ curl 192.168.0.4:9000/hostname
webserver # reply from the webserver

다른 UDN이나 기본 클러스터 네트워크에 연결된 작업 부하에서 웹 서버에 액세스하려고 하면 성공하지 못합니다. 

인터넷으로의 이탈

인터넷으로의 이탈이 예상대로 작동하는지 확인할 수도 있습니다.

[fedora@vm-a ~]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc fq_codel state UP group default qlen 1000
    link/ether 0a:58:cb:cb:00:05 brd ff:ff:ff:ff:ff:ff
    altname enp1s0
    inet 203.203.0.5/16 brd 203.203.255.255 scope global dynamic noprefixroute eth0
       valid_lft 2786sec preferred_lft 2786sec
    inet6 fe80::858:cbff:fecb:5/64 scope link
       valid_lft forever preferred_lft forever
[fedora@vm-a ~]$ ip r
default via 203.203.0.1 dev eth0 proto dhcp metric 100
203.203.0.0/16 dev eth0 proto kernel scope link src 203.203.0.5 metric 100
[fedora@vm-a ~]$ ping -c 2 www.google.com
PING www.google.com (142.250.200.100) 56(84) bytes of data.
64 bytes from 142.250.200.100 (142.250.200.100): icmp_seq=1 ttl=115 time=7.15 ms
64 bytes from 142.250.200.100 (142.250.200.100): icmp_seq=2 ttl=115 time=6.55 ms
--- www.google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 6.548/6.849/7.150/0.301 ms

원활한 라이브 마이그레이션 

이제 이 가상 머신 중 하나의 원활한 라이브 마이그레이션을 확인해 보겠습니다. TCP 연결이 가상 머신 마이그레이션 후에도 유지된다는 주장을 입증하기 위해, iperf 서버를 시작할 vm-b를 마이그레이션하는 동안 iperf 세션의 처리량을 모니터링할 것입니다. 

# let's start the iperf server in `vm-b` (for instance)
# password is also fedora/fedora
virtctl console -ngreen vm-b
Successfully connected to vm-b console. The escape sequence is ^]
vm-b login: fedora
Password:
[fedora@vm-b ~]$ iperf3 -s -p 9500 -1
-----------------------------------------------------------
Server listening on 9500 (test #1)
-----------------------------------------------------------

iperf 클라이언트가 될 vm-a VM  에서 연결해보겠습니다.

virtctl console -ngreen vm-a
Successfully connected to vm-a console. The escape sequence is ^]
# keep in mind the iperf server is located at 203.203.0.6, port 9500 ...
[fedora@vm-a ~]$ iperf3 -c 203.203.0.6 -p9500 -t3600
Connecting to host 203.203.0.6, port 9500
[  5] local 203.203.0.5 port 56618 connected to 203.203.0.6 port 9500
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  2.59 GBytes  22.2 Gbits/sec    0   2.30 MBytes
[  5]   1.00-2.00   sec  2.53 GBytes  21.7 Gbits/sec    0   2.78 MBytes
[  5]   2.00-3.00   sec  2.95 GBytes  25.3 Gbits/sec    0   3.04 MBytes
[  5]   3.00-4.00   sec  3.93 GBytes  33.7 Gbits/sec    0   3.04 MBytes
[  5]   4.00-5.00   sec  3.83 GBytes  32.9 Gbits/sec    0   3.04 MBytes
[  5]   5.00-6.00   sec  3.99 GBytes  34.3 Gbits/sec    0   3.04 MBytes
[  5]   6.00-7.00   sec  3.89 GBytes  33.4 Gbits/sec    0   3.04 MBytes
[  5]   7.00-8.00   sec  3.88 GBytes  33.3 Gbits/sec    0   3.04 MBytes
[  5]   8.00-9.00   sec  3.94 GBytes  33.8 Gbits/sec    0   3.04 MBytes
[  5]   9.00-10.00  sec  3.80 GBytes  32.7 Gbits/sec    0   3.04 MBytes
[  5]  10.00-11.00  sec  3.98 GBytes  34.2 Gbits/sec    0   3.04 MBytes
[  5]  11.00-12.00  sec  3.67 GBytes  31.5 Gbits/sec    0   3.04 MBytes
[  5]  12.00-13.00  sec  3.87 GBytes  33.3 Gbits/sec    0   3.04 MBytes
[  5]  13.00-14.00  sec  3.82 GBytes  32.8 Gbits/sec    0   3.04 MBytes
[  5]  14.00-15.00  sec  3.80 GBytes  32.6 Gbits/sec    0   3.04 MBytes
...

이제 virtctl 도구를 사용하여 서버 VM을 마이그레이션해 보겠습니다.

virtctl migrate -ngreen vm-b
VM vm-b was scheduled to migrate
kubectl get pods -ngreen -w
NAME                       READY   STATUS            RESTARTS   AGE
virt-launcher-vm-a-r44bp   2/2     Running           0          22m
virt-launcher-vm-b-44vs4   2/2     Running           0          22m
virt-launcher-vm-b-wct76   0/2     PodInitializing   0          8s
webserver                  1/1     Running           0          22m
virt-launcher-vm-b-wct76   2/2     Running           0          13s
virt-launcher-vm-b-wct76   2/2     Running           0          16s
virt-launcher-vm-b-wct76   2/2     Running           0          16s
virt-launcher-vm-b-wct76   2/2     Running           0          16s
virt-launcher-vm-b-wct76   2/2     Running           0          17s
virt-launcher-vm-b-44vs4   1/2     NotReady          0          22m
virt-launcher-vm-b-44vs4   0/2     Completed         0          23m
virt-launcher-vm-b-44vs4   0/2     Completed         0          23m
# and this is what the client sees when the migration occurs:
...
[  5]  40.00-41.00  sec  2.79 GBytes  23.9 Gbits/sec    0   3.04 MBytes
[  5]  41.00-42.00  sec  2.53 GBytes  21.7 Gbits/sec    0   3.04 MBytes
[  5]  42.00-43.00  sec  2.57 GBytes  22.1 Gbits/sec    0   3.04 MBytes
[  5]  43.00-44.00  sec  2.67 GBytes  22.9 Gbits/sec    0   3.04 MBytes
[  5]  44.00-45.00  sec  2.94 GBytes  25.2 Gbits/sec    0   3.04 MBytes
[  5]  45.00-46.00  sec  3.16 GBytes  27.1 Gbits/sec    0   3.04 MBytes
[  5]  46.00-47.00  sec  3.33 GBytes  28.6 Gbits/sec    0   3.04 MBytes
[  5]  47.00-48.00  sec  3.11 GBytes  26.7 Gbits/sec    0   3.04 MBytes
[  5]  48.00-49.00  sec  2.62 GBytes  22.5 Gbits/sec    0   3.04 MBytes
[  5]  49.00-50.00  sec  2.77 GBytes  23.8 Gbits/sec    0   3.04 MBytes
[  5]  50.00-51.00  sec  2.79 GBytes  23.9 Gbits/sec    0   3.04 MBytes
[  5]  51.00-52.00  sec  2.19 GBytes  18.8 Gbits/sec    0   3.04 MBytes
[  5]  52.00-53.00  sec  3.29 GBytes  28.3 Gbits/sec  2229   2.30 MBytes
[  5]  53.00-54.00  sec  3.80 GBytes  32.6 Gbits/sec    0   2.43 MBytes
[  5]  54.00-55.00  sec  5.08 GBytes  43.6 Gbits/sec    0   2.60 MBytes
[  5]  55.00-56.00  sec  5.18 GBytes  44.5 Gbits/sec    0   2.62 MBytes
[  5]  56.00-57.00  sec  5.35 GBytes  46.0 Gbits/sec    0   2.74 MBytes
[  5]  57.00-58.00  sec  5.14 GBytes  44.2 Gbits/sec    0   2.89 MBytes
[  5]  58.00-59.00  sec  5.28 GBytes  45.4 Gbits/sec    0   2.93 MBytes
[  5]  59.00-60.00  sec  5.22 GBytes  44.9 Gbits/sec    0   2.97 MBytes
[  5]  60.00-61.00  sec  4.97 GBytes  42.7 Gbits/sec    0   2.99 MBytes
[  5]  61.00-62.00  sec  4.78 GBytes  41.1 Gbits/sec    0   3.02 MBytes
[  5]  62.00-63.00  sec  5.14 GBytes  44.1 Gbits/sec    0   3.02 MBytes
[  5]  63.00-64.00  sec  5.06 GBytes  43.5 Gbits/sec    0   3.02 MBytes
[  5]  64.00-65.00  sec  5.02 GBytes  43.1 Gbits/sec    0   3.02 MBytes
[  5]  65.00-66.00  sec  5.07 GBytes  43.5 Gbits/sec    0   3.02 MBytes
[  5]  66.00-67.00  sec  5.34 GBytes  45.9 Gbits/sec    0   3.02 MBytes
[  5]  67.00-68.00  sec  4.99 GBytes  42.9 Gbits/sec    0   3.02 MBytes
[  5]  68.00-69.00  sec  5.17 GBytes  44.4 Gbits/sec    0   3.02 MBytes
[  5]  69.00-70.00  sec  5.39 GBytes  46.3 Gbits/sec    0   3.02 MBytes
[  5]  70.00-70.29  sec  1.57 GBytes  46.0 Gbits/sec    0   3.02 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-70.29  sec   260 GBytes  31.8 Gbits/sec  2229             sender
[  5]   0.00-70.29  sec  0.00 Bytes  0.00 bits/sec                  receiver

보시다시피 TCP 연결은 마이그레이션을 견뎌냈고 다운타임도 없었습니다. 단지 52~53초 동안 약간의 문제가 발생했을 뿐입니다.

NetworkPolicy 통합

이전 섹션에서 동서 연결에 대해 설명했는데, 웹 서버 포드에 어떻게 접속할 수 있었는지 기억하시나요? 그런 일이 발생하지 않도록 네트워크 정책을 프로비저닝해 보겠습니다. TCP 포트 9001을 통한 수신 트래픽만 허용하는 더 유용한 네트워크 정책을 프로비저닝해 보겠습니다. 

다음 매니페스트를 참조하세요.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ingress-port-9001-only
  namespace: green
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - ports:
      - protocol: TCP
        port: 9001

이 정책이 프로비저닝되면 VM은 더 이상 웹 서버 포드에 액세스할 수 없습니다. 하지만 올바른 포트(예: 9001)를 수신하는 다른 웹 서버를 만들어 보겠습니다.

---
apiVersion: v1
kind: Pod
metadata:
  name: new-webserver
  namespace: green
spec:
  containers:
  - args:
	- "netexec"
	- "--http-port"
	- "9001"
	image: registry.k8s.io/e2e-test-images/agnhost:2.45
	imagePullPolicy: IfNotPresent
	name: agnhost-container
  nodeName: ovn-worker
  restartPolicy: Always
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default

접근해 보겠습니다.

# example for a pod named new-webserver, which listens on port 9001
kubectl get pods -ngreen new-webserver -ojsonpath="{@.metadata.annotations.k8s\.v1\.cni\.cncf\.io\/network-status}" | jq
[
  {
    "name": "ovn-kubernetes",
    "interface": "eth0",
    "ips": [
      "10.244.1.13"
    ],
    "mac": "0a:58:0a:f4:01:0d",
    "dns": {}
  },
  {
    "name": "ovn-kubernetes",
    "interface": "ovn-udn1",
    "ips": [
      "203.203.0.8"
    ],
    "mac": "0a:58:cb:cb:00:08",
    "default": true,
    "dns": {}
  }
]
virtctl console -ngreen vm-a
Successfully connected to vm-a console. The escape sequence is ^]
[fedora@vm-a ~]$ curl 203.203.0.8:9001/hostname
new-webserver

상호 연결된 네임스페이스 격리 

이 다른 사용 사례를 통해 사용자는 필요에 따라 여러 네임스페이스를 연결할 수 있습니다. 이렇게 상호 연결된 네임스페이스는 격리된 네트워크에 연결된 모든 워크로드에서 접근 가능하지만, 다른 클러스터 전체 UDN 또는 클러스터 기본 네트워크에 연결된 워크로드에서는 접근할 수 없습니다. 

그림 2에서 이 개념의 그래픽 표현을 볼 수 있는데, 빨간색과 파란색 네임스페이스의 포드는 해피 네트워크를 통해 서로 통신할 수 있지만, 슬픈 네트워크에 연결된 주황색과 녹색 네임스페이스에는 연결되어 있지 않습니다. 

대체 텍스트
그림 2: 클러스터 UserDefinedNetwork에 대한 연결 다이어그램.

이 사용 사례를 구현하려면 사용자는 ClusterUserDefinedNetwork라는 별도의 CR을 프로비저닝해야 합니다. 이는 클러스터 관리자만 프로비저닝할 수 있는 클러스터 전체 리소스입니다. 네트워크를 정의하고 해당 네트워크로 상호 연결되는 네임스페이스를 지정할 수 있습니다. 

이 시나리오는 다음 YAML 매니페스트를 적용하여 구성됩니다. 

---
apiVersion: v1
kind: Namespace
metadata:
  name: red-namespace
  labels:
    k8s.ovn.org/primary-user-defined-network: ""
---
apiVersion: v1
kind: Namespace
metadata:
  name: blue-namespace
  labels:
    k8s.ovn.org/primary-user-defined-network: ""
---
apiVersion: k8s.ovn.org/v1
kind: ClusterUserDefinedNetwork
metadata:
  name: happy-tenant
spec:
  namespaceSelector:
    matchExpressions:
    - key: kubernetes.io/metadata.name
      operator: In
      values:
        - red-namespace
        - blue-namespace
  network:
    topology: Layer2
    layer2:
      role: Primary
      ipam:
        lifecycle: Persistent
      subnets:
        - 203.203.0.0/16
---
apiVersion: v1
kind: Namespace
metadata:
  name: orange-namespace
  labels:
    k8s.ovn.org/primary-user-defined-network: ""
---
apiVersion: v1
kind: Namespace
metadata:
  name: green-namespace
  labels:
    k8s.ovn.org/primary-user-defined-network: ""
---
apiVersion: k8s.ovn.org/v1
kind: ClusterUserDefinedNetwork
metadata:
  name: happy-tenant
spec:
  namespaceSelector:
    matchExpressions:
    - key: kubernetes.io/metadata.name
      operator: In
      values:
        - orange-namespace
        - green-namespace
  network:
    topology: Layer2
    layer2:
      role: Primary
      ipam:
        lifecycle: Persistent
      subnets:
        - 192.168.0.0/16

보시다시피 UserDefinedNetwork CRD와 두 가지 차이점이 있습니다.

  • 클러스터 전체 리소스입니다(즉, metadata.namespace 없음)
  • 관리자가 상호 연결할 네임스페이스를 나열할 수 있는 spec.namespace 선택기를 찾습니다.

후자의 구성은 Kubernetes 표준 네임스페이스 선택기입니다. 

이 시나리오는 다른 CRD를 사용하여 구성되지만, 기본 네임스페이스 격리 사용 사례를 구성하는 UserDefinedNetwork CRD에 사용 가능한 모든 기능 세트는 상호 연결된 네임스페이스 격리 시나리오에 사용할 수 있습니다. 따라서 이 다른 사용 사례에서는 동서 연결, 인터넷으로의 이탈, 원활한 라이브 마이그레이션 및 네트워크 정책 통합이 모두 가능합니다. 

ClusterUserDefinedNetwork CRD를 사용하여 서비스 통합을 보여드리겠습니다(하지만 동일한 기능은 UDN CRD를 통해 구성되는 네임스페이스 격리 사용 사례에도 사용 가능합니다). 

서비스 

Kubernetes 서비스를 사용하여 UDN(또는 cUDN)에 연결된 VM을 노출할 수 있습니다. 일반적인 사용 사례는 VM에서 실행 중인 애플리케이션을 외부에 노출하는 것입니다. 

HTTP 서버를 실행할 VM을 만들어 보겠습니다. 다음 YAML을 프로비저닝하세요.

---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  labels:
    kubevirt.io/vm: red
  name: red
  namespace: red-namespace
spec:
  runStrategy: Always
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nginx
    spec:
      domain:
        devices:
          disks:
            - disk:
                bus: virtio
              name: containerdisk
            - disk:
                bus: virtio
              name: cloudinitdisk
          interfaces:
            - name: happy
        	  binding:
                name: l2bridge
      	rng: {}
      resources:
        requests:
          memory: 2048M
      networks:
        - pod: {}
          name: happy
  	 terminationGracePeriodSeconds: 0
      volumes:
        - containerDisk:
            image: quay.io/kubevirt/fedora-with-test-tooling-container-disk:v1.4.0
          name: containerdisk
        - cloudInitNoCloud:
            userData: |-
              #cloud-config
              password: fedora
              chpasswd: { expire: False }
              packages:
                - nginx
              runcmd:
                - [ "systemctl", "enable", "--now", "nginx" ]
          name: cloudinitdisk

이 VM은 기본적으로 80번 포트에서 웹 서버를 실행하는 nginx를 실행합니다. 이제 이 포트를 클러스터 외부에 노출하는 로드 밸런서 서비스를 프로비저닝해 보겠습니다. 이를 위해 다음 YAML 파일을 프로비저닝합니다.

apiVersion: v1
kind: Service
metadata:
  name: webapp
  namespace: red-namespace
spec:
  selector:
	app.kubernetes.io/name: nginx
  ports:
	- protocol: TCP
  	  port: 80
  	  targetPort: 80
  type: LoadBalancer

이제 서비스 상태를 확인할 수 있습니다.

kubectl get service -nred-namespace webapp -oyaml
apiVersion: v1
kind: Service
metadata:
  ...
  name: webapp
  namespace: red-namespace
  resourceVersion: "588976"
  uid: 9d8a723f-bd79-48e5-95e7-3e235c023314
spec:
  allocateLoadBalancerNodePorts: true
  clusterIP: 172.30.162.106
  clusterIPs:
  - 172.30.162.106
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - nodePort: 30600
	port: 80
	protocol: TCP
	targetPort: 80
  selector:
	app.kubernetes.io/name: redvm
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
	ingress:
	- ip: 192.168.10.0
  	ipMode: VIP
# lets ensure we have endpoints ...
kubectl get endpoints -nred-namespace
NAME 	ENDPOINTS      	AGE
webapp   10.132.2.66:80   16m

서비스 상태를 통해 노출된 VM에 액세스하는 데 사용할 IP 주소를 파악할 수 있습니다. 

클러스터 외부에서 접근해 보겠습니다.

curl -I 192.168.10.0
HTTP/1.1 200 OK
Server: nginx/1.22.1
Date: Tue, 11 Mar 2025 18:09:12 GMT
Content-Type: text/html
Content-Length: 8474
Last-Modified: Fri, 26 Mar 2021 17:49:58 GMT
Connection: keep-alive
ETag: "605e1ec6-211a"
Accept-Ranges: bytes

결론 

이 글에서는 주요 UDN 기능을 소개하고, 이 기능을 사용하여 OpenShift 클러스터에서 가상화 워크로드에 대한 기본 네트워크 세분화를 제공하고 네임스페이스 격리를 달성하거나 선택한 네임스페이스를 상호 연결하는 방법을 설명했습니다.  

또한 UDN 기능이 Kubernetes API와 기본적으로 통합되는 방식을 보여드렸으며, 이를 통해 사용자는 Kubernetes 서비스를 사용하여 UDN에 연결된 워크로드를 노출하고 UDN에 마이크로 세그먼테이션을 작성하여 UDN에서 허용되는 트래픽을 더욱 제한할 수 있습니다.

자세히 알아보세요: 

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

You May Also Like