-
Message Queue 트러블슈팅: RabbitMQ 구축이코에코(Eco²)/Troubleshooting 2025. 12. 22. 10:40

RabbitMQ Operator를 GitOps(ArgoCD app-of-apps, sync-wave)로 배포하면서 발생한 문제들과 해결 과정을 기록합니다.
트러블슈팅 요약
# 문제 증상 원인 해결 시간 1 Operator Path 오류 ServiceAccount not found 잘못된 kustomize path 10분 2 Control-Plane Toleration Pod Pending 누락된 taint toleration 15분 3 Namespace 충돌 Sync 실패 두 Operator가 동일 NS 생성 20분 4 401 Unauthorized Topology CR Ready=False Network Policy + 인증 1시간 5 Finalizer Stuck CR Deleting 상태 고착 리소스 정리 실패 30분 6 DNS 미등록 NXDOMAIN ExternalDNS 전파 대기 5분 7 Management UI 401 브라우저 로그인 실패 Istio retry + localStorage 30분
1. Operator Path 오류
증상
$ kubectl get pods -n rabbitmq-system Error from server: pods is forbidden: error looking up service account rabbitmq-system/rabbitmq-cluster-operator: serviceaccount "rabbitmq-cluster-operator" not foundArgoCD Application은 Synced 상태이지만, Operator Pod가 생성되지 않았다.
원인 분석
ArgoCD Application에서 잘못된 path를 참조.
# 잘못된 설정 source: path: config/manager # Deployment만 포함config/manager는 Deployment 정의만 포함,ServiceAccount, ClusterRole, ClusterRoleBinding, Namespace, CRD가 없음.
RabbitMQ Cluster Operator 디렉토리 구조
cluster-operator/config/ ├── crd/ # CRD 정의만 ├── manager/ # Deployment만 (❌ 불완전) ├── rbac/ # RBAC만 ├── default/ # CRD + RBAC + Deployment (NS 제외) └── installation/ # 완전한 설치 패키지 (✅ 권장) ├── namespace.yaml ├── serviceaccount.yaml ├── role.yaml ├── clusterrole.yaml ├── rolebinding.yaml ├── clusterrolebinding.yaml ├── deployment.yaml └── kustomization.yaml해결
# 올바른 설정 source: path: config/installation # 완전한 설치 패키지교훈: Operator 배포 시 공식 문서에서 권장하는 설치 경로를 확인해야 한다.
2. Control-Plane Toleration 누락
증상
$ kubectl get pods -n rabbitmq-system NAME READY STATUS RESTARTS AGE rabbitmq-cluster-operator-6c58b89fd5-xxxxx 0/1 Pending 0 5m $ kubectl describe pod rabbitmq-cluster-operator-xxx -n rabbitmq-system Events: Warning FailedScheduling 0/16 nodes are available: 1 node(s) had untolerated taint {role=control-plane: NoSchedule} 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: } ...원인 분석
Operator를 control-plane 노드에 배치하려 했으나, node-role taint 지정 누락.
# 누락된 설정 tolerations: - key: role operator: Equal value: control-plane effect: NoSchedule # node-role.kubernetes.io/control-plane taint 누락!k8s-master 노드의 실제 taint:
$ kubectl describe node k8s-master | grep Taints Taints: role=control-plane:NoSchedule node-role.kubernetes.io/control-plane:NoSchedule # kubeadm 기본 taint해결
두 taint 모두에 대한 toleration 추가:
tolerations: - key: role operator: Equal value: control-plane effect: NoSchedule - key: node-role.kubernetes.io/control-plane # 추가 operator: Exists effect: NoSchedule교훈: 노드의 실제 taint를
kubectl describe node로 확인한 후 toleration을 설정해야 한다.
3. Namespace 충돌
증상
$ kubectl get applications -n argocd dev-rabbitmq-operator Synced Healthy dev-rabbitmq-topology-operator OutOfSync Degraded # ArgoCD 에러 메시지 Namespace/rabbitmq-system is part of applications argocd/dev-rabbitmq-operator and argocd/dev-rabbitmq-topology-operator원인 분석
cluster-operator/config/installation/ └── namespace.yaml # rabbitmq-system messaging-topology-operator/config/installation/cert-manager/ └── namespace.yaml # rabbitmq-system (동일)두 Operator의 config/installation 경로 모두 namespace.yaml을 포함.
ArgoCD는 동일 리소스가 여러 Application에 속하면 충돌로 판단.
해결
Topology Operator에서 Namespace 리소스를 제외한다:
# 30-rabbitmq-topology-operator.yaml source: kustomize: patches: - target: kind: Namespace name: rabbitmq-system patch: | $patch: delete apiVersion: v1 kind: Namespace metadata: name: rabbitmq-system destination: namespace: rabbitmq-system syncPolicy: syncOptions: - CreateNamespace=false # 중요: Cluster Operator가 이미 생성핵심 포인트:
$patch: delete로 kustomize 빌드에서 Namespace 제외CreateNamespace=false로 ArgoCD가 자동 생성하지 않도록 설정- Cluster Operator(sync-wave 29)가 먼저 Namespace를 생성
교훈: 여러 Operator를 같은 namespace에 배포할 때 리소스 소유권을 명확히 해야 한다.
4. Topology Operator 401 Unauthorized
증상
$ kubectl get vhost -n rabbitmq NAME AGE READY eco2-vhost 10m False $ kubectl describe vhost eco2-vhost -n rabbitmq Status: Conditions: Message: Error: API responded with a 401 Unauthorized Reason: FailedCreateOrUpdate Status: False Type: ReadyTopology Operator가 RabbitMQ Management API에 인증 실패.
원인 분석 1: Network Policy
처음에는 Topology Operator가 RabbitMQ에 연결조차 못했다:
$ kubectl logs -n rabbitmq-system deployment/messaging-topology-operator dial tcp 10.96.x.x:15672: connection refused문제: Network Policy에서
rabbitmq-systemnamespace가 15672 포트 접근을 허용 X해결 1: Network Policy 수정
# allow-rabbitmq-access.yaml - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: monitoring - namespaceSelector: matchLabels: kubernetes.io/metadata.name: rabbitmq-system # 추가 ports: - protocol: TCP port: 15672원인 분석 2: 인증 정보 불일치
Network Policy 수정 후에도 credentials 불일치로 401 오류가 지속.
RabbitmqCluster CR의 override 설정 (문제의 설정):
# cluster.yaml (잘못된 설정) spec: override: statefulSet: spec: template: spec: containers: - name: rabbitmq env: - name: RABBITMQ_DEFAULT_USER valueFrom: secretKeyRef: name: rabbitmq-default-user # 수동 생성 Secret key: username - name: RABBITMQ_DEFAULT_PASS valueFrom: secretKeyRef: name: rabbitmq-default-user key: password문제점:
- Operator는 자동으로
eco2-rabbitmq-default-userSecret을 생성 - Topology Operator는 이 자동 생성된 Secret을 참조
- 하지만 RabbitMQ Pod는 수동 Secret의 credentials로 시작
- 결과: credentials 불일치 → 401 Unauthorized
┌──────────────────┐ "guest/guest" ┌──────────────────┐ │ Topology Operator│ ─────────────────────▶ │ RabbitMQ │ │ (auto-generated) │ │ (manual secret) │ └──────────────────┘ └──────────────────┘ │ │ │ eco2-rabbitmq-default-user │ rabbitmq-default-user │ username: default_user_xxx │ username: admin │ password: abc123... │ password: secret123 └───────────────────────────────────────────┘ 불일치!해결 2: Override 제거
RabbitmqCluster CR에서 credentials override를 제거하고, Operator 자동 생성 Secret을 사용.
# cluster.yaml (올바른 설정) spec: # override 섹션 제거 # Credentials: Operator가 자동 생성하는 eco2-rabbitmq-default-user secret 사용 # Topology Operator가 이 secret을 참조하여 Management API 인증주의: 기존 PersistentVolume에 이전 credentials가 저장되어 있으면 Pod 재시작 후에도 이전 사용자 정보가 유지.
해결 2-1: 수동 사용자 추가 (PV 데이터 유지 시)
PV를 삭제하지 않고 해결하려면 auto-generated 사용자를 수동으로 추가:
# Auto-generated Secret 확인 $ kubectl get secret eco2-rabbitmq-default-user -n rabbitmq -o jsonpath='{.data.username}' | base64 -d default_user_cv3Zf99xxx $ kubectl get secret eco2-rabbitmq-default-user -n rabbitmq -o jsonpath='{.data.password}' | base64 -d abc123xyz... # RabbitMQ에 사용자 추가 $ kubectl exec -it eco2-rabbitmq-server-0 -n rabbitmq -- \ rabbitmqctl add_user default_user_cv3Zf99xxx abc123xyz... $ kubectl exec -it eco2-rabbitmq-server-0 -n rabbitmq -- \ rabbitmqctl set_user_tags default_user_cv3Zf99xxx administrator $ kubectl exec -it eco2-rabbitmq-server-0 -n rabbitmq -- \ rabbitmqctl set_permissions -p / default_user_cv3Zf99xxx ".*" ".*" ".*" $ kubectl exec -it eco2-rabbitmq-server-0 -n rabbitmq -- \ rabbitmqctl set_permissions -p eco2 default_user_cv3Zf99xxx ".*" ".*" ".*"교훈:
- RabbitMQ Operator는 credentials를 자동 관리한다. 수동 override는 Topology Operator와 충돌.
- Network Policy 설정 시 Operator namespace도 고려.
5. Finalizer Stuck
증상
$ kubectl get vhost,exchange,queue,binding -n rabbitmq NAME AGE READY vhost.rabbitmq.com/eco2-vhost 1h False $ kubectl delete vhost eco2-vhost -n rabbitmq # 무한 대기... $ kubectl get vhost eco2-vhost -n rabbitmq -o yaml metadata: deletionTimestamp: "2025-12-20T10:00:00Z" finalizers: - deletion.finalizers.vhosts.rabbitmq.comTopology CRs가
Deleting상태에서 영구히 고착.원인 분석
Kubernetes Custom Resource의 Finalizer 메커니즘:
┌────────────────────────────────────────────────────────────────────┐ │ Finalizer 동작 흐름 │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ 1. kubectl delete vhost eco2-vhost │ │ ↓ │ │ 2. API Server: deletionTimestamp 설정 │ │ ↓ │ │ 3. Topology Operator: RabbitMQ에서 vhost 삭제 시도 │ │ ↓ │ │ 4-a. 성공 → finalizer 제거 → CR 완전 삭제 │ │ 4-b. 실패 (401 등) → finalizer 유지 → 삭제 불가 (stuck!) │ │ │ └────────────────────────────────────────────────────────────────────┘Operator가 401 오류로 RabbitMQ와 통신할 수 없어서 finalizer를 제거 실패.
해결: 수동 Finalizer 제거
# 단일 리소스 $ kubectl patch vhost eco2-vhost -n rabbitmq \ --type=json \ -p='[{"op": "remove", "path": "/metadata/finalizers"}]' # 전체 Topology CRs (스크립트) for type in binding exchange queue vhost; do for name in $(kubectl get $type -n rabbitmq -o name 2>/dev/null); do kubectl patch $name -n rabbitmq \ --type=json \ -p='[{"op": "remove", "path": "/metadata/finalizers"}]' \ 2>/dev/null || true done done주의: Finalizer를 수동 제거하면 RabbitMQ 내부의 실제 리소스는 삭제되지 않음. 필요 시
rabbitmqctl로 수동 정리.ArgoCD Application 복구
Topology CRs 삭제 후 ArgoCD Application이
Missing상태가 될 수 있음:$ kubectl get application dev-rabbitmq-topology -n argocd NAME SYNC STATUS HEALTH STATUS dev-rabbitmq-topology Unknown Missing해결: Root Application 강제 동기화
$ kubectl patch application dev-root -n argocd \ --type=merge \ -p '{"operation":{"sync":{"prune":true}}}'교훈:
- Finalizer는 Operator의 정리 작업 완료를 보장하는 메커니즘.
- Operator와 대상 시스템 간 통신이 실패하면 finalizer가 stuck.
- 강제 삭제 시 실제 리소스 정리 여부를 확인.
6. DNS 미등록 (NXDOMAIN)
증상
브라우저에서
rabbitmq.dev.growbin.app접속 시:사이트에 연결할 수 없음 DNS_PROBE_FINISHED_NXDOMAIN원인 분석
ExternalDNS가 Ingress annotation을 감지하고 Route53에 레코드를 생성하는 데 시간이 걸린다.
# ExternalDNS 로그 확인 $ kubectl logs -n platform-system -l app.kubernetes.io/name=external-dns --tail=50 | grep rabbitmq time="2025-12-22T02:10:47Z" level=info msg="Desired change: CREATE rabbitmq.dev.growbin.app CNAME" time="2025-12-22T02:10:47Z" level=info msg="3 record(s) were successfully updated"DNS 레코드는 생성되었지만, 로컬 DNS 캐시에 이전 NXDOMAIN 응답이 캐시되어 있었다.
해결
1. DNS 전파 확인:
$ nslookup rabbitmq.dev.growbin.app 8.8.8.8 Server: 8.8.8.8 Address: 8.8.8.8#53 Non-authoritative answer: rabbitmq.dev.growbin.app canonical name = k8s-ecoecomain-xxx.ap-northeast-2.elb.amazonaws.com2. 로컬 DNS 캐시 플러시:
# macOS sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder # Chrome 브라우저 chrome://net-internals/#dns → Clear host cache교훈: 새 도메인 추가 후 접속 실패 시, 먼저 공용 DNS(8.8.8.8)로 레코드 존재를 확인하고 로컬 캐시를 플러시해야 한다.
7. Management UI 브라우저 401 Unauthorized
증상
// 브라우저 콘솔 GET https://rabbitmq.dev.growbin.app/api/whoami 401 (Unauthorized) main.js:1393 Failed to load resource: the server responded with a status of 401그러나 curl로는 정상 응답:
$ curl -u 'admin:admin123' https://rabbitmq.dev.growbin.app/api/whoami {"name":"admin","tags":["administrator"]}원인 분석 1: Istio VirtualService retry 설정
VirtualService에
retriable-4xx가 포함되어 있었다:retries: attempts: 3 retryOn: connect-failure,reset,retriable-4xx,503 # 문제!문제점: 401 응답 시 Istio가 재시도하면서 Authorization 헤더가 손실될 수 있다.
해결 1: retriable-4xx 제거
retries: attempts: 3 retryOn: connect-failure,reset,503 # retriable-4xx 제거원인 분석 2: 브라우저 localStorage 캐시
RabbitMQ Management UI는 로그인 정보를 localStorage에 저장한다. 이전 잘못된 인증 정보가 캐시되어 있으면 새 비밀번호로 로그인해도 실패한다.
┌─────────────────────────────────────────────────────────────────┐ │ 브라우저 인증 흐름 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 로그인 폼에서 username/password 입력 │ │ ↓ │ │ 2. JavaScript가 Base64 인코딩 후 localStorage에 저장 │ │ ↓ │ │ 3. API 호출 시 localStorage에서 credentials 읽어서 사용 │ │ ↓ │ │ 4. 이전 잘못된 credentials 캐시 → 401 Unauthorized │ │ │ └─────────────────────────────────────────────────────────────────┘해결 2: localStorage 삭제
방법 1: 시크릿/프라이빗 모드
Chrome: Cmd + Shift + N Safari: Cmd + Shift + N방법 2: 개발자 도구에서 삭제
Cmd + Option + I(개발자 도구)- Application 탭 → Storage 섹션
- Local Storage →
https://rabbitmq.dev.growbin.app→ Clear - Cookies →
rabbitmq.dev.growbin.app→ Clear - 새로고침
방법 3: 사이트 데이터 삭제
Cmd + Shift + Delete(캐시 삭제 창)- 시간 범위: 전체 기간
- "쿠키 및 사이트 데이터" 체크
- 삭제
검증
# curl로 인증 테스트 $ curl -s -u 'admin:admin123' https://rabbitmq.dev.growbin.app/api/whoami {"name":"admin","tags":["administrator"]}교훈:
- Istio VirtualService의
retriable-4xx는 인증이 필요한 서비스에서 문제를 일으킬 수 있다. - 브라우저에서만 401 발생 시 localStorage/Cookie 캐시를 의심해야 한다.
- 시크릿 모드로 먼저 테스트하면 캐시 문제를 빠르게 확인할 수 있다.
체크리스트: RabbitMQ Operator 배포
배포 전 확인 사항:
- Cert-Manager 설치 완료 (Topology Operator Webhook용)
- 노드 taint 확인 (
kubectl describe node) - Operator path 확인 (
config/installation권장) - Network Policy에
rabbitmq-systemnamespace 포함 - RabbitmqCluster CR에서 credentials override 사용 안 함
- Sync-wave 순서 확인 (Operator → Cluster → Topology)
'이코에코(Eco²) > Troubleshooting' 카테고리의 다른 글
Message Queue 트러블슈팅: Gevent Pool 마이그레이션 및 Stateless 체이닝의 한계 (0) 2025.12.25 Message Queue 트러블슈팅: Quorum Queue -> Classic Queue 마이그레이션 (0) 2025.12.24 분산 트레이싱 트러블슈팅: OpenTelemetry 커버리지 확장 (0) 2025.12.19 분산 트레이싱 트러블슈팅: Log-Trace 연동 및 Kibana 검색 구조 (0) 2025.12.19 선언적 배포 트러블슈팅: eck-custom-resources operator 도입 실패 포스트모템 (0) 2025.12.18