Open vSwitch (OVS) 커널 데이터패스 심화
Open vSwitch는 SDN(Software-Defined Networking) 환경의 핵심 가상 스위치입니다.
커널 데이터패스 모듈(openvswitch.ko)은 수백만 pps 수준의 패킷 포워딩을 담당하며,
유저스페이스 데몬(ovs-vswitchd)과 Netlink 채널로 연동하여 플로우 테이블을 관리합니다.
이 문서는 OVS 아키텍처, 커널 모듈 내부 구조, 데이터패스 파이프라인, Megaflow/Microflow 캐시 계층,
upcall 메커니즘, conntrack(CT) 통합, 터널링, TC offload, DPDK 데이터패스, OpenFlow 프로토콜 연동,
Netlink 프로토콜 상세, 성능 분석, 디버깅 기법, 운영 장애 대응 플레이북,
용량 계획(SLO), 멀티테넌시 보안 하드닝까지 전 영역을 심층 분석합니다.
sk_buff, Netlink 소켓,
브리지 개념에 대한 사전 이해가 필요합니다.
핵심 요약
- openvswitch.ko -- 커널 데이터패스 모듈. 패킷 매칭과 액션 실행을 고속 처리합니다.
- ovs-vswitchd -- 유저스페이스 스위치 데몬. OpenFlow 컨트롤러와 통신하고 플로우 룰을 관리합니다.
- ovsdb-server -- OVS 구성 데이터베이스. 포트, 브리지, QoS 등 설정을 저장합니다.
- Megaflow 캐시 -- 마스크 기반 와일드카드 매칭 캐시. 단일 플로우 엔트리로 다수 패킷을 처리합니다.
- upcall -- 커널 캐시 미스 시 유저스페이스로 패킷을 전달하는 메커니즘입니다.
단계별 이해
- 패킷 수신
NIC에서 패킷이 도착하면 OVS 커널 모듈의 vport를 통해 데이터패스에 진입합니다. - 플로우 룩업
커널은 Microflow 캐시 -> Megaflow 캐시 -> 정확한 플로우 테이블 순서로 매칭을 시도합니다. - 캐시 히트 시 액션 실행
매칭된 플로우의 액션(출력, VLAN 태깅, NAT 등)을 커널에서 직접 수행합니다. - 캐시 미스 시 upcall
매칭 실패 시 Netlink를 통해 ovs-vswitchd에 패킷을 전달하고, 데몬이 새 플로우를 설치합니다. - 후속 패킷 고속 처리
설치된 플로우 캐시에 의해 동일 패턴의 후속 패킷은 커널에서 직접 처리됩니다.
Open vSwitch 개요
OVS란 무엇인가
Open vSwitch(OVS)는 프로덕션 환경에서 사용되는 다계층 가상 스위치입니다. Apache 2.0 라이선스로 배포되며, 하드웨어 스위치와 동등한 수준의 기능을 소프트웨어로 구현합니다. VMware NSX, Red Hat OpenStack, Kubernetes(OVN) 등 주요 가상화/클라우드 플랫폼에서 핵심 네트워킹 컴포넌트로 활용됩니다.
OVS가 필요한 이유
리눅스 커널에는 이미 bridge 모듈이 있지만, 클라우드/SDN 환경에서는 몇 가지 한계가 있습니다.
| 기능 | 리눅스 Bridge | Open vSwitch |
|---|---|---|
| OpenFlow 지원 | 미지원 | 완전 지원 (1.0 ~ 1.5) |
| 플로우 기반 포워딩 | MAC 학습 기반 | L2-L4 다계층 매칭 |
| 터널 오버레이 | 제한적 (VXLAN 별도 설정) | VXLAN/Geneve/GRE/STT 통합 |
| 중앙 집중 관리 | 각 호스트 개별 관리 | OVSDB 프로토콜 원격 관리 |
| QoS | TC로 별도 구성 | 내장 QoS (큐잉, 미터링) |
| 상태 기반 방화벽 | Netfilter 별도 사용 | CT 액션으로 통합 |
| 하드웨어 오프로드 | 제한적 | TC flower / SmartNIC 오프로드 |
| DPDK 데이터패스 | 미지원 | OVS-DPDK PMD 지원 |
| 분산 가상 라우터 | 미지원 | OVN을 통한 DVR 지원 |
OVS 역사와 발전
OVS는 2009년 Nicira Networks에서 시작되었습니다. 2012년 VMware가 Nicira를 인수한 뒤에도 오픈소스 프로젝트로 유지되어 활발히 발전하고 있습니다.
| 시기 | 이벤트 | 기술적 의미 |
|---|---|---|
| 2009 | OVS 1.0 초기 릴리즈 | 커널 데이터패스 + 유저스페이스 컨트롤 플레인 아키텍처 확립 |
| 2012 | VMware, Nicira 인수 | NSX 플랫폼의 핵심 컴포넌트로 편입 |
| 2014 (Linux 3.3+) | 커널 트리 통합 | net/openvswitch/ 디렉터리로 메인라인 커널 편입 |
| 2015 | Megaflow 캐시 도입 | 와일드카드 매칭으로 upcall 빈도 대폭 감소 |
| 2016 | Conntrack(CT) 액션 지원 | 상태 기반 방화벽 기능 통합 |
| 2016 | OVS-DPDK 안정화 | 유저스페이스 데이터패스로 수십 Mpps 달성 |
| 2018 | TC flower offload 지원 | SmartNIC 하드웨어 가속 경로 확립 |
| 2019 | OVN (Open Virtual Network) 분리 | 분산 가상 라우팅/스위칭 독립 프로젝트 |
| 2023+ | P4-OVS, eBPF 데이터패스 실험 | 차세대 프로그래밍 가능 데이터패스 탐색 |
커널 데이터패스 vs 유저스페이스 데이터패스
OVS는 두 가지 데이터패스 모드를 지원합니다. 커널 데이터패스(system)는
openvswitch.ko 모듈을 통해 커널 공간에서 패킷을 처리합니다.
유저스페이스 데이터패스(netdev)는 DPDK PMD 또는 AF_XDP를 사용하여
커널 바이패스로 패킷을 처리합니다.
| 특성 | 커널 데이터패스 (system) | 유저스페이스 데이터패스 (netdev/DPDK) |
|---|---|---|
| 패킷 처리 위치 | 커널 공간 (openvswitch.ko) | 유저스페이스 (PMD 스레드) |
| 성능 (64B 패킷) | 수백만 pps | 수천만 pps (DPDK) |
| 지연 시간 | 수 마이크로초 | 수백 나노초 |
| CPU 사용 | 트래픽 비례 (인터럽트 기반) | 항상 100% (폴링 기반) |
| 운영 복잡도 | 낮음 | 높음 (hugepage, CPU 격리 필요) |
| 커널 스택 통합 | 완전 통합 (iptables, TC 등) | 커널 바이패스 (별도 구성 필요) |
OVS 전체 아키텍처
계층별 컴포넌트
OVS 아키텍처는 크게 세 계층으로 나뉩니다. 관리 평면(Management Plane)의 ovsdb-server,
제어 평면(Control Plane)의 ovs-vswitchd, 데이터 평면(Data Plane)의 커널 모듈입니다.
이들은 서로 다른 프로세스/모듈로 분리되어 있으며, 표준 프로토콜로 통신합니다.
| 계층 | 컴포넌트 | 역할 | 통신 프로토콜 |
|---|---|---|---|
| 관리 평면 | ovsdb-server |
구성 데이터베이스 (브리지, 포트, QoS, 미러링 등) | OVSDB 프로토콜 (JSON-RPC over TCP/Unix) |
| 제어 평면 | ovs-vswitchd |
OpenFlow 에이전트, 플로우 관리, 학습 | OpenFlow (TCP 6653), Netlink (커널 통신) |
| 데이터 평면 | openvswitch.ko |
패킷 매칭, 액션 실행, 캐싱 | Generic Netlink (ovs-vswitchd와 통신) |
| 외부 컨트롤러 | SDN Controller (ONOS, ODL, Ryu 등) | 네트워크 토폴로지, 정책 결정 | OpenFlow (TCP 6653) |
주요 유저스페이스 컴포넌트
ovsdb-server:
OVS 구성 정보를 저장하는 경량 데이터베이스 서버입니다. OVSDB 프로토콜(RFC 7047)을 구현하며,
브리지, 포트, 인터페이스, QoS, 미러링 등의 설정 데이터를 JSON 형식으로 관리합니다.
/etc/openvswitch/conf.db에 영속적으로 저장됩니다.
ovs-vswitchd: OVS의 핵심 데몬으로, 세 가지 주요 역할을 수행합니다.
- OpenFlow 에이전트: SDN 컨트롤러와 통신하여 플로우 테이블을 수신합니다.
- 커널 데이터패스 관리: Netlink를 통해 커널 플로우 캐시를 설치/삭제합니다.
- 학습 로직: 캐시 미스(upcall) 시 OpenFlow 테이블을 참조하여 적절한 액션을 결정합니다.
OVS 설치와 기본 설정
# OVS 커널 모듈 로드
modprobe openvswitch
# 데몬 시작
systemctl start openvswitch-switch
# 브리지 생성
ovs-vsctl add-br br0
# 물리 포트 추가
ovs-vsctl add-port br0 eth0
# VXLAN 터널 포트 추가
ovs-vsctl add-port br0 vxlan0 -- set interface vxlan0 \
type=vxlan options:remote_ip=10.0.0.2 options:key=flow
# OpenFlow 컨트롤러 연결
ovs-vsctl set-controller br0 tcp:192.168.1.100:6653
# 현재 구성 확인
ovs-vsctl show
커널 모듈 구조
소스 디렉터리 구조
OVS 커널 모듈은 net/openvswitch/ 디렉터리에 위치합니다.
주요 소스 파일과 역할은 다음과 같습니다.
| 파일 | 역할 |
|---|---|
datapath.c | 데이터패스 핵심 로직, Netlink 인터페이스, upcall |
datapath.h | datapath 구조체, 매크로 정의 |
flow.c | 패킷에서 플로우 키 추출 |
flow.h | sw_flow_key 구조체 정의 |
flow_table.c | 플로우 테이블 관리, 해시 테이블 |
flow_netlink.c | Netlink 메시지 <-> 플로우 키/액션 변환 |
actions.c | OVS 액션 실행 엔진 |
conntrack.c | Conntrack(CT) 통합 액션 |
vport.c | 가상 포트 추상화 계층 |
vport-netdev.c | 일반 net_device vport |
vport-internal.c | 내부(브리지 자체) 포트 |
vport-vxlan.c | VXLAN 터널 포트 |
vport-geneve.c | Geneve 터널 포트 |
vport-gre.c | GRE 터널 포트 |
meter.c | OpenFlow 미터 테이블 |
핵심 자료구조: struct datapath
/* net/openvswitch/datapath.h */
struct datapath {
struct rcu_head rcu;
struct list_head list_node; /* 전역 데이터패스 목록 */
struct flow_table table; /* 플로우 테이블 */
struct hlist_head *ports; /* vport 해시 테이블 */
struct dp_stats_percpu __percpu *stats_percpu; /* Per-CPU 통계 */
struct dp_nlsk_pids __rcu *upcall_portids; /* upcall Netlink PID */
u32 user_features; /* 유저스페이스 피처 비트 */
struct net *net; /* 네트워크 네임스페이스 */
char name[IFNAMSIZ]; /* 데이터패스 이름 (예: "ovs-system") */
};
코드 설명
-
4행
list_node: 시스템 내 모든 OVS 데이터패스를 연결하는 리스트 노드입니다. -
6행
table: 이 데이터패스의 플로우 테이블 구조체. Megaflow/Microflow 캐시를 포함합니다. -
8행
ports: 이 데이터패스에 등록된 vport들의 해시 테이블. 포트 번호로 빠른 검색이 가능합니다. -
9행
stats_percpu: Per-CPU 통계로 캐시 히트, 캐시 미스, 에러 등 패킷 처리 카운터를 집계합니다. -
10행
upcall_portids: upcall을 수신할 유저스페이스 Netlink 포트 ID. 다수 핸들러 스레드 지원을 위해 배열 형태입니다.
vport 추상화 계층
vport는 OVS 커널 모듈의 포트 추상화입니다. 물리 NIC, VM 탭 디바이스, 내부 포트,
터널 포트 등 다양한 유형의 포트를 통일된 인터페이스로 다룹니다.
/* net/openvswitch/vport.h */
struct vport {
struct net_device *dev; /* 대응하는 net_device */
struct datapath *dp; /* 소속 데이터패스 */
struct vport_portids __rcu *upcall_portids; /* Per-vport upcall PID */
u16 port_no; /* OVS 포트 번호 */
struct hlist_node hash_node; /* 데이터패스 해시 */
struct hlist_node dp_hash_node; /* 전역 해시 */
const struct vport_ops *ops; /* 포트 타입별 연산 */
};
struct vport_ops {
enum ovs_vport_type type;
struct vport *(*create)(const struct vport_parms *);
void (*destroy)(struct vport *);
int (*set_options)(struct vport *, struct nlattr *[]);
int (*get_options)(const struct vport *, struct sk_buff *);
int (*send)(struct vport *, struct sk_buff *);
};
데이터패스 파이프라인
패킷 처리 흐름 개요
커널 데이터패스에서 패킷이 처리되는 과정은 다음과 같습니다. NIC에서 수신된 패킷은
vport의 rx_handler를 통해 OVS 데이터패스에 진입합니다.
데이터패스 코어는 패킷에서 플로우 키를 추출하고, 캐시 계층에서 매칭되는 플로우를 검색합니다.
히트 시 해당 플로우의 액션을 실행하고, 미스 시 upcall을 통해 유저스페이스에 판단을 위임합니다.
핵심 함수 호출 흐름
/* net/openvswitch/datapath.c - 패킷 처리 진입점 */
void ovs_dp_process_packet(struct sk_buff *skb,
struct sw_flow_key *key)
{
struct datapath *dp = get_dp(dev_net(skb->dev), ovs_cb(skb)->input_vport->dp->dp_ifindex);
struct sw_flow *flow;
struct sw_flow_actions *sf_acts;
struct dp_stats_percpu *stats;
stats = this_cpu_ptr(dp->stats_percpu);
/* 1단계: 플로우 룩업 (캐시 계층 통과) */
flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb), &n_mask_hit, &n_cache_hit);
if (unlikely(!flow)) {
/* 캐시 미스: upcall 경로 */
struct dp_upcall_info upcall;
upcall.cmd = OVS_PACKET_CMD_MISS;
upcall.portid = ovs_vport_find_upcall_portid(
ovs_cb(skb)->input_vport, skb);
ovs_dp_upcall(dp, skb, key, &upcall, 0);
stats->n_missed++;
consume_skb(skb);
return;
}
/* 캐시 히트: 액션 실행 */
ovs_flow_stats_update(flow, key->tp.flags, skb);
sf_acts = rcu_dereference(flow->sf_acts);
ovs_execute_actions(dp, skb, sf_acts, key);
stats->n_hit++;
}
코드 설명
-
12행
ovs_flow_tbl_lookup_stats()가 EMC -> SMC -> mask_array 순서로 플로우를 검색합니다.n_cache_hit으로 어느 캐시 단계에서 히트했는지 추적합니다. -
14-23행
매칭 실패 시
OVS_PACKET_CMD_MISS명령으로 upcall을 발생시킵니다.ovs_vport_find_upcall_portid()는 라운드 로빈으로 핸들러 스레드를 선택합니다. -
26-29행
매칭 성공 시
ovs_flow_stats_update()로 플로우 통계를 갱신하고, RCU로 보호된 액션 리스트를 가져와ovs_execute_actions()로 실행합니다.
플로우 키 추출
ovs_flow_key_extract()는 패킷 헤더를 파싱하여 sw_flow_key 구조체를 채웁니다.
이 키는 Ethernet(src/dst MAC, EtherType), IPv4/IPv6(src/dst IP, protocol, ToS, TTL),
TCP/UDP/SCTP(src/dst port), ICMP(type/code), ARP, 터널 메타데이터 등 L2-L4 전체 필드를 포함합니다.
/* net/openvswitch/flow.c */
int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
struct sk_buff *skb,
struct sw_flow_key *key)
{
int error;
/* 1. 메타데이터: 입력 포트, 패킷 마크 등 */
key->phy.priority = skb->priority;
key->phy.in_port = OVS_CB(skb)->input_vport->port_no;
key->phy.skb_mark = skb->mark;
key->ovs_flow_hash = 0;
key->recirc_id = 0;
/* 2. 터널 정보 (터널 포트에서 수신한 경우) */
if (tun_info) {
ovs_flow_tun_info_init(key, tun_info);
}
/* 3. L2/L3/L4 헤더 파싱 */
error = key_extract(skb, key);
return error;
}
플로우 테이블 구조
핵심 자료구조
OVS 플로우 테이블의 핵심은 sw_flow, sw_flow_key,
sw_flow_mask 세 구조체입니다. 플로우 테이블은 마스크 배열(mask_array)과
마스크별 해시 테이블 구조로 구성됩니다.
sw_flow_key 구조체 상세
/* net/openvswitch/flow.h - 플로우 매칭 키 */
struct sw_flow_key {
u8 tun_opts[IP_TUNNEL_OPTS_MAX]; /* 터널 옵션 데이터 */
u8 tun_opts_len;
struct {
u32 priority; /* sk_buff->priority */
u16 in_port; /* OVS 입력 포트 번호 */
u32 skb_mark; /* sk_buff->mark (fwmark) */
} phy;
u32 ovs_flow_hash; /* skb hash (RSS) */
u32 recirc_id; /* 재순환 ID */
struct {
u8 src[ETH_ALEN]; /* 소스 MAC */
u8 dst[ETH_ALEN]; /* 목적지 MAC */
__be16 tci; /* VLAN TCI */
__be16 type; /* EtherType */
struct {
__be16 tpid;
__be16 tci;
} cvlan; /* QinQ 이중 VLAN */
} eth;
union {
struct { /* IPv4 */
__be32 src, dst;
u8 proto, tos, ttl, frag;
} ipv4;
struct { /* IPv6 */
struct in6_addr src, dst;
__be32 label;
u8 proto, tclass, hlimit, frag;
} ipv6;
struct { /* ARP */
__be32 sip, tip;
u8 sha[ETH_ALEN], tha[ETH_ALEN];
u8 op;
} arp;
} ip;
struct {
__be16 src, dst; /* L4 소스/목적지 포트 */
__be16 flags; /* TCP 플래그 또는 ICMP type/code */
} tp;
struct {
u32 state; /* CT 상태 (NEW, ESTABLISHED 등) */
u16 zone; /* CT 존 */
u32 mark; /* CT 마크 */
struct ovs_key_ct_labels labels; /* CT 레이블 */
} ct;
};
플로우 룩업 알고리즘
플로우 룩업은 마스크 배열(mask_array)을 순회하면서 각 마스크를 키에 적용한 뒤,
해시 테이블에서 정확한 매칭을 시도합니다. 이는 TSS(Tuple Space Search) 알고리즘의 변형입니다.
/* net/openvswitch/flow_table.c - 플로우 테이블 룩업 */
struct sw_flow *ovs_flow_tbl_lookup_stats(
struct flow_table *tbl,
const struct sw_flow_key *key,
u32 skb_hash,
u32 *n_mask_hit,
u32 *n_cache_hit)
{
struct mask_array *ma = rcu_dereference(tbl->mask_array);
struct table_instance *ti = rcu_dereference(tbl->ti);
struct sw_flow_mask *mask;
struct sw_flow *flow;
int i;
/* 마스크 캐시(EMC/SMC)를 먼저 시도 -- 생략 */
/* mask_array 순회: 각 마스크에 대해 해시 룩업 */
for (i = 0; i < ma->max; i++) {
mask = rcu_dereference(ma->masks[i]);
if (!mask)
continue;
flow = masked_flow_lookup(ti, key, mask, n_mask_hit);
if (flow)
return flow;
}
return NULL;
}
Megaflow 캐시와 Microflow 캐시
2단계 캐시 계층 구조
OVS 커널 데이터패스는 성능 최적화를 위해 2단계 캐시 계층을 사용합니다. Microflow 캐시(EMC, Exact Match Cache)는 최근 사용된 개별 플로우의 정확한 매칭을 캐싱하며, Megaflow 캐시(SMC, Signature Match Cache)는 마스크 기반 와일드카드 매칭을 캐싱합니다.
| 특성 | Microflow 캐시 (EMC) | Megaflow 캐시 (SMC) |
|---|---|---|
| 매칭 방식 | 정확한 5-tuple 매칭 | 마스크 기반 와일드카드 매칭 |
| 엔트리 수 | Per-CPU 8192개 (기본값) | 커널 플로우 테이블 전체 |
| 룩업 비용 | O(1) 해시 룩업 | O(M) (M = 마스크 수) |
| 캐시 범위 | 단일 플로우 (좁음) | 다수 플로우 커버 (넓음) |
| 적합한 트래픽 | 반복되는 동일 플로우 | 다양한 소스/목적지 조합 |
| 커널 변수 제어 | 유저스페이스 비활성화 가능 | 항상 활성 |
Megaflow의 핵심 개념: 와일드카드 매칭
Megaflow의 핵심 혁신은 하나의 캐시 엔트리로 다수의 패킷 플로우를 커버한다는 것입니다. 예를 들어, "10.0.1.0/24에서 10.0.2.0/24로 가는 모든 TCP 패킷"을 단일 Megaflow 엔트리로 표현할 수 있습니다. 이는 OpenFlow 테이블의 와일드카드 매칭 의미론을 커널 캐시에 반영한 것입니다.
EMC 비활성화와 SMC 모드 전환
# EMC 상태 확인
ovs-appctl dpif-netdev/pmd-stats-show
# EMC 비활성화 (대규모 플로우 환경에서 유용)
ovs-vsctl -- --id=@s create Flow_Sample_Collector_Set id=1 \
-- set Open_vSwitch . other_config:emc-insert-inv-prob=0
# SMC 활성화 (OVS 2.10+)
ovs-vsctl set Open_vSwitch . other_config:smc-enable=true
# EMC 엔트리 수 조정
ovs-vsctl set Open_vSwitch . other_config:emc-max-entries=16384
Upcall 메커니즘
upcall 발생 조건과 흐름
upcall은 커널 데이터패스에서 매칭되는 플로우를 찾지 못했을 때 발생합니다.
커널은 Netlink 메시지를 통해 패킷(또는 패킷 일부)을 ovs-vswitchd에 전달합니다.
데몬은 OpenFlow 테이블을 참조하여 적절한 액션을 결정하고, 두 가지 동작을 수행합니다:
(1) 현재 패킷에 대한 즉시 액션 실행, (2) 향후 동일 패턴 패킷을 위한 커널 플로우 캐시 설치.
upcall 핸들러 스레드
ovs-vswitchd는 다수의 upcall 핸들러 스레드를 실행하여 병렬로 upcall을 처리합니다.
각 핸들러 스레드는 전용 Netlink 소켓을 가지며, 커널은 패킷 해시를 기반으로
라운드 로빈 방식으로 핸들러를 선택합니다.
# upcall 핸들러 스레드 수 조정 (기본: 1)
ovs-vsctl set Open_vSwitch . other_config:n-handler-threads=4
# revalidator 스레드 수 조정
ovs-vsctl set Open_vSwitch . other_config:n-revalidator-threads=4
# upcall 통계 확인
ovs-appctl upcall/show
# upcall 속도 제한
ovs-vsctl set Open_vSwitch . other_config:upcall-rate-limit=1000
ovs-appctl coverage/show에서 upcall 카운터가 비정상적으로 높다면
플로우 설치 로직을 점검해야 합니다.
OVS 액션 처리
액션 타입 전체 목록
OVS 액션은 Netlink 속성(OVS_ACTION_ATTR_*)으로 인코딩됩니다.
커널의 do_execute_actions() 함수가 액션 리스트를 순회하며 각 액션을 실행합니다.
| 액션 (Netlink Attr) | 설명 | 사용 예시 |
|---|---|---|
OVS_ACTION_ATTR_OUTPUT |
지정된 포트로 패킷 출력 | 브리지 포워딩, 포트 미러링 |
OVS_ACTION_ATTR_USERSPACE |
유저스페이스로 패킷 전달 | sFlow/IPFIX 샘플링, 컨트롤러 전달 |
OVS_ACTION_ATTR_SET |
패킷 헤더 필드 설정(Set-Field) | MAC/IP/포트 재작성, DSCP 변경 |
OVS_ACTION_ATTR_SET_MASKED |
마스크 기반 부분 필드 설정 | 특정 비트만 변경 (예: TCP 플래그) |
OVS_ACTION_ATTR_PUSH_VLAN |
VLAN 태그 푸시 | VLAN 트렁킹 |
OVS_ACTION_ATTR_POP_VLAN |
VLAN 태그 팝 | 액세스 포트 전달 |
OVS_ACTION_ATTR_PUSH_MPLS |
MPLS 레이블 푸시 | MPLS 터널링 |
OVS_ACTION_ATTR_POP_MPLS |
MPLS 레이블 팝 | MPLS 종단 |
OVS_ACTION_ATTR_CT |
Conntrack 처리 (상태 추적) | 상태 기반 방화벽, NAT |
OVS_ACTION_ATTR_CT_CLEAR |
Conntrack 상태 초기화 | 서비스 체이닝 시 CT 리셋 |
OVS_ACTION_ATTR_RECIRC |
데이터패스 재순환 | CT 후 재매칭, 터널 디캡슐화 후 처리 |
OVS_ACTION_ATTR_HASH |
해시 계산 (ECMP/본딩) | 다중 경로 분산 |
OVS_ACTION_ATTR_SAMPLE |
확률적 패킷 샘플링 | sFlow, 네트워크 모니터링 |
OVS_ACTION_ATTR_CLONE |
패킷 복제 후 별도 액션 실행 | 미러링, 모니터링 |
OVS_ACTION_ATTR_TRUNC |
패킷 잘라내기 | 미러링 시 헤더만 캡처 |
OVS_ACTION_ATTR_PUSH_ETH |
이더넷 헤더 푸시 | L3 패킷에 L2 헤더 추가 |
OVS_ACTION_ATTR_POP_ETH |
이더넷 헤더 팝 | L2 -> L3 변환 |
OVS_ACTION_ATTR_METER |
미터링 (속도 제한) | QoS 대역폭 제어 |
OVS_ACTION_ATTR_CHECK_PKT_LEN |
패킷 길이 검사 후 분기 | MTU 기반 경로 선택 |
OVS_ACTION_ATTR_DROP |
패킷 드롭 | ACL 차단, 속도 제한 초과 |
재순환(Recirculation) 메커니즘
재순환은 패킷을 데이터패스 파이프라인 시작으로 되돌리는 강력한 메커니즘입니다.
주로 CT(conntrack) 처리 후 상태 기반 매칭을 수행하거나,
터널 디캡슐화 후 내부 패킷에 대한 새로운 매칭을 수행할 때 사용됩니다.
재순환 시 recirc_id가 증가하여 순환 단계를 구분합니다.
/* net/openvswitch/actions.c - 재순환 구현 */
static int execute_recirc(struct datapath *dp,
struct sk_buff *skb,
struct sw_flow_key *key,
const struct nlattr *a)
{
struct sw_flow_key recirc_key;
int err;
/* 키 복사 및 재순환 ID 설정 */
memcpy(&recirc_key, key, sizeof(recirc_key));
recirc_key.recirc_id = nla_get_u32(a);
/* 새 키로 플로우 키 재추출 (L2-L4 헤더가 변경되었을 수 있음) */
err = ovs_flow_key_update(skb, &recirc_key);
if (err)
return err;
/* 데이터패스 재진입 */
ovs_dp_process_packet(skb, &recirc_key);
return 0;
}
Conntrack 통합
OVS CT 액션 개요
OVS는 리눅스 커널의 nf_conntrack 프레임워크를 직접 활용하여
상태 기반 패킷 처리(Stateful Packet Processing)를 지원합니다.
OVS_ACTION_ATTR_CT 액션은 패킷을 conntrack 서브시스템에 통과시키고,
연결 상태(NEW, ESTABLISHED, RELATED, INVALID)를 sw_flow_key.ct 필드에 기록합니다.
이후 재순환을 통해 상태 기반 매칭이 가능합니다.
CT 액션 구현
/* net/openvswitch/conntrack.c */
int ovs_ct_execute(struct net *net,
struct sk_buff *skb,
struct sw_flow_key *key,
const struct ovs_conntrack_info *info)
{
int err;
/* 1. conntrack 존(zone) 설정 */
nf_ct_zone_init(&zone, info->zone.id, NF_CT_DEFAULT_ZONE_DIR, 0);
/* 2. NAT 처리 (요청 시) */
if (info->nat) {
err = ovs_ct_nat(net, key, info, skb, &ct, ctinfo);
}
/* 3. nf_conntrack 경로 통과 */
err = nf_conntrack_in(skb, &state);
/* 4. commit (연결 확정) */
if (info->commit) {
err = ovs_ct_commit(net, key, info, skb);
}
/* 5. CT 상태를 sw_flow_key에 기록 */
ovs_ct_update_key(skb, info, key, true);
return 0;
}
OpenFlow CT 규칙 예시
# 상태 기반 방화벽: ESTABLISHED/RELATED 허용, NEW는 정책 검사
# 테이블 0: CT 처리
ovs-ofctl add-flow br0 \
"table=0, priority=100, ip, action=ct(zone=1,table=1)"
# 테이블 1: ESTABLISHED/RELATED 허용
ovs-ofctl add-flow br0 \
"table=1, priority=100, ct_state=+est+trk, action=normal"
ovs-ofctl add-flow br0 \
"table=1, priority=100, ct_state=+rel+trk, action=normal"
# 테이블 1: NEW 연결 - 특정 포트만 허용
ovs-ofctl add-flow br0 \
"table=1, priority=90, ct_state=+new+trk, tcp, tp_dst=80, action=ct(commit,zone=1),normal"
ovs-ofctl add-flow br0 \
"table=1, priority=90, ct_state=+new+trk, tcp, tp_dst=443, action=ct(commit,zone=1),normal"
# 테이블 1: INVALID 차단
ovs-ofctl add-flow br0 \
"table=1, priority=80, ct_state=+inv+trk, action=drop"
# NAT 규칙 (SNAT)
ovs-ofctl add-flow br0 \
"table=0, priority=100, in_port=vm1, ip, action=ct(zone=1,nat(src=10.0.0.1),commit,table=1)"
터널링 (VXLAN/Geneve/GRE)
OVS 터널 아키텍처
OVS는 VXLAN, Geneve, GRE, STT 등 다양한 오버레이 터널 프로토콜을 네이티브로 지원합니다.
터널 포트는 vport 추상화를 통해 일반 포트와 동일한 방식으로 처리됩니다.
특히 key=flow 옵션을 사용하면 OpenFlow 규칙에서 터널 키(VNI)를 동적으로 설정할 수 있어,
단일 터널 포트로 수천 개의 가상 네트워크를 다중화할 수 있습니다.
터널 프로토콜 비교
| 프로토콜 | 캡슐화 | 헤더 크기 | 확장성 | HW 오프로드 | OVS 지원 |
|---|---|---|---|---|---|
| VXLAN | UDP (4789) | 50 바이트 | 24-bit VNI (16M) | 광범위 지원 | 완전 지원 |
| Geneve | UDP (6081) | 50+ 바이트 (가변 TLV) | 24-bit VNI + TLV 옵션 | 증가 추세 | 완전 지원 (권장) |
| GRE | IP 프로토콜 47 | 24-42 바이트 | 32-bit 키 | 광범위 지원 | 완전 지원 |
| STT | TCP-like (사실상 비표준) | 54 바이트 | 64-bit 컨텍스트 ID | 매우 제한적 | out-of-tree 모듈 |
터널 설정 예시
# VXLAN 터널 포트 (flow 기반 VNI)
ovs-vsctl add-port br-int vxlan0 \
-- set interface vxlan0 type=vxlan \
options:remote_ip=flow \
options:key=flow \
options:dst_port=4789
# Geneve 터널 포트 (TLV 옵션 지원)
ovs-vsctl add-port br-int geneve0 \
-- set interface geneve0 type=geneve \
options:remote_ip=flow \
options:key=flow
# GRE 터널 포트
ovs-vsctl add-port br-int gre0 \
-- set interface gre0 type=gre \
options:remote_ip=10.0.0.2 \
options:key=flow
# OpenFlow 규칙: VNI 기반 트래픽 격리
ovs-ofctl add-flow br-int \
"table=0, in_port=vm1, action=set_field:100->tun_id, output:vxlan0"
ovs-ofctl add-flow br-int \
"table=0, in_port=vxlan0, tun_id=100, action=output:vm1"
TC Offload와 하드웨어 가속
OVS TC Flower Offload 개요
OVS는 리눅스 TC(Traffic Control) flower 분류기를 통해 SmartNIC 하드웨어에 플로우 규칙을 오프로드할 수 있습니다. 이를 통해 호스트 CPU 부하 없이 라인 레이트에 가까운 패킷 포워딩이 가능합니다. Mellanox(NVIDIA) ConnectX, Intel E810, Broadcom Stingray 등의 SmartNIC이 이 기능을 지원합니다.
TC offload 설정
# 1. eSwitch를 switchdev 모드로 전환 (Mellanox 예시)
devlink dev eswitch set pci/0000:03:00.0 mode switchdev
# 2. TC offload 활성화
ethtool -K enp3s0f0 hw-tc-offload on
# 3. OVS HW offload 설정
ovs-vsctl set Open_vSwitch . other_config:hw-offload=true
ovs-vsctl set Open_vSwitch . other_config:tc-policy=skip_sw
# 4. OVS 서비스 재시작
systemctl restart openvswitch-switch
# 5. offload 상태 확인
ovs-appctl dpctl/dump-flows type=offloaded
tc -s filter show dev enp3s0f0_0 ingress
tc-policy=skip_sw 설정 시 오프로드 불가한 플로우는 설치 자체가 실패하므로
tc-policy=none(기본값)으로 SW/HW 혼합 사용을 권장합니다.
DPDK 유저스페이스 데이터패스
OVS-DPDK 아키텍처
OVS-DPDK는 커널 데이터패스 대신 DPDK(Data Plane Development Kit)의 PMD(Poll Mode Driver)를 사용하여 유저스페이스에서 패킷을 처리합니다. 커널 인터럽트와 시스템 콜 오버헤드를 완전히 제거하여 수십 Mpps 이상의 처리량을 달성합니다.
| 특성 | 커널 데이터패스 | OVS-DPDK |
|---|---|---|
| 64B 패킷 처리량 (1코어) | ~3-5 Mpps | ~15-20 Mpps |
| 지연 시간 (평균) | ~5-10 us | ~0.5-2 us |
| CPU 활용 | 트래픽 비례 (효율적) | 항상 100% 폴링 (전용 코어) |
| 메모리 요구사항 | 일반 커널 메모리 | hugepage (1GB/2MB) 필수 |
| 포트 유형 | 모든 커널 netdev | DPDK 지원 NIC (dpdk, dpdkvhostuser) |
| VM 연동 | TAP / virtio-net | vhost-user (공유 메모리) |
| 컨테이너 연동 | veth 직접 지원 | AF_XDP 또는 vhost-user |
| 운영 복잡도 | 낮음 | 높음 (NUMA, CPU 격리, hugepage) |
OVS-DPDK 구성 예시
# 1. hugepage 설정
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
mount -t hugetlbfs none /dev/hugepages
# 2. OVS-DPDK 초기화
ovs-vsctl -- --no-wait set Open_vSwitch . other_config:dpdk-init=true
ovs-vsctl set Open_vSwitch . other_config:dpdk-socket-mem="1024,1024"
ovs-vsctl set Open_vSwitch . other_config:dpdk-lcore-mask="0x1"
# 3. PMD 스레드 CPU 마스크 설정
ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask="0x6"
# 4. DPDK 브리지 생성
ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev
# 5. DPDK 포트 추가
ovs-vsctl add-port br0 dpdk0 \
-- set Interface dpdk0 type=dpdk \
options:dpdk-devargs=0000:03:00.0
# 6. vhost-user 포트 추가 (VM 연동)
ovs-vsctl add-port br0 vhost-user-1 \
-- set Interface vhost-user-1 type=dpdkvhostuserclient \
options:vhost-server-path="/tmp/vhost-user-1"
# 7. PMD 스레드 상태 확인
ovs-appctl dpif-netdev/pmd-stats-show
ovs-appctl dpif-netdev/pmd-rxq-show
PMD 스레드 구조
OVS-DPDK에서 PMD(Poll Mode Driver) 스레드는 전용 CPU 코어에서 끊임없이 NIC 수신 큐를 폴링합니다. 각 PMD 스레드는 하나 이상의 RX 큐를 담당하며, 배치 단위로 패킷을 수신하여 EMC -> SMC -> 플로우 테이블 순서로 매칭하고 액션을 실행합니다.
# PMD 스레드별 RX 큐 할당 확인
ovs-appctl dpif-netdev/pmd-rxq-show
# 출력 예시:
# pmd thread numa_id 0 core_id 1:
# isolated : false
# port: dpdk0 queue-id: 0 pmd usage: 78%
# port: dpdk0 queue-id: 1 pmd usage: 65%
# PMD 스레드 통계
ovs-appctl dpif-netdev/pmd-stats-show
# 출력 예시:
# pmd thread numa_id 0 core_id 1:
# packets received: 1234567890
# emc hits: 1100000000 (89%)
# smc hits: 100000000 (8%)
# megaflow hits: 30000000 (2%)
# upcalls: 4567890 (0.4%)
# lost: 0
OpenFlow 프로토콜 연동
OpenFlow 테이블 구조
OVS는 OpenFlow 1.0~1.5 프로토콜을 구현합니다. OpenFlow 파이프라인은 다수의 테이블(table 0~254)로 구성되며, 패킷은 테이블 0에서 시작하여 순차적으로 상위 테이블로 진행합니다. 각 테이블의 플로우 엔트리는 매치(match), 우선순위(priority), 인스트럭션(instruction)으로 구성됩니다.
| OpenFlow 컴포넌트 | 설명 | OVS 매핑 |
|---|---|---|
| Flow Table (0-254) | 매칭 규칙과 인스트럭션 집합 | ofproto->tables[] |
| Flow Entry | match + priority + instruction + counters + timeout | struct rule_dpif |
| Match Fields | L2-L4 패킷 헤더, 메타데이터 | OXM(OpenFlow Extensible Match) |
| Instructions | Apply-Actions, Write-Actions, Goto-Table, Meter | struct ofpact[] |
| Group Table | 다중 출력, ECMP, Fast Failover | ofproto->groups |
| Meter Table | 속도 제한 (QoS) | ofproto->meters |
OpenFlow 규칙 설정 예시
# 다중 테이블 파이프라인 예시
# 테이블 0: 분류 (Classifier)
ovs-ofctl add-flow br0 \
"table=0, priority=100, in_port=1, dl_type=0x0800, action=goto_table:10"
ovs-ofctl add-flow br0 \
"table=0, priority=100, in_port=1, dl_type=0x0806, action=goto_table:20"
ovs-ofctl add-flow br0 \
"table=0, priority=0, action=drop"
# 테이블 10: ACL (Access Control List)
ovs-ofctl add-flow br0 \
"table=10, priority=100, ip, nw_dst=10.0.1.0/24, action=goto_table:30"
ovs-ofctl add-flow br0 \
"table=10, priority=0, action=drop"
# 테이블 20: ARP 처리
ovs-ofctl add-flow br0 \
"table=20, priority=100, arp, action=normal"
# 테이블 30: L3 라우팅
ovs-ofctl add-flow br0 \
"table=30, priority=100, ip, nw_dst=10.0.1.1, action=mod_dl_dst:aa:bb:cc:dd:ee:01, output:2"
ovs-ofctl add-flow br0 \
"table=30, priority=100, ip, nw_dst=10.0.1.2, action=mod_dl_dst:aa:bb:cc:dd:ee:02, output:3"
# Group 테이블 (ECMP)
ovs-ofctl add-group br0 \
"group_id=1, type=select, bucket=output:2, bucket=output:3, bucket=output:4"
ovs-ofctl add-flow br0 \
"table=30, priority=90, ip, nw_dst=10.0.2.0/24, action=group:1"
# Meter 테이블 (속도 제한: 1 Mbps)
ovs-ofctl add-meter br0 \
"meter=1, kbps, band=type=drop,rate=1000"
ovs-ofctl add-flow br0 \
"table=10, priority=50, ip, nw_src=10.0.3.0/24, action=meter:1, goto_table:30"
# 플로우 테이블 확인
ovs-ofctl dump-flows br0
ovs-ofctl dump-groups br0
ovs-ofctl dump-meters br0
OVS Netlink 프로토콜 상세
OVS Generic Netlink 패밀리
OVS 커널 모듈은 4개의 Generic Netlink 패밀리를 등록합니다. 각 패밀리는 특정 리소스 타입에 대한 CRUD 연산을 제공합니다.
| 패밀리 | 매크로 | 관리 대상 | 주요 명령 |
|---|---|---|---|
ovs_datapath |
OVS_DATAPATH_FAMILY |
데이터패스 (브리지) | NEW, DEL, GET, SET |
ovs_vport |
OVS_VPORT_FAMILY |
가상 포트 | NEW, DEL, GET, SET |
ovs_flow |
OVS_FLOW_FAMILY |
플로우 엔트리 | NEW, DEL, GET, SET |
ovs_packet |
OVS_PACKET_FAMILY |
패킷 upcall/execute | MISS, ACTION, EXECUTE |
Netlink 메시지 형식
/* include/uapi/linux/openvswitch.h */
/* 데이터패스 명령 */
enum ovs_datapath_cmd {
OVS_DP_CMD_UNSPEC,
OVS_DP_CMD_NEW, /* 데이터패스 생성 */
OVS_DP_CMD_DEL, /* 데이터패스 삭제 */
OVS_DP_CMD_GET, /* 데이터패스 조회 */
OVS_DP_CMD_SET, /* 데이터패스 설정 */
};
/* 플로우 명령 */
enum ovs_flow_cmd {
OVS_FLOW_CMD_UNSPEC,
OVS_FLOW_CMD_NEW, /* 플로우 생성 */
OVS_FLOW_CMD_DEL, /* 플로우 삭제 */
OVS_FLOW_CMD_GET, /* 플로우 조회 */
OVS_FLOW_CMD_SET, /* 플로우 갱신 (액션 변경) */
};
/* 패킷 명령 */
enum ovs_packet_cmd {
OVS_PACKET_CMD_UNSPEC,
OVS_PACKET_CMD_MISS, /* 커널->유저: 캐시 미스 upcall */
OVS_PACKET_CMD_ACTION, /* 커널->유저: 유저스페이스 액션 */
OVS_PACKET_CMD_EXECUTE, /* 유저->커널: 패킷 실행 명령 */
};
/* 플로우 키 속성 (매칭 필드) */
enum ovs_key_attr {
OVS_KEY_ATTR_UNSPEC,
OVS_KEY_ATTR_ENCAP, /* 중첩 캡슐화 */
OVS_KEY_ATTR_PRIORITY, /* skb->priority */
OVS_KEY_ATTR_IN_PORT, /* 입력 포트 */
OVS_KEY_ATTR_ETHERNET, /* L2 헤더 */
OVS_KEY_ATTR_VLAN, /* VLAN TCI */
OVS_KEY_ATTR_ETHERTYPE, /* EtherType */
OVS_KEY_ATTR_IPV4, /* IPv4 헤더 */
OVS_KEY_ATTR_IPV6, /* IPv6 헤더 */
OVS_KEY_ATTR_TCP, /* TCP 헤더 */
OVS_KEY_ATTR_UDP, /* UDP 헤더 */
OVS_KEY_ATTR_ICMP, /* ICMP 헤더 */
OVS_KEY_ATTR_ARP, /* ARP 헤더 */
OVS_KEY_ATTR_TUNNEL, /* 터널 메타데이터 */
OVS_KEY_ATTR_CT_STATE, /* CT 상태 */
OVS_KEY_ATTR_CT_ZONE, /* CT 존 */
OVS_KEY_ATTR_CT_MARK, /* CT 마크 */
OVS_KEY_ATTR_CT_LABELS, /* CT 레이블 */
OVS_KEY_ATTR_RECIRC_ID, /* 재순환 ID */
/* ... 추가 필드 ... */
};
Netlink 플로우 설치 과정
ovs-vswitchd가 커널에 플로우를 설치할 때의 Netlink 메시지 구조입니다.
/* ovs-vswitchd에서 커널로 전송되는 OVS_FLOW_CMD_NEW 메시지 */
/*
* [Netlink Header]
* nlmsg_type = GENL_ID_OVS_FLOW
* nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL
* [Generic Netlink Header]
* cmd = OVS_FLOW_CMD_NEW
* [OVS Header]
* dp_ifindex = 데이터패스 인덱스
* [Netlink Attributes]
* OVS_FLOW_ATTR_KEY: [매칭 키 (중첩 속성)]
* OVS_KEY_ATTR_IN_PORT: 입력 포트
* OVS_KEY_ATTR_ETHERNET: src/dst MAC
* OVS_KEY_ATTR_IPV4: src/dst IP, proto
* OVS_KEY_ATTR_TCP: src/dst 포트
* OVS_FLOW_ATTR_MASK: [마스크 (와일드카드)]
* (키와 동일 구조, 0xFF = exact, 0x00 = wild)
* OVS_FLOW_ATTR_ACTIONS: [액션 리스트 (중첩)]
* OVS_ACTION_ATTR_OUTPUT: 출력 포트
* OVS_FLOW_ATTR_UFID: [고유 플로우 ID (128비트)]
*/
성능 분석과 튜닝
성능 핵심 지표
| 지표 | 정상 범위 | 경고 임계값 | 확인 방법 |
|---|---|---|---|
| 캐시 히트율 | 95% 이상 | 90% 미만 | ovs-appctl dpctl/show -s |
| upcall 비율 | 총 패킷의 1% 미만 | 5% 초과 | ovs-appctl coverage/show |
| Megaflow 마스크 수 | 100개 미만 | 500개 초과 | ovs-appctl dpctl/dump-flows |
| upcall 지연 | 100us 미만 | 1ms 초과 | ovs-appctl upcall/show |
| 커널 플로우 수 | 플로우 다양성에 비례 | 200,000개 초과 | ovs-dpctl show |
| revalidator 지연 | 1초 미만 | 5초 초과 | ovs-appctl upcall/show |
성능 벤치마크 데이터
| 환경 | 데이터패스 | 64B pps | 1518B pps | 지연 (P99) |
|---|---|---|---|---|
| 1코어 Xeon Platinum 8380 | 커널 (EMC 히트) | ~5 Mpps | ~3 Mpps | ~5 us |
| 1코어 Xeon Platinum 8380 | OVS-DPDK (EMC 히트) | ~18 Mpps | ~14 Mpps | ~1 us |
| ConnectX-6 Dx (25G) | TC Offload (HW) | ~37 Mpps (라인 레이트) | ~2.4 Mpps (라인 레이트) | ~0.5 us |
| 1코어 (upcall 경로) | 커널 | ~50 Kpps | ~50 Kpps | ~100-500 us |
주요 튜닝 포인트
# 1. 플로우 캐시 타임아웃 조정
ovs-vsctl set Open_vSwitch . other_config:max-idle=30000 # 기본 10초 -> 30초
# 2. Netlink 수신 버퍼 크기 증가 (upcall 드롭 방지)
sysctl -w net.core.rmem_max=8388608
# 3. NUMA 인식 설정 (OVS-DPDK)
ovs-vsctl set Interface dpdk0 options:n_rxq=4
ovs-vsctl set Interface dpdk0 other_config:pmd-rxq-affinity="0:2,1:4,2:6,3:8"
# 4. Megaflow 마스크 최적화: 불필요한 매칭 필드 제거
# 나쁜 예: 모든 필드 매칭 -> 마스크 수 폭발
# 좋은 예: 필요한 필드만 매칭 -> 와일드카드 최대화
# 5. EMC 확률 조정
ovs-vsctl set Open_vSwitch . other_config:emc-insert-inv-prob=100 # 1/100 확률로 EMC 삽입
# 6. handler/revalidator 스레드 수 최적화
ovs-vsctl set Open_vSwitch . other_config:n-handler-threads=4
ovs-vsctl set Open_vSwitch . other_config:n-revalidator-threads=4
디버깅과 모니터링
OVS 디버깅 도구 총정리
| 도구 | 용도 | 핵심 명령어 |
|---|---|---|
ovs-vsctl |
브리지/포트/인터페이스 구성 | show, list interface |
ovs-ofctl |
OpenFlow 테이블 조회/설정 | dump-flows, dump-ports-desc |
ovs-dpctl |
커널 데이터패스 직접 조회 | dump-flows, show -s |
ovs-appctl |
데몬 런타임 명령 | bridge/dump-flows, dpif/show |
ovsdb-client |
OVSDB 데이터베이스 직접 조회 | dump, monitor |
ovs-tcpdump |
OVS 포트에서 패킷 캡처 | ovs-tcpdump -i port_name |
ovs-pcap |
pcap 파일 텍스트 변환 | ovs-pcap file.pcap |
실전 디버깅 시나리오
# === 시나리오 1: 패킷이 왜 드롭되는가? ===
# 1-1. OpenFlow 플로우 확인 (패킷 카운터 포함)
ovs-ofctl dump-flows br0 --names --no-stats=false
# 1-2. 특정 패킷의 매칭 결과 추적
ovs-appctl ofproto/trace br0 in_port=1,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80
# 1-3. 커널 데이터패스 플로우 덤프
ovs-dpctl dump-flows -m
# 1-4. 포트별 패킷/에러 통계
ovs-ofctl dump-ports br0
# === 시나리오 2: 성능 저하 원인 분석 ===
# 2-1. 캐시 히트율 확인
ovs-appctl dpctl/show -s
# 2-2. coverage 카운터 (upcall 빈도 등)
ovs-appctl coverage/show
# 2-3. 마스크 수와 Megaflow 효율
ovs-appctl dpctl/dump-flows | wc -l # 커널 플로우 수
ovs-appctl dpctl/dump-flows | grep -oP 'masks:\K\d+' | sort -un # 마스크 수
# 2-4. 로그 레벨 동적 변경
ovs-appctl vlog/set dpif:dbg
ovs-appctl vlog/set ofproto:dbg
# 2-5. PMD 스레드 통계 (OVS-DPDK)
ovs-appctl dpif-netdev/pmd-stats-show
ovs-appctl dpif-netdev/pmd-stats-clear
# === 시나리오 3: CT 상태 디버깅 ===
# 3-1. conntrack 테이블 덤프
ovs-appctl dpctl/ct-stats-show
conntrack -L --zone=1
# 3-2. CT 시뮬레이션
ovs-appctl ofproto/trace br0 \
"in_port=1,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_src=12345,tp_dst=80,tcp_flags=syn"
# === 시나리오 4: ftrace로 커널 경로 추적 ===
# 4-1. OVS 커널 함수 추적
echo ovs_dp_process_packet > /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
cat /sys/kernel/debug/tracing/trace
# 4-2. perf로 CPU 프로파일링
perf top -g -p $(pgrep ovs-vswitchd)
perf record -g -a -F 99 -- sleep 10
perf report
ofproto/trace 출력 예시 해석
ovs-appctl ofproto/trace br0 \
in_port=1,tcp,nw_src=10.0.0.1,nw_dst=10.0.0.2,tp_dst=80
# 출력 해석:
# Flow: tcp,in_port=1,vlan_tci=0x0000,dl_src=...,dl_dst=...,
# nw_src=10.0.0.1,nw_dst=10.0.0.2,nw_tos=0,nw_ecn=0,
# nw_ttl=64,tp_src=0,tp_dst=80,tcp_flags=0
#
# bridge("br0")
# -------------
# 0. priority 100, tcp,in_port=1, ct_state=-trk
# -> ct(zone=1,table=1) <- CT 액션 실행
# 1. priority 100, tcp,ct_state=+new+trk,tp_dst=80
# -> ct(commit,zone=1),output:2 <- 커밋 후 포트 2로 출력
#
# Final flow: ... ct_state=new|trk ...
# Megaflow: recirc_id=0,eth,ip,in_port=1,nw_proto=6,tp_dst=80
# Datapath actions: ct(zone=1),recirc(0x1)
커널 설정
필수 및 권장 Kconfig 옵션
| 옵션 | 필수/권장 | 설명 |
|---|---|---|
CONFIG_OPENVSWITCH |
필수 | OVS 커널 데이터패스 모듈 (openvswitch.ko) |
CONFIG_OPENVSWITCH_VXLAN |
권장 | VXLAN 터널 포트 지원 |
CONFIG_OPENVSWITCH_GRE |
권장 | GRE 터널 포트 지원 |
CONFIG_OPENVSWITCH_GENEVE |
권장 | Geneve 터널 포트 지원 |
CONFIG_NF_CONNTRACK |
CT 액션 사용 시 필수 | conntrack 프레임워크 |
CONFIG_NF_NAT |
NAT 사용 시 필수 | NAT 프레임워크 |
CONFIG_NET_CLS_FLOWER |
TC offload 시 필수 | TC flower 분류기 |
CONFIG_NET_ACT_CT |
TC CT offload 시 필수 | TC conntrack 액션 |
CONFIG_NET_SWITCHDEV |
HW offload 시 필수 | switchdev 프레임워크 |
CONFIG_VXLAN |
VXLAN 사용 시 필수 | VXLAN 커널 모듈 |
CONFIG_GENEVE |
Geneve 사용 시 필수 | Geneve 커널 모듈 |
CONFIG_GRE |
GRE 사용 시 필수 | GRE 커널 모듈 |
커널 모듈 파라미터
# 모듈 정보 확인
modinfo openvswitch
# 로드된 OVS 관련 모듈 확인
lsmod | grep openvswitch
# 출력 예시:
# openvswitch 131072 1
# nf_conntrack 172032 3 openvswitch,nf_nat,nf_conntrack_netlink
# nf_nat 49152 1 openvswitch
# libcrc32c 16384 3 openvswitch,nf_conntrack,nf_nat
# OVS 커널 모듈 버전 확인
dmesg | grep -i openvswitch
# openvswitch: Open vSwitch switching datapath
커널 컴파일 설정 예시
# .config 파일에서 OVS 관련 설정 확인
grep -E "OPENVSWITCH|NF_CONNTRACK|NET_CLS_FLOWER|SWITCHDEV|VXLAN|GENEVE|GRE" /boot/config-$(uname -r)
# 또는 menuconfig에서:
# Networking support --->
# Networking options --->
# [*] Open vSwitch
# [*] Open vSwitch VXLAN tunneling support
# [*] Open vSwitch GRE tunneling support
# [*] Open vSwitch Geneve tunneling support
# Network packet filtering framework (Netfilter) --->
# [*] Netfilter connection tracking support
# [*] Connection tracking NAT support
운영 장애 대응 플레이북
OVS 운영 장애는 보통 upcall 폭증, 마스크 폭발(mask explosion), PMD 불균형, CT 테이블 포화로 수렴합니다. 아래 플레이북은 "10분 내 상태 파악 -> 30분 내 원인 축소 -> 1시간 내 완화 적용" 흐름을 목표로 합니다.
| 증상 | 우선 점검 | 즉시 완화 |
|---|---|---|
| 처리량 급락, CPU 급등 | coverage/show의 upcall 카운터, dpctl/show -s 캐시 히트율 |
룰 집합 단순화, n-handler-threads 임시 상향 |
| 특정 코어만 과부하 | PMD stats의 큐 불균형 | RXQ 분산, pmd-rxq-affinity 재설정 |
| 신규 연결 지연 증가 | CT zone별 엔트리/드롭, NAT 충돌 | zone 분리, timeout 완화, 신규 플로우 burst 제한 |
| 터널 트래픽만 손실 | MTU, UDP checksum, DF 설정 | 경로 MTU 재조정, 터널 옵션 정합성 통일 |
# === OVS 장애 1차 수집 템플릿 ===
TS=$(date +%Y%m%d-%H%M%S)
OUT=/var/tmp/ovs-incident-$TS
mkdir -p "$OUT"
ovs-vsctl show > "$OUT/ovs-vsctl-show.txt"
ovs-appctl dpctl/show -s > "$OUT/dp-show-s.txt"
ovs-appctl coverage/show > "$OUT/coverage.txt"
ovs-ofctl dump-flows br0 --names > "$OUT/ofctl-flows.txt"
ovs-dpctl dump-flows -m > "$OUT/dp-flows-masked.txt"
ovs-appctl dpif/show > "$OUT/dpif-show.txt"
# PMD/DPDK 사용 시
ovs-appctl dpif-netdev/pmd-stats-show > "$OUT/pmd-stats.txt"
# 커널/인터럽트 관찰
grep -E "NAPI|softirq|NET_RX|openvswitch" /proc/softirqs > "$OUT/softirqs.txt"
cat /proc/interrupts > "$OUT/interrupts.txt"
tar czf "$OUT.tar.gz" -C /var/tmp "$(basename "$OUT")"
용량 계획과 SLO 설계
OVS 성능 문제는 단순 pps 최대치보다 워크로드 특성(플로우 수, 신규 연결 비율, 터널 비중, CT/NAT 사용량)에서 먼저 발생합니다. 운영 SLO는 다음 세 지표를 분리해서 정의하는 것이 안전합니다.
- 포워딩 SLO: p99 지연, 드롭률, 재전송률
- 제어면 SLO: upcall 처리 지연, 플로우 설치 지연
- 자원 SLO: 코어별 PMD 편차, 마스크 수, CT 엔트리 사용률
권장 SLO 템플릿
| 카테고리 | 지표 | 권장 목표 |
|---|---|---|
| 포워딩 | p99 패킷 지연 | < 250us (커널 path 기준) |
| 포워딩 | 드롭률 | < 0.1% |
| 제어면 | upcall 처리 지연 | p99 < 5ms |
| 캐시 | Megaflow hit 비율 | > 95% |
| 자원 | PMD core 편차 | < 25% |
| 상태 | CT 테이블 사용률 | < 70% |
보안 하드닝과 멀티테넌시 운영
OVS는 L2~L4까지 강력한 정책을 적용할 수 있지만, 멀티테넌트 환경에서는 테넌트 분리 경계와 운영 권한 경계를 동시에 설계해야 합니다. 특히 CT zone, 터널 키, OpenFlow 우선순위 충돌은 보안 사고의 빈번한 원인입니다.
보안 하드닝 체크리스트
| 영역 | 점검 항목 | 권장 상태 |
|---|---|---|
| 제어 채널 | OpenFlow/OVSDB TLS 사용 여부 | mTLS 강제, 평문 TCP 금지 |
| 정책 | 기본 정책(default) 허용 여부 | default deny + 명시 허용 |
| CT | 테넌트 간 zone 공유 | zone 분리 필수 |
| 터널 | VNI 키 충돌 가능성 | 테넌트별 키 공간 예약 |
| 운영 | ofctl 수동 변경 추적 | 변경 감사 로그와 롤백 절차 확보 |
| 오프로드 | HW offload 정책 일관성 | SW/HW 결과 정합성 검증 자동화 |
# OpenFlow/OVSDB TLS 확인 (예시)
ovs-vsctl get Open_vSwitch . ssl
ovs-vsctl get-controller br0
# CT zone/정책 검증 보조 (예시)
ovs-ofctl dump-flows br0 | grep -E "ct\\(zone=|priority="
# 구성 변경 이력 확인
ovsdb-client dump Open_vSwitch
ovs-appctl vlog/list
# 하드웨어 오프로드 사용 시 정책 불일치 점검
tc -s filter show dev eth0 ingress
ovs-appctl dpctl/dump-flows -m
OVS vs Linux Bridge vs eBPF/XDP 선택 기준
실무에서는 "어떤 기술이 더 빠른가"보다 "현재 요구사항에 어떤 데이터 경로가 맞는가"가 더 중요합니다. 아래 매트릭스는 운영 복잡도, 기능 폭, 성능 특성, 장애 대응 난이도를 함께 비교해 OVS/Bridge/eBPF-XDP/DPDK 중 우선 선택지를 빠르게 좁히기 위한 기준입니다.
| 항목 | Linux Bridge | OVS (Kernel) | eBPF/XDP + TC | OVS-DPDK |
|---|---|---|---|---|
| 주요 강점 | 단순 L2 브리징, 낮은 운영 복잡도 | 플로우 기반 정책, 터널, OpenFlow/OVN 연계 | 초저지연 필터/리다이렉트, 커스텀 로직 | 최고 pps 처리량, 사용자 공간 고속 경로 |
| 정책 표현력 | 낮음 | 높음 (L2~L4 + CT + 미터) | 매우 높음 (프로그램 가능) | 높음 (OVS 정책 재사용 가능) |
| 터널 오버레이 | 제한적 | 강함 (VXLAN/Geneve/GRE) | 구현 가능하나 운영 난이도 높음 | 강함 (DPDK 포트/터널 경로) |
| 최소 지연 | 낮음 | 낮음~중간 (룰/캐시 구조 의존) | 매우 낮음 | 매우 낮음 (polling 기반) |
| 최대 처리량 | 중간 | 중간~높음 | 높음 (기능 범위에 따라 변동) | 매우 높음 |
| 운영 복잡도 | 낮음 | 중간 | 높음 (프로그램/검증 체계 필요) | 매우 높음 (NUMA/PMD/hugepage) |
| 장애 분석 난이도 | 낮음 | 중간 (flow/upcall/CT 추적) | 높음 (BPF verifier + runtime 추적) | 높음 (PMD/queue/pinning 포함) |
| 추천 시나리오 | 단순 VM/컨테이너 L2 연결 | 멀티테넌시, SDN, 오버레이 네트워크 | DDoS 완화, 고성능 트래픽 조작 | NFV, 고정 기능 고처리량 데이터플레인 |
실전 랩 시나리오 (재현 + 검증)
OVS 학습은 개념만 읽는 것보다 "재현 가능한 장애/성능 이벤트"를 직접 만들어 보는 것이 훨씬 빠릅니다. 아래 시나리오는 단일 호스트에서도 재현 가능한 형태로 구성했으며, 각 단계마다 기대 결과와 검증 명령을 함께 제공합니다.
| 랩 | 목표 | 핵심 확인 항목 |
|---|---|---|
| 랩 A 캐시 효율 | Megaflow/EMC 히트율 개선 | upcall 감소, hit 비율 증가, CPU 사용률 안정화 |
| 랩 B CT 경로 | stateful 정책과 zone 분리 검증 | CT state 전이, 신규/재전송 처리, zone 충돌 여부 |
| 랩 C PMD 밸런싱 | 큐/코어 편향 완화 | 코어별 처리량 편차 감소, tail latency 개선 |
| 랩 D 장애 대응 | upcall 폭증 이벤트 대응 자동화 | 수집 번들 생성, 임시 완화, 롤백 절차 유효성 |
랩 A: 캐시 효율 실험
# 1) 기준 상태 수집
ovs-appctl dpctl/show -s
ovs-appctl coverage/show
# 2) 트래픽 주입 (예: iperf 또는 tcpreplay)
iperf3 -c 10.0.0.2 -t 30 -P 8
# 3) 룰 단순화 후 재측정
ovs-ofctl dump-flows br0
ovs-appctl dpctl/show -s
# 기대 결과: upcall 비율 감소, megaflow hit 증가
랩 B: CT zone 분리 검증
# 1) zone별 CT 룰 적용 예시
ovs-ofctl add-flow br0 "priority=200,in_port=10,ip,actions=ct(zone=101,table=1)"
ovs-ofctl add-flow br0 "priority=200,in_port=20,ip,actions=ct(zone=201,table=1)"
# 2) trace로 상태 전이 확인
ovs-appctl ofproto/trace br0 "in_port=10,tcp,nw_src=10.10.0.2,nw_dst=10.10.0.3,tp_dst=443"
ovs-appctl ofproto/trace br0 "in_port=20,tcp,nw_src=10.20.0.2,nw_dst=10.20.0.3,tp_dst=443"
# 3) conntrack 엔트리 점검
ovs-appctl dpctl/ct-stats-show
conntrack -L | head
# 기대 결과: 테넌트 간 CT state/entry가 섞이지 않음
랩 C: PMD 밸런싱 실험 (DPDK 환경)
# 1) 현재 PMD 편차 확인
ovs-appctl dpif-netdev/pmd-stats-show
# 2) RXQ affinity 조정
ovs-vsctl set Interface dpdk0 other_config:pmd-rxq-affinity="0:2,1:4,2:6,3:8"
# 3) 재부하 후 비교
ovs-appctl dpif-netdev/pmd-stats-clear
iperf3 -c 10.0.0.2 -t 60 -P 16
ovs-appctl dpif-netdev/pmd-stats-show
# 기대 결과: 특정 코어 쏠림 완화, p99 지연 감소
랩 D: 장애 대응 리허설
# 1) 이벤트 발생 전 수집 스크립트 준비
cat >/usr/local/bin/ovs-incident-capture.sh <<'EOF'
#!/bin/bash
TS=$(date +%Y%m%d-%H%M%S)
OUT=/var/tmp/ovs-incident-$TS
mkdir -p "$OUT"
ovs-appctl dpctl/show -s > "$OUT/dp.txt"
ovs-appctl coverage/show > "$OUT/coverage.txt"
ovs-ofctl dump-flows br0 > "$OUT/flows.txt"
tar czf "$OUT.tar.gz" -C /var/tmp "$(basename "$OUT")"
EOF
chmod +x /usr/local/bin/ovs-incident-capture.sh
# 2) 리허설 실행
/usr/local/bin/ovs-incident-capture.sh
ls -lh /var/tmp/ovs-incident-*.tar.gz
# 기대 결과: 1분 내 분석 번들 생성 완료
커널/OVS/DPDK 버전 호환 매트릭스
OVS 운영에서 가장 많은 장애 원인은 "기능 미지원" 자체보다 버전 조합 불일치입니다. 특히 커널 CT/NAT 동작, TC offload, DPDK PMD 옵션은 OVS 릴리즈와 커널 기능 수준이 함께 맞아야 안정적으로 동작합니다.
| 기능 | 커널 의존성 | OVS 의존성 | 운영 체크 포인트 |
|---|---|---|---|
| CT/Stateful ACL | NF_CONNTRACK, NF_NAT |
2.x 전반 지원 | zone 분리, timeout 정책, conntrack 용량 한계 확인 |
| TC flower offload | NET_CLS_FLOWER, NIC 드라이버 지원 |
중반 이후 안정화 | SW/HW rule 정합성 검증 자동화 필수 |
| OVS-DPDK PMD | hugepage, NUMA, vfio/uio | DPDK ABI 호환 범위 필요 | PMD pinning, RXQ 분배, 코어 편차 점검 |
| Geneve 옵션 처리 | Geneve 커널 모듈 수준 | OVN/OVS 정책 연동 | MTU/option 길이/파싱 오버헤드 검증 |
| Recirculation 최적화 | 데이터패스 경로 안정성 | Megaflow/SMC 개선 릴리즈 | recirc depth 과도 증가 여부 모니터링 |
권장 조합 가이드 (실무 기준)
| 운영 목표 | 권장 커널 계열 | 권장 OVS 계열 | DPDK | 비고 |
|---|---|---|---|---|
| 일반 가상화 + 오버레이 | LTS 커널 (배포판 벤더 지원) | 배포판 기본 OVS 또는 LTS 브랜치 | 선택 | 운영 안정성 우선, 업그레이드 주기 길게 |
| 멀티테넌시 + 고급 정책 | LTS + 최신 네트워크 백포트 | 최근 OVS 안정 릴리즈 | 선택 | CT/터널/OVN 기능 검증 후 배포 |
| 초고성능 NFV | DPDK 친화 커널 설정 | OVS-DPDK 검증 릴리즈 | 필수 | PMD/NUMA 설계가 성능 좌우 |
| HW offload 중심 | NIC 벤더 권장 커널 | TC offload 검증 버전 | 선택 | 드라이버/펌웨어와 3자 호환성 확인 |
# 현재 커널/OVS/DPDK 버전과 빌드 옵션 확인
uname -r
ovs-vswitchd --version
ovsdb-server --version
ovs-vsctl get Open_vSwitch . ovs_version
# datapath 타입(system/netdev)과 기능 플래그 확인
ovs-vsctl get Open_vSwitch . datapath_types
ovs-vsctl get Open_vSwitch . iface_types
ovs-appctl dpif/show
# DPDK 연동 시
ovs-vsctl get Open_vSwitch . dpdk_initialized
ovs-vsctl get Open_vSwitch . dpdk_version
# 커널 OVS 모듈 및 의존 모듈 확인
modinfo openvswitch
lsmod | grep -E "openvswitch|nf_conntrack|nf_nat"
참고자료
공식 문서
커널 소스 코드
설계 문서 및 논문
- Pfaff, B. et al., "The Design and Implementation of Open vSwitch," NSDI 2015
- RFC 7047: The Open vSwitch Database Management Protocol
- OpenFlow Specification (ONF)
관련 프로젝트
- OVN (Open Virtual Network) -- OVS 기반 분산 가상 라우팅/스위칭
- FD.io VPP -- OVS 대안 유저스페이스 패킷 처리 엔진
- Netlink -- OVS 커널/유저스페이스 제어 채널 이해를 위한 선행 문서
- Netfilter -- CT/NAT 동작과 상태 기반 정책 해석에 필수
- TC (Traffic Control) -- TC flower offload와 OVS 하드웨어 가속 연계
- eSwitch (Embedded Switch) -- switchdev/오프로드 파이프라인 심화
- DPDK -- OVS-DPDK PMD 경로와 성능 최적화 심화
- SmartNIC/DPU -- 인프라 수준의 오프로드 아키텍처 확장
- Bridge/VLAN/Bonding -- 리눅스 브리지와 운영 선택 기준 비교
- GRE -- OVS 터널 경로 디버깅을 위한 GRE 세부 이해
- VPP (FD.io) -- OVS 대비 유저스페이스 대안 데이터패스 비교