도메인을 사서 Cloudflare Tunnel로 K8s에 연결하기
이중 NAT 사무실이라 지금까지는 Quick Tunnel(*.trycloudflare.com)로 서비스를 공개했습니다. 문제는 두 가지였습니다.
- 터널 Pod가 재시작되면 주소가 바뀝니다.
- 사무실 회선이 잠깐 끊기면 연결이 하나뿐이라 그대로 응답이 끊깁니다.
도메인을 하나 사서 이름 있는 터널(named tunnel) 로 바꿨습니다.
결과
| 주소 | 환경 |
|---|---|
https://claudharmony.io, https://www.claudharmony.io |
prd |
https://dev.claudharmony.io |
dev |
- 주소가 고정됩니다.
- HTTPS 인증서는 Cloudflare가 발급하고 갱신합니다.
- 포트포워딩은 여전히 필요 없습니다. 클러스터 안의
cloudflared가 바깥으로 연결합니다. cloudflared를 2개 띄워서 엣지 연결이 4개입니다. 하나가 끊겨도 나머지가 받습니다.
구조
사용자 ─HTTPS→ Cloudflare 엣지 (DNS · TLS)
║ 터널 (아웃바운드 연결 4개)
cloudflared ×2 (ns: edge)
│ Host 헤더 그대로
Gateway (Envoy)
┌────────┴─────────┐
dev.claudharmony.io 그 외 (기본 라우트)
ns dev · web ns prd · web
터널은 모든 호스트를 Gateway 하나로 보내고, dev/prd 구분은 Gateway API의 HTTPRoute.hostnames가 합니다. 라우팅 규칙이 Kubernetes 한 곳에 모이고, 서브도메인을 늘릴 때 터널 쪽은 호스트 이름만 추가하면 됩니다.
순서
1. 도메인 구매
Cloudflare Registrar에서 샀습니다. 원가로 팔고, 갱신 가격도 같고, 네임서버가 자동으로 Cloudflare로 잡힙니다. .kr은 지원하지 않으니 국내 등록업체에서 사서 네임서버만 Cloudflare로 바꾸면 됩니다.
2. Zero Trust 활성화
터널은 Zero Trust 안의 기능입니다. 대시보드에서 팀 이름을 정하고 Free 플랜을 고르면 됩니다. 이걸 안 하면 API로 터널을 만들 때 그냥 Authentication error가 납니다.
3. API 토큰 — 최소 권한
계정 API 토큰을 처음부터(Custom) 만들었습니다. "모든 리소스 쓰기" 템플릿은 쓰지 않습니다.
| 범위 | 권한 |
|---|---|
| 계정 | Cloudflare One Connector: cloudflared — Edit |
| 계정 | Cloudflare One Connectors — Edit |
| 계정 | Access: Apps and Policies — Edit (dev 보호용) |
| 영역 (도메인 하나) | DNS — Edit, Zone — Read |
토큰은 저장소 밖 파일(chmod 600)에만 두고, 배포 스크립트가 이 스택을 실행할 때만 환경변수로 읽습니다.
4. Terraform
플랫폼 단계에 edge 스택을 하나 더 만들었습니다.
resource "cloudflare_zero_trust_tunnel_cloudflared" "this" {
account_id = var.cloudflare_account_id
name = "claudharmony-office"
config_src = "cloudflare"
}
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "this" {
account_id = var.cloudflare_account_id
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.this.id
config = {
ingress = concat(
[for h in var.hostnames : { hostname = h, service = var.gateway_service }],
[{ service = "http_status:404" }]
)
}
}
resource "cloudflare_dns_record" "this" {
for_each = toset(var.hostnames)
zone_id = data.cloudflare_zone.this.zone_id
name = each.value
type = "CNAME"
content = "${cloudflare_zero_trust_tunnel_cloudflared.this.id}.cfargotunnel.com"
proxied = true
ttl = 1
}
같은 스택에서 터널 실행 토큰을 Kubernetes Secret에 넣고, cloudflared tunnel run을 Deployment 2개로 띄웁니다. readiness probe는 cloudflared의 /ready(엣지 연결이 하나라도 살아 있으면 200)를 씁니다.
막혔던 곳
터널 목록은 보이는데 생성만 Authentication error
토큰 화면에서 "Cloudflare Tunnel"로 검색하면 Argo Tunnel (Legacy) 만 나옵니다. 이름상 맞아 보이지만 이것만으로는 생성이 안 됐습니다. 지금 이름은 Cloudflare One Connector: cloudflared 입니다.
권한 문제인지 입력 문제인지는 빈 body로 POST해 보면 바로 갈립니다.
10000 Authentication error → 권한 문제
1030 missing field `name` → 권한 통과, 입력만 잘못
권한을 넣었는데 그대로
정책 화면의 카운터가 0/72였습니다. 체크가 저장되지 않은 상태였습니다. 마지막 업데이트 버튼까지 눌러야 합니다.
DNS 권한이 Account DNS Settings로만 보임
정책 범위가 "전체 계정"이라서 그렇습니다. 범위를 영역 → 도메인으로 바꾸면 DNS, Zone 항목이 나옵니다.
정리
- 고정 주소와 HTTPS가 생겼고, 포트포워딩은 여전히 0개입니다.
- 커넥터를 이중화해서 회선이 잠깐 끊겨도 버팁니다.
- 임시 터널은 모두 지웠습니다.
다음은 dev.claudharmony.io를 Cloudflare Access 이메일 로그인 뒤로 숨기는 것입니다.