![](https://tech.osci.kr/wp-content/uploads/2025/02/ovn-logo.png)
들어가며
안녕하세요. 오픈소스컨설팅의 엔지니어 조철진 입니다.
저는 오픈소스컨설팅에서 업무를 하다 보니 다양한 Private Cloud 환경을 접할 수 있었는데요. OpenStack 기술 지원을 하면서 발생하는 다양한 문제를 살펴보면 다른 구성 요소보다 네트워크에 대한 병목이 가장 시급하게 개선 되어야 할 문제였던 것 같습니다.
따라서, OVS만을 사용하였을 때 발생할 수 있는 문제들을 해결하기 위해 다양한 컨퍼런스와 서밋에서 소개되어 안정성과 성능이 입증된 OVN에 대한 학습과 적용이 필요했습니다. 이번 글에서는 OVN을 직접 KVM에 적용해보면서 학습해본 경험을 공유 드려보겠습니다.
이론
OVN에 대한 구조와 이론적인 내용을 다루는 글은 다른 블로그나 공식 사이트에도 상당히 많이 소개가 되어있으며, 광범위한 사항들을 다루기 때문에 여기 서는 간단히 개념만 짚어보겠습니다. 제가 정리한 내용은 하단에 접어 둘 테니 나중에 시간이 되면 실습을 하며 다뤘던 부분이 어떤 이론에 해당하는지 살펴보면 좋겠습니다.
▶ OVN 구조 및 설명
OVN Architecture
![](https://tech.osci.kr/wp-content/uploads/2025/02/image-6.png)
NorthBound 구성 요소
OVN/CMS Plugin
CMS의 구성 요소로, OVN의 인터페이스 역할을 합니다. OpenStack에서는 이 플러그인이 Neutron Plugin 으로 구현됩니다. 플러그인의 주요 목적은 CMS 고유 형식의 Logical Network 구성을 OVN이 이해할 수 있는 중간 표현(Intermediate Representation)방식으로 변환하는 것입니다.
OVN Northbound Database
OVN/CMS Plugin에서 전달 받은 중간 표현 방식의 Logical Network 구성을 저장합니다. 이 데이터베이스의 스키마는 CMS에서 사용하는 Logical Switch, Router, ACL 등의 개념을 OVN이 바로 해석할 수 있도록 설계되어 있습니다.
ovn-northd
OVN Northbound Database와 OVN Southbound Database를 연결합니다.
Northbound Database에서 가져온 Logical Network 구성을 기존 Network 개념으로 변환하여 Southbound Database에 Logical Datapath Flows로 저장합니다.
SouthBound 구성 요소
OVN Southbound Database
시스템의 중심 역할을 하는 데이터베이스로 ovn-northd와 각 ovn-controller 가 클라이언트로 연결됩니다.
OVN Southbound Database는 다음과 같은 세 가지 주요 데이터를 포함합니다.
- Physical Network (PN) Tables
하이퍼바이저 및 다른 노드에 어떻게 접근하는지 대한 방법을 정의합니다.
이 테이블은 주로 하드웨어와 네트워크의 실제 물리적 연결과 관련된 정보를 담고 있습니다. - Logical Network (LN) Tables
논리 데이터 경로 흐름(logical datapath flows)의 관점에서 논리 네트워크를 설명합니다.
이는 가상화된 네트워크 환경에서 데이터가 어떻게 흐르는 지를 나타내며, 네트워크의 가상 측면에 대한 정보를 제공합니다. - Binding Tables
Logical Network의 각 구성 요소가 Physical Network의 어떤 위치에 연결되는지 매핑하는 역할을 합니다.
Hipervisor는 PN 및 Binding 테이블을 채우는 반면, ovn-northd는 LN 테이블을 채우는 역할을 합니다. 이는 각 구성 요소가 자신의 역할에 따라 필요한 데이터를 입력하고 관리하는 방식으로 작동합니다.
Hypervisor 구성 요소
ovn-controller
각 Hypervisor 및 Gateway 에 설치되는 OVN의 에이전트 입니다.
Geneve(Generic Network Virtualization Encapsulation)라는 OVN의 기본 캡슐화 프로토콜을 통해, Hypervisor 간 논리적 네트워크 트래픽을 전달합니다.
- OVN Southbound Database에 연결하여 OVN의 구성 및 상태를 학습합니다.
- Hypervisor와 관련된 물리 네트워크 정보를 PN Table에 기록합니다.
- 현재 Hypervisor의 상태를 Binding Table의 Chassis 열에 기록하여, Logical Network와 Physical Network 간의 연결 상태를 반영합니다.
- Southbound의 ovs-vswitchd에 OpenFlow 컨트롤러로 연결되어 네트워크 트래픽을 제어합니다.
- OpenFlow 프로토콜을 통해 네트워크 트래픽을 제어하며, Logical Network의 트래픽 흐름이 Physical Network에서 올바르게 처리되도록 관리합니다.
- 로컬 ovsdb-server에 연결해 네트워크 설정 변경 사항을 동기화하고, OpenvSwitch가 OVN의 논리 네트워크 구성과 일치하도록 유지합니다.
OVN DB 예시
다음은 두 개의 Hypervisor가 연결된 OVN DB 예시입니다.
구분 선을 기준으로 좌측 테이블은 논리적인 네트워크를 담당하는 Northbound Database, 우측은
물리적인 네트워크를 담당하는 Southbound Database에 해당합니다.
![](https://tech.osci.kr/wp-content/uploads/2025/02/ovn-table-1.png)
Logical Switch (ls-int)
ls-int은 논리적 스위치로, LP1(포트 1)과 LP2(포트 2)가 연결되어 있습니다.
Logical Port (LP1, LP2)
LP1의 MAC 주소는 AA
.
LP2의 MAC 주소는 BB
.
Chassis (ovn-controller)
HV1: Geneve 터널링 프로토콜을 사용하며 IP는 10.0.0.10
HV2: Geneve 터널링 프로토콜을 사용하며 IP는 10.0.0.11
Bindings (ovn-controller)
LP1은 HV1에 바인딩되어 있습니다.
LP2는 아직 하이퍼바이저에 바인딩 되기 전 상태입니다.
Pipeline (ovn-northd)
데이터 패킷 흐름이 다음과 같이 정의됩니다:
eth.dst == AA
: LP1으로 라우팅eth.dst == BB
: LP2로 라우팅eth.dst == <broadcast>
: 브로드캐스트
![](https://tech.osci.kr/wp-content/uploads/2025/02/ovn-table-2.png)
Bindings 테이블에 LP2는 HV2 하이퍼바이저로 바인딩 되도록 업데이트된 모습입니다.
이로서, ls-int 스위치에 연결된 두 하이퍼 바이저는 통신이 가능하게 됩니다.
OVN
Open vSwitch (OVS)의 가상 네트워크 관리 기능을 확장한 솔루션으로, 복잡한 멀티-테넌트 환경에서 보다 세밀한 네트워크 정책을 제공합니다. OVN은 OVS의 기본적인 L2, L3 스위칭 기능에 로드 밸런싱, ACL, NAT 등의 고급 기능을 제공하여, 가상 네트워크의 관리와 구성을 효율적으로 수행할 수 있도록 돕습니다.
Northbound
Northbound 는 주로 네트워크 가상화의 관리를 위한 API를 애플리케이션에 제공하는 역할을 합니다. 이 인터페이스를 통해 사용자나 관리 시스템은 OVN 시스템에 논리 네트워크 구성을 명령할 수 있습니다. 예를 들어, 가상 네트워크, 스위치, 라우터의 생성 및 구성, ACL과 같은 요소들을 관리할 수 있습니다
Southbound
Southbound 는 OVN 컨트롤러에서 명령을 내렸을 때, OVS 스위치에 실제로 설정을 적용하는 역할을 합니다. 또한 네트워크 상태를 실시간으로 모니터링 하여 최단 경로의 Flow로 통신하도록 경로를 최적화하고 장애 발생 시 트래픽을 다른 경로로 우회하는 등 성능 및 신뢰성 관련된 역할도 담당합니다.
실습
구성도 및 네트워크 정보
![](https://tech.osci.kr/wp-content/uploads/2025/02/OVN-실습구성도-1024x562.png)
서버/사용 네트워크 | Public Network: 10.10.10.0/24 Gateway: 10.10.10.1 | Private Network: 10.10.20.0/24 | VM IP Network: 172.16.0.0/24 Gateway: 172.16.0.1 |
---|---|---|---|
OVN Controller | 10.10.10.233 | X | X |
KVM1(VM1) | 10.10.10.234 | 10.10.20.234 | 172.16.0.201 |
KVM2(VM2) | 10.10.10.235 | 10.10.20.235 | 172.16.0.202 |
- 구분을 위하여 OVN Controller 전용 1대, Chassis 역할의 KVM1, KVM2 총 3대 서버로 구성하였습니다.
- 명령 수행 이후 상태를 확인하는 명령어의 경우, 구분을 위해 호스트네임을 포함하였습니다.
OVN 구성 및 준비
OVN 컨트롤러 노드
- OVN 관련 패키지 설치
- Nortbound, Southbound DB 포트 오픈
apt install -y ovn-host ovn-central ovn-common
# Northbound DB (6641), Southbound DB (6642) 포트 열기
ovn-nbctl set-connection ptcp:6641
ovn-sbctl set-connection ptcp:6642
KVM1, KVM2 노드
- OVN 관련 패키지 설치
- OVN 컨트롤러 연결
- KVM에서 사용할 OVS 네트워크 생성
apt-get -y install ovn-host openvswitch-switch
# kvm1
ovs-vsctl set open . external_ids:ovn-remote="tcp:10.10.10.233:6642" \
external_ids:ovn-nb="tcp:10.10.10.233:6641" \
external_ids:ovn-encap-ip=10.10.10.234 \
external_ids:ovn-encap-type=geneve
# kvm2
ovs-vsctl set open . external_ids:ovn-remote="tcp:10.10.10.233:6642" \
external_ids:ovn-nb="tcp:10.10.10.233:6641" \
external_ids:ovn-encap-ip=10.10.10.235 \
external_ids:ovn-encap-type=geneve
# kvm1, kvm2
cat << EOF > ovs_net.xml
<network>
<name>ovs-net</name>
<forward mode='bridge'/>
<bridge name='br-int'/>
<virtualport type='openvswitch'/>
</network>
EOF
virsh net-define ovs_net.xml
virsh net-autostart ovs-net
virsh net-start ovs-net
root@ovn:~# ovn-sbctl show
Chassis "021bff7f-68cf-4625-8b37-340cae655674"
hostname: kvm1
Encap geneve
ip: "10.10.10.234"
options: {csum="true"}
Chassis "23e299e3-da54-457d-a8e7-850dfeaebfb7"
hostname: kvm2
Encap geneve
ip: "10.10.10.235"
options: {csum="true"}
root@kvm1:~# ovs-vsctl show
0215e66a-1ced-4105-8651-eb2eae66c6cb
Bridge br-int
fail_mode: secure
datapath_type: system
Port br-int
Interface br-int
type: internal
Port ovn-23e299-0
Interface ovn-23e299-0
type: geneve
options: {csum="true", key=flow, remote_ip="10.10.10.235"}
ovs_version: "2.17.9"
root@kvm2:~# ovs-vsctl show
0215e66a-1ced-4105-8651-eb2eae66c6cb
Bridge br-int
fail_mode: secure
datapath_type: system
Port br-int
Interface br-int
type: internal
Port ovn-021bff-0
Interface ovn-021bff-0
type: geneve
options: {csum="true", key=flow, remote_ip="10.10.10.234"}
ovs_version: "2.17.9"
- OVS 네트워크에 연결되는 VM Guest 생성
- 생성 이후 VM Guest IP 설정 필요
# kvm1
virt-install \
--name vm1 \
--virt-type kvm \
--os-variant=generic \
--vcpus 2 \
--memory 2048 \
--disk "/var/lib/libvirt/images/ubuntest.qcow2",device=disk,bus=virtio,format=qcow2 \
--network bridge=br-int,virtualport_type=openvswitch,model=virtio \
--graphics none --noautoconsole --import
# kvm2
virt-install \
--name vm2 \
--virt-type kvm \
--os-variant=generic \
--vcpus 2 \
--memory 2048 \
--disk "/var/lib/libvirt/images/ubuntest.qcow2",device=disk,bus=virtio,format=qcow2 \
--network bridge=br-int,virtualport_type=openvswitch,model=virtio \
--graphics none --noautoconsole --import
root@kvm1:~# ovs-vsctl show
0215e66a-1ced-4105-8651-eb2eae66c6cb
Bridge br-int
fail_mode: secure
datapath_type: system
Port br-int
Interface br-int
type: internal
Port vnet0
Interface vnet0
Port ovn-23e299-0
Interface ovn-23e299-0
type: geneve
options: {csum="true", key=flow, remote_ip="10.10.10.235"}
ovs_version: "2.17.9"
root@kvm2:~# ovs-vsctl show
0215e66a-1ced-4105-8651-eb2eae66c6cb
Bridge br-int
fail_mode: secure
datapath_type: system
Port br-int
Interface br-int
type: internal
Port ovn-021bff-0
Interface ovn-021bff-0
type: geneve
options: {csum="true", key=flow, remote_ip="10.10.10.234"}
Port vnet0
Interface vnet0
ovs_version: "2.17.9"
- 생성된 VM의 OVS 네트워크 정보 기록(추후 사용)
- iface-id
- attached-mac
root@kvm1:~# ovs-vsctl get interface vnet0 external_ids:iface-id
"c6c32709-bcd2-4585-b321-1606c349767a"
root@kvm1:~# ovs-vsctl get interface vnet0 external_ids:attached-mac
"52:54:00:c4:a1:03"
root@kvm2:~# ovs-vsctl get interface vnet0 external_ids:iface-id
"476e1fee-c08c-4495-9673-80246200de1f"
root@kvm2:~# ovs-vsctl get interface vnet0 external_ids:attached-mac
"52:54:00:5f:d5:6f"
내부 스위치 구성
OVN 컨트롤러 노드
- 내부 연결용 논리 스위치 ls-int 생성 및 포트 생성
- 논리 스위치 포트에 앞서 기록한 iface-id, attached-mac 설정
- 해당 설정을 완료하면, VM Guest 간 내부 통신이 가능합니다.
ovn-nbctl ls-add ls-int
ovn-nbctl lsp-add ls-int c6c32709-bcd2-4585-b321-1606c349767a
ovn-nbctl lsp-set-addresses c6c32709-bcd2-4585-b321-1606c349767a "52:54:00:c4:a1:03 172.16.0.201/24"
ovn-nbctl lsp-add ls-int 476e1fee-c08c-4495-9673-80246200de1f
ovn-nbctl lsp-set-addresses 476e1fee-c08c-4495-9673-80246200de1f "52:54:00:5f:d5:6f 172.16.0.202/24"
root@ovn:~# ovn-nbctl list Logical_switch_port
_uuid : 606a0825-5dfa-4e5a-b516-b0aac9f15079
addresses : ["52:54:00:c4:a1:03 172.16.0.201/24"]
dhcpv4_options : []
dhcpv6_options : []
dynamic_addresses : []
enabled : []
external_ids : {}
ha_chassis_group : []
name : "c6c32709-bcd2-4585-b321-1606c349767a"
options : {}
parent_name : []
port_security : []
tag : []
tag_request : []
type : ""
up : true
_uuid : 6afc92eb-44a8-4cd8-9094-2be047fc3185
addresses : ["52:54:00:5f:d5:6f 172.16.0.202/24"]
dhcpv4_options : []
dhcpv6_options : []
dynamic_addresses : []
enabled : []
external_ids : {}
ha_chassis_group : []
name : "476e1fee-c08c-4495-9673-80246200de1f"
options : {}
parent_name : []
port_security : []
tag : []
tag_request : []
type : ""
up : true
외부 라우터 – 스위치 구성
KVM1, KVM2 노드
- 외부 인터넷과 연결되는 인터페이스 전용 OVS 브릿지 구성
cat << /etc/netplan/0-default.yaml > EOF
network:
version: 2
renderer: networkd
ethernets:
ens2:
dhcp4: no
bridges:
br-ext:
openvswitch: {}
addresses:
- 10.10.10.234/24
routes:
- to: 0.0.0.0/0
via: 10.10.10.1
nameservers:
addresses: [8.8.8.8]
interfaces:
- ens2
EOF
netplan apply
- OVS에서 br-ext 브릿지를 ‘UPLINK’ 라는 이름으로 매핑 설정
- 추후 OVN에서 설정한 이름인 ‘UPLINK’를 활용해 외부로 라우팅
ovs-vsctl set open . external-ids:ovn-bridge-mappings=UPLINK:br-ext
root@kvm1:~# ovs-vsctl show
0215e66a-1ced-4105-8651-eb2eae66c6cb
Bridge br-ext
fail_mode: standalone
Port br-ext
Interface br-ext
type: internal
Port ens2
Interface ens2
root@kvm1:~# ovs-vsctl list open .
_uuid : 0215e66a-1ced-4105-8651-eb2eae66c6cb
bridges : [3f241805-581b-46cd-b6cc-0cf50c0e9b51, ed0ffe18-e5e2-4b15-9417-203a707aac0a]
cur_cfg : 51
datapath_types : [netdev, system]
datapaths : {system=dfa80abf-9e7a-4c44-a972-924ec2756944}
db_version : "8.3.0"
dpdk_initialized : false
dpdk_version : none
external_ids : {hostname=kvm1, ovn-bridge-mappings="UPLINK:br-ext", ovn-encap-ip="10.10.10.234", ovn-encap-type=geneve, ovn-nb="tcp:10.10.10.233:6641", ovn-remote="tcp:10.10.10.233:6642", rundir="/var/run/openvswitch", system-id="021bff7f-68cf-4625-8b37-340cae655674"}
iface_types : [bareudp, erspan, geneve, gre, gtpu, internal, ip6erspan, ip6gre, lisp, patch, stt, system, tap, vxlan]
manager_options : []
next_cfg : 51
other_config : {vlan-limit="0"}
ovs_version : "2.17.9"
ssl : []
statistics : {}
system_type : ubuntu
system_version : "22.04"
OVN 컨트롤러 노드
- 외부 인터넷 연결용 논리 스위치 ls-ext 생성
- 논리 라우터 lr-ext 생성 및 게이트웨이 IP 설정
ovn-nbctl ls-add ls-ext
ovn-nbctl lr-add lr-ext
ovn-nbctl lrp-add lr-ext lr-ext-ls-int 00:00:00:00:00:01 172.16.0.1/24
ovn-nbctl lrp-add lr-ext lr-ext-ls-ext 00:00:00:00:00:02 10.10.10.240/24
- 내부 스위치(ls-int)와 라우터(lr-ext) 연결
- 외부 스위치(ls-ext) 및 라우터(lr-ext) 연결
- 외부 스위치(ls-ext) 업링크 설정
ovn-nbctl lsp-add ls-int ls-int-lr-ext
ovn-nbctl lsp-set-type ls-int-lr-ext router
ovn-nbctl lsp-set-addresses ls-int-lr-ext router
ovn-nbctl lsp-set-options ls-int-lr-ext router-port=lr-ext-ls-int
ovn-nbctl lsp-add ls-ext ls-ext-lr-ext
ovn-nbctl lsp-set-type ls-ext-lr-ext router
ovn-nbctl lsp-set-addresses ls-ext-lr-ext router
ovn-nbctl lsp-set-options ls-ext-lr-ext router router-port=lr-ext-ls-ext
ovn-nbctl lsp-add ls-ext ls-ext-br-ext
ovn-nbctl lsp-set-type ls-ext-br-ext localnet
ovn-nbctl lsp-set-addresses ls-ext-br-ext unknown
ovn-nbctl lsp-set-options ls-ext-br-ext network_name=UPLINK
- 논리 라우터 Default Route 설정
ovn-nbctl lr-route-add lr-ext 0.0.0.0/0 10.10.10.1
- 어떤 호스트가 라우터 포트의 트래픽을 처리할 것인지 우선 순위 설정
- 호스트 uuid 값은 앞서
ovn-sbctl show
로 조회한 Chassis 명을 사용
ovn-nbctl lrp-set-gateway-chassis lr-ext-ls-ext 021bff7f-68cf-4625-8b37-340cae655674 100
ovn-nbctl lrp-set-gateway-chassis lr-ext-ls-ext 23e299e3-da54-457d-a8e7-850dfeaebfb7 75
- VM이 사용하는 내부 IP 대역 172.16.0.0/24에 대하여 논리 라우터 IP 10.10.10.240을 통해 외부 통신이 가능하도록 SNAT 설정
ovn-nbctl lr-nat-add lr-ext snat 10.10.10.240 172.16.0.0/24
최종 토플로지 확인
root@ovn:~# ovn-nbctl show
switch 8bc87a18-7236-474e-802a-072e952c97d8 (ls-ext)
port ls-ext-lr-ext
type: router
router-port: lr-ext-ls-ext
port ls-ext-br-ext
type: localnet
addresses: ["unknown"]
switch 516923e7-805d-473f-9a7a-e29301dd7f74 (ls-int)
port ls-int-lr-ext
type: router
router-port: lr-ext-ls-int
port c6c32709-bcd2-4585-b321-1606c349767a
addresses: ["52:54:00:c4:a1:03 172.16.0.201/24"]
port 476e1fee-c08c-4495-9673-80246200de1f
addresses: ["52:54:00:5f:d5:6f 172.16.0.202/24"]
router 7cc0137d-83c1-4fe4-8a60-5bbf615279ae (lr-ext)
port lr-ext-ls-int
mac: "00:00:00:00:00:01"
networks: ["172.16.0.1/24"]
port lr-ext-ls-ext
mac: "00:00:00:00:00:02"
networks: ["10.10.10.240/24"]
gateway chassis: [23e299e3-da54-457d-a8e7-850dfeaebfb7 021bff7f-68cf-4625-8b37-340cae655674]
nat c29444cf-190e-4e86-b150-7691c025a699
external ip: "10.10.10.240"
logical ip: "172.16.0.0/24"
type: "snat"
여기까지, 진행했다면 내부 통신만 가능했던 VM에서 외부 인터넷으로 통신이 가능합니다.
결론
지금까지 OVN을 구축해서 KVM에 적용해본 사례를 공유 드려보았습니다.
사실 이렇게 OVN을 따로 직접 만들어보면서 적용하는 것은 어떻게 보면 다소 무의미하고, 무모한 일이라고 생각되기도 합니다. 하지만 훨씬 크고 복잡한 클라우드 환경으로 구성을 해보기 앞서 이러한 구성 요소를 직접 다뤄보면서 학습하고 알아가는 과정은 추후 문제해결이나 개선 시에 도움이 되고, SDN/NFV에 대한 전반적 이해에도 도움이 될 것 입니다.
다음에는 OpenStack이나 Kubernetes에 적용한 OVN 사례를 보면서 이야기를 이어나가 볼 수 있도록 하겠습니다.
감사합니다!
참고문서
[1] https://www.ovn.org/en/architecture/
[2] https://www.ducksource.blog/blog/sdn-with-ovn-and-ovs-lab-build/