RabbitMQ 연결 실패 해결: 단계별 문제 해결 가이드

타임아웃, 소켓 거부, TLS 문제, 자격 증명, 가상 호스트 및 제한 사항에 대한 실용적인 RabbitMQ 연결 문제 해결 체크리스트입니다.

RabbitMQ 연결 실패 해결: 단계별 문제 해결 가이드

RabbitMQ는 강력하고 널리 사용되는 메시지 브로커이지만, 가장 탄력적인 시스템도 가끔 연결 문제를 겪습니다. 연결 실패는 개발자와 운영팀이 직면하는 가장 흔한 장애물 중 하나로, 종종 "연결 거부" 또는 "연결 시간 초과"와 같은 모호한 오류로 나타납니다.

이 종합 가이드는 이러한 연결 문제를 진단하고 해결하기 위한 체계적이고 단계별 접근 방식을 제공합니다. 네트워킹, 서비스 상태, 구성 및 인증 계층을 방법론적으로 확인함으로써 근본 원인을 효율적으로 파악하고 클라이언트 애플리케이션과 RabbitMQ 클러스터 간의 안정적인 통신을 복원할 수 있습니다.

일반적인 오류 유형의 차이점을 이해하는 것이 효과적인 문제 해결의 첫 번째 중요한 단계입니다. 거부된 연결은 서버가 요청을 적극적으로 거부했음을 의미하고, 시간 초과는 클라이언트가 서버에 도달할 수 없었음을 의미합니다.


1. 연결 오류 유형 이해

단계를 시작하기 전에 클라이언트 오류 메시지가 실패 지점에 대해 무엇을 암시하는지 인식하는 것이 중요합니다.

연결 시간 초과

시간 초과 오류는 클라이언트 애플리케이션이 소켓 연결을 설정하려고 시도하지만 지정된 기간 내에 응답을 받지 못할 때 발생합니다. 이는 일반적으로 요청이 RabbitMQ 애플리케이션 계층에 도달하기 전에 차단이 있음을 나타냅니다.

가능한 원인: 네트워킹, DNS 또는 방화벽 문제.

연결 거부

연결 거부 오류는 서버가 TCP 연결 요청을 적극적으로 거부할 때 발생합니다. 이는 요청이 서버 호스트에 도달했지만 특정 포트가 닫혀 있거나 해당 포트에서 실행 중인 서비스가 연결 시도를 거부했음을 확인합니다.

가능한 원인: 서비스가 실행 중이 아님, 잘못된 포트 또는 인증/액세스 제어 문제.

2. 단계별 문제 해결 프로토콜

네트워크 계층(2.1단계)에서 시작하여 애플리케이션 계층(2.5단계)까지 작업하십시오.

2.1. 네트워크 연결 가능성 및 DNS 확인

여기서 목표는 클라이언트 시스템이 RabbitMQ 서버 IP 주소와 물리적으로 통신할 수 있고 호스트 이름을 올바르게 확인할 수 있는지 확인하는 것입니다.

  1. 호스트 이름 확인 확인: 클라이언트가 RabbitMQ 호스트 이름을 올바른 IP 주소로 확인하는지 확인합니다.

    ping rabbitmq.yourdomain.com
    
  2. 기본 IP 연결: 간단한 연결 가능성을 확인합니다.

    ping <RabbitMQ 서버 IP>
    
  3. 포트 접근성 (중요 테스트): telnet 또는 netcat (nc)을 사용하여 클라이언트 관점에서 특정 RabbitMQ 포트(기본 AMQP 포트: 5672)가 열려 있고 수신 대기 중인지 테스트합니다.

    # 성공하면 화면이 비어 있거나 연결 메시지가 표시됩니다.
    # 실패하면 문제는 네트워크 또는 방화벽 관련일 가능성이 높습니다.
    telnet <RabbitMQ 서버 IP> 5672
    

문제 해결 팁: 방화벽 차단

telnet 테스트가 실패했지만 서버가 실행 중인 경우(나중에 확인), 방화벽이 연결을 차단하고 있을 가능성이 높습니다. 로컬 시스템 방화벽(iptables, firewalld)과 외부 보안 그룹(AWS, Azure, GCP)을 모두 확인하십시오.

2.2. RabbitMQ 서비스 상태 확인

네트워크 계층이 명확하면 RabbitMQ 서비스가 서버에서 활발히 실행 중인지 확인합니다.

  1. 서비스 상태 확인: 배포판의 서비스 관리 도구를 사용합니다.

    # Systemd 시스템의 경우
    sudo systemctl status rabbitmq-server
    # 또는 OS에 해당하는 명령어
    sudo service rabbitmq-server status
    

    조치: 서비스가 중지된 경우 다시 시작합니다: sudo systemctl start rabbitmq-server.

  2. 노드 상태 확인: 관리 CLI 도구를 사용하여 실행 중인 노드의 내부 상태를 확인합니다.

    sudo rabbitmqctl status
    

    running_applications 목록에서 필요한 구성 요소가 활성화되어 있는지 확인합니다.

  3. 서버 로그 검토: 연결 거부는 종종 로그에 자세한 메시지를 남깁니다. 기본 로그 파일을 확인하십시오(위치는 설치에 따라 다르며, 일반적으로 /var/log/rabbitmq/). 바인딩, 포트 충돌 또는 시작 시 충돌과 관련된 오류를 찾습니다.

2.3. 서버 구성 및 수신 포트 확인

서비스가 실행 중이더라도 예상된 인터페이스나 포트에서 수신 대기하지 않을 수 있습니다.

  1. 수신 인터페이스 확인: RabbitMQ가 올바른 네트워크 인터페이스에서 수신 대기하도록 구성되어야 합니다. 127.0.0.1(localhost)에만 바인딩된 경우 원격 클라이언트가 연결할 수 없습니다.

  2. 활성 포트 확인: RabbitMQ 서버에서 시스템 도구를 사용하여 프로세스가 표준 AMQP 포트(5672) 및/또는 TLS 포트(사용하는 경우)에 바인딩되어 있는지 확인합니다.

    # ss 또는 netstat을 사용하여 수신 중인 TCP 소켓을 나열합니다.
    sudo ss -tulpn | grep 5672
    # 예상 출력은 프로세스가 0.0.0.0 또는 올바른 서버 IP에서 수신 대기 중임을 보여야 합니다.
    

2.4. 인증 및 권한 부여 실패

클라이언트가 핸드셰이크를 시도한 직후 연결 거부를 받는 경우, 특히 네트워크 연결이 확인된 경우 문제는 사용자 자격 증명 또는 권한일 가능성이 높습니다.

일반적인 인증 문제

  1. 잘못된 자격 증명: 클라이언트 애플리케이션에서 사용하는 사용자 이름과 암호를 다시 확인하십시오. 자격 증명은 대소문자를 구분합니다.
  2. 게스트 사용자 제한: 기본 guest 사용자는 일반적으로 localhost에서만 연결하도록 제한됩니다. 클라이언트가 guest를 사용하여 원격으로 연결하는 경우 거부됩니다.
  3. VHost 권한: 연결하는 사용자는 액세스하려는 가상 호스트(vhost)에 대해 적절한 권한(구성, 쓰기, 읽기)이 설정되어 있어야 합니다.

인증 문제 해결

rabbitmqctl 도구를 사용하여 사용자 설정 및 권한을 확인합니다.

# 모든 사용자 나열
sudo rabbitmqctl list_users

# 특정 vhost(예: 기본 '/')에 대한 권한 확인
sudo rabbitmqctl list_permissions -p /

# 예: 새 원격 연결 가능 사용자 생성(필요한 경우)
# 1. 사용자 추가
sudo rabbitmqctl add_user my_remote_app strongpassword
# 2. VHost '/'에 대한 권한 설정
sudo rabbitmqctl set_permissions -p / my_remote_app ".*" ".*" ".*"

⚠️ 보안 모범 사례

프로덕션 애플리케이션에서 기본 guest 사용자에 의존하지 마십시오. 각 클라이언트 애플리케이션 또는 마이크로서비스에 대해 특정하고 제한된 권한을 가진 전용 사용자를 만드십시오.

2.5. 클라이언트 측 환경 및 구성

때로는 문제가 전적으로 연결을 시도하는 애플리케이션 내에 있습니다.

  1. 구성 확인: 애플리케이션의 구성 파일 또는 환경 변수에서 호스트 이름, 포트 번호 또는 자격 증명의 오타를 확인합니다.
  2. 클라이언트 라이브러리 버전: 클라이언트 라이브러리(예: Python용 Pika, Node.js용 amqplib)가 최신 버전이고 RabbitMQ 서버 버전과 호환되는지 확인합니다.
  3. TLS/SSL 불일치: RabbitMQ가 TLS를 요구하도록 구성된 경우 클라이언트는 SSL/TLS를 사용하도록 구성되고 올바른 인증서를 제공해야 합니다. 클라이언트가 TLS 전용 포트에 대해 일반 AMQP 연결을 시도하면 연결이 실패합니다.
  4. 연결 풀링/조절: 간헐적인 실패가 발생하는 경우 클라이언트 애플리케이션이 연결을 빠르게 열고 닫아 OS의 파일 디스크립터 제한 또는 브로커가 설정한 연결 제한에 도달하는지 확인합니다.

3. 고급 진단 도구

지속적인 문제의 경우 관리 플러그인과 네트워크 패킷 검사를 활용하십시오.

RabbitMQ 관리 플러그인 (포트 15672)

관리 인터페이스(브라우저를 통해)에 액세스할 수 있는 경우 브로커 상태, 열린 포트를 확인하고 실시간 로그 정보를 볼 수 있으며, 이는 CLI를 통해 사용할 수 없는 단서를 제공하는 경우가 많습니다.

네트워크 추적 (Wireshark/tcpdump)

복잡한 네트워크 문제의 경우 클라이언트 또는 서버 시스템에서 패킷 분석기를 사용하여 연결 시도가 실패하는 정확한 위치를 확인합니다.

  • 클라이언트가 SYN 패킷을 보내고 아무것도 받지 못하면 방화벽 문제입니다.
  • 클라이언트가 SYN 패킷을 보내고 RST/ACK 패킷을 받으면 서버가 연결을 적극적으로 거부하는 것입니다(서비스 또는 바인딩 문제일 가능성 높음).
# 예: 서버 측에서 포트 5672를 모니터링하기 위해 tcpdump 실행
sudo tcpdump -i eth0 port 5672 -nn

클라이언트 오류를 더 주의 깊게 읽기

클라이언트 라이브러리는 RabbitMQ 연결 실패를 동일한 방식으로 표현하지 않습니다. Java 클라이언트는 AuthenticationFailureException을 보고할 수 있습니다. Pika를 사용하는 Python 서비스는 AMQPConnectionError 또는 ProbableAuthenticationError를 표시할 수 있습니다. Node.js 서비스는 소켓이 닫혔다고만 기록할 수 있습니다. 브로커 설정을 변경하기 전에 정확한 오류, 타임스탬프, 대상 호스트, 대상 포트 및 실패가 AMQP 핸드셰이크 전후에 발생하는지 여부를 캡처하십시오.

그 시점이 중요합니다.

소켓을 전혀 열 수 없으면 여전히 DNS, 라우팅, 방화벽, 리스너 또는 포트 영역에 있습니다. TCP 연결이 열렸다가 AMQP 협상 중에 닫히면 TLS, 프로토콜 버전, 자격 증명, vhost 권한 또는 브로커 측 연결 제한을 살펴보십시오. 연결이 성공한 후 몇 분 후에 끊어지면 하트비트, 로드 밸런서, NAT 시간 초과, 클라이언트 연결 변동 및 리소스 알람을 조사하십시오.

저는 보통 먼저 이 네 가지 사실을 요청합니다:

클라이언트 호스트:
브로커 호스트:
포트:
정확한 오류 및 타임스탬프:

그런 다음 타임스탬프를 RabbitMQ 로그와 일치시킵니다. 브로커 로그에 항목이 전혀 없으면 연결 시도가 RabbitMQ에 도달하지 못했을 가능성이 높습니다. 브로커 로그에 인증 또는 vhost 오류가 기록되면 네트워크는 이미 입증되었으며 문제는 더 상위 스택에 있습니다.

빠른 의사 결정 트리

프로덕션이 중단되었을 때 이 순서를 사용하십시오. 계층 간에 이동하는 것을 방지합니다.

  1. 클라이언트에서 브로커 호스트 이름을 확인합니다.
  2. 클라이언트에서 TCP 포트를 엽니다.
  3. RabbitMQ가 해당 포트와 인터페이스에서 수신 대기 중인지 확인합니다.
  4. 동일한 타임스탬프에서 RabbitMQ 로그를 확인합니다.
  5. TLS가 관련된 경우 TLS 모드와 인증서의 유효성을 검사합니다.
  6. 사용자 이름, 암호, vhost 및 권한의 유효성을 검사합니다.
  7. 연결 제한, 파일 디스크립터, 메모리 알람 및 디스크 알람을 확인합니다.
  8. 로드 밸런서, 프록시, Kubernetes 서비스 또는 보안 그룹을 검토합니다.

예를 들어:

getent hosts rabbitmq.internal
nc -vz rabbitmq.internal 5672
nc -vz rabbitmq.internal 5671

가능하면 telnet 대신 nc를 사용하십시오. 많은 서버 이미지에 설치되어 있고 스크립트에 더 깔끔한 종료 코드를 제공하기 때문입니다. 성공적인 TCP 연결이 인증이 작동한다는 것을 증명하지는 않습니다. 클라이언트가 해당 포트에서 수신 대기 중인 무언가에 도달할 수 있다는 것만 증명합니다.

브로커에서:

sudo ss -ltnp | grep -E '5671|5672|15672'
sudo rabbitmq-diagnostics listeners
sudo rabbitmq-diagnostics status

rabbitmq-diagnostics listeners는 RabbitMQ가 열었다고 생각하는 리스너를 보여주기 때문에 특히 유용합니다. ss와 RabbitMQ가 일치하지 않으면 컨테이너, 네임스페이스 또는 잘못된 호스트 문제를 보고 있을 수 있습니다.

로컬호스트 바인딩 및 컨테이너 문제

한 가지 일반적인 연결 실패는 로컬 테스트 성공 후에 발생합니다. 누군가 브로커 시스템에서 localhost:5672로 RabbitMQ를 확인하고 다른 호스트에 앱을 배포한 다음 앱이 거부됩니다.

브로커가 루프백에서만 수신 대기 중일 수 있습니다. 서버 자체에서는 괜찮아 보입니다. 다른 시스템에서는 연결할 수 없습니다.

다음과 같은 출력을 확인하십시오:

sudo ss -ltnp | grep 5672

127.0.0.1:5672가 표시되면 원격 클라이언트가 사용할 수 없습니다. 일반적으로 네트워크 설계에 따라 RabbitMQ를 서버 주소 또는 모든 인터페이스에 바인딩하려고 합니다. AMQP를 인터넷에 광범위하게 노출하지 마십시오. 개인 인터페이스에 바인딩하고 방화벽 규칙 또는 보안 그룹을 사용하여 연결할 수 있는 클라이언트를 제한하십시오.

컨테이너는 또 다른 계층을 추가합니다. RabbitMQ는 컨테이너 내부에서 수신 대기 중일 수 있지만 호스트 포트가 게시되지 않았을 수 있습니다. Docker에서 확인:

docker ps
docker port <rabbitmq-container>

Kubernetes에서 서비스 선택기, 엔드포인트, 대상 포트 및 포드 준비 상태를 확인:

kubectl get svc,endpoints -n messaging
kubectl describe svc rabbitmq -n messaging
kubectl get pods -n messaging -o wide

서비스에 엔드포인트가 없으면 RabbitMQ는 독립적으로는 정상일 수 있지만 서비스에 의해 선택되지 않을 수 있습니다. 이는 종종 레이블 불일치 또는 준비 상태 프로브 실패로 인해 발생합니다.

TLS 불일치는 연결 문제처럼 보입니다.

TLS 실패는 종종 무작위 RabbitMQ 불안정성으로 오해됩니다. 가장 기본적인 실수는 TLS 포트에 일반 AMQP로 연결하거나 일반 AMQP 포트에 TLS로 연결하는 것입니다. 표준 AMQP는 일반적으로 5672에 있고, AMQPS는 일반적으로 5671에 있지만 환경에 따라 다를 수 있습니다.

클라이언트 시스템에서 TLS 리스너를 직접 테스트:

openssl s_client -connect rabbitmq.internal:5671 -servername rabbitmq.internal

인증서 확인 오류, 호스트 이름 불일치, 만료된 인증서 또는 누락된 중간 인증서를 찾으십시오. 인증서 일반 이름 또는 주체 대체 이름이 클라이언트가 사용하는 호스트 이름과 일치하지 않으면 더 엄격한 클라이언트가 연결을 거부합니다.

또한 브로커가 클라이언트 인증서를 요구하는지 확인하십시오. 상호 TLS가 활성화된 경우 서버 인증서만 신뢰하는 클라이언트는 자체 인증서를 제시하지 않았기 때문에 여전히 실패할 수 있습니다.

애플리케이션 구성의 경우 ssl=true와 같은 모호한 설정이 무엇을 하는지 모르는 상태에서 사용하지 마십시오. CA 파일, 클라이언트 인증서, 클라이언트 키, 서버 이름 확인 및 포트를 확인하십시오. 작동하는 openssl s_client 테스트는 완전한 AMQP 테스트가 아니지만 인증서 문제를 RabbitMQ 사용자 문제와 빠르게 분리합니다.

인증은 암호 이상입니다.

RabbitMQ 인증에는 여러 부분이 있습니다:

  • 사용자 이름이 존재합니다.
  • 암호가 올바릅니다.
  • 제한 사항이 있는 경우 사용자가 해당 위치에서 연결할 수 있습니다.
  • 요청된 가상 호스트가 존재합니다.
  • 사용자가 해당 가상 호스트에 대한 권한이 있습니다.

기본 guest 사용자는 일반적인 RabbitMQ 설치에서 localhost로 제한됩니다. 이는 의도적인 안전 기본값입니다. 원격 앱이 guest를 사용하는 경우 기본 계정을 약화시키는 대신 전용 사용자를 만드십시오.

유용한 확인:

sudo rabbitmqctl list_users
sudo rabbitmqctl list_vhosts
sudo rabbitmqctl list_permissions -p /
sudo rabbitmqctl authenticate_user app_user 'the-password'

권한은 구성, 쓰기 및 읽기 작업에 대한 정규식입니다. 사용자는 인증에 성공할 수 있지만 채널을 열거나 큐를 선언할 때 여전히 실패할 수 있습니다. 간단한 애플리케이션 vhost의 경우 다음과 같이 권한을 부여할 수 있습니다:

sudo rabbitmqctl add_vhost app_prod
sudo rabbitmqctl add_user app_service 'use-a-secret-manager'
sudo rabbitmqctl set_permissions -p app_prod app_service '^app\.' '^app\.' '^app\.'

이 예제는 app.으로 시작하는 리소스만 허용합니다. 많은 튜토리얼이 편의상 모든 것에 .*를 사용하지만 프로덕션 권한은 일반적으로 더 좁아야 합니다.

때때로 작동할 때

간헐적인 연결 실패는 다른 사고 방식이 필요합니다. 대부분의 연결이 작동하지만 일부가 실패하면 제한 및 미들박스를 찾으십시오.

RabbitMQ는 파일 디스크립터가 부족할 수 있습니다. 운영 체제는 임시 포트가 부족할 수 있습니다. 클라이언트가 너무 많은 단기 연결을 생성할 수 있습니다. 로드 밸런서가 하트비트 설정이 로드 밸런서 시간 초과보다 긴 경우 유휴 연결을 닫을 수 있습니다.

브로커 측 카운트 확인:

sudo rabbitmqctl list_connections name peer_host peer_port state channels recv_cnt send_cnt
sudo rabbitmqctl list_channels connection number user vhost
sudo rabbitmq-diagnostics status

동일한 앱에서 수천 개의 연결이 표시되면 앱이 메시지 또는 웹 요청당 연결을 열고 있을 수 있습니다. RabbitMQ 연결은 수명이 긴 것으로 간주됩니다. 프로세스당 하나의 연결 또는 작은 풀을 사용한 다음 클라이언트 라이브러리에서 권장하는 대로 동시 작업을 위해 채널을 만드십시오.

하트비트는 또 다른 조용한 원인입니다. 클라이언트 이벤트 루프가 차단되면 하트비트를 놓칠 수 있고 RabbitMQ가 연결을 닫습니다. 프록시가 60초 후에 유휴 TCP 연결을 자동으로 끊는 반면 RabbitMQ 하트비트는 훨씬 더 긴 경우 클라이언트는 게시를 시도할 때만 끊어진 연결을 발견할 수 있습니다. 하트비트와 로드 밸런서 유휴 시간 초과 설정을 정렬하여 실패가 빠르고 의도적으로 감지되도록 하십시오.

에스컬레이션 전에 캡처할 내용

간단한 확인으로 해결되지 않을 때는 다음 사람이 추측 없이 도울 수 있도록 충분한 증거를 수집하십시오:

date -u
hostname -f
getent hosts rabbitmq.internal
nc -vz rabbitmq.internal 5672
nc -vz rabbitmq.internal 5671
sudo rabbitmq-diagnostics listeners
sudo rabbitmq-diagnostics status
sudo rabbitmqctl list_connections name user vhost peer_host state

비밀을 제거한 애플리케이션 연결 문자열, 클라이언트 라이브러리 이름 및 버전, RabbitMQ 버전 및 양쪽의 정확한 로그 줄을 추가하십시오. 대부분의 어려운 연결 사례는 클라이언트와 브로커 타임스탬프가 일치하면 간단해집니다.

최종 확인

RabbitMQ 연결 실패를 계층적 문제로 취급하십시오. 먼저 DNS를 증명한 다음 TCP 연결 가능성, 브로커 리스너, TLS, 자격 증명 및 vhost 권한을 증명하십시오. 시간 초과는 일반적으로 요청이 대상 경로에서 유용한 응답을 받지 못하고 있음을 의미합니다. 거부된 연결은 일반적으로 무언가 응답했지만 예상된 리스너 또는 액세스 경로가 잘못되었음을 의미합니다. 이 두 가지 경우를 분리하면 대부분의 인시던트를 훨씬 빠르게 좁힐 수 있습니다.