디버깅 · 성능 측정

VPP 운영 중 실제로 마주치는 문제를 진단·복구하고, 성능을 측정·비교하는 방법을 정리합니다. 패킷 트레이싱·PCAP·이벤트 로거 같은 내부 계측 도구, 증상별 트러블슈팅 매트릭스, 흔한 설정 실수와 안티패턴, HTTP 프로토콜 레벨 트러블슈팅, TRex 기반 외부 벤치마크까지 다룹니다.

연관 문서: 일상 운영·설치·CLI/API·플러그인 개발·Kubernetes 통합 등 정상 상태 운용은 운영 · 플랫폼 · 확장에서 다룹니다. 본 페이지는 문제 상황과 측정 방법에 집중합니다.

성능 측정 및 벤치마크

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의 패킷 트레이싱은 가장 강력한 디버깅 도구입니다. 패킷이 어떤 노드를 거쳐 어떤 결정을 받았는지 라인별로 확인할 수 있습니다.

패킷 트레이싱 수집 원리와 출력 구조 ① trace add 명령 vppctl trace add dpdk-input 50 (입력 노드 + 채집 개수) ② 트레이스 버퍼 할당 워커별 tracing table vlib_buffer->flags: TRACED ③ 첫 N개 패킷 태깅 dpdk-input 진입 시점에 trace index 할당 ④ show trace trace buffer 덤프 노드별 format_fn 실행 태깅된 패킷이 그래프를 통과하며 각 노드가 자기 몫의 trace record를 남김 dpdk-input rx queue · len ethernet-input MAC · EtherType ip4-input src/dst · ttl ip4-lookup fib index · adj ip4-rewrite next-hop · mtu interface-tx sw_if_index TX → NIC 각 노드는 format_trace 콜백을 등록해 자기 상태를 텍스트로 포맷합니다. 트레이스 수집은 한 번뿐(N개)이므로 성능에 영향이 없습니다. show trace 출력 (예시) Packet 1 00:01:23:456 dpdk-input GigabitEthernet0/8/0 rx queue 0 len 98 IP4 192.168.1.10 -> 10.0.0.1 00:01:23:456 ip4-input src 192.168.1.10 dst 10.0.0.1 ttl 64 proto ICMP len 84 00:01:23:456 ip4-lookup fib 0 dpo-load-balance via 10.0.0.1/32 adj 5 00:01:23:456 ip4-rewrite tx_sw_if_index 2 adj-idx 5 : via 10.0.0.1 ttl 63 -> GigabitEthernet0/9/0
# 트레이싱 활성화 (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-inputRX 큐 수신 여부
라우팅(Routing) 문제ip4-inputip4-lookup의 dpo-idx
NAT 변환 문제nat44-in2out-output세션 매칭, 주소 변환(Address Translation)
ACL 드롭acl-plugin-in-ip4-fa매치 규칙, deny 여부
IPsec 터널(Tunnel) 문제ipsec-input-ip4SA 매칭, 복호화(Decryption) 에러
인터페이스 미출력interface-outputTX 큐 도달 여부

에러 카운터 분석

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 bufferdpdk-input버퍼 풀 고갈buffers-per-numa 증가, hugepage 추가
ttl-exceededip4-inputTTL이 0에 도달라우팅 루프 확인
destination lookup missip4-lookupFIB에 경로 없음show ip fib로 경로 확인
adjacency incompleteip4-rewriteARP 미해석show ip neighbor, ARP 상태 확인
acl denyacl-pluginACL 규칙에 의한 차단show acl-plugin acl로 규칙 확인
no translationnat44NAT 세션/주소 풀 소진show nat44 sessions, 주소 풀 확인
SA not foundipsec-inputIPsec SA 매칭 실패show ipsec sa, SPI 확인
interface downinterface-output출력 인터페이스 downshow interface, admin up 확인
worker handoff dropshandoff핸드오프 큐 오버플로워커 부하 분산(Load Balancing), RSS 확인
dpdk driver init faildpdkNIC 바인딩 실패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
Wireshark 연계: /tmp/*.pcap 파일을 Wireshark에서 바로 열 수 있습니다. pcap trace drop은 VPP가 드롭한 패킷만 캡처하므로, "왜 패킷이 전달되지 않는가?"에 대한 직접적인 답을 제공합니다.

이벤트 로거와 코어 덤프(Core Dump)

VPP 디버깅 의사결정 트리 패킷이 전달되지 않음 1. show interface → RX 증가? No NIC/DPDK 확인 Yes 2. show errors → 에러 있음? Yes 에러 테이블 참조 No 3. trace add → 경로 추적 4. pcap trace drop → 드롭 패킷 Wireshark 분석 5. show ip fib / show ip neighbor 확인
# 이벤트 로거 활성화
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
코어 덤프(Dump) 분석: VPP 코어 덤프 크기가 수 GB에 달할 수 있습니다(hugepage 매핑 포함). coredump_filter를 조정하여 hugepage를 제외하면 코어 크기를 줄일 수 있습니다: echo 0x33 > /proc/$(pidof vpp)/coredump_filter

자주 발생하는 문제와 해결

문제증상진단 방법해결
버퍼 고갈RX 드롭 급증show errors no-bufferbuffers-per-numa 증가
DPDK 초기화 실패VPP 시작 실패/var/log/vpp/vpp.logVFIO/UIO 바인딩, IOMMU 확인
Hugepage 부족VPP 시작 실패 또는 OOMcat /proc/meminfohugepage 추가 할당
워커 정지특정 큐 처리 중단show runtime Suspends배리어 장기 홀드 확인
ARP 미해석adjacency incompleteshow ip neighbor연결성 확인, proxy-arp
NAT 세션 소진no translation 에러show nat44 sessions세션 한도/타이머(Timer) 조정
RSS 불균형일부 워커만 과부하show runtime per-workerRSS 해시 키 변경, 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 vppHugepage 부족, DPDK 바인딩 실패echo 1024 > /proc/sys/vm/nr_hugepages, dpdk-devbind.py -b vfio-pci
DPDK 인터페이스 미표시show interfaceNIC가 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 sessionsin/out 방향 설정 오류set interface nat44 in/out 방향 교차 확인
memif 연결 안됨show memif소켓 경로/권한, master/slave 역할양쪽 소켓 파일 경로 동일 확인, 권한 chmod 770
IPsec 단방향만 동작show ipsec sa detailSPI/키 불일치, 라우팅 미설정양쪽 설정 교차 검증, 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
주의: pcap 캡처는 성능에 영향을 줍니다. 운영 환경에서는 max를 작게 설정하고, 분석이 끝나면 즉시 pcap trace off로 중지하세요. 대량 캡처가 필요하면 trace addshow trace로 노드 경로만 먼저 확인하는 것이 효율적입니다.

흔한 실수와 안티패턴

VPP 운영 시 자주 발생하는 실수와 권장 해결 방법을 정리합니다. 특히 초기 구축 시 환경 설정 오류가 대부분의 장애 원인입니다.

Hugepage 설정 누락/부족

증상원인해결 방법
VPP 시작 실패: rte_eal_init errorHugepage 미할당커널 부트 파라미터에 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에 바인딩된 NIC를 커널로 돌리려면 dpdk-devbind.py --bind=virtio-pci 0000:00:08.0을 사용합니다. 관리 인터페이스(SSH 접속용)를 실수로 DPDK에 바인딩하면 원격 접속이 끊어지므로 반드시 OOB(Out-of-Band) 관리 인터페이스를 별도 확보하세요.

startup.conf 워커 스레드 / CPU 핀닝 실수

실수 패턴증상올바른 설정
main-corecorelist-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 runtimeVectors/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 증상을 골라, 증상 → 가설 → 진단 명령 → 해결 패턴 순으로 정리합니다.

관련 페이지:

증상별 진단 매트릭스

증상1차 의심진단 명령해결
특정 클라이언트만 400 폭증HTTP 파서 엄격성 / 헤더 한도show stat /http/parse_errors헤더 한도 상향 또는 클라이언트 수정
p99 지연만 튐워커 편중 / 캐시 미스show stat /http/in_flight per-workerRSS 재구성 또는 캐시 튜닝
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 cacheVary 정규화 / 키 설계 재검토

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

  1. rate(http_requests_total{status=~"5..", vhost="api.example.com"}[5m]) 추세 확인
  2. 같은 시간대 http_upstream_failures_total{upstream="api-backend-1"} 동반 상승 여부
  3. 업스트림 헬스체크 결과(show http gateway upstream)에서 응답 시간 분포 확인
  4. 해결: 업스트림 health check interval 단축, 회로 차단 임계 강화, 트래픽 일부를 백업 업스트림으로 이동

레시피 2: HTTP/2 스트림이 멈춤

  1. show http2 stream에서 send_window=0 확인
  2. 같은 연결의 다른 스트림도 같은 상태면 연결 윈도 고갈, 한 스트림만이면 스트림 윈도 고갈
  3. 대형 응답 BDP 계산: BDP = bandwidth × RTT. 100Mbps × 100ms = 1.25MB
  4. 해결: SETTINGS_INITIAL_WINDOW_SIZE를 BDP × 2 이상으로 설정. VPP CLI 또는 startup.conf의 http2 절

레시피 3: 워커 편중으로 한 워커만 100%

  1. vppctl show runtime에서 워커별 vector rate 비교 — 한 워커만 0.9 이상이면 편중
  2. show session thread N에서 활성 세션 수 비교
  3. 가설 1: NIC RSS 키가 비대칭 → show hardware-interfaces에서 RSS 큐 분포 확인
  4. 가설 2: 한 클라이언트가 단일 연결로 거대한 HTTP/2 스트림 폭주 → SETTINGS_MAX_CONCURRENT_STREAMS 하향
  5. 해결: RSS 대칭 해시(Toeplitz 키 0x6d5a) 적용, 또는 HAProxy 같은 L7 LB로 사전 분산

레시피 4: 캐시 적중률 급락

  1. show http gateway cache의 entries / bytes / evictions 추세 확인
  2. evictions 폭증 + 적중률 감소 → 캐시 크기 부족 또는 키 폭증
  3. 키 폭증 가설: 새 배포 후 Vary 헤더에 변동성 큰 헤더(예: User-Agent) 추가됨
  4. 해결: Vary 정규화, 캐시 크기 상향, 또는 stale-while-revalidate 도입으로 백그라운드 갱신
골든 시그널(Signal) 4종: 구글 SRE 책의 latency·traffic·errors·saturation을 VPP L7에 매핑하면 다음과 같습니다 — latency = http_request_duration_seconds p99, traffic = http_requests_total rate, errors = 5xx 비율, saturation = http_in_flight + 워커 vector rate. 이 네 가지만 모니터링해도 운영 사고의 80%는 즉시 감지할 수 있습니다.

Common Pitfalls

디버깅 보조 도구 — 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

실전 시나리오 예:

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 운영 환경에서 특정 플로우만 정밀 관찰할 수 있어 진단 시간이 크게 단축됩니다.

실전 예제 — ip4-rewrite 간헐적 드롭 추적

시나리오: 운영 환경에서 ip4-rewrite 노드가 특정 목적지(10.0.0.5)로 향하는 패킷을 간헐적으로 드롭한다는 신고가 들어왔습니다. 전체 트레이스를 켜면 초당 수십만 패킷이 기록되어 버퍼가 즉시 차버립니다. BPF Trace Filter로 문제 플로우만 골라냅니다.

1단계 — BPF 필터 적용 및 트레이스 시작

# ip4-rewrite 진입 패킷 중 dst=10.0.0.5 인 것만 캡처
vpp# set trace filter function-name vlib_trace_filter_bpf expr "dst host 10.0.0.5"
vpp# clear trace
vpp# trace add dpdk-input 500
# 혹은 tap 인터페이스가 rx 소스라면:
# vpp# trace add virtio-input 500

2단계 — 문제 재현

# 외부 호스트에서 10.0.0.5로 패킷 전송 (ping 또는 iperf)
ping -c 20 10.0.0.5 -I eth1

3단계 — 트레이스 출력 읽기

vpp# show trace
------------------- 정상 패킷 (목적지 도달) -------------------
Packet 1
00:00:00:001234 dpdk-input: sw_if_index 1, next-index 4
  ip4-input: next ip4-rewrite
  ip4-rewrite: adj-idx 12, next-index 0
    Rewrite: 00:11:22:aa:bb:cc -> 10.0.0.5 (GigabitEthernet0/0/1)
  GigabitEthernet0/0/1-output: 82 bytes

------------------- 드롭된 패킷 -------------------
Packet 7
00:00:00:003210 dpdk-input: sw_if_index 1, next-index 4
  ip4-input: next ip4-rewrite
  ip4-rewrite: adj-idx 12, next-index 1  <-- next-index 1 = ip4-drop
    drop: no_route
  error-drop: ip4-rewrite: no_route

트레이스 출력 읽는 법:

필드의미
next-index다음으로 이동한 그래프 노드 인덱스. 0이면 정상 전달, 1 이상이면 드롭·에러 경로인 경우가 많음
adj-idxadjacency 테이블 인덱스. show ip adjacencies로 매핑 확인
drop: no_routeFIB에 해당 목적지 경로 없음 — show ip fib 10.0.0.5로 경로 상태 점검
error-drop최종 drop 노드. 직전 노드가 실제 원인 노드

4단계 — 원인 확인 및 수정

# adjacency 확인
vpp# show ip adjacencies 12
# FIB 경로 상태 확인
vpp# show ip fib 10.0.0.5
# 경로가 없으면 정적 경로 추가
vpp# ip route add 10.0.0.5/32 via 192.168.1.1 GigabitEthernet0/0/0
# 필터 제거
vpp# set trace filter function-name vlib_trace_filter_bpf expr ""
팁: BPF 필터 표현식은 libpcap 문법을 그대로 따릅니다. tcpdump -d "dst host 10.0.0.5 and icmp"로 먼저 BPF 바이트코드를 확인하면 표현식이 의도대로 컴파일되는지 검증할 수 있습니다.

도구 선택 요약

상황권장
특정 패킷 시나리오 재현Packet Generator + show trace
WAN 조건 아래서 버그 검증nsim + iperf3
폭주 중인 트래픽에서 한 플로우만 보기BPF Trace Filter
노드 성능 hot spot 찾기perfmon topdown
워커 간 시간 분포 보기이벤트 로그 + G2

GDB로 플러그인 디버깅

패킷 트레이스나 카운터만으로 원인을 좁힐 수 없을 때는 GDB를 사용하여 실행 중인 VPP 프로세스에 직접 붙거나, 재현 시나리오를 빌드한 뒤 코어 덤프를 분석합니다. 이 섹션에서는 플러그인 개발자 관점에서 자주 쓰는 GDB 워크플로를 다룹니다.

실행 중인 VPP에 GDB 붙이기

VPP는 멀티스레드 프로세스이므로 attach 직후 모든 스레드가 일시 중지됩니다. 운영 트래픽에 영향을 주므로 반드시 스테이징 환경에서 진행합니다.

# VPP 프로세스 PID 확인
pgrep vpp

# GDB로 실행 중인 VPP에 attach
sudo gdb -p $(pgrep vpp)

# attach 후 전체 스레드 목록 확인
(gdb) info threads
# Thread 1은 메인, Thread 2+ 는 워커 스레드

# 특정 스레드로 전환
(gdb) thread 2

# 현재 스레드 콜 스택 확인
(gdb) bt
주의: 운영 환경에서 GDB attach는 전체 VPP 워커가 멈추므로 패킷 처리가 즉시 중단됩니다. 반드시 트래픽이 없는 환경 또는 유지 보수 윈도우 내에서 진행하십시오.

노드 함수에 브레이크포인트 설정

VPP 그래프 노드의 dispatch 함수는 VLIB_NODE_FN 매크로로 정의됩니다. 실제 심볼 이름은 빌드 시스템에 따라 달라지지만, 패턴은 일관적입니다.

# 플러그인 so 파일을 GDB에 수동 로드 (이미 attach된 경우 자동 로드됨)
(gdb) sharedlibrary my_plugin

# 노드 함수 심볼 검색 (심볼 이름 패턴 확인)
(gdb) info functions my_node

# 브레이크포인트 설정 — 함수명 방식
(gdb) break my_node_fn

# 소스 파일 + 라인 방식 (디버그 빌드 필요)
(gdb) break src/plugins/my_plugin/my_node.c:85

# 조건부 브레이크포인트 — 특정 인터페이스 인덱스일 때만
(gdb) break my_node_fn if frame->sw_if_index == 3

# 계속 실행
(gdb) continue

vlib_buffer_t 구조 확인:

# 브레이크포인트에 걸렸을 때 버퍼 내용 출력
(gdb) p *b0
# 패킷 데이터 덤프 (현재 위치에서 64바이트)
(gdb) x/64xb (char*)b0 + b0->current_data

# vlib_frame_t 내 버퍼 인덱스 배열 출력
(gdb) p/d from[0]@16

VPP GDB 헬퍼

VPP 소스 트리에는 GDB 매크로와 Python pretty-printer가 포함되어 있습니다. 디버그 빌드 환경에서 사용할 수 있습니다.

# VPP 소스의 GDB 초기화 파일 로드
# (빌드 디렉터리에 gdbinit 파일이 생성됨)
(gdb) source /path/to/vpp/build-root/build-vpp_debug-native/vpp/vpp-api/gdb_hooks.py

# 로드 후 사용 가능한 커스텀 커맨드 예시
(gdb) vpp_buffer b0          # vlib_buffer_t 요약 출력
(gdb) vpp_trace              # 현재 패킷의 트레이스 출력
디버그 빌드: 심볼과 GDB 헬퍼를 모두 사용하려면 릴리즈 빌드 대신 디버그 빌드가 필요합니다. CMake 빌드라면 -DCMAKE_BUILD_TYPE=Debug, Makefile 빌드라면 make build-vpp_debug를 사용하십시오. 패키지 설치 환경이라면 vpp-dbg 패키지를 추가로 설치합니다.

코어 덤프 사후 분석

# startup.conf에서 코어 덤프 활성화
# unix { full-coredump }

# 프로세스 locked memory 제한 해제 (ulimit)
ulimit -c unlimited

# 코어 덤프 파일로 GDB 실행
sudo gdb /usr/bin/vpp /var/lib/vpp/vpp.core

# 크래시 시점의 콜 스택 확인
(gdb) bt full

# 전체 스레드 스택 한 번에 보기
(gdb) thread apply all bt

ASAN / Valgrind 활용

GDB 브레이크포인트보다 메모리 버그(사용 후 해제, 버퍼 오버플로)를 찾는 데는 AddressSanitizer(ASAN)이나 Valgrind가 더 효과적입니다.

# ASAN 빌드 — CMake 옵션 (VPP 소스 빌드 시)
cmake -DVPP_ENABLE_SANITIZE_ADDR=ON ..
make -j$(nproc)

# ASAN 환경 변수 설정 후 VPP 실행
ASAN_OPTIONS=abort_on_error=1:detect_leaks=0 sudo -E /usr/bin/vpp -c /etc/vpp/startup.conf

# Valgrind — 성능 오버헤드가 매우 크므로 단위 테스트나 최소 재현에만 사용
sudo valgrind --tool=memcheck --leak-check=full \
  --suppressions=/path/to/vpp/extras/valgrind/vpp.supp \
  /usr/bin/vpp -c /etc/vpp/startup.conf
Valgrind 주의사항: VPP의 커스텀 메모리 할당자(clib_mem)와 hugepage 매핑은 Valgrind 기본 suppression 없이 수천 개의 오탐을 발생시킵니다. VPP 소스 트리의 extras/valgrind/vpp.supp suppression 파일을 반드시 함께 사용하십시오. ASAN은 VPP 내장 할당자를 교체하므로 Valgrind보다 결과가 깔끔한 편입니다.

트러블슈팅 3축 매트릭스

증상(symptom) × 계층(layer) × 도구(tool) 3차원 접근법은 VPP 장애 대응 시간을 단축하는 체계적 진단 프레임워크입니다. 먼저 관찰된 증상에서 의심 계층을 좁히고, 해당 계층에 맞는 도구를 순서대로 적용합니다. "모든 것을 동시에 보는" 접근 대신, 1차 확인으로 가설을 세우고 2차 확인으로 검증하는 순서를 지키면 불필요한 수집 오버헤드를 줄일 수 있습니다.

증상별 계층 매핑

증상의심 계층1차 확인 명령2차 확인 명령
패킷 드롭L2/L3show errorsshow trace
지연시간 급증L3/L4show runtimeperfmon
연결 불가L4/TLSshow sessionshow tcp
처리량 저하드라이버/버퍼show buffersshow hardware-interfaces
CPU 100%노드show runtimeperf top

계층 × 도구 매트릭스

계층tracepcaperrorsperfmongdb
L1 (드라이버)dpdk-input / af-xdp-input 노드 대상RX 큐 통계 확인 후 pcap 보조rx-miss, no-mbuf 카운터 주목cache miss · CPI 측정DPDK PMD 브레이크포인트
L2 (이더넷)ethernet-input 노드 추적L2 프레임 덤프bad-checksum, unknown-etypeL2 노드 클럭 수ethernet_input 함수 진입점
L3 (IP)ip4-input · ip6-inputip4/ip6 필터로 캡처ip4-error, ttl-expiredFIB 룩업 사이클ip4_lookup_inline 중단점
L4 (TCP/UDP)tcp-input · udp-input포트 필터 pcaptcp-drop, udp-no-port세션 처리 클럭tcp_input 함수 상태 검사
TLStls-input · tls-output평문 레코드 캡처(복호화 전)tls-error, handshake-fail암복호화 사이클OpenSSL / Picotls 콜백
버퍼버퍼 alloc/free 노드해당 없음buffer-alloc-fail 카운터buffer pool 점유율vlib_buffer_alloc 내부 추적

L4 세션 디버깅

VPP의 호스트 스택(Host Stack)은 TCP/UDP 세션을 독립 테이블로 관리합니다. 세션 상태 이상, 타임아웃 불일치, 테이블 누수는 연결 문제의 주요 원인이며, 아래 명령으로 계층적으로 진단할 수 있습니다.

show session 출력 해석

# 활성 세션 목록 조회
vpp# show session

Session table for fib 0
  proto  lcl           rmt           state          index
  TCP    10.0.0.1:80   192.168.1.100:54321  ESTABLISHED  3
  TCP    10.0.0.1:80   192.168.1.101:44210  SYN_RCVD     7

주요 필드 설명:

# 상세 정보 — FIFO 포인터, 세그먼트 크기, 이벤트 큐 포함
vpp# show session verbose

Session 3 (TCP 10.0.0.1:80 <-> 192.168.1.100:54321) ESTABLISHED
  Rx FIFO:  size 65536  tail 0x00001A00  head 0x00001800  deq-notif 1
  Tx FIFO:  size 65536  tail 0x00004000  head 0x00004000  deq-notif 0
  tx-fifo-size: 65536  rx-fifo-size: 65536
  app-index: 2  thread-index: 1

TCP 세션 상세: 재전송과 SACK

# TCP 세션 통계 — 재전송 카운터, SACK 상태 확인
vpp# show tcp session index 3

Connection 3: state ESTABLISHED
  Local  10.0.0.1:80  Remote 192.168.1.100:54321
  snd_una 0x12345678  snd_nxt 0x12346000  snd_wnd 65535
  rcv_nxt 0xABCD0000  rcv_wnd 65535
  retransmit count: 4           <-- 재전송 횟수; 임계값 초과 시 경로 품질 의심
  SACK enabled: yes
  SACK blocks: [0xABCD0100-0xABCD0200]  <-- 수신측 hole 위치
  RTO: 204ms  SRTT: 12ms  RTTVAR: 8ms

retransmit count가 단시간에 급증하면 경로 손실 또는 수신 버퍼 포화를 의심합니다. SACK blocks가 지속적으로 쌓이면 중간 노드의 재정렬(reorder) 또는 선택적 드롭을 확인합니다.

세션 타임아웃 디버깅

# 현재 타임아웃 설정 확인
vpp# show session timeout

  close-idle:         10.0 sec
  transport-close:     5.0 sec
  established:       600.0 sec
  half-open:          10.0 sec
  timed-wait:         60.0 sec
# 타임아웃 값 조정 — 서비스 특성에 맞게 변경
# established 타임아웃 단축 (유휴 연결 빠르게 회수)
vpp# set session timeout established 120

# half-open 타임아웃 단축 (SYN flooding 방어)
vpp# set session timeout half-open 5

# 변경 후 즉시 확인
vpp# show session timeout
타임아웃 튜닝 가이드라인: established 값을 지나치게 줄이면 장기 유휴 연결(HTTP keep-alive, DB 커넥션 풀)이 예기치 않게 종료됩니다. 변경 전에 show session count로 현재 세션 수를 기록해 기준선을 확보하십시오.

세션 테이블 누수 감지

# 세션 수 시계열 관찰 패턴
# 1단계: 기준선 기록
vpp# show session count

Session counts by state:
  ESTABLISHED:   142
  CLOSED:          3
  TIME-WAIT:      18
  Total:         163
# 2단계: 트래픽 재현 또는 5분 대기 후 재측정
vpp# show session count

  ESTABLISHED:   145
  CLOSED:         87    <-- CLOSED가 증가했지만 회수되지 않으면 누수 의심
  TIME-WAIT:     231    <-- TIME-WAIT 급증은 close_idle 타임아웃 확인 필요
  Total:         463

누수 판정 패턴: