물리 서버 수신 패킷 손실을 NIC·softirq·프로토콜 스택으로 구분하기

물리 서버 수신 패킷 손실을 NIC·softirq·프로토콜 스택으로 구분하기

업무 요청이 간헐적으로 시간 초과되고 인터페이스의 수신 드롭 카운터도 증가한다고 해서 NIC가 고장 났다고 단정할 수는 없습니다. 물리 서버의 수신 경로는 NIC 큐, 드라이버, 소프트웨어 인터럽트(softirq), 프로토콜 스택으로 구성되며, 같은 증상이 서로 다른 계층에서 발생할 수 있습니다. 원인을 조사할 때는 업무 오류와 같은 시간 구간의 카운터 증가량을 대조해야 합니다. dropped가 보인다는 이유만으로 모든 버퍼를 늘려서는 안 됩니다.

이 글은 Debian 12를 기준으로 하며, 관리자 권한과 iproute2, ethtool, sysstat, python3 도구가 필요합니다. AmdMetal 물리 서버는 결제 후 콘솔에서 접속 정보를 확인할 수 있으며, SSH 접속에는 첫 번째 IPv4 주소를 사용합니다. 여기서는 먼저 읽기 전용으로 데이터를 수집하며, 주소나 라우팅은 변경하지 않습니다.

1. 수신 인터페이스와 장애 발생 구간 확인

먼저 ip -br address로 업무용 주소가 설정된 인터페이스를 확인한 다음, VLAN, 브리지 또는 본딩 장치를 따라가며 실제 트래픽을 처리하는 물리 인터페이스를 찾습니다. 이름순으로 맨 앞에 있는 NIC를 바로 선택해서는 안 됩니다. 아래 명령은 대화형 입력으로 인터페이스를 지정하며, 수집 디렉터리는 자동으로 생성합니다.

read -r -p "Physical interface: " IFACE
ip link show dev "$IFACE" || exit 1
D=$(mktemp -d /var/tmp/rx-audit.XXXXXX)
printf '%s\n' "$IFACE" > "$D/interface"
uname -r > "$D/kernel"
ethtool -i "$IFACE" > "$D/driver"
ip -d link show dev "$IFACE" > "$D/link"
printf '%s\n' "$D"

업무 오류가 발생한 시각, 요청량, 패킷 크기, 대량 가져오기 작업의 유무를 기록합니다. 인터페이스 누적 카운터에는 오랜 가동 시간 동안의 값이 쌓여 있을 수 있으므로, 방금 발생한 한 번의 시간 초과와 곧바로 인과관계를 연결해서는 안 됩니다. 드라이버 재로드, 재부팅, 인터페이스 재생성으로 카운터가 초기화될 수도 있습니다.

NIC 통계, 인터페이스 통계, 프로토콜 스택 통계는 서로 독립적이어서 그대로 합산할 수 있는 수치가 아닙니다. 일부 필드는 같은 이벤트를 나타낼 수 있으므로, 먼저 드라이버의 정의를 확인한 뒤 증가량을 해석해야 합니다.

애플리케이션이 네트워크 네임스페이스 안에서 실행된다면, 물리 인터페이스에서는 호스트의 수신 경로를 관찰하고 프로토콜 스택 통계는 애플리케이션이 속한 네임스페이스에서도 수집해야 합니다. 호스트에서 오류가 보이지 않는다고 컨테이너 내부까지 정상이라는 뜻은 아닙니다.

2. 인터페이스 통계와 드라이버 통계 함께 저장

같은 셸에서 이어서 실행합니다. 예제의 관찰 구간은 30초이며, 이는 수집 매개변수일 뿐 서비스에 대한 약속이 아닙니다. 실제 문제가 발생하는 업무 구간을 선택하고, 정상 구간에서도 같은 길이로 대조 데이터를 한 세트 수집합니다.

snapshot() {
    label=$1
    date -Is > "$D/$label.time"
    ip -s -s link show dev "$IFACE" > "$D/$label.link"
    ethtool -S "$IFACE" > "$D/$label.nic" 2>&1
    cat /proc/net/softnet_stat > "$D/$label.softnet"
    nstat -az > "$D/$label.protocol"
}
snapshot before
mpstat -P ALL 1 30 > "$D/cpu"
snapshot after
diff -u "$D/before.nic" "$D/after.nic"

각 읽기 작업은 순차적으로 실행되므로 원자적 스냅샷은 아닙니다. 짧은 버스트가 발생하는 상황에서는 타임스탬프를 함께 보고 해석해야 하며, 서로 다른 파일의 값이 패킷 단위까지 일치할 것으로 기대해서는 안 됩니다. ethtool -S가 지원되지 않는다는 결과를 반환하면 그 결과도 보관합니다. “통계 없음”을 “패킷 손실 없음”으로 기록해서는 안 됩니다.

먼저 카운터의 의미 확인

이름에 CRC나 frame이 포함된 오류 카운터가 증가한다면 링크 계층의 증거를 확인해야 합니다. missed, no_buffer 또는 discard가 포함된 필드는 현재 드라이버에서 어떻게 정의하는지 확인해야 합니다. 이러한 필드는 수신 디스크립터, 장치 버퍼 또는 필터링 동작과 관련될 수 있으므로, 영문 이름만 보고 원인을 판단해서는 안 됩니다.

ethtool -g "$IFACE"와 ethtool -l "$IFACE"의 결과도 함께 저장해 각각 링 큐와 채널 정보를 확인합니다. 이 조회를 지원하지 않는다고 해서 장애라는 뜻은 아닙니다. 최댓값은 장치가 지원하는 한계일 뿐, 운영 환경의 권장값이 아닙니다.

3. CPU별 softnet 증가량 계산

/proc/net/softnet_stat은 16진수를 사용하며, 처음 세 열은 차례로 processed, dropped, time_squeeze입니다. 이후 열은 커널에 따라 달라질 수 있으므로 여기서는 해석하지 않습니다. 아래 코드는 행 단위로 비교하며, 출력되는 번호는 수집된 데이터의 행 번호입니다. CPU 핫플러그가 발생했다면 다시 수집해야 하며, 기존 행을 그대로 대응시켜서는 안 됩니다.

python3 - "$D/before.softnet" "$D/after.softnet" <<'PY'
import sys
from pathlib import Path

def load(path):
    return [
        [int(v, 16) for v in line.split()[:3]]
        for line in Path(path).read_text().splitlines()
    ]

before, after = map(load, sys.argv[1:])
if len(before) != len(after):
    raise SystemExit("CPU layout changed; sample again")

print("row processed_delta dropped_delta time_squeeze_delta")
for row, (old, new) in enumerate(zip(before, after)):
    delta = [b - a for a, b in zip(old, new)]
    if any(v < 0 for v in delta):
        raise SystemExit("Counter reset or wrap; sample again")
    print(row, *delta)
PY

여기서 dropped 증가는 소프트웨어 수신 백로그 큐에서 드롭이 발생했음을 나타냅니다. time_squeeze 증가는 처리 사이클이 예산 한도에 도달했음을 의미하며, 이미 패킷 손실이 발생했다는 뜻은 아닙니다. softnet 통계는 CPU별로 집계되며 특정 NIC만의 통계가 아니므로, 같은 시간대에 다른 인터페이스를 통과한 트래픽의 영향을 배제해야 합니다.

증가량을 mpstat의 %soft와 대조합니다. 서버 전체 CPU에 여유가 있어도 특정 코어에는 수신 처리 부하가 집중될 수 있으므로 평균 사용률만 봐서는 안 됩니다. 필요하다면 /proc/interrupts를 저장해 NIC 큐의 인터럽트가 특정 CPU에 뚜렷하게 집중되는지 확인합니다. 인터럽트 분포는 단서일 뿐, 반드시 균등해야 하는 검증 기준은 아닙니다.

4. 증거를 바탕으로 조정할 계층 선택

NIC의 missed 계열 카운터는 증가하지만 softnet dropped는 변하지 않는다면, 수신 링과 드라이버 큐를 우선 점검합니다. 반대로 softnet dropped가 증가하면서 특정 코어의 부하가 높아진다면 소프트웨어 백로그와 트래픽 분산을 확인해야 합니다. 둘 다 안정적이라면 nstat의 프로토콜 오류 증가량과 애플리케이션 로그를 비교하고, 근거 없이 NIC 조정을 계속하지 않습니다.

조정하기 전에 현재 설정을 저장합니다.

ethtool -g "$IFACE" > "$D/rings" 2>&1
ethtool -l "$IFACE" > "$D/channels" 2>&1
sysctl net.core.netdev_max_backlog \
       net.core.netdev_budget \
       net.core.netdev_budget_usecs > "$D/budgets"

링 용량 변경은 드라이버가 지원하고 이를 뒷받침하는 증거가 있을 때만 검토합니다. “최댓값으로 설정”을 보편적인 해결책으로 삼아서는 안 됩니다. 백로그 큐를 늘리면 드롭을 줄이는 대신 대기 지연이 늘어날 수 있으며, 처리 예산을 높이면 애플리케이션 실행 시간을 잠식할 수 있습니다. 멀티큐 분산을 조정할 때는 IRQ, RSS, 업무 트래픽의 플로 수를 함께 고려해야 합니다. 조정한다고 해서 단일 플로가 저절로 모든 코어에 분산되는 것도 아닙니다.

원격 환경에서는 채널 재구성, 드라이버 재로드, 링크 리셋을 바로 시도해서는 안 됩니다. 이러한 작업은 SSH 연결에 영향을 줄 수 있으므로, 먼저 통제된 작업 계획과 기존 값 기록, 복구 경로를 준비해야 합니다. 한 번에 한 항목만 변경하고, 이상이 생기면 먼저 원래 값으로 복원합니다.

5. 업무 부하로 재시험하여 검증 완료

재시험에서는 요청 유형, 지속 시간, 동시 요청 방식을 서로 비교할 수 있게 유지하고 실제 트래픽도 함께 기록합니다. 두 구간의 절대 드롭 수만 비교하면 부하가 줄어든 것을 최적화 성공으로 오인할 수 있습니다. 처리량이 같더라도 작은 패킷의 비중이 높아지면 수신 처리 부담은 달라질 수 있습니다.

검증에는 최소한 다음 세 가지 결과가 포함되어야 합니다.

하위 계층 카운터가 안정적인데도 업무 오류가 계속된다면, 결론은 “이번 관찰 구간에서는 해당 수신 드롭의 증거를 찾지 못했다”로 한정해야 합니다. 프로토콜 스택과 애플리케이션 처리를 계속 조사하되, 네트워크가 완전히 정상이라고 단정해서는 안 됩니다. 인터페이스, 드라이버, 커널 정보와 수집 파일, 변경 전 값을 보관해야 다음에 같은 증상이 나타났을 때 인과관계를 다시 검토할 수 있습니다.

자주 묻는 질문

ip -s link의 dropped 증가가 NIC 고장을 의미하나요?

아닙니다. 인터페이스 통계는 진단 단서일 뿐입니다. 같은 구간의 드라이버 통계, 링크 오류, 업무 증상을 함께 확인해야 하며 이름에 drop이 들어간 항목을 모두 더하면 안 됩니다.

softnet의 time_squeeze 증가도 패킷 손실인가요?

그 자체로 손실을 뜻하지 않습니다. 수신 처리 회차가 예산 한계에 도달했다는 신호이므로 dropped 증가량, 코어별 softirq 부하, 업무 오류를 함께 확인해야 합니다.

AMD EPYC 7543P 전용 물리 서버

월 $458.7부터

AmdMetal 물리 서버 구성 AmdMetal 이용 요금 확인

물리 서버 사양을 하나씩 확인하세요

AMD EPYC 7543P 전체 서버를 단독으로 사용하며, 서버 사양과 추가 옵션은 구성 페이지에서 함께 확인할 수 있습니다.