디버깅 · 성능 측정
VPP 운영 중 실제로 마주치는 문제를 진단·복구하고, 성능을 측정·비교하는 방법을 정리합니다. 패킷 트레이싱·PCAP·이벤트 로거 같은 내부 계측 도구, 증상별 트러블슈팅 매트릭스, 흔한 설정 실수와 안티패턴, HTTP 프로토콜 레벨 트러블슈팅, TRex 기반 외부 벤치마크까지 다룹니다.
성능 측정 및 벤치마크
VPP 구성을 완료한 뒤, 실제 처리량과 지연을 측정하는 체계적인 방법을 소개합니다. 수치 없는 "잘 되는 것 같다"는 운영에서 통하지 않습니다.
TRex를 이용한 트래픽 생성
TRex는 DPDK 기반 고성능 트래픽 생성기로, VPP 벤치마크의 사실상 표준입니다. CSIT(Continuous System Integration Testing)에서도 TRex를 사용합니다.
# TRex STL(Stateless) 프로파일 — NAT44 부하 테스트
from trex_stl_lib.api import *
class STLS1(object):
def create_stream(self):
base_pkt = Ether() / IP(
src="192.168.1.10",
dst="198.51.100.10"
) / UDP(
sport=1024,
dport=80
) / ("X" * 16)
# 소스 IP와 포트를 변경하여 다양한 플로우 생성
vm = STLScVmRaw([
STLVmFlowVar(name="src_ip",
min_value="192.168.1.10",
max_value="192.168.1.250",
size=4, op="inc"),
STLVmWrFlowVar(fv_name="src_ip",
pkt_offset="IP.src"),
STLVmFlowVar(name="src_port",
min_value=1024,
max_value=60000,
size=2, op="inc"),
STLVmWrFlowVar(fv_name="src_port",
pkt_offset="UDP.sport"),
STLVmFixIpv4(offset="IP"),
])
return STLStream(
packet=STLPktBuilder(pkt=base_pkt, vm=vm),
mode=STLTXCont(pps=1000000) # 1Mpps부터 시작
)
def get_streams(self, **kwargs):
return [self.create_stream()]
def register():
return STLS1()
# TRex 실행 및 결과 확인
$ sudo ./t-rex-64 -i -c 4 --cfg /etc/trex_cfg.yaml
# TRex 콘솔에서:
trex> start -f nat44_test.py -m 5mpps -d 60
trex> stats
# TX: 5.00 Mpps, RX: 4.98 Mpps → 드롭율 0.04%
# VPP 측에서 동시에 확인:
vpp# show runtime
vpp# show interface GigabitEthernet0/8/0
vpp# show nat44 sessions count
VPP 내부 성능 카운터 해석
show runtime 출력의 각 열이 무엇을 뜻하는지 정확히 알아야 병목을 찾을 수 있습니다.
Name State Calls Vectors Suspends Clocks Vectors/Call
dpdk-input polling 1234567 9.87e7 0 22.1 79.9
ethernet-input active 1234567 9.87e7 0 8.3 79.9
ip4-input active 1234567 9.87e7 0 12.5 79.9
ip4-lookup active 1234567 9.87e7 0 15.2 79.9
nat44-ed-in2out active 1200000 9.60e7 0 44.3 80.0
ip4-rewrite active 1200000 9.60e7 0 9.8 80.0
GigE0/9/0-output active 1200000 9.60e7 0 5.1 80.0
GigE0/9/0-tx active 1200000 9.60e7 0 18.7 80.0
| 열 | 의미 | 정상 범위 | 이상 징후 |
|---|---|---|---|
| Calls | 노드가 호출된 횟수 | — | — |
| Vectors | 처리한 총 패킷 수 | — | 이전 노드보다 현저히 적으면 드롭 발생 |
| Vectors/Call | 호출당 평균 배치 크기 | 64~256 | <10이면 배치 효율 저하 |
| Clocks | 패킷당 평균 CPU 클럭 | 노드 의존 | 특정 노드가 100+ 이면 병목 |
| Suspends | 자발적 중단 횟수 | 0 | >0이면 스케줄링 문제 |
Clocks가 가장 높은 노드가 처리 비용의 병목입니다. NAT의 경우 nat44-ed-in2out이 보통 가장 높고, IPsec에서는 esp-encrypt/esp-decrypt가 지배적입니다. Vectors/Call이 낮다면 입력 큐가 적절히 배치되지 않아 벡터 효율이 떨어지는 것이므로 RSS 큐와 워커 매핑을 점검하세요.
디버깅 및 트러블슈팅
패킷 트레이싱
VPP의 패킷 트레이싱은 가장 강력한 디버깅 도구입니다. 패킷이 어떤 노드를 거쳐 어떤 결정을 받았는지 라인별로 확인할 수 있습니다.
# 트레이싱 활성화 (dpdk-input 노드에서 10개 패킷)
vpp# trace add dpdk-input 10
# 트래픽 발생 후 결과 확인
vpp# show trace
Packet 1
00:00:01:123456: dpdk-input
GigabitEthernet0/8/0 rx queue 0
buffer 0x9a340: current data 0, length 98, buffer-pool 0
trace_handle 0x1000001
l2-hdr-offset 0 ip4-hdr-offset 14
00:00:01:123458: ethernet-input ← L2 분류
IP4: fa:16:3e:aa:bb:cc -> fa:16:3e:dd:ee:ff
00:00:01:123459: ip4-input ← IPv4 처리 진입
TCP: 192.168.1.100 -> 10.0.0.1
tos 0x00, ttl 64, length 84, checksum 0x1234 dscp CS0 ecn NON_ECN
fragment id 0x0001, flags DONT_FRAGMENT
TCP: 45000 -> 80
seq 0x12345678 ack 0x00000000
flags [SYN] window 65535
00:00:01:123460: ip4-lookup ← FIB 룩업
fib 0 dpo-idx 7 flow hash: 0x00001234
TCP: 192.168.1.100 -> 10.0.0.1
00:00:01:123461: ip4-rewrite ← MAC 재작성
tx_sw_if_index 2 dpo-idx 7
adjacency rewrite: GigabitEthernet0/9/0
dst aa:bb:cc:dd:ee:01 src fa:16:3e:xx:yy:zz
00:00:01:123462: GigabitEthernet0/9/0-output ← 인터페이스 출력
GigabitEthernet0/9/0
IP4: fa:16:3e:xx:yy:zz -> aa:bb:cc:dd:ee:01
00:00:01:123463: GigabitEthernet0/9/0-tx ← TX 큐 전송
buffer 0x9a340: current data -14, length 112
| 디버깅 시나리오 | trace add 대상 | 확인 포인트 |
|---|---|---|
| 패킷이 VPP에 도달하지 않음 | dpdk-input 또는 af-xdp-input | RX 큐 수신 여부 |
| 라우팅(Routing) 문제 | ip4-input | ip4-lookup의 dpo-idx |
| NAT 변환 문제 | nat44-in2out-output | 세션 매칭, 주소 변환(Address Translation) |
| ACL 드롭 | acl-plugin-in-ip4-fa | 매치 규칙, deny 여부 |
| IPsec 터널(Tunnel) 문제 | ipsec-input-ip4 | SA 매칭, 복호화(Decryption) 에러 |
| 인터페이스 미출력 | interface-output | TX 큐 도달 여부 |
에러 카운터 분석
vpp# show errors
Count Node Reason
15230 dpdk-input no buffer available
842 ip4-input ip4 spoofed local-address packet drops
156 ip4-lookup ip4 destination lookup miss
23 acl-plugin-in-ip4-fa acl deny
5 nat44-in2out no translation
2 ipsec-input-ip4 SA not found
| 에러 | 노드 | 원인 | 해결 |
|---|---|---|---|
| no buffer | dpdk-input | 버퍼 풀 고갈 | buffers-per-numa 증가, hugepage 추가 |
| ttl-exceeded | ip4-input | TTL이 0에 도달 | 라우팅 루프 확인 |
| destination lookup miss | ip4-lookup | FIB에 경로 없음 | show ip fib로 경로 확인 |
| adjacency incomplete | ip4-rewrite | ARP 미해석 | show ip neighbor, ARP 상태 확인 |
| acl deny | acl-plugin | ACL 규칙에 의한 차단 | show acl-plugin acl로 규칙 확인 |
| no translation | nat44 | NAT 세션/주소 풀 소진 | show nat44 sessions, 주소 풀 확인 |
| SA not found | ipsec-input | IPsec SA 매칭 실패 | show ipsec sa, SPI 확인 |
| interface down | interface-output | 출력 인터페이스 down | show interface, admin up 확인 |
| worker handoff drops | handoff | 핸드오프 큐 오버플로 | 워커 부하 분산(Load Balancing), RSS 확인 |
| dpdk driver init fail | dpdk | NIC 바인딩 실패 | dpdk-devbind --status, VFIO/UIO 확인 |
PCAP 캡처
# 인입 패킷 PCAP 캡처
vpp# pcap trace rx max 1000 file rx-capture.pcap
# 송출 패킷 캡처
vpp# pcap trace tx max 1000 file tx-capture.pcap
# 드롭 패킷 캡처 (문제 진단에 가장 유용)
vpp# pcap trace drop max 1000 file drop-capture.pcap
# 특정 인터페이스만 캡처
vpp# pcap trace rx max 500 intfc GigabitEthernet0/8/0 file intf-rx.pcap
# 캡처 중지 및 저장
vpp# pcap trace off
# 파일 위치: /tmp/*.pcap
/tmp/*.pcap 파일을 Wireshark에서 바로 열 수 있습니다. pcap trace drop은 VPP가 드롭한 패킷만 캡처하므로, "왜 패킷이 전달되지 않는가?"에 대한 직접적인 답을 제공합니다.
이벤트 로거와 코어 덤프(Core Dump)
# 이벤트 로거 활성화
vpp# event-logger resize 1000000
vpp# event-logger restart
vpp# event-logger save /tmp/elog.dat
# GDB로 VPP 디버깅 (디버그 빌드 필요)
$ gdb /usr/bin/vpp_main core.12345
(gdb) bt # 백트레이스
(gdb) thread apply all bt # 모든 스레드 백트레이스
(gdb) frame 3 # 특정 프레임으로 이동
(gdb) p *b # vlib_buffer_t 내용 출력
# startup.conf에서 코어 덤프 활성화
# unix { full-coredump }
# coredump 크기 제한 해제: ulimit -c unlimited
coredump_filter를 조정하여 hugepage를 제외하면 코어 크기를 줄일 수 있습니다: echo 0x33 > /proc/$(pidof vpp)/coredump_filter
자주 발생하는 문제와 해결
| 문제 | 증상 | 진단 방법 | 해결 |
|---|---|---|---|
| 버퍼 고갈 | RX 드롭 급증 | show errors no-buffer | buffers-per-numa 증가 |
| DPDK 초기화 실패 | VPP 시작 실패 | /var/log/vpp/vpp.log | VFIO/UIO 바인딩, IOMMU 확인 |
| Hugepage 부족 | VPP 시작 실패 또는 OOM | cat /proc/meminfo | hugepage 추가 할당 |
| 워커 정지 | 특정 큐 처리 중단 | show runtime Suspends | 배리어 장기 홀드 확인 |
| ARP 미해석 | adjacency incomplete | show ip neighbor | 연결성 확인, proxy-arp |
| NAT 세션 소진 | no translation 에러 | show nat44 sessions | 세션 한도/타이머(Timer) 조정 |
| RSS 불균형 | 일부 워커만 과부하 | show runtime per-worker | RSS 해시 키 변경, handoff |
| MTU 불일치 | 패킷 드롭/단편화(Fragmentation) | show hardware | 인터페이스 MTU 통일 |
| CLI 소켓 거부 | vppctl 연결 실패 | 소켓 권한 확인 | api-segment { gid vpp } |
| 플러그인 충돌 | VPP 크래시 | 코어 덤프 분석 | 문제 플러그인 비활성, 버전 확인 |
트러블슈팅 가이드
VPP 실전 구성에서 자주 만나는 문제와 체계적인 진단 방법을 정리합니다.
패킷이 전달되지 않는 경우
# 1단계: 인터페이스 상태 확인
vpp# show interface
# 모든 인터페이스가 'up'인지 확인
# 'admin-down'이면: set interface state up
# 2단계: 수신 패킷 확인
vpp# clear interface counters
# 트래픽 생성 후:
vpp# show interface
# rx-pkts가 증가하는지 확인
# 증가하지 않으면: DPDK 바인딩, Hugepage, NIC 드라이버 문제
# 3단계: 패킷 추적
vpp# clear trace
vpp# trace add dpdk-input 20
# 트래픽 생성 후:
vpp# show trace
# 패킷이 어느 노드에서 드롭되는지 확인
# 'error' 또는 'drop' 노드에서 끝나면 해당 에러 메시지 확인
# 4단계: 에러 카운터 확인
vpp# show errors
# 가장 높은 에러 카운터부터 추적
# 5단계: FIB(라우팅 테이블) 확인
vpp# show ip fib # 전체 라우팅 테이블
vpp# show ip fib 198.51.100.0/24 # 특정 서브넷 경로
자주 발생하는 문제와 해결법
| 증상 | 진단 명령 | 원인 | 해결 |
|---|---|---|---|
| VPP 시작 실패 | journalctl -u vpp | Hugepage 부족, DPDK 바인딩 실패 | echo 1024 > /proc/sys/vm/nr_hugepages, dpdk-devbind.py -b vfio-pci |
| DPDK 인터페이스 미표시 | show interface | NIC가 DPDK에 바인딩되지 않음 | dpdk-devbind.py --status로 확인, modprobe vfio-pci |
| ARP 미응답 | show ip neighbors | 인터페이스에 IP 미할당, uRPF 드롭 | set interface ip address 재확인. uRPF/ip4-local 트랩의 자세한 진단은 데이터 경로 — L3 라우팅 및 VRF 기초 참조 |
| NAT 세션 미생성 | show nat44 sessions | in/out 방향 설정 오류 | set interface nat44 in/out 방향 교차 확인 |
| memif 연결 안됨 | show memif | 소켓 경로/권한, master/slave 역할 | 양쪽 소켓 파일 경로 동일 확인, 권한 chmod 770 |
| IPsec 단방향만 동작 | show ipsec sa detail | SPI/키 불일치, 라우팅 미설정 | 양쪽 설정 교차 검증, show ip fib |
| 처리량 기대 이하 | show runtime | 워커 불균형, 배치 효율 저하 | RSS 큐 수와 워커 수 일치, isolcpus 확인 |
패킷 캡처 (pcap)
패킷 내용을 직접 확인해야 할 때는 VPP의 내장 pcap 기능을 사용합니다. tcpdump와 달리 커널을 거치지 않으므로 DPDK 인터페이스의 패킷도 캡처할 수 있습니다.
# 특정 인터페이스의 수신 패킷 캡처
vpp# pcap trace rx tx max 1000 intfc GigabitEthernet0/8/0 \
file /tmp/vpp-capture.pcap
# 트래픽 발생 후 캡처 중지
vpp# pcap trace off
# 호스트에서 Wireshark로 분석
$ wireshark /tmp/vpp-capture.pcap
# 또는 tcpdump로 빠르게 확인
$ tcpdump -r /tmp/vpp-capture.pcap -nn | head -20
# 그래프 노드 단위 캡처 (더 세밀한 분석)
vpp# pcap trace rx tx max 500 \
classify-table-index 0 \
file /tmp/vpp-classified.pcap
max를 작게 설정하고, 분석이 끝나면 즉시 pcap trace off로 중지하세요. 대량 캡처가 필요하면 trace add와 show trace로 노드 경로만 먼저 확인하는 것이 효율적입니다.
흔한 실수와 안티패턴
VPP 운영 시 자주 발생하는 실수와 권장 해결 방법을 정리합니다. 특히 초기 구축 시 환경 설정 오류가 대부분의 장애 원인입니다.
Hugepage 설정 누락/부족
| 증상 | 원인 | 해결 방법 |
|---|---|---|
VPP 시작 실패: rte_eal_init error | Hugepage 미할당 | 커널 부트 파라미터에 hugepages=2048 추가 |
| VPP 시작 후 버퍼 부족 경고 | Hugepage 부족 | 워커 수 × 4GB 이상 확보 (NUMA 당) |
| DPDK 인터페이스 생성 실패 | 1G hugepage만 할당, 2M 미할당 | hugeadm --pool-pages-min 2MB:2048 |
| NUMA 편향 성능 저하 | NIC NUMA와 hugepage NUMA 불일치 | NIC NUMA 노드에 hugepage 분배: echo 1024 > /sys/devices/system/node/node0/hugepages/.../nr_hugepages |
# Hugepage 상태 확인
$ cat /proc/meminfo | grep -i huge
HugePages_Total: 2048
HugePages_Free: 1536
HugePages_Rsvd: 0
Hugepagesize: 2048 kB
# 부팅 시 영구 설정 (GRUB)
GRUB_CMDLINE_LINUX="default_hugepagesz=2M hugepagesz=2M hugepages=2048 \
hugepagesz=1G hugepages=4 iommu=pt intel_iommu=on"
# 런타임 할당 (재부팅 없이)
$ echo 2048 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
$ sudo mkdir -p /dev/hugepages
$ sudo mount -t hugetlbfs none /dev/hugepages
DPDK 인터페이스 바인딩 오류
| 드라이버 | IOMMU 필요 | 장점 | 주의사항 |
|---|---|---|---|
| vfio-pci (권장) | 예 (intel_iommu=on) | DMA 격리, 보안, SR-IOV 지원 | IOMMU 미활성 시 바인딩 실패 |
| uio_pci_generic | 아니요 | IOMMU 없는 환경에서 동작 | 보안 격리 없음, root 필요 |
| igb_uio (DPDK) | 아니요 | 레거시 NIC 호환 | 별도 모듈 빌드 필요, 유지보수 중단 추세 |
# 잘못된 예: IOMMU 없이 vfio-pci 바인딩 시도
$ sudo dpdk-devbind.py --bind=vfio-pci 0000:00:08.0
# Error: Cannot bind to driver vfio-pci: No IOMMU group
# 올바른 해결 방법 1: IOMMU 활성화
# GRUB에 intel_iommu=on iommu=pt 추가 후 재부팅
# 올바른 해결 방법 2: IOMMU 없는 환경에서 vfio 사용
$ sudo modprobe vfio enable_unsafe_noiommu_mode=1
$ echo 1 | sudo tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode
$ sudo dpdk-devbind.py --bind=vfio-pci 0000:00:08.0
# NIC 현재 상태 확인
$ dpdk-devbind.py --status
Network devices using DPDK-compatible driver
============================================
0000:00:08.0 'Virtio network device' drv=vfio-pci
Network devices using kernel driver
====================================
0000:00:03.0 'Virtio network device' drv=virtio-pci
dpdk-devbind.py --bind=virtio-pci 0000:00:08.0을 사용합니다. 관리 인터페이스(SSH 접속용)를 실수로 DPDK에 바인딩하면 원격 접속이 끊어지므로 반드시 OOB(Out-of-Band) 관리 인터페이스를 별도 확보하세요.
startup.conf 워커 스레드 / CPU 핀닝 실수
| 실수 패턴 | 증상 | 올바른 설정 |
|---|---|---|
main-core와 corelist-workers 중복 | 메인/워커 경합, 불안정 | 겹치지 않는 CPU 코어 할당 |
| 하이퍼스레딩 형제 코어에 워커 배치 | 캐시 경합으로 성능 50% 저하 | 물리 코어만 사용: lscpu -e로 확인 |
| NUMA 노드 교차 배치 | 원격 메모리 접근 지연 | NIC와 같은 NUMA 노드의 코어 사용 |
| 워커 수 > RSS 큐 수 | 일부 워커 유휴 | num-rx-queues와 워커 수 일치 |
| isolcpus 미적용 | OS 프로세스가 VPP 코어에 스케줄됨 | 커널 부트 파라미터: isolcpus=1-3 |
# CPU 토폴로지 확인
$ lscpu -e
CPU NODE SOCKET CORE L1d L2 L3 ONLINE
0 0 0 0 0 0 0 yes ← main-core (관리용)
1 0 0 1 1 1 0 yes ← worker 0
2 0 0 2 2 2 0 yes ← worker 1
3 0 0 3 3 3 0 yes ← worker 2
4 0 0 0 0 0 0 yes ← HT sibling of core 0 (사용 금지)
# NIC NUMA 노드 확인
$ cat /sys/bus/pci/devices/0000:00:08.0/numa_node
0
# 올바른 startup.conf
cpu {
main-core 0 /* 관리 전용 */
corelist-workers 1-3 /* NIC와 같은 NUMA, HT 제외 */
skip-cores 0 /* 기본값 유지 */
}
# 커널 부트 파라미터 (VPP 코어 격리)
GRUB_CMDLINE_LINUX="isolcpus=1-3 nohz_full=1-3 rcu_nocbs=1-3"
벡터 크기와 배치 처리 오해
VPP의 벡터 처리 모델에서 벡터 크기(VLIB_FRAME_SIZE)는 한 번에 처리되는 패킷 묶음의 크기입니다. 벡터가 클수록 I-cache 효율이 높아지지만, 지연는 증가합니다.
| 오해 | 실제 |
|---|---|
| 벡터 크기가 클수록 항상 좋다 | 처리량은 증가하지만 지연도 증가. 실시간 트래픽(VoIP 등)에서는 VLIB_FRAME_SIZE를 줄이는 것이 유리 |
| 벡터 크기를 임의로 조절할 수 있습니다 | 입력 노드의 수신 버스트 크기(DPDK rx-burst)가 실질적 벡터 크기 결정. show runtime의 Vectors/Call로 확인 |
| 모든 노드가 동일한 벡터 크기를 처리 | 분기 노드(classify, ACL)에서 벡터가 분할될 수 있음. show runtime에서 노드별 Vectors/Call 차이 확인 |
| 패킷이 없으면 CPU를 쉰다 | 기본 DPDK 폴링은 항상 100% CPU 사용. poll-sleep-poll 또는 interrupt mode로 완화 가능 (아래 성능 최적화 참조) |
show runtime의 Vectors/Call 값이 1에 가까우면 패킷이 산발적으로 도착하는 것이므로 벡터 처리의 이점이 줄어듭니다. 이 경우 배치 크기를 늘리거나(num-rx-desc 2048), 트래픽 패턴을 분석하여 폴링 주기를 조정하세요.
memif 연결 시 소켓 권한 문제
# 흔한 오류: Permission denied
vpp# create interface memif id 0 socket-id 1 slave
# create memif: connect error (Permission denied)
# 원인 진단
$ ls -la /run/vpp/
srwxrwx--- 1 root root 0 memif-chain.sock ← 그룹 접근 필요
# 해결: 공통 그룹 사용
$ sudo groupadd vpp-memif
$ sudo usermod -aG vpp-memif vpp-user1
$ sudo usermod -aG vpp-memif vpp-user2
# startup.conf에서 그룹 지정
unix {
gid vpp-memif
}
# 소켓 디렉터리 권한 확인 (실행 권한 필수)
$ sudo chmod 2770 /run/vpp/
$ sudo chgrp vpp-memif /run/vpp/
# 연결 상태 확인
vpp# show memif
interface memif1/0
socket-id 1 id 0 mode ethernet
status: connected ← 정상
ring 0: region 0 num-s 2048 ...
link up
L7 트러블슈팅 — HTTP 파싱 오류부터 워커 편중까지
L4 트러블슈팅이 패킷 드롭·재전송·MTU 같은 전송 계층 문제에 집중한다면, L7 트러블슈팅은 한 단계 더 위에서 시작합니다. 이 절은 운영에서 자주 마주치는 6가지 L7 증상을 골라, 증상 → 가설 → 진단 명령 → 해결 패턴 순으로 정리합니다.
- 호스트 스택 — 트러블슈팅 —
show session verbose컬럼 해석, TLS CPS 저하 진단, 세션 누수, 워커 편향의 자세한 절차 - QUIC — 디버깅 및 트러블슈팅 — QUIC 0-RTT, 패킷 손실, 핸드셰이크 실패 진단
- TLS · 프록시 — 인증서·SNI·TLS 종단 관련 깊이 있는 디버깅
증상별 진단 매트릭스
| 증상 | 1차 의심 | 진단 명령 | 해결 |
|---|---|---|---|
| 특정 클라이언트만 400 폭증 | HTTP 파서 엄격성 / 헤더 한도 | show stat /http/parse_errors | 헤더 한도 상향 또는 클라이언트 수정 |
| p99 지연만 튐 | 워커 편중 / 캐시 미스 | show stat /http/in_flight per-worker | RSS 재구성 또는 캐시 튜닝 |
| 5xx 단속 발생 | 업스트림 실패 / 회로 차단 | show http gateway upstream | 업스트림 헬스체크 강화 |
| HTTP/2 스트림 멈춤 | 흐름 제어 윈도 고갈 | show http2 stream <id> | SETTINGS_INITIAL_WINDOW_SIZE 상향 |
| TLS 핸드셰이크 실패율 상승 | 인증서 만료 / SNI 미일치 | show stat /tls/handshakes_total | 인증서 갱신 / SNI 설정 |
| 캐시 적중률 급락 | Vary 헤더 변화 / 키 폭증 | show http gateway cache | Vary 정규화 / 키 설계 재검토 |
L7 pcap — vppctl trace + 외부 분석
VPP는 자체 패킷 트레이스(trace add)와 pcap 캡처(pcap trace)를 모두 제공합니다. L7 문제 해결에는 두 도구를 조합해야 합니다.
# 1) HTTP 게이트웨이 인터페이스에서 캡처
vppctl pcap trace rx tx max 5000 \
intfc TenGigabitEthernet0/0/0 \
file /tmp/http-debug.pcap \
filter classify table 0 match l3 ip4 dst 192.0.2.10 \
l4 tcp dst-port 443
# 2) 캡처 시작 후 재현
curl -k -v https://192.0.2.10/api/v1/version
# 3) 캡처 종료
vppctl pcap trace off
# 4) Wireshark에서 분석
wireshark /tmp/http-debug.pcap
# - TLS pre-master secret 있으면 복호화 후 HTTP 메시지 확인
# - HTTP/2는 Wireshark가 자동 해석 (SETTINGS·HEADERS·DATA 프레임)
# 5) 그래프 노드 트레이스 (헤더 파싱 단계까지 추적)
vppctl trace add dpdk-input 100
vppctl show trace | grep -A 20 'http-static'
show 명령 카드
# 세션 레이어 상태
vppctl show session verbose 2 # 모든 활성 세션
vppctl show session thread 0 verbose # 워커 0만
# HTTP/2 스트림 상세
vppctl show http2 conn
vppctl show http2 stream <conn-id> <stream-id>
# - state: open / half-closed / closed
# - recv_window / send_window
# - bytes_sent / bytes_recv
# TLS 세션
vppctl show tls
vppctl show tls openssl ctx 0
# - cipher / version / sni / alpn
# - resumption: full / psk / 0-rtt
# 게이트웨이 라우팅·캐시
vppctl show http gateway routes
vppctl show http gateway upstream
vppctl show http gateway cache top 20
# 워커 편중 진단
for i in 0 1 2 3; do
echo "Worker $i:"
vppctl show session thread $i | grep -c 'state ESTABLISHED'
done
자주 마주치는 증상 — 해결 레시피 4가지
레시피 1: 특정 vhost만 5xx
rate(http_requests_total{status=~"5..", vhost="api.example.com"}[5m])추세 확인- 같은 시간대
http_upstream_failures_total{upstream="api-backend-1"}동반 상승 여부 - 업스트림 헬스체크 결과(
show http gateway upstream)에서 응답 시간 분포 확인 - 해결: 업스트림 health check interval 단축, 회로 차단 임계 강화, 트래픽 일부를 백업 업스트림으로 이동
레시피 2: HTTP/2 스트림이 멈춤
show http2 stream에서send_window=0확인- 같은 연결의 다른 스트림도 같은 상태면 연결 윈도 고갈, 한 스트림만이면 스트림 윈도 고갈
- 대형 응답 BDP 계산:
BDP = bandwidth × RTT. 100Mbps × 100ms = 1.25MB - 해결:
SETTINGS_INITIAL_WINDOW_SIZE를 BDP × 2 이상으로 설정. VPP CLI 또는 startup.conf의 http2 절
레시피 3: 워커 편중으로 한 워커만 100%
vppctl show runtime에서 워커별 vector rate 비교 — 한 워커만 0.9 이상이면 편중show session thread N에서 활성 세션 수 비교- 가설 1: NIC RSS 키가 비대칭 →
show hardware-interfaces에서 RSS 큐 분포 확인 - 가설 2: 한 클라이언트가 단일 연결로 거대한 HTTP/2 스트림 폭주 →
SETTINGS_MAX_CONCURRENT_STREAMS하향 - 해결: RSS 대칭 해시(Toeplitz 키 0x6d5a) 적용, 또는 HAProxy 같은 L7 LB로 사전 분산
레시피 4: 캐시 적중률 급락
show http gateway cache의 entries / bytes / evictions 추세 확인- evictions 폭증 + 적중률 감소 → 캐시 크기 부족 또는 키 폭증
- 키 폭증 가설: 새 배포 후
Vary헤더에 변동성 큰 헤더(예:User-Agent) 추가됨 - 해결:
Vary정규화, 캐시 크기 상향, 또는 stale-while-revalidate 도입으로 백그라운드 갱신
http_request_duration_seconds p99, traffic = http_requests_total rate, errors = 5xx 비율, saturation = http_in_flight + 워커 vector rate. 이 네 가지만 모니터링해도 운영 사고의 80%는 즉시 감지할 수 있습니다.
Common Pitfalls
- 총량 메트릭만 봄 — 전체 5xx가 0.5%여도 특정 vhost나 path는 50%일 수 있습니다. 라벨로 분해해야 합니다.
- p99 대신 평균만 봄 — 평균은 long-tail을 숨깁니다. 사용자 체감은 p95~p99에서 결정됩니다.
- 워커 단위 미관찰 — 노드 전체 CPU가 50%여도 한 워커는 100%일 수 있습니다. per-worker 게이지가 필수입니다.
- pcap 대용량 — 100Gbps 트래픽을 그대로 캡처하면 디스크가 즉시 가득 찹니다. 반드시 classify table로 5튜플 필터를 좁히고, max를 작게 잡아야 합니다.
- 메트릭 카디널리티 폭발 — 라벨에 path를 그대로 넣으면 카디널리티가 폭발합니다.
/api/v1/users/12345→/api/v1/users/:id로 정규화해야 합니다.
디버깅 보조 도구 — PG · nsim · BPF Trace Filter
VPP에는 장애 재현과 조건부 트레이스를 돕는 소규모 도구 세 개가 있습니다. 일상 운영에서 자주 언급되지 않지만 어려운 버그를 잡을 때 결정적인 역할을 합니다. 상시 관측 도구(perfmon/sFlow/IPFIX 등)는 관측성 · 원격 측정에서 다룹니다.
Packet Generator (PG) — 재현 주입
문제를 겪은 패킷 패턴을 외부 장비 없이 VPP 안에서 직접 재현하고 싶을 때 PG가 최적입니다. 헤더 조합을 스크립트로 정의하고, 원하는 비율로 특정 노드에 주입할 수 있어 엣지 케이스 회귀 테스트에도 적합합니다.
vpp# packet-generator new {
name bug-repro
limit 100
node ip4-input
size 64-1500
data { IP4: 10.0.0.1 -> 10.0.0.2
UDP: 1234 -> 5678
incrementing 100 }
}
vpp# trace add pg-input 100
vpp# packet-generator enable-stream bug-repro
vpp# show trace
장점은 외부 트래픽 생성기(TRex 등) 없이도 패킷 레벨 정확도로 동일 시나리오를 재현할 수 있다는 점입니다. 단점은 pps가 제한적이라 성능 재현 테스트에는 부적합합니다.
nsim — 네트워크 지연 시뮬레이션
nsim은 인터페이스 송신 경로에 지연·대역폭 한계·손실을 주입해 WAN 환경을 재현합니다. 리눅스 netem의 VPP 버전으로, TCP 혼잡 제어가 특정 조건에서 어떻게 동작하는지 검증할 때 결정적입니다.
vpp# nsim output-feature enable-disable <if>
vpp# set nsim delay 100.0 ms bandwidth 10.0 mbit packets-per-drop 1000
vpp# show nsim
실전 시나리오 예:
- 클라이언트가 고속 백본을 거쳐 접속할 때만 발생하는 TLS 재전송 버그 재현
- HTTP/3 QUIC connection migration의 handoff 지연 측정
- TCP BBR vs CUBIC 혼잡 제어 차이 검증
BPF Trace Filter — 조건부 트레이스
일반 trace add는 지정 노드에 들어오는 모든 패킷을 캡처합니다. 트래픽이 많으면 금세 버퍼가 가득 차고, 정작 문제 패킷은 놓치기 쉽습니다. BPF Trace Filter는 tcpdump 문법으로 트레이스 대상을 정밀 필터링합니다.
vpp# set trace filter function-name vlib_trace_filter_bpf expr "host 192.0.2.100 and tcp port 443"
vpp# trace add dpdk-input 1000
# 문제 재현 후
vpp# show trace
BPF 식은 libpcap 문법을 그대로 사용하므로, tcpdump -d로 익숙한 사람은 바로 쓸 수 있습니다. 고PPS 운영 환경에서 특정 플로우만 정밀 관찰할 수 있어 진단 시간이 크게 단축됩니다.
도구 선택 요약
| 상황 | 권장 |
|---|---|
| 특정 패킷 시나리오 재현 | Packet Generator + show trace |
| WAN 조건 아래서 버그 검증 | nsim + iperf3 |
| 폭주 중인 트래픽에서 한 플로우만 보기 | BPF Trace Filter |
| 노드 성능 hot spot 찾기 | perfmon topdown |
| 워커 간 시간 분포 보기 | 이벤트 로그 + G2 |