[태그:] Broken Pipe 오류

  • 서버에서 Broken Pipe 오류가 반복적으로 발생하는 원인과 해결 방법

    서버에서 Broken Pipe 오류가 반복적으로 발생하는 원인과 해결 방법

    SSH로 서버에 접속해 작업하던 중 갑자기 client_loop: send disconnect: Broken pipe 오류가 나타나면서 연결이 끊어진 경험이 있으신가요? 또는 Nginx와 애플리케이션 서버를 운영하면서 로그에 Broken pipe나 Errno 32 메시지가 반복적으로 기록되는 경우도 있습니다.

    특히 SSH 접속을 장시간 유지하거나, 대용량 파일을 전송하거나, 웹서버에서 응답 시간이 긴 요청을 처리할 때 이런 문제가 나타날 수 있습니다. 처음에는 서버가 다운됐거나 네트워크가 완전히 끊어졌다고 생각하기 쉽지만, 실제로는 상대방이 이미 연결을 종료했거나 TCP 연결이 더 이상 정상적으로 유지되지 않는 상태에서 데이터를 전송하려 할 때 발생하는 경우가 많습니다.

    다만 Broken Pipe는 특정 프로그램 하나의 오류가 아닙니다. SSH 클라이언트, Python 애플리케이션, 웹서버, 프록시 서버 등 어느 계층에서 메시지가 발생했는지에 따라 점검해야 할 원인이 달라집니다.

    이번 글에서는 Broken Pipe 오류 메시지 구분 → TCP 연결 상태 확인 → SSH KeepAlive 점검 → Nginx와 애플리케이션 로그 분석 → 안전한 설정 변경 순서로 반복적인 연결 종료 문제를 진단하는 방법을 살펴봅니다.

    Broken Pipe 핵심 명령어 한눈에 보기

    오류가 발생했다면 먼저 어떤 프로그램에서 메시지가 출력됐는지 확인해야 합니다. 아래 명령어는 Linux와 OpenSSH 환경에서 사용할 수 있는 대표적인 점검 방법입니다.

    점검 목적 명령어
    SSH 상세 로그 ssh -vvv user@server.example.com
    SSH 클라이언트 설정 ssh -G server.example.com
    TCP 연결 상태 ss -tan
    SSH 서버 로그 sudo journalctl -u sshd -n 100
    Nginx 오류 로그 sudo tail -n 100 /var/log/nginx/error.log
    네트워크 경로 확인 tracepath server.example.com

    Ubuntu와 Debian 계열에서는 SSH 서비스 이름이 ssh인 경우가 많으므로 journalctl -u ssh를 사용해야 할 수 있습니다. 또한 tracepath는 별도 패키지 설치가 필요할 수 있습니다.

    서버 Broken Pipe 오류 메시지와 SSH 연결 상태 점검 화면
    SSH 연결 중 Broken Pipe 오류가 발생했을 때 연결 로그와 TCP 상태를 확인하는 터미널 화면 예시입니다.

    1. Broken Pipe 오류는 어떤 상황에서 발생할까?

    리눅스에서 Broken pipe는 일반적으로 EPIPE 오류와 관련이 있습니다. 파이프 또는 소켓의 상대편이 더 이상 데이터를 받지 않는 상태에서 쓰기 작업을 시도할 때 발생할 수 있습니다.

    예를 들어 서버에서 다음과 같은 오류가 표시될 수 있습니다.

    client_loop: send disconnect: Broken pipe

    또는 Python 애플리케이션에서는 다음과 같이 나타날 수 있습니다.

    BrokenPipeError: [Errno 32] Broken pipe

    이 두 메시지는 모두 Broken Pipe와 관련이 있지만 동일한 원인으로 발생했다고 단정할 수는 없습니다.

    • SSH: 연결이 끊어진 상태에서 클라이언트가 데이터를 보내려는 경우
    • 웹 애플리케이션: 클라이언트가 응답을 기다리지 않고 연결을 종료한 경우
    • 프록시 환경: 중간 프록시 또는 로드밸런서가 연결을 종료한 경우
    • 파일 전송: 전송 도중 네트워크 연결이 끊기거나 상대 프로세스가 종료된 경우
    • 셸 파이프라인: 데이터를 받는 명령어가 먼저 종료된 경우

    따라서 오류가 발생한 위치를 구분하지 않고 SSH 설정이나 Nginx 타임아웃만 변경하는 것은 올바른 접근이 아닙니다.

    2. SSH 접속 중 Broken Pipe가 반복되는 경우

    SSH 연결을 장시간 유지하다가 갑자기 끊기는 경우라면 클라이언트와 서버 사이의 네트워크 연결 상태부터 확인해야 합니다.

    특히 방화벽, NAT 장비, VPN 또는 로드밸런서가 오랫동안 데이터 전송이 없는 연결을 정리하면서 문제가 발생할 수 있습니다.

    먼저 상세 로그를 활성화해 SSH에 접속합니다.

    ssh -vvv user@server.example.com

    연결이 끊어지는 시점의 로그를 확인하면 키 교환, 인증, 세션 유지 또는 데이터 전송 중 어느 단계에서 문제가 발생했는지 판단하는 데 도움이 됩니다.

    현재 적용되는 SSH 클라이언트 설정은 다음과 같이 확인할 수 있습니다.

    ssh -G server.example.com | grep -Ei 'serveralive|tcpkeepalive'

    여기서 ServerAliveInterval은 SSH 클라이언트가 일정 시간 동안 서버로부터 데이터를 받지 못했을 때 서버에 응답을 요청하는 간격을 지정합니다.

    ServerAliveCountMax는 응답이 없는 상태에서 허용할 연속적인 서버 응답 요청 횟수와 관련된 설정입니다.

    이 값들은 네트워크 장비의 유휴 연결 정리 문제를 완화하는 데 도움이 될 수 있지만, 실제 네트워크 장애를 복구하거나 모든 연결 끊김을 방지하는 설정은 아닙니다.

    3. SSH KeepAlive 설정으로 유휴 연결 끊김 완화하기

    SSH 접속이 일정 시간 사용하지 않았을 때만 반복적으로 끊긴다면 클라이언트의 KeepAlive 설정을 검토할 수 있습니다.

    먼저 현재 접속에만 옵션을 적용해 테스트합니다.

    ssh -o ServerAliveInterval=60 \
        -o ServerAliveCountMax=3 \
        user@server.example.com

    위 설정은 서버로부터 데이터를 받지 못한 시간이 60초에 도달하면 서버 응답을 요청하도록 구성합니다. 연속적으로 응답이 없으면 설정에 따라 연결을 종료할 수 있습니다.

    테스트 결과 유휴 상태에서의 연결 끊김이 줄어든다면 클라이언트 설정 파일에 해당 서버에 대한 설정을 추가할 수 있습니다.

    Host myserver
        HostName server.example.com
        User user
        ServerAliveInterval 60
        ServerAliveCountMax 3

    위 내용은 SSH 클라이언트의 ~/.ssh/config에 추가하는 예시입니다.

    모든 서버에 일괄 적용하기보다 실제로 문제가 발생하는 서버에만 설정한 뒤 연결 안정성을 확인하는 것이 좋습니다.

    4. SSH 서버 측 ClientAlive 설정 확인하기

    클라이언트 설정으로 문제가 해결되지 않는다면 SSH 서버의 연결 유지 및 세션 종료 설정을 확인할 수 있습니다.

    서버에서 현재 적용되는 SSHD 설정을 확인합니다.

    sudo sshd -T | grep -Ei 'clientalive|tcpkeepalive'

    SSH 서버에서는 ClientAliveInterval과 ClientAliveCountMax 설정을 사용할 수 있습니다.

    예를 들어 다음과 같은 설정을 검토할 수 있습니다.

    ClientAliveInterval 60
    ClientAliveCountMax 3

    다만 이 설정은 SSH 서버가 클라이언트의 응답 상태를 확인하는 데 사용되며, 유휴 연결을 무조건 유지하는 기능과 동일하지 않습니다.

    설정을 변경해야 한다면 기존 SSH 세션을 유지한 상태에서 설정 파일의 문법을 먼저 검사해야 합니다.

    sudo sshd -t

    설정이 정상임을 확인한 뒤 실제 서비스 이름과 운영 환경에 맞게 SSH 서비스를 다시 불러오거나 재시작할 수 있습니다.

    원격 서버에서 SSH 설정을 잘못 변경하면 재접속이 불가능해질 수 있으므로 콘솔 접근 수단을 확보한 상태에서 작업하는 것이 안전합니다.

    5. Nginx와 애플리케이션에서 Broken Pipe가 발생하는 경우

    웹서버 로그에 Broken Pipe가 반복적으로 기록된다면 SSH 설정이 아니라 HTTP 연결과 애플리케이션 응답 처리 과정을 확인해야 합니다.

    예를 들어 클라이언트가 요청을 보낸 뒤 응답을 기다리다가 브라우저를 닫거나 연결을 종료했는데, 서버가 뒤늦게 응답 데이터를 전송하려 하면 쓰기 오류가 발생할 수 있습니다.

    먼저 Nginx 오류 로그를 확인합니다.

    sudo tail -n 100 /var/log/nginx/error.log

    Access Log에서는 요청 경로와 상태 코드, 응답시간을 함께 확인하는 것이 좋습니다. 단, 응답시간 필드가 기록되는지는 실제 로그 포맷에 따라 다릅니다.

    sudo tail -n 100 /var/log/nginx/access.log

    Nginx의 499 상태 코드는 Nginx가 응답을 완료하기 전에 클라이언트가 연결을 종료했음을 나타내는 Nginx 전용 로그 코드입니다.

    하지만 Broken Pipe 오류가 발생했다고 해서 반드시 499가 함께 기록되는 것은 아닙니다. 애플리케이션이 프록시나 클라이언트로 데이터를 보내는 과정에서 발생한 별도의 소켓 오류일 수도 있습니다.

    따라서 오류가 발생한 시간대의 Nginx 로그와 애플리케이션 로그를 함께 비교해야 원인을 좁힐 수 있습니다.

    6. Python 애플리케이션의 BrokenPipeError 확인하기

    Python 서버나 스크립트에서는 다음과 같은 오류가 발생할 수 있습니다.

    BrokenPipeError: [Errno 32] Broken pipe

    예를 들어 HTTP 클라이언트가 연결을 종료한 뒤 애플리케이션이 응답을 전송하려는 상황에서 발생할 수 있습니다.

    또한 셸에서 다음과 같은 파이프라인을 실행할 때도 출력을 받는 명령어가 먼저 종료되면서 Broken Pipe가 발생할 수 있습니다.

    python3 generate_data.py | head -n 10

    head가 필요한 10줄을 읽고 종료하면 데이터를 계속 출력하려는 Python 프로세스에서 BrokenPipeError가 발생할 수 있습니다.

    이런 경우는 반드시 네트워크 장애를 의미하지 않습니다. 파이프를 읽는 프로세스가 먼저 종료된 정상적인 실행 흐름에서 발생한 부수적인 오류일 수도 있습니다.

    반면 웹 애플리케이션에서 동일한 오류가 반복된다면 클라이언트 연결 종료 시점, 응답 생성 시간, 프록시 타임아웃 및 요청 처리 구조를 함께 분석해야 합니다.

    7. TCP 연결과 네트워크 상태 확인하기

    Broken Pipe가 SSH와 웹 애플리케이션에서 동시에 발생한다면 서버의 네트워크 상태와 TCP 연결 상태도 확인할 필요가 있습니다.

    먼저 TCP 연결 목록을 확인합니다.

    ss -tan

    특정 SSH 연결을 확인하려면 다음과 같이 포트를 필터링할 수 있습니다.

    ss -tan '( sport = :22 or dport = :22 )'

    주요 TCP 상태에는 ESTAB, TIME-WAIT, CLOSE-WAIT 등이 있습니다.

    특히 CLOSE-WAIT 상태가 지속적으로 누적된다면 상대방이 연결 종료를 요청했지만 로컬 애플리케이션이 소켓을 적절하게 정리하지 못하고 있을 가능성을 확인해야 합니다.

    다만 특정 TCP 상태가 보인다는 이유만으로 Broken Pipe의 원인이 확정되는 것은 아닙니다. 오류 발생 시점의 로그와 연결 상태를 함께 비교해야 합니다.

    네트워크 경로 점검에는 다음 명령어를 사용할 수 있습니다.

    ping -c 5 server.example.com
    tracepath server.example.com

    ICMP 응답이 차단된 환경에서는 ping이나 tracepath가 실패해도 SSH나 HTTP 연결은 정상일 수 있습니다. 따라서 이 결과만으로 네트워크 장애 여부를 판단해서는 안 됩니다.

    8. 타임아웃 설정을 무조건 늘리면 해결될까?

    Broken Pipe가 반복되면 SSH KeepAlive나 Nginx의 proxy_read_timeout 값을 크게 늘리는 방법을 먼저 떠올리기 쉽습니다.

    하지만 타임아웃 설정은 실제로 어떤 연결이 먼저 종료되는지 확인한 뒤 조정해야 합니다.

    예를 들어 Nginx가 업스트림 응답을 기다리다가 타임아웃이 발생한 상황과 브라우저가 먼저 연결을 종료한 상황은 해결 방향이 다릅니다.

    응답시간이 긴 API라면 다음 항목을 함께 확인해야 합니다.

    • 애플리케이션 요청 처리 시간
    • 데이터베이스 쿼리 지연
    • 리버스 프록시와 업스트림 타임아웃
    • 로드밸런서 및 CDN 연결 제한
    • 클라이언트의 요청 취소 및 타임아웃
    • 대용량 응답 전송과 스트리밍 처리 방식

    타임아웃을 크게 늘리는 것만으로 근본 원인이 해결되는 것은 아니며, 불필요하게 오래 유지되는 연결이 늘어날 수도 있습니다.

    실전 점검 순서

    Broken Pipe 오류가 반복된다면 먼저 발생 위치를 구분하고 다음 순서로 점검하는 것이 좋습니다.

    # 1. SSH 상세 로그 확인
    ssh -vvv user@server.example.com
    
    # 2. SSH 클라이언트 KeepAlive 설정 확인
    ssh -G server.example.com | grep -Ei 'serveralive|tcpkeepalive'
    
    # 3. 서버 측 SSH 설정 확인
    sudo sshd -T | grep -Ei 'clientalive|tcpkeepalive'
    
    # 4. TCP 연결 상태 확인
    ss -tan
    
    # 5. Nginx 오류 로그 확인
    sudo tail -n 100 /var/log/nginx/error.log
    
    # 6. Nginx Access Log 확인
    sudo tail -n 100 /var/log/nginx/access.log
    
    # 7. SSH 서버 로그 확인
    sudo journalctl -u sshd -n 100

    위 명령어는 모든 서버에서 한꺼번에 실행해야 하는 절차가 아닙니다. SSH에서만 오류가 발생한다면 SSH와 네트워크를 우선 점검하고, 웹 애플리케이션에서만 발생한다면 해당 애플리케이션과 프록시 로그부터 확인해야 합니다.

    설정 변경 전에는 오류 발생 시간과 빈도, 특정 요청 또는 특정 사용자에게만 발생하는지, 유휴 상태에서 발생하는지 등을 기록해두면 변경 후 개선 여부를 비교하기 쉽습니다.

    정리

    서버에서 Broken Pipe 오류가 반복적으로 발생한다면 상대방이 데이터를 더 이상 받지 않는 상태에서 쓰기 작업이 발생했는지부터 확인해야 합니다.

    SSH에서는 유휴 연결 종료, 네트워크 경로, KeepAlive 설정을 확인하고, Nginx나 애플리케이션에서는 클라이언트 연결 종료와 응답시간, 프록시 타임아웃을 함께 분석해야 합니다.

    특히 Broken Pipe가 발생했다는 사실만으로 서버 다운이나 네트워크 장애를 단정할 수는 없습니다. 프로그램별 오류 메시지와 발생 시점의 로그를 비교해 어느 쪽에서 연결이 종료됐는지 확인하는 것이 중요합니다.

    핵심 점검 순서는 오류 발생 위치 확인 → 연결 종료 시점 분석 → TCP·로그 점검 → 필요한 설정만 변경 → 재발 여부 확인입니다.