느린 성능 문제 해결: 'netstat'과 'ss' 효과적으로 사용하기

효율적인 성능 문제 해결을 위해 필수 리눅스 네트워킹 도구인 `netstat`과 `ss`를 마스터하세요. 이 가이드는 레거시 `netstat`과 현대적이고 빠른 `ss` 유틸리티를 비교하며, 실용적인 명령어 예제를 제공합니다. 연결 상태별 결과 필터링, 수신 대기 서비스 식별, Netlink 소켓 통계를 사용한 네트워크 병목 현상 진단 방법을 배웁니다.

느린 성능 문제 해결: 'netstat'과 'ss' 효과적으로 사용하기

리눅스 서비스가 느리게 느껴질 때, 소켓 테이블은 "앱이 과부하 상태인지"와 "네트워크 경로가 복잡한지"를 가장 빠르게 구분할 수 있는 곳 중 하나입니다. 새 연결을 수락할 수 없는 웹 서버, 데이터베이스 세션을 열지 못하는 워커, 반쯤 열린 TCP 핸드셰이크가 쌓인 호스트는 모두 상대방에게는 막연한 느림으로 보입니다.

netstatss는 더 좁은 질문에 답하는 데 도움을 줍니다: 이 머신에 현재 어떤 네트워크 소켓이 존재하며, 어떤 상태에 있고, 어떤 프로세스가 소유하고 있는가? netstat은 여전히 오래된 시스템과 오래된 런북에서 유용합니다. ss는 현대 리눅스에서 제가 가장 먼저 사용하는 도구인데, 바쁜 호스트에서 더 빠르고 내장 필터링이 더 좋기 때문입니다.

왜 네트워크 소켓을 모니터링해야 하나?

네트워크 지연과 느림은 CPU나 메모리 고갈보다는 연결 문제와 관련된 경우가 많습니다. 소켓 모니터링은 관리자가 다음과 같은 중요한 질문에 답하는 데 도움을 줍니다:

  • 어떤 포트가 연결을 적극적으로 수신 대기하고 있습니까?
  • SYN_RECV 또는 TIME_WAIT 상태에 갇힌 연결이 너무 많습니까?
  • 특정 포트를 사용하는 프로세스(PID)는 무엇입니까?
  • 예상치 못한 아웃바운드 연결이 발생하고 있습니까?

소켓 통계를 검토함으로써 네트워크 구성 문제를 신속히 배제하거나 연결 처리와 관련된 리소스 경합을 식별할 수 있습니다.

레거시 도구: netstat

netstat은 수십 년 동안 네트워크 연결, 라우팅 테이블, 인터페이스 통계, 마스커레이드 연결을 표시하는 표준 유틸리티였습니다. 많은 현대 시스템에서 ss로 대체되었지만, 여전히 널리 사용 가능하며 오랜 관리자에게 익숙합니다.

일반적인 netstat 예제

netstat과 함께 사용되는 가장 일반적인 플래그는 포괄적인 개요를 제공합니다:

플래그 설명
-a 모든 소켓 표시 (수신 및 비수신)
-n 호스트 이름 및 서비스 이름을 확인하지 않고 숫자 주소 표시 (출력 속도 향상)
-t TCP 연결 표시
-u UDP 연결 표시
-l 수신 소켓만 표시
-p 소켓과 연결된 PID/프로그램 이름 표시 (루트 권한 필요)

예제: 모든 활성 TCP 연결을 숫자로 보기

sudo netstat -ant

예제: 포트 80(HTTP)에서 수신 대기 중인 항목 찾기

sudo netstat -tulpen | grep ':80'

연결 상태 이해하기 (netstat)

netstat의 출력에는 종종 State 열이 포함됩니다. 주목해야 할 주요 상태는 다음과 같습니다:

  • LISTEN: 들어오는 연결을 기다리는 중.
  • ESTABLISHED: 활성화된 열린 연결.
  • TIME_WAIT: 지연된 패킷이 처리되도록 잠시 기다리는 소켓.
  • SYN_RECV: 3방향 핸드셰이크의 최종 확인을 기다리는 중 (과도할 경우 SYN 플러드 공격을 나타낼 수 있음).

netstat에 대한 경고: netstat은 종종 /proc/net/* 파일을 구문 분석하는 데 의존하며, 특히 활성 연결이 매우 많은 시스템(수천 개)에서는 느릴 수 있습니다. 이것이 ss가 개발된 주된 이유입니다.

현대적인 대체 도구: ss (소켓 통계)

ss 유틸리티는 netstat보다 훨씬 빠릅니다. Netlink 소켓을 사용하여 커널 공간에서 직접 소켓 정보를 검색하고, 느린 파일 시스템 조회를 우회하기 때문입니다.

일반적인 ss 예제

ss의 플래그 구조는 netstat과 매우 유사하여 쉬운 전환을 촉진합니다:

플래그 설명
-a 모든 소켓 표시
-n 숫자 주소 표시
-t TCP 소켓 표시
-u UDP 소켓 표시
-l 수신 소켓 표시
-p 프로세스 정보 표시 (PID/프로그램)

예제: 모든 활성 TCP 연결을 숫자로 보기 (netstat -ant와 동일)

ss -ant

예제: 포트 443(HTTPS)에서 수신 대기 중인 항목 찾기

sudo ss -tulpen | grep ':443'

ss를 사용한 고급 필터링

ss의 가장 큰 장점 중 하나는 연결 상태에 대한 직접 필터링을 수행할 수 있다는 점으로, netstat 출력을 grep으로 파이핑하는 것보다 훨씬 효율적입니다.

연결 상태별 필터링

ss 명령 내에서 직접 state 옵션을 사용할 수 있습니다. 이는 연결 축적을 진단하는 데 매우 유용합니다.

현재 TIME-WAIT 상태인 모든 소켓 찾기:

ss -tan state time-wait

SYN-SENT 상태인 모든 소켓 찾기 (서버 응답을 기다리는 클라이언트 측):

ss -tan state syn-sent

포트 또는 주소별 필터링

대상 또는 소스 주소/포트별 필터링은 간단합니다:

포트 22(SSH)로 향하는 설정된 연결 표시:

ss -tn state established '( dport = :22 or sport = :22 )'

특정 로컬 IP 주소와 관련된 연결 표시:

ss -ant '( daddr = 192.168.1.100 or saddr = 192.168.1.100 )'

성능 분석: netstat vs. ss 비교

문제 해결 시 도구 선택은 종종 속도와 세부 정보의 차이로 귀결됩니다.

기능 netstat ss
속도 느림 (파일 읽기) 훨씬 빠름 (Netlink 소켓 사용)
구문 성숙하고 문서화 잘 됨 유사한 플래그, 새로운 특정 옵션
필터링 grep으로 파이핑 필요 기본 상태 및 주소 필터링 지원
정보 깊이 기본에 적합 소켓 버퍼 크기 등 더 많은 세부 정보 (TCP 정보)
가용성 거의 보편적 현대 리눅스 배포판에서 표준

느린 연결 설정 진단

클라이언트가 느린 연결을 보고하면 핸드셰이크를 기다리며 멈춘 소켓이 있는지 확인하세요. ss를 사용하는 것이 이를 확인하는 가장 빠른 방법입니다:

  1. 높은 SYN-RECV 수 확인: 서버가 연결 요청을 받고 있지만 핸드셰이크를 완료하지 못하고 있음을 시사하며, 종종 리소스 고갈이나 높은 트래픽 부하로 인해 발생합니다.
    ss -s | grep syn-rec
    
  2. 높은 SYN-SENT 수 확인: 서버 자체가 많은 연결을 시작하는 경우(예: 데이터베이스나 다른 API에 대한 클라이언트 역할), 응답을 기다리고 있음을 보여줍니다.
    ss -s | grep syn-sent
    

어느 범주에서든 지속적으로 높은 숫자가 보이면, 이를 최종 결론이 아닌 단서로 취급하세요. SYN-SENT는 원격 호스트가 다운되었거나, 경로가 잘못되었거나, 방화벽이 트래픽을 조용히 드롭하고 있거나, 원격 서비스가 과부하 상태임을 의미할 수 있습니다. SYN-RECV는 서버가 부하 상태이거나, 패킷이 손실되고 있거나, 클라이언트가 연결을 열고 완료하지 않음을 의미할 수 있습니다.

실용적인 분류 흐름

누군가 "앱이 느리다"고 말하면, 저는 보통 짧고 반복 가능한 패스로 시작합니다:

sudo ss -tulpen
ss -s
sudo ss -tan state established '( sport = :443 or dport = :443 )' | head
sudo ss -tan state syn-recv
sudo ss -tan state time-wait | head

첫 번째 명령은 예상 서비스가 실제로 수신 대기 중이며 소유 프로세스를 보여줍니다. 요약은 호스트에 놀라운 수의 TCP 소켓이 있는지 보여줍니다. 필터링된 설정 연결 명령은 실제 클라이언트 트래픽이 포트에 연결되어 있는지 증명합니다. syn-recvtime-wait 확인은 연결 설정 또는 연결 변동이 주목할 가치가 있는지 보여줍니다.

예를 들어, 사용자가 새 요청이 몇 초 동안 멈춘다고 불평하는 Nginx 리버스 프록시를 상상해 보세요. sudo ss -tulpen | grep ':443'은 Nginx가 HTTPS 리스너를 소유하고 있음을 확인합니다. ss -s는 큰 TCP 총계를 보여주고, sudo ss -tan state syn-recv '( sport = :443 )'는 동일한 소스 범위에서 계속 행을 반환합니다. 이것이 자동으로 공격을 증명하지는 않지만, 로드 밸런서 상태 확인, 업스트림 패킷 손실, SYN 백로그 압력, 방화벽 로그 및 가능한 속도 제한을 살펴보라고 알려줍니다.

이제 동일한 프록시에 SYN_RECV 소켓은 거의 없지만 포트 5432의 업스트림 데이터베이스에 대한 많은 설정 연결이 있다고 상상해 보세요. 이는 공개 HTTPS에서 데이터베이스 경로로 주의를 돌리게 합니다:

sudo ss -tanp '( dport = :5432 or sport = :5432 )'

소유 프로세스가 애플리케이션이고 수가 계속 증가한다면, 다음 유용한 질문은 애플리케이션이 연결을 누출하고 있는지, 느린 쿼리를 기다리고 있는지, 아니면 연결을 풀로 반환하지 못하는지입니다. ss는 애플리케이션 수준의 질문에 답하지 않지만, 올바른 방으로 안내합니다.

TIME_WAIT 읽기: 당황하지 않기

TIME_WAIT는 정상적인 TCP 상태이며, 그 자체로 오류가 아닙니다. 수명이 짧은 연결을 많이 처리하는 서버는 자연스럽게 TIME_WAIT 소켓을 보여줍니다. 이는 이전 연결의 지연된 패킷이 새 연결과 혼동되지 않도록 존재합니다.

유용한 질문은 TIME_WAIT가 워크로드와 일치하는지 여부입니다. 모든 작은 요청에 대해 새 HTTP 연결을 여는 배치 작업은 TIME_WAIT의 물결을 만들 수 있습니다. keep-alive를 사용해야 하지만 사용하지 않는 서비스도 마찬가지일 수 있습니다. 커널 설정을 조정하기 전에 애플리케이션이 연결을 재사용할 수 있는지, HTTP keep-alive를 활성화할 수 있는지, 또는 적절한 클라이언트 풀을 사용할 수 있는지 확인하세요.

TIME_WAIT를 "수정"하기 위해 맹목적으로 TCP sysctl을 변경하라고 제안하는 오래된 조언에 주의하세요. 일부 설정은 커널 버전에 따라 다르며, 일부는 시간이 지남에 따라 제거되거나 권장되지 않았으며, 일부는 NAT 또는 로드 밸런서 뒤에서 미묘한 실패를 만듭니다. 연결이 왜 수명이 짧은지 이해하는 것부터 시작하세요.

로컬 대 원격 압력 확인

시간을 절약해 주는 한 가지 세부 사항은 로컬 호스트가 주로 연결을 수락하는지 아니면 주로 연결을 만드는지입니다. 프론트엔드 프록시는 일반적으로 로컬 포트가 80 또는 443인 많은 연결을 가지고 있습니다. 데이터베이스 및 API와 통신하는 애플리케이션 서버는 원격 포트가 5432, 3306, 6379 또는 443인 많은 연결을 가질 수 있습니다.

로컬 리스너 및 인바운드 트래픽의 경우:

sudo ss -tan '( sport = :443 )'

종속성에 대한 아웃바운드 트래픽의 경우:

sudo ss -tan '( dport = :6379 )'

이 구분은 다음 대화를 바꿉니다. 인바운드 HTTPS가 쌓이면 로드 밸런서, TLS 종료, 워커 제한 또는 클라이언트 동작을 검사해야 할 수 있습니다. 아웃바운드 Redis 연결이 쌓이면 로컬 애플리케이션이 너무 많은 클라이언트 연결을 만들거나, Redis를 기다리거나, 너무 공격적으로 재시도하고 있을 수 있습니다.

수백 개의 행을 읽지 않고 빠른 개수가 필요할 때는 ss를 간단한 셸 도구와 결합하세요:

sudo ss -tan state established '( dport = :443 )' | wc -l
sudo ss -tan state established '( dport = :5432 )' | wc -l

개수에는 헤더 행이 포함되므로 완벽한 지표는 아닙니다. 분류에는 여전히 유용합니다. 장애 발생 시 숫자가 매분 두 배로 증가한다면, 단일 스냅샷보다 더 강력한 신호를 얻는 것입니다.

컨테이너 및 네트워크 네임스페이스

컨테이너화된 호스트에서는 명령을 실행하는 위치에 주의하세요. 호스트에서 ss를 실행하면 호스트 네트워크 네임스페이스와 게시된 포트가 표시되지만, 프로세스가 컨테이너 내부에서 보는 것과 동일한 보기를 표시하지 않을 수 있습니다. 서비스가 컨테이너에서 실행되는 경우 두 보기를 비교하세요:

sudo ss -tulpen
docker exec <container> ss -tulpen

Kubernetes의 경우 호스트 수준 리스너에는 노드 보기를 사용하고, 포드의 네트워크 네임스페이스에는 kubectl exec를 사용하세요. 포트는 컨테이너 내부에서 열려 있을 수 있지만, 호스트, 서비스, 인그레스 또는 네트워크 정책이 여전히 트래픽이 도달하는 것을 방지할 수 있습니다. ss는 로컬 진실 도구이지, 종단 간 연결성 테스트가 아닙니다.

네트워크 문제 해결을 위한 모범 사례

  1. 항상 -n 사용: 성능 문제 해결이나 스크립팅 시 숫자 플래그(-n)를 사용하여 DNS 확인 지연을 피하세요. 이는 진단을 느리게 만들 수 있습니다.
  2. ss 우선 사용: 기본 도구로 ss를 채택하세요. ss를 사용할 수 없는 레거시 시스템에서만 netstat을 사용하세요.
  3. PID를 위해 루트로 실행: 포트를 사용하는 프로그램을 보려면 두 유틸리티 모두 -p 플래그를 사용할 때 일반적으로 sudo 또는 루트 권한이 필요합니다.
  4. 인터페이스 통계 확인: 인터페이스 카운터를 잊지 마세요. ip -s link show <interface_name>을 사용하여 드롭된 패킷이나 오류를 확인하세요. 이는 소켓 문제보다는 물리적 계층 문제를 나타낼 수 있습니다.
  5. 스냅샷 비교: 하나의 ss 출력은 사진입니다. 1분 간격으로 찍은 두 개의 출력은 상황이 증가, 감소 또는 안정적인지 알려줍니다.
  6. 정확한 필터 기록: 장애 발생 시 ss -tan '( dport = :5432 )'와 같은 저장된 명령은 반쯤 기억된 grep 파이프라인보다 반복하고 비교하기 쉽습니다.

성과를 내는 습관은 간단합니다: 리스너로 시작하고, 연결 상태로 이동하고, 소유 프로세스를 식별한 다음, 다음 단계가 앱, 네트워크 경로, 방화벽 또는 커널 중 어디에 속하는지 결정하세요.