물리 서버의 새 연결 실패, conntrack 포화가 원인일까?

물리 서버의 새 연결 실패, conntrack 포화가 원인일까?

물리 서버의 새 연결 실패, conntrack 포화가 원인일까?

서비스 프로세스는 정상이고 기존 장기 연결에서도 데이터가 오가는데, 새 요청만 간헐적으로 시간 초과가 발생한다면 원인이 반드시 애플리케이션의 리슨 큐에 있는 것은 아닙니다. 물리 서버가 상태 기반 방화벽이나 네트워크 주소 변환(NAT)을 담당할 때는 Linux의 연결 추적 테이블이 병목이 될 수도 있습니다. 진단의 핵심은 실패 시점, 테이블 사용량, 커널 카운터를 같은 시간대에 맞춰 확인하는 것입니다. 연결 수가 많다는 이유만으로 상한부터 높여서는 안 됩니다.

이 글에서는 Debian 13을 기준으로 설명하며, 모든 점검은 호스트의 네트워크 네임스페이스에서 수행합니다. CloudUDP 서버는 결제 후 콘솔에서 접속 정보를 확인하고, Linux에는 SSH로 접속합니다. 주소가 여러 개라면 첫 번째 주소를 사용합니다. 아래 작업은 네트워크 규칙을 변경하거나 기존 연결 상태를 삭제하지 않습니다.

1. 문제가 발생한 트래픽이 연결 추적 경로를 거치는지 확인

연결 추적(conntrack)은 통신 흐름의 프로토콜, 주소, 상태, 시간 제한을 기록해 상태 매칭과 NAT에 활용합니다. 서비스 연결 하나가 애플리케이션 스레드 하나를 뜻하는 것은 아니며, 애플리케이션이 연결을 닫은 뒤에도 테이블 항목이 한동안 남을 수 있습니다. 짧은 연결의 대량 생성, 비정상적인 스캔, 트래픽 포워딩 부하 모두 테이블 사용량을 높일 수 있습니다.

먼저 점검 도구를 설치하고 설정을 읽습니다.

sudo apt-get update
sudo apt-get install conntrack
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
sudo conntrack -S
sudo nft list ruleset

규칙에 ct state나 NAT가 있다면 연결 추적 용량을 살펴볼 필요가 있다는 단서입니다. 다만 이런 규칙이 없다고 해서 연결 추적이 전혀 이루어지지 않는다고 단정할 수는 없습니다. 파라미터를 읽을 때 존재하지 않는다는 메시지가 나오면 먼저 모듈과 실제 트래픽 경로를 확인해야 합니다. 단지 “출력을 채우기 위해” 무작정 모듈을 로드하지 마세요.

컨테이너 내부에서 얻은 결과를 호스트의 결과로 대신할 수는 없습니다. 호스트의 포워딩과 컨테이너 내부 통신은 서로 다른 네임스페이스를 거칠 수 있으므로, 명령을 어디에서 실행했는지 반드시 기록해야 합니다. 그렇지 않으면 컨테이너의 낮은 사용량을 근거로 호스트의 패킷 손실을 잘못 해석하기 쉽습니다.

일반적인 복구 방법으로 conntrack -F를 실행하지 마세요. 테이블을 비우면 기존 상태가 삭제되어 상태 매칭이나 NAT에 의존하는 세션이 끊길 수 있습니다. 보존해야 할 장애 당시의 증거도 사라집니다.

2. 같은 시간대의 사용량과 오류 증가분 수집

nf_conntrack_count는 현재 테이블 항목 수이고, nf_conntrack_max는 용량 상한입니다. 사용량이 상한에 가까우면 여유가 줄었다는 뜻이지만, 이것만으로 요청 실패의 원인을 입증할 수는 없습니다. conntrack -S는 프로세서별 통계를 출력하므로, 각 항목을 전체 프로세서에 걸쳐 합산한 뒤 두 번의 측정값 차이를 비교해야 합니다. 출력의 한 행을 서버 전체의 합계로 보지 마세요.

umask 077
out=$(mktemp -d /var/tmp/ct-check.XXXXXX)
for sample in 1 2; do
  {
    date -Is
    sysctl net.netfilter.nf_conntrack_count
    sysctl net.netfilter.nf_conntrack_max
    sudo conntrack -S
  } > "$out/sample-$sample.txt"
  if [ "$sample" -eq 1 ]; then
    sleep 10
  fi
done
sudo journalctl -k --since "-10 min" --no-pager \
  > "$out/kernel.txt"
printf '%s\n' "$out"

여기서 사용한 측정 간격은 작업 예시일 뿐입니다. 실제로는 서비스 실패가 발생한 시간대를 포함하도록 측정해야 합니다. insert_failed, drop, early_drop이 계속 증가하는지, 커널 로그에 연결 추적 테이블이 가득 찼다는 메시지가 있는지 중점적으로 확인합니다. 필드는 커널 버전에 따라 다를 수 있으므로, 특정 필드가 없다는 이유만으로 시스템이 정상이라고 판단해서는 안 됩니다.

애플리케이션의 새 연결 실패 수, 요청량, 오류 메시지 원문도 함께 저장합니다. 서버를 방금 재부팅했다면 누적 카운터의 기준이 달라졌으므로 재부팅 전후의 값을 그대로 빼서는 안 됩니다. 한산한 시간대에 오류 카운터가 더 이상 증가하지 않는다고 해서 피크 시간대의 용량 검증까지 통과한 것은 아닙니다.

3. 테이블 항목이 쌓이는 이유 파악

먼저 “정상적인 동시 연결 증가”와 “비정상적인 생성 속도”를 구분합니다. 유입 요청, 애플리케이션이 직접 여는 외부 연결, 포워딩 트래픽, 연결 재사용 상태를 함께 대조하세요. 짧은 연결은 유지 시간이 짧더라도 생성 속도가 높으면 항목이 쌓일 수 있습니다. 장기 연결은 실제 동시 연결 규모의 영향을 더 크게 받습니다.

전체 테이블 덤프부터 시작하지 않기

부하가 높은 호스트에서는 모든 테이블 항목을 나열하는 작업 자체가 진단 부담을 늘릴 수 있으며, 출력에는 통신 주소도 포함됩니다. 먼저 집계 카운터를 읽고, 꼭 필요한 경우에만 프로토콜과 출력 규모를 제한합니다.

sudo timeout 5s conntrack -L -p tcp -o extended 2>/dev/null \
  | head -n 100

이 출력은 편향된 표본일 뿐, 상태 분포를 보여 주는 통계가 아닙니다. 시간 초과나 파이프의 조기 종료로 명령이 0이 아닌 종료 코드를 반환할 수도 있으므로, 이를 근거로 테이블이 손상되었다고 판단해서는 안 됩니다. 주소가 포함된 기록은 접근이 제한된 디렉터리에만 보관하고, 지원 티켓에 첨부하기 전에 관련 없는 서비스 정보를 제거하세요.

리스닝 프로세스, 애플리케이션 오류, 업스트림 응답도 동시에 확인해야 합니다. 테이블 용량에 여유가 있고 관련 카운터도 증가하지 않는다면 애플리케이션의 블로킹, 라우팅, 다른 패킷 손실 지점으로 조사 범위를 옮겨야 합니다. conntrack 파라미터 조정에만 매달리지 마세요.

모든 프로토콜의 시간 제한부터 줄이지 마세요. 시간 제한은 유휴 연결 상태를 얼마나 오래 유지할지 결정하며, 지나치게 줄이면 장기 연결에 영향을 줄 수 있습니다. 연결 풀을 재사용하지 않거나 실패 후 재시도가 통제되지 않는 문제는 호출 측에서 수정해야 합니다.

4. 메모리 예산에 맞춰 되돌릴 수 있는 변경 적용

상한을 높이는 것은 더 많은 테이블 항목을 허용할 뿐, 비정상 트래픽의 발생 원인을 해결하지는 않습니다. 테이블 항목은 커널 메모리를 사용하며, 구체적인 사용량은 프로토콜 확장과 커널 설정 등에 따라 달라집니다. 따라서 고정된 “항목당 몇 바이트” 값으로 정확한 메모리 예산을 계산할 수는 없습니다.

변경 전에는 free -h와 /proc/meminfo의 내용을 기록하고, sudo slabtop -o로 커널 객체 캐시를 살펴봅니다. Slab은 커널이 객체를 관리하는 데 사용하는 캐시 메커니즘입니다. 그 안의 연결 추적 객체 통계 역시 관련 메모리 비용 전체를 나타내지는 않습니다.

증거가 용량 부족을 뒷받침하고 메모리에도 여유가 있다면, 소폭 조정해 효과를 검증할 수 있습니다. 아래 예시는 기존 상한을 4분의 1만큼 높입니다. 이는 시험을 위한 변경 폭일 뿐, 고정된 권장 설정이 아닙니다.

old=$(sysctl -n net.netfilter.nf_conntrack_max) || exit 1
case "$old" in
  ''|*[!0-9]*) exit 1 ;;
esac
new=$((old + old / 4))
printf 'old=%s new=%s\n' "$old" "$new"
sudo sysctl -w net.netfilter.nf_conntrack_max="$new"

현재 세션과 원래 값을 유지한 상태에서, 비슷한 요청량으로 다시 측정합니다. 실패가 개선되지 않는다면 파라미터를 계속 높이지 말고 원래 값으로 되돌린 뒤 다른 증거를 확인합니다.

sudo sysctl -w net.netfilter.nf_conntrack_max="$old"

되돌리기 전에 현재 사용량을 확인하세요. 이미 원래 상한을 넘었다면 먼저 새 연결의 유입 부담을 낮춰야 합니다. 그렇지 않으면 원복 과정에서 용량 문제가 다시 발생할 수 있습니다. 해시 버킷, 프로토콜 시간 제한, 방화벽 규칙을 동시에 바꾸지 마세요. 한 번에 하나의 변수만 바꿔야 효과를 검증할 수 있습니다.

5. 서비스 결과를 기준으로 영구 적용 여부 결정

검증을 통과하려면 세 가지 증거를 동시에 충족해야 합니다. 비슷한 서비스 부하에서 새 연결 성공률이 회복되고, 관련 오류 카운터가 더 이상 지속적으로 증가하지 않으며, 메모리와 커널 객체 캐시에 새로운 압박이 없어야 합니다. 사용률만 낮아졌다고 해서 문제가 해결된 것은 아닙니다.

효과를 확인한 뒤 검증된 숫자를 /etc/sysctl.d/90-conntrack-capacity.conf에 기록합니다. 기록하기 전에 다른 설정 파일에서 같은 파라미터를 중복 정의하고 있는지 확인하여, 로드 순서에 따라 값이 덮어써지지 않도록 합니다. 이번 파일만 로드하고, 서버의 다른 미검증 설정까지 함께 적용하지 마세요.

sudo sysctl -p /etc/sysctl.d/90-conntrack-capacity.conf
sysctl net.netfilter.nf_conntrack_max

파일에는 net.netfilter.nf_conntrack_max = 검증된 정수 한 줄을 넣어야 하며, 설명 문구는 반드시 실제 숫자로 바꿔야 합니다. 운영 담당자가 계획한 다음 재부팅 후에 적용값과 로드 오류를 다시 확인하세요. 파라미터 검증만을 위해 운영 서버를 즉시 재부팅하지 마세요.

마지막으로 원래 값, 새 값, 측정 구간, 요청량, 카운터 차이, 원복 조건을 보관합니다. 연결 추적 용량 관리의 목표는 더 큰 숫자가 아닙니다. 연결 실패를 설명하고, 변경이 효과적이었다는 것을 입증하며, 부하가 달라진 뒤에도 같은 증거를 다시 확인할 수 있어야 합니다.

자주 묻는 질문

conntrack 사용량이 한도에 가까우면 포화라고 판단해도 될까요?

그렇지 않습니다. 같은 시간대의 insert_failed, drop, early_drop 증가분을 커널 로그 및 새 연결 오류와 대조해야 합니다. 높은 사용량은 용량 위험 신호이며, 개별 카운터만으로도 원인을 확정할 수 없습니다.

빠른 복구를 위해 conntrack 테이블을 비워도 될까요?

일반적인 복구 수단으로 사용하면 안 됩니다. 기존 연결의 추적 상태가 삭제되어 상태 기반 규칙이나 NAT에 의존하는 세션이 중단될 수 있습니다. 먼저 카운터와 로그를 보존한 뒤 비정상 연결 발생원을 처리하거나 메모리 예산에 맞춰 용량을 조정해야 합니다.

EPYC 7543P 단독 사용 물리 서버, 월 $454부터

물리 서버 월 단위 임대

결제 전에 구성 항목과 이번 결제 금액을 확인할 수 있습니다. 모든 가격은 미국 달러로 결제됩니다.