conntrack 고갈을 진단하고 연결 추적 테이블의 용량 변경 검증하기
서비스에서 간헐적으로 연결 시간 초과가 발생하지만 기존 세션은 계속 데이터를 전송하고, 애플리케이션 프로세스와 리슨 상태도 정상이라면 Linux의 연결 추적 테이블을 확인해야 한다. 상태 기반 방화벽, 주소 변환, 일부 컨테이너 네트워크는 conntrack에 의존한다. 테이블 항목을 생성할 수 없으면 새 트래픽이 애플리케이션에 도달하기 전에 드롭될 수 있다. 이 글에서는 Debian 12를 기준으로 증거 수집, 원인 분석, 용량 조정, 원복 순서로 작업을 진행한다. 방화벽 규칙을 변경하거나 기존 연결을 비우지는 않는다.
1. 문제가 발생한 네트워크 네임스페이스부터 확인하기
CloudDDR 물리 서버는 AMD EPYC 7543P와 128GB 또는 256GB ECC 메모리를 제공하지만, 메모리 용량이 conntrack 한도를 직접 결정하지는 않는다. 확인할 대상은 시스템 커널, 네트워크 네임스페이스, 서비스 트래픽이다. 결제 후 콘솔에서 접속 자격 증명을 확인하고 SSH로 접속한다. 주소가 여러 개라면 첫 번째 주소를 사용한다.
실제로 트래픽을 처리하는 네트워크 네임스페이스에서 명령을 실행한다. 호스트의 결과가 게스트 시스템의 상태를 나타내는 것은 아니다. 컨테이너 트래픽이 호스트의 주소 변환을 거친다면 호스트도 함께 확인해야 한다. 먼저 커널 버전, 현재 네트워크 네임스페이스, 테이블 항목 수와 한도를 기록한다.
uname -r
readlink /proc/self/ns/net
sudo sysctl net.netfilter.nf_conntrack_count
sudo sysctl net.netfilter.nf_conntrack_max
sudo sysctl net.netfilter.nf_conntrack_buckets
키가 없다면 현재 네임스페이스에서 해당 매개변수를 제공하지 않는다는 뜻이며, 이를 곧바로 고갈로 판단해서는 안 된다. 관련 모듈이 로드되어 있는지, 서비스에서 연결 추적을 사용하는지부터 확인한다. 단지 “출력 항목을 채우기 위해” 장애 중에 새로운 기능을 임시로 활성화하지 않는다.
conntrack -F는 실행하지 않는다. 테이블을 비우면 추적 상태가 삭제되어 기존 세션과 주소 변환 매핑이 손상될 수 있으며, 원인 분석에 필요한 현장 정보도 사라진다.
2. 카운터 증가분으로 증거 연결하기
사용자 공간 도구를 설치해도 기존 규칙은 변경되지 않지만, 설치는 기존 변경 관리 절차를 따라야 한다. 다음 작업은 현장 상태를 읽고, 시간 간격을 둔 두 표본을 비교해 카운터가 계속 증가하는지 확인한다.
sudo apt-get install conntrack
umask 077
sudo conntrack -S > /tmp/conntrack-before.txt
sudo journalctl -k --since "-10 min" --no-pager \
| grep -Ei 'nf_conntrack|table full'
sleep 30
sudo conntrack -S > /tmp/conntrack-after.txt
diff -u /tmp/conntrack-before.txt /tmp/conntrack-after.txt
conntrack -S는 보통 CPU별 누적 카운터를 출력한다. 같은 CPU의 행끼리 비교하면서 insert_failed, drop, early_drop의 증가분을 살펴봐야 한다. 부팅 이후의 누적값을 현재 장애의 증거로 간주해서는 안 된다. 카운터의 의미와 출력 필드는 커널에 따라 달라질 수 있으며, 삽입 실패가 모두 용량 부족 때문인 것도 아니다.
서비스의 시간 초과 건수, 테이블 사용량, 커널 로그도 함께 기록한다. 사용량이 한도에 근접하고 실패 카운터가 증가하며 서비스 오류도 같은 시점에 발생해야 비교적 완전한 증거가 된다. 일치하는 로그가 없어도 문제를 배제할 수는 없다. 로그 출력에 속도 제한이 적용되었을 수 있기 때문이다. 표본 수집 간격 사이의 짧은 피크는 놓칠 수 있으므로 필요하면 관찰 간격을 줄이되, 전체 테이블을 반복해서 덤프하지 않는다.
3. 연결 수와 연결 생성 속도 구분하기
표본으로 테이블 항목이 쌓이는 이유 파악하기
테이블 항목 수는 현재 요청 수와 같지 않다. TCP 연결 하나로 여러 요청을 처리할 수 있고, UDP도 추적 항목을 사용한다. 통신이 끝난 뒤에도 해당 타임아웃이 만료될 때까지 항목이 남아 있을 수 있다. 따라서 장기간 안정적으로 유지되는 연결, 단기 연결의 급증, 비정상적인 트래픽 출처를 구분해야 한다.
고부하 상태에서 전체 연결 테이블을 반복 출력하지 않도록, 범위를 제한한 표본을 한 번 수집할 수 있다.
sudo timeout 5s conntrack -L -o extended 2>/tmp/conntrack-sample.err \
| head -n 200 > /tmp/conntrack-sample.txt
이 명령은 제한된 출력만 가져오므로 조기에 종료되어 0이 아닌 종료 상태를 반환할 수 있다. 이 표본은 전체 통계가 아니다. 파일에는 주소 정보가 들어 있으므로 읽기 권한을 제한하고, 지원 티켓에 첨부할 때는 필요에 따라 민감 정보를 가린다. 출력에는 원래 방향과 응답 방향의 튜플이 모두 포함된다. 트래픽 출처를 분석할 때 두 주소 묶음을 섞어서 집계하지 않는다.
프로토콜, TCP 상태, 남은 타임아웃, 원래 방향의 출발지를 확인한다. 단기 연결이 많다면 애플리케이션 연결 풀과 재시도 로그를 함께 살펴본다. 소수의 출발지가 계속 새 흐름을 생성한다면 접근 기록과 대조해 판단한다. UDP 항목이 장기간 누적된다면 실제 통신 주기를 확인해야 한다. 상태 이름 하나만 보고 공격으로 단정하거나, 트래픽 이상을 단순한 용량 문제로 취급해서는 안 된다.
연결 생성 속도가 줄지 않으면 테이블을 늘려도 다시 한도에 도달하는 시점만 늦출 뿐이다. 무제한 재시도 수정, 연결 재사용 복구, 비정상적인 트래픽 출처 대응은 대개 용량 조정과 별도로 검증해야 한다.
4. 메모리를 평가한 뒤 변수 하나만 조정하기
한도는 사전 할당 메모리도, 처리량 보장도 아니다
nf_conntrack_max를 높인다고 모든 항목이 즉시 생성되는 것은 아니다. 다만 테이블이 계속 채워지면 커널 메모리가 소모된다. free -h, /proc/meminfo, sudo slabtop -o로 메모리와 slab을 관찰한다. 이때 캐시 객체 크기만으로는 확장 필드와 할당 오버헤드 등을 포함한 전체 비용을 파악할 수 없으므로, 고정된 “연결당 바이트 수” 공식은 적용하지 않는 편이 좋다.
nf_conntrack_buckets는 해시 버킷 수이지 연결 용량이 아니다. 한도만 늘리면 버킷 체인이 길어지고 조회 비용이 증가할 수 있으므로 CPU와 서비스 지연도 함께 관찰해야 한다. 버킷 수를 실행 중에 변경할 수 있는지는 커널과 네임스페이스의 제약을 받는다. 이번 작업에서는 함께 변경하지 않는다.
다음은 목표 한도를 262144로 임시 변경하는 예시일 뿐이다. 실행 전에 이 값이 현재 값보다 크고, 메모리 예산 내에서 수용 가능하며, 별도의 관리 세션을 사용할 수 있는지 반드시 확인해야 한다. 모든 서버에 적용할 수 있는 권장값은 아니다. 명령은 관리 권한이 있는 shell에서 실행한다.
sudo -i
OLD=$(sysctl -n net.netfilter.nf_conntrack_max)
test "$OLD" -lt 262144 || exit 1
umask 077
printf '%s\n' "$OLD" > /run/conntrack-max.before
sysctl -w net.netfilter.nf_conntrack_max=262144
sysctl -n net.netfilter.nf_conntrack_max
원래 값을 보관한 뒤 동일한 서비스 점검과 카운터 표본 수집을 반복한다. TCP established 상태의 타임아웃을 동시에 줄이지 않는다. 유휴 상태의 장기 연결도 서비스에서 계속 사용 중일 수 있으며, 타임아웃을 전역적으로 줄이면 새로운 연결 끊김 원인이 생길 수 있다.
5. 검증, 원복, 영구 적용을 분리하기
검증에는 최소한 원래 장애가 발생한 트래픽과 유휴 상태 이후 통신을 재개하는 장기 연결이 포함되어야 한다. 새 연결 성공률, 실패 카운터 증가분, 사용량 증가 추세, 메모리 여유, CPU, 서비스 지연을 확인한다. 한도가 늘어난 것만으로 문제가 해결되었다고 볼 수는 없다.
메모리 압박, CPU 상태 또는 지연이 악화되면 저장해 둔 값으로 원복한다.
sudo sysctl -w \
net.netfilter.nf_conntrack_max="$(sudo cat /run/conntrack-max.before)"
한도를 낮춰도 기존 항목은 즉시 삭제되지 않는다. 사용량이 원복한 한도보다 여전히 높다면 새 항목 생성이 계속 차단될 수 있다. 항목이 자연스럽게 제거될 때까지 기다리면서 트래픽 출처에도 대응해야 한다. 복구를 서두르려고 테이블을 비우지 않는다.
임시 조정이 검증을 통과한 뒤에는 /etc/sysctl.conf와 /etc/sysctl.d/에 같은 이름의 설정이 이미 있는지 확인한다. 여러 파일이 서로 설정을 덮어쓰지 않도록 하기 위해서다. 검증된 값을 버전 관리 대상인 전용 설정 파일에 기록한다. 예시는 다음과 같다.
net.netfilter.nf_conntrack_max = 262144
해당 파일만 로드하고 실제 값을 다시 확인한다. 장애 중에 관련 없는 매개변수를 한꺼번에 다시 로드하지 않는다. 이후 통제된 절차에 따라 재부팅할 때도 모듈 로드 후의 최종값을 검증해야 한다. 파일에 기록했다고 부팅 시 반드시 적용되는 것은 아니기 때문이다.
최종 기록에는 트래픽 출처, 변경 전후 카운터, 서비스 점검 결과, 메모리 관찰 내용, 원래 값, 원복 절차가 포함되어야 한다. 연결 추적 용량 관리가 완료되었다는 기준은 장애 증거를 설명할 수 있고 조정 결과를 재현할 수 있는지에 있다. 테이블 한도가 더 큰 숫자로 바뀌었는지가 기준은 아니다.
자주 묻는 질문
conntrack 테이블이 거의 찼다면 패킷 손실 원인으로 확정할 수 있는가?
확정할 수 없다. 같은 관측 구간의 insert_failed, drop 또는 early_drop 증가분을 커널 로그와 업무 오류에 대조해야 한다. 높은 사용량만으로는 용량 압박만 확인된다.
nf_conntrack_max를 높이면서 TCP 타임아웃도 줄여야 하는가?
함께 바꿀 필요는 없다. 프로토콜 타임아웃은 유지하고 메모리 예산에 맞는 한도부터 검증한다. 전역 TCP established 타임아웃을 줄이면 유휴 상태의 장기 연결이 끊길 수 있다.
AMD EPYC 7543P 단독 사용 물리 서버, 월 $447부터.