Rancher 502와 단일 장애점: Tunnel부터 Pod까지 추적하기
이슈 발생: 2026-08-08 · 발행 기준: 2026-08-09 18:43 KST
환경: RKE2, Rancher, Cloudflare Tunnel, Traefik/Service DNS
Rancher를 Cloudflare Tunnel로 공개할 때 502만 보고 Rancher 자체의 문제라고 단정하면 진단이 길어진다. 이 구성에서 502는 Cloudflare Edge, Tunnel connector, Kubernetes DNS, Service, EndpointSlice, Rancher Pod 중 어느 계층에서도 만들어질 수 있다.
이번 점검에서는 접속 경로와 고가용성을 함께 살폈다. 기존에는 Rancher를 한 노드의 Pod 하나로 실행하고, cloudflared는 두 Pod로 구성했지만 서로 다른 노드에 배치되도록 강제하지 않았다. 외부 접속이 되더라도 구조상 단일 장애점이 남아 있던 셈이다.
구조부터 한 줄로 그리기
사용자 → Cloudflare → Tunnel → cloudflared Pod ×2
→ rancher.cattle-system.svc.cluster.local:80
→ Rancher Service → Rancher Pod ×3
Cloudflare Tunnel은 내부 connector가 바깥으로 연결을 생성하는 outbound-only 방식이다. 홈 라우터나 클라우드 VM의 80/443 포트를 직접 인터넷에 열지 않아도 된다.
502를 계층별로 분리하기
1. 외부 DNS와 Tunnel 상태
공개 hostname이 올바른 Tunnel을 가리키는지, connector가 Connected 상태인지 먼저 본다. 이 단계가 실패하면 Kubernetes 설정을 바꿔도 해결되지 않는다.
2. cloudflared가 내부 DNS를 해석하는지
Tunnel 원점으로 Service DNS를 사용하려면 cloudflared가 클러스터 안에서 실행되고 CoreDNS를 사용할 수 있어야 한다.
kubectl -n cloudflared get pod -o wide
kubectl -n cloudflared logs deploy/cloudflared --tail=200
kubectl -n cloudflared exec deploy/cloudflared -- \
sh -c 'getent hosts rancher.cattle-system.svc.cluster.local'
이미지에 getent나 shell이 없을 수 있다. 그때는 같은 namespace에 임시 진단 Pod를 띄운다.
3. Service와 EndpointSlice
Service가 있어도 Ready Pod를 선택하지 못하면 트래픽을 보낼 곳이 없다.
kubectl -n cattle-system get svc rancher -o wide
kubectl -n cattle-system get endpointslice \
-l kubernetes.io/service-name=rancher -o wide
kubectl -n cattle-system get pod -l app=rancher -o wide
EndpointSlice 주소 수와 Ready Rancher Pod 수를 비교한다. selector가 Pod label과 어긋났거나 readiness probe가 실패하면 endpoint가 비어 있을 수 있다.
4. 클러스터 내부 요청
kubectl run rancher-curl --rm -it --restart=Never \
--image=curlimages/curl -- \
curl -sv http://rancher.cattle-system.svc.cluster.local/ping
내부 /ping이 정상인데 외부만 502라면 Tunnel hostname/origin, TLS 설정, Host 헤더 쪽으로 범위를 좁힐 수 있다.
단일 Pod에서 3개 Pod로
Rancher의 replicas를 3으로 늘리는 것만으로는 충분하지 않다. 스케줄러가 세 Pod를 한 노드에 놓으면 노드 장애 한 번에 모두 사라진다. 안정적으로 사용할 core 노드에 label을 붙이고, 같은 hostname에 두 Rancher Pod를 놓지 않는 anti-affinity를 추가했다.
spec:
replicas: 3
template:
spec:
nodeSelector:
rancher-stable: "true"
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [rancher]
topologyKey: kubernetes.io/hostname
required...는 조건을 만족하지 않으면 Pod를 Pending으로 남기는 강한 규칙이다. 노드가 세 대보다 적은데 replicas가 3이면 일부가 Pending이 될 수 있으므로 가용 노드 수를 먼저 확인해야 한다.
kubectl get nodes -L rancher-stable
kubectl -n cattle-system rollout status deploy/rancher
kubectl -n cattle-system get pod -l app=rancher -o wide
cloudflared도 노드 장애를 견디게 만들기
cloudflared를 두 replicas로 유지하고 동일한 방식의 필수 pod anti-affinity를 적용했다. 두 connector는 같은 Tunnel token을 사용해도 된다. 이 복제의 핵심 목적은 애플리케이션 트래픽의 일반적인 로드밸런싱보다는 connector 또는 노드 장애 시 Tunnel 연결을 유지하는 것이다.
kubectl -n cloudflared rollout status deploy/cloudflared
kubectl -n cloudflared get pod -o wide
Pod 이름보다 NODE 열을 본다. 두 Pod가 Running이어도 같은 노드라면 노드 장애에 대한 이중화는 아니다.
Helm 환경에서 놓치기 쉬운 것
실행 중 Deployment를 kubectl patch로 수정하면 즉시 효과는 난다. 그러나 Helm으로 설치한 Rancher나 cloudflared는 다음 helm upgrade에서 저장된 values로 되돌아갈 수 있다.
따라서 최종 상태를 다음처럼 남겨야 한다.
- replicas
- nodeSelector 또는 nodeAffinity
- podAntiAffinity
- image 버전 또는 digest
- readiness/liveness 설정
- Rancher hostname 및 인증서 SAN
운영 중인 live 리소스와 Helm values의 차이를 배포 전에 비교하는 절차도 필요하다.
검증 체크리스트
- Rancher Pod 3개가 서로 다른 안정 노드에 존재한다.
- Rancher Service EndpointSlice에 Ready endpoint가 3개 보인다.
- cloudflared Pod 2개가 서로 다른 노드에 존재한다.
- cloudflared 로그에 origin DNS/TCP 오류가 없다.
- 클러스터 내부 Rancher
/ping이 성공한다. - 외부 주소가 HTTPS로 열리고 Cloudflare Access 정책도 정상이다.
- 노드 한 대를 배제했을 때도 나머지 경로로 접속할 수 있다.
- 변경값이 Helm values에 영구 반영돼 있다.