[Cloudflared] Cloud Tunnel 도입기
Cloudflare Tunnel 502 에러 해결기
1. 시작은 단순한 요구였다
외부에서 접근만 되면 되잖아?
프로젝트를 진행하면서 외부에서 접근 가능한 API 엔드포인트가 필요했다. 하지만 서버 환경은 꽤 제한적이었다.
내부 네트워크 또는 로컬 환경
인바운드 포트 개방 불가
공인 IP 사용 지양
VPN은 과한 해결책
이 조건들을 모두 만족시키면서 서버를 외부에 노출할 방법이 필요했고, 이때 선택한 것이 Cloudflare Tunnel이었다.
포트 포워딩 없이도 로컬 서버를 외부로 노출할 수 있다는 점이 좋아 보였다.
하지만 이 편리함이 첫 번째 오해였다..

2. 처음엔 Cloud Tunnel을 너무 쉽게 봤다
Cloud Tunnel을 처음 접했을 때의 인식은 이랬다.
로컬 서버를 외부에 잠깐 열어주는 도구겠지
그래서 구조에 대한 깊은 이해 없이 바로 설정부터 시작했다. 처음 Cloud Tunnel을 도입했을 때의 전제는 단순했다.
하나의 인스턴스에 올라와 있는 dev, prod 서버
둘 다 외부에서 접근 가능해야 함
Cloud Tunnel은 어차피 연결만 해주는 역할
그래서 자연스럽게 “인스턴스는 하나니깐 Tunnel도 하나 만들어서 dev 도메인, prod 도메인 둘 다 연결하면 되는 거 아닌가?”
이 판단은 틀리지는 않았지만, 불완전했다… 🥹
3. 최초 구조: 하나의 Tunnel + 두 개의 Hostman
초기 구성은 대략 이런 형태였다.
tunnel: finders-tunnel
ingress:
- hostname: dev-api.finders.it.kr
service: http://localhost:8081
- hostname: api.finders.it.kr
service: http://localhost:8080
DNS 역시 다음처럼 설정했다.
dev-api.finders.it.kr→ Cloud Tunnelapi.finders.it.kr→ Cloud Tunnel
이론적으로는 완벽해 보였다.
hostname으로 환경 구분
포트로 서비스 구분
Tunnel은 공통
hostname으로 환경을 구분하고 포트로 서비스를 나누니, 이론적으로는 완벽해 보였다.
실제로 dev 환경 배포 직후에는 모든 것이 잘 동작했다!! 새로운 기술을 적용했기에 기뻐했는데,,
진짜 문제는 prod 서버를 올리면서 시작되었다..
4. 첫 번째 이상 징후: 200 → 502 → CORS 에러?
main 브랜치를 배포하고 나서 갑자기 API 응답이 요동치기 시작했다. 어떨 때는 200 OK가 뜨다가, 갑자기 502 Bad Gateway와 함께 CORS 에러가 터져 나왔다!!!!!

CORS 설정은 완벽했기에 대체 왜 이런 일이 발생했는지 의문이었다.
대체 뭐가 문제인가 싶어서 확인했더니 Cloud Tunnel 문제였다.👿
내가 했던 착각
hostname이 다르면 Cloudflare가 알아서 완벽하게 분리해 주겠지?
하지만 정말 착각이었따..
터널의 기준은 hostname이 아니라 Tunnel Demon(Cloudflared) 그 자체였다. 하나의 터널 프로세스가 여러 호스트네임의 트래픽을 동시에 처리하다 보니, Connection Pool의 혼선이나 라우팅 우선순위 문제가 발생했던 것이다.
Cloudflare Edge 서버 입장에서는 같은 터널로 들어오는 요청이 꼬이면서 응답을 제대로 받지 못해 502를 던졌고, 이 과정에서 필수 헤더가 누락되어 브라우저가 이를 CORS 에러로 오인한 것이었다.
5. 해결책 1: 터널 분리
hostname 기준으로 다 나뉠 줄 알았다.. (그렇게 해도 된다며..!!!)
결국 터널을 물리적으로 두 개로 분리하기로 했다. prod용 터널과 dev용 터널을 각각 생성하여 각자의 설정 파일과 프로세스를 가지게 했다.
이렇게 하면 장애 전파 범위를 완벽히 격리할 수 있었다. dev 터널에 과부하가 걸리거나 설정 오류가 나더라도 prod 터널은 물리적으로 분리된 통로를 사용하므로 영향을 받지 않게 되는 것이다!
→ 이것이 바로 격리(Isolation)이었다.
6. 두 번째 문제: 사라지지 않는 유령(Zombie) 설정
dev를 고쳐놨더니 prod가 말썽이네?
dev가 너무 정상적으로 실행되었고, 에러가 하나도 나지 않았기 때문에 prod도 문제 없을 거라 생각했다.
하지만 prod 환경이 여전히 불안정했다. 설정은 완벽한데 왜일까?
Tunnel을 분리함으로써 각각의 역할을 명시해주었고, 그에 따라 동일한 설정을 가지고 있음에도 dev는 멀쩡했는데 prod만 문제가 발생하니 어떻게 해야할지가 막막했다..

답은 라우팅 전파(Propagation)의 지연에 있었다..
찾아보니 기존 터널 정보를 지우고 새 터널을 연결해도, 퍼져 있는 Cloudflare의 Edge 노드들에는 이전의 라우팅 캐시나 ‘좀비 커넥션’이 남아있을 수도 있다고 했다.
그래서 바로 완전히 새로 터널을 생성하여서 ID 자체를 갱신하였다.
두근.. 두근..!!!
결론적으로는 그 이후로는 아주 안정화되었다!!! 아마도 이전 설정 기록들이 어딘가에 남아있었고, 그게 계속 문제를 일으키고 있었던 것 같다.
7. 마치며
이제는 왜 두 개의 Tunnel을 쓰는지 설명할 수 있다
이번 경험을 통해 단순한 네트워크 도구라도 운영 환경에서는 책임과 경계를 명확히 해야 함을 배웠다.
터널은 단순한 통로가 아니다: 터널은 하나의 리소스 단위이며 장애 전파의 단위였다.
논리적 분리보다 물리적 분리: hostname(논리적) 구분보다 Tunnel Process(물리적) 분리가 훨씬 안전하다.
인프라 설정은 때로 재생성이 답이다: 때로는 수정보다 ‘재생성’이 더 빠르고 확실한 해결책이 될 수 있다.
처음엔 Tunnel 하나로 dev / prod를 동시에 운영하려 했다. 그리고 실제로 한동안은 잘 되는 것처럼 보였다. 하지만 dev와 prod의 분리는 필요하였고, 이는 편의가 아니라 안정성과 책임의 문제였다.

바이요