Cloudflare Tunnel 뒤의 Traefik·Hubble이 열리지 않았던 이유
이슈 발생: 2026-08-08 · 발행 기준: 2026-08-09 10:17 KST
환경: RKE2, Traefik, Cilium Hubble, Cloudflare Tunnel
Cloudflare Tunnel의 원점을 http://rke2-traefik.kube-system.svc.cluster.local:80으로 지정했는데도 Hubble UI와 Traefik Dashboard가 바로 열리지는 않았다. 결론부터 말하면 Tunnel 원점은 Traefik까지 오는 길만 정할 뿐, Traefik 뒤의 목적지는 Ingress 규칙이 결정한다.
요청이 지나가는 실제 경로
브라우저
→ Cloudflare Edge
→ Cloudflare Tunnel
→ cloudflared Pod
→ rke2-traefik Service:80
→ Host/Path 규칙
├─ hubble.example.com → Hubble Ingress → hubble-ui Service
└─ traefik.example.com → IngressRoute → api@internal
*.svc.cluster.local은 Kubernetes 내부 Service DNS다. cloudflared가 클러스터 안에서 Pod로 실행되므로 이 주소를 사용할 수 있다. 반면 인터넷의 브라우저가 이 DNS 이름을 직접 해석하는 것은 아니다.
처음에 헷갈린 지점
Tunnel에 Traefik Service 주소를 넣으면 Traefik Dashboard가 자동으로 표시될 것이라고 생각하기 쉽다. 그러나 Service는 고정된 진입점과 Pod 선택 기능을 제공할 뿐, hubble.example.com과 traefik.example.com 중 어느 애플리케이션으로 보낼지는 모른다.
그 결정에 필요한 정보가 HTTP Host와 Path다. Traefik은 다음과 같이 요청을 구분한다.
- Hubble은 Kubernetes 표준
Ingress로hubble-ui:80에 연결한다. - Traefik Dashboard는 일반 Service가 아니라 Traefik 내부 서비스인
api@internal에 연결하므로IngressRouteCRD를 사용한다. - Dashboard는
/dashboard/와/api경로를 사용한다. 루트/만 열면 404처럼 보일 수 있으므로 리다이렉트를 둔다.
Hubble UI 라우팅
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hubble-ui
namespace: kube-system
spec:
ingressClassName: traefik
rules:
- host: hubble.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hubble-ui
port:
number: 80
여기서 중요한 것은 Ingress와 backend Service가 같은 namespace에 있다는 점이다. 표준 Ingress backend는 다른 namespace의 Service를 이름만으로 바로 가리킬 수 없다.
Traefik Dashboard 라우팅
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: dashboard-root-redirect
namespace: kube-system
spec:
redirectRegex:
regex: ^https?://([^/]+)/?$
replacement: https://${1}/dashboard/
permanent: true
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: traefik-dashboard
namespace: kube-system
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.example.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
kind: Rule
services:
- name: api@internal
kind: TraefikService
- match: Host(`traefik.example.com`) && Path(`/`)
kind: Rule
middlewares:
- name: dashboard-root-redirect
services:
- name: api@internal
kind: TraefikService
traefik.io/v1alpha1은 Kubernetes 기본 API가 아니다. Traefik이 설치한 CRD이므로 먼저 다음을 확인한다.
kubectl get crd | grep -E 'ingressroutes|middlewares'
kubectl -n kube-system get ingress,ingressroute,middleware
바깥에서 안쪽으로 진단하기
한 번에 설정을 모두 바꾸지 않고 요청 경로를 계층별로 확인했다.
# 1. Traefik과 backend Service 존재 여부
kubectl -n kube-system get svc rke2-traefik hubble-ui
# 2. Service가 실제 endpoint를 갖는지
kubectl -n kube-system get endpointslice \
-l kubernetes.io/service-name=rke2-traefik
kubectl -n kube-system get endpointslice \
-l kubernetes.io/service-name=hubble-ui
# 3. 라우팅 객체와 이벤트
kubectl -n kube-system describe ingress hubble-ui
kubectl -n kube-system describe ingressroute traefik-dashboard
# 4. 클러스터 내부에서 Service DNS 및 Host 라우팅 시험
kubectl run curl-test --rm -it --restart=Never \
--image=curlimages/curl -- \
curl -sv -H 'Host: hubble.example.com' \
http://rke2-traefik.kube-system.svc.cluster.local/
Service만 호출할 때는 Host가 내부 DNS 이름이 된다. Host 기반 규칙을 시험하려면 -H 'Host: ...'를 반드시 넣어야 한다.
해결과 검증
두 공개 hostname의 Tunnel 원점은 동일한 Traefik Service로 유지했다. 대신 Hubble에는 표준 Ingress, Dashboard에는 IngressRoute와 루트 리다이렉트를 적용했다. 이후 내부 curl에서 올바른 backend가 선택되는지 확인하고, 마지막으로 외부 HTTPS 주소를 확인했다.
Traefik Dashboard는 관리 정보가 노출되는 화면이다. 공개 DNS가 동작한다고 끝이 아니라 Cloudflare Access, SSO 또는 적절한 인증 계층으로 반드시 보호해야 한다.
기억할 점
- Tunnel 원점은 입구이고 Ingress는 교차로다.
- Pod IP 대신 Service DNS를 사용한다.
- 같은 원점도
Host규칙에 따라 여러 서비스로 나눌 수 있다. Service 존재와Endpoint 존재는 별개다.- Dashboard의
/dashboard/,/api, 루트 리다이렉트를 함께 확인한다. - Dashboard를 인증 없이 인터넷에 공개하지 않는다.