이 글이 필요한 상황
Docker Hub에서 이미지를 내려받을 때만 연결이 느려지거나 context deadline exceeded, TLS handshake timeout, i/o timeout 같은 오류가 반복된다면 Docker Engine의 트래픽이 브라우저와 다른 경로를 사용하고 있을 가능성이 큽니다. 브라우저는 운영체제의 시스템 프록시를 따르지만, Docker 데몬은 별도의 서비스로 실행되며 자체 네트워크 네임스페이스와 DNS 설정을 사용할 수 있습니다. 따라서 Clash Verge Rev에서 시스템 프록시를 켰는데도 docker pull만 실패하는 현상이 나타날 수 있습니다.
이 문서는 Clash의 TUN 모드를 이용해 Docker Hub와 이미지 레지스트리 트래픽을 투명하게 분기하는 방법을 설명합니다. 특정 구독 서비스나 노드 이름을 전제로 하지 않고, YAML 규칙, DNS 처리, 라우팅 경로, Docker 데몬의 실행 위치를 함께 점검하는 방식으로 구성했습니다. 로컬 개발 PC에서 Docker Desktop을 사용하는 경우와 Linux 호스트에서 Docker Engine을 직접 실행하는 경우는 동작 방식이 다르므로, 두 환경의 차이도 구분해 설명합니다.
TUN은 모든 문제를 자동으로 해결하는 기능이 아닙니다. 가상 인터페이스가 제대로 생성되지 않았거나, Docker가 별도 VM 안에서 실행되거나, 규칙이 실제 접속 호스트를 포함하지 않으면 TUN을 켜도 트래픽이 DIRECT로 빠질 수 있습니다. 그러므로 설정을 한 번에 많이 바꾸기보다, 먼저 DNS와 연결 로그를 확인하고 그다음 YAML을 좁혀 가는 것이 안전합니다.
Docker 트래픽이 브라우저와 다른 이유
Docker CLI는 명령을 입력하는 클라이언트이지만, 실제 이미지 다운로드 요청은 대개 백그라운드에서 실행 중인 Docker daemon이 처리합니다. Linux에서는 dockerd가 호스트 서비스로 동작하고, Docker Desktop for Windows·macOS에서는 별도의 Linux VM 또는 가상화된 네트워크 계층을 거칩니다. 이 차이 때문에 셸에서 HTTPS_PROXY를 설정하는 것만으로는 데몬의 모든 요청이 프록시를 통과한다고 보장할 수 없습니다.
Docker Hub에 로그인하거나 이미지를 내려받을 때는 하나의 호스트만 사용하는 것도 아닙니다. 보통 인증 단계의 auth 서비스, 레지스트리 API, 이미지 레이어를 제공하는 CDN 또는 저장소 호스트가 차례로 등장합니다. 첫 번째 인증 요청은 성공했는데 실제 레이어 다운로드에서 멈춘다면, registry-1.docker.io만 규칙에 넣고 인증·CDN 경로를 놓쳤을 가능성이 있습니다.
- 인증 단계: Docker Hub 계정 토큰을 요청하는 과정에서 인증 호스트와 TLS 연결을 사용합니다.
- 레지스트리 단계: 이미지 매니페스트와 태그 정보를 조회하며 레지스트리 API에 접속합니다.
- 레이어 단계: 큰 이미지 조각을 여러 번 내려받으므로 짧은 요청보다 노드 안정성과 MTU의 영향을 크게 받습니다.
- 내부 레지스트리 단계: 사내 Harbor나 자체 Registry는 외부 프록시가 아니라
DIRECT로 남겨야 할 수 있습니다.
이 구조를 모르면 모든 Docker 트래픽을 무조건 글로벌 프록시로 보내게 됩니다. 그러면 Docker Hub는 열리더라도 사내 레지스트리, 로컬 미러, 패키지 저장소까지 해외 노드를 거치면서 속도가 떨어지거나 인증서 검사가 실패할 수 있습니다.
TUN 모드와 YAML 기본 구성
먼저 Clash 클라이언트에서 현재 사용 중인 코어가 TUN을 지원하는지 확인합니다. Mihomo 계열 코어에서는 일반적으로 TUN 설정을 프로필 또는 클라이언트의 고급 설정에서 활성화할 수 있습니다. 메뉴 이름은 Clash Verge, Clash Verge Rev, Mihomo 기반 클라이언트마다 다르지만 핵심은 가상 인터페이스 생성, 자동 라우트, DNS 가로채기 세 가지입니다.
초기 테스트에서는 모든 옵션을 동시에 복잡하게 켜기보다, 관리자 권한 승인 여부와 가상 인터페이스 생성 여부부터 확인하세요. Windows에서는 방화벽 또는 서비스 권한 팝업이 나타날 수 있고, Linux에서는 TUN 디바이스 접근 권한이 필요할 수 있습니다. 권한을 승인했는데도 인터페이스가 보이지 않는다면 YAML을 다시 편집하기 전에 클라이언트 로그에서 TUN 초기화 오류를 확인해야 합니다.
아래는 구조를 이해하기 위한 축약 예시입니다. 실제 포트와 프록시 그룹 이름은 사용하는 프로필에 맞춰 바꿔야 하며, 구독 파일이 업데이트될 때 사용자 설정이 덮어써지는지도 확인해야 합니다.
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- https://dns.cloudflare.com/dns-query
fallback:
- https://1.1.1.1/dns-query
rules:
- DOMAIN-SUFFIX,docker.io,PROXY
- DOMAIN-SUFFIX,docker.com,PROXY
- DOMAIN-SUFFIX,dockerusercontent.com,PROXY
- DOMAIN,auth.docker.io,PROXY
- DOMAIN,registry-1.docker.io,PROXY
- DOMAIN-SUFFIX,내부레지스트리.example,DIRECT
- MATCH,DIRECT
이 예시는 모든 실제 Docker CDN 호스트를 완벽하게 열거한다는 뜻이 아닙니다. Docker Hub가 사용하는 저장소 호스트는 시점과 이미지에 따라 달라질 수 있으므로, 첫 테스트 후 연결 로그에 나타난 실제 도메인을 확인해야 합니다. 지나치게 넓은 DOMAIN-SUFFIX 규칙을 추가하면 Docker와 관계없는 서비스도 같은 프록시 그룹으로 들어갈 수 있으므로, 로그로 검증한 호스트부터 추가하는 편이 좋습니다.
DNS와 라우팅을 순서대로 검증하기
Docker Hub 문제에서 DNS는 단순히 도메인 주소를 찾는 기능 이상의 의미를 가집니다. Docker 데몬이 호스트 DNS를 직접 사용하고, Clash는 클라이언트 프로세스의 DNS만 처리한다면 브라우저와 Docker의 해석 결과가 달라질 수 있습니다. 한쪽은 프록시가 접근 가능한 주소를 얻고 다른 쪽은 차단된 주소나 응답하지 않는 주소를 얻는 식입니다.
fake-ip 모드를 사용하는 경우에는 애플리케이션과 가상 주소의 호환성도 확인해야 합니다. 일반적인 HTTPS 클라이언트는 문제가 적지만, 일부 컨테이너 런타임이나 보안 도구는 DNS 응답의 형태를 예상과 다르게 처리할 수 있습니다. 이런 환경에서는 fake-ip와 redir-host를 번갈아 테스트하고, 어느 모드에서 Docker의 인증·매니페스트·레이어 다운로드가 모두 유지되는지 비교하는 것이 좋습니다.
테스트 순서는 다음처럼 짧게 나누면 원인을 빠르게 좁힐 수 있습니다.
- Clash 로그에서 Docker Hub 도메인에 대한 DNS 요청이 보이는지 확인합니다.
- 호스트 운영체제에서 해당 도메인을 조회하고, 결과가 반복해서 바뀌거나 응답 시간이 과도하게 길지 않은지 봅니다.
docker pull을 실행하면서 Clash 연결 로그에 같은 시각의 인증·레지스트리 호스트가 기록되는지 비교합니다.- 로그에 아무것도 나타나지 않으면 Docker Desktop VM 또는 Linux 데몬이 TUN 경로 밖에 있는지 확인합니다.
라우팅에서는 auto-route가 켜져 있어도 기존 VPN, 기업용 보안 에이전트, WSL 가상 스위치가 우선순위를 차지할 수 있습니다. Windows에서는 네트워크 어댑터의 메트릭과 WSL2 경로를 확인하고, macOS에서는 다른 VPN 프로파일이나 네트워크 확장이 TUN과 충돌하지 않는지 살펴보세요. Linux 서버에서는 기본 라우트, 정책 기반 라우팅, 컨테이너 브리지 인터페이스가 서로 다른 경로를 만들 수 있습니다.
Docker Desktop과 Linux Engine별 적용 방법
Docker Desktop을 사용하는 경우
Docker Desktop은 명령을 실행하는 터미널과 실제 데몬이 같은 네트워크 공간에 있지 않을 수 있습니다. 따라서 호스트에서 TUN이 정상 동작해도 Desktop 내부 VM의 트래픽이 자동으로 같은 인터페이스를 통과한다고 단정하면 안 됩니다. 먼저 Docker Desktop 자체의 프록시 설정이 있는지 확인하고, 그 설정과 Clash의 mixed port 또는 HTTP 포트가 충돌하지 않는지 살펴보세요.
Desktop 프록시와 TUN을 동시에 사용할 때는 이중 프록시가 되지 않도록 주의해야 합니다. Desktop이 이미 모든 외부 요청을 로컬 HTTP 프록시로 보내는데 TUN이 다시 그 프록시의 연결을 가로채면 루프, 인증 실패, 비정상적인 지연이 발생할 수 있습니다. 한 번에 하나의 주 경로만 선택한 뒤, Docker Desktop 재시작 후 docker info와 실제 docker pull을 함께 확인하세요.
Linux에서 Docker Engine을 직접 실행하는 경우
Linux 호스트에서는 데몬이 시스템 서비스로 실행되는 경우가 많으므로, 셸의 환경 변수와 데몬의 환경 변수를 분리해서 생각해야 합니다. systemd 서비스에 프록시를 지정하는 방식과 TUN으로 투명하게 처리하는 방식은 서로 다른 선택지입니다. 두 방식을 동시에 적용하면 요청이 두 번 프록시를 통과하거나, 내부 레지스트리까지 외부 노드로 나갈 수 있습니다.
투명 프록시를 우선한다면 데몬에 불필요한 HTTP_PROXY를 남겨 두지 않고, TUN 로그에서 실제 패킷이 잡히는지 확인하는 편이 관리하기 쉽습니다. 반대로 서버 정책상 TUN을 사용할 수 없다면 Docker 서비스 프록시를 별도로 구성해야 하며, 이 글의 TUN 경로와 섞지 않는 것이 좋습니다. 설정을 변경한 뒤에는 데몬을 재시작하고, 기존 이미지 캐시가 아닌 새 태그나 작은 테스트 이미지를 사용해 검증해야 합니다.
실전 테스트와 로그 판독
가장 먼저 작은 공개 이미지를 대상으로 테스트한 뒤, 문제가 발생했던 업무용 이미지를 시도하세요. 큰 이미지부터 실행하면 다운로드 시간이 길어져 DNS 실패인지, 인증 실패인지, 특정 레이어 CDN의 연결 문제인지 구분하기 어렵습니다. 또한 이미지가 이미 로컬 캐시에 있으면 네트워크 요청이 발생하지 않으므로, 테스트 전에 태그와 캐시 상태를 확인해야 합니다.
Clash 로그에서 확인할 항목은 단순히 연결 성공이라는 문구가 아닙니다. 실제 호스트 이름, 선택된 규칙, 프록시 그룹, 업스트림 노드, 연결 종료 시각을 함께 봐야 합니다. 인증 호스트는 성공했지만 레지스트리 호스트가 DIRECT로 처리되었다면 규칙 누락에 가깝습니다. 모든 호스트가 프록시를 탔는데 큰 레이어에서만 끊긴다면 노드의 장시간 연결 안정성, MTU, HTTP/2 또는 TLS 세션 유지 문제를 의심할 수 있습니다.
- DNS lookup timeout: DNS 리스너, 가상 인터페이스, 컨테이너 내부 DNS 전달 경로를 확인합니다.
- 401 Unauthorized: 네트워크보다 Docker Hub 인증 정보나 토큰 교환 상태를 먼저 확인합니다.
- connection reset: 업스트림 노드 변경, MTU, 장시간 스트림 유지 가능성을 비교합니다.
- no route to host: TUN 라우트, 방화벽, Docker Desktop VM 경로를 점검합니다.
- 속도만 느림: 규칙은 맞더라도 선택된 노드와 CDN 경로가 비효율적일 수 있습니다.
실패 시각을 기록하고 Docker 데몬 로그와 Clash 연결 로그를 나란히 비교하면 추측을 크게 줄일 수 있습니다. 특히 같은 명령을 TUN을 끈 상태와 켠 상태에서 각각 실행해 차이를 보세요. TUN을 켰을 때만 인증이 실패한다면 DNS 가로채기나 가상 주소 호환성에 초점을 맞추고, 두 상태 모두 실패한다면 계정·레지스트리·방화벽 문제를 먼저 분리해야 합니다.
자주 묻는 질문
Docker에 HTTPS_PROXY를 설정했는데 TUN도 필요한가요?
반드시 필요한 것은 아닙니다. Docker 데몬이 지원하는 프록시 환경 변수를 정확히 설정할 수 있고, 모든 외부 요청이 HTTP 프록시로 충분히 처리된다면 TUN 없이 운영할 수도 있습니다. 다만 여러 프로세스와 컨테이너의 DNS·HTTPS 경로를 한 정책으로 묶고 싶거나, 프록시를 인식하지 않는 도구까지 동일하게 처리해야 한다면 TUN이 더 편리할 수 있습니다. 두 방식을 동시에 켜기 전에 어느 계층이 최종 출구를 담당할지 정해야 합니다.
Docker Hub 규칙을 넣었는데 레이어 다운로드가 계속 실패합니다.
인증과 레지스트리 API만 규칙에 들어가고 실제 레이어를 제공하는 저장소 또는 CDN 호스트가 빠졌을 가능성이 있습니다. 실패 직후 Clash 로그에서 새로 나타난 호스트를 확인한 뒤, 해당 도메인이 신뢰할 수 있는 Docker Hub 전달 경로인지 검토하고 필요한 경우 좁은 규칙을 추가하세요. 무작정 모든 CDN을 프록시로 보내는 방식은 다른 서비스까지 영향을 줄 수 있습니다.
사내 레지스트리는 프록시를 거치지 않게 할 수 있나요?
가능합니다. 내부 도메인과 사설 IP 대역을 명시적인 DIRECT 규칙으로 앞쪽에 배치하고, 사내 DNS가 Clash에서 정상적으로 해석되는지 확인하세요. 규칙 순서가 중요하므로 마지막의 광범위한 프록시 또는 MATCH 규칙보다 내부 예외가 먼저 와야 합니다. 기업 네트워크 정책상 외부 프록시 사용이 제한될 수 있으므로 보안팀의 기준도 함께 확인해야 합니다.
TUN을 켜면 모든 Docker 트래픽이 외부 노드로 전송되나요?
그렇지는 않습니다. 실제 경로는 YAML 규칙, DNS 설정, 기본 라우팅, 예외 규칙에 따라 결정됩니다. 하지만 규칙이 너무 넓거나 마지막 기본 규칙이 프록시 그룹을 가리키면 예상보다 많은 트래픽이 외부 노드로 갈 수 있습니다. 내부 주소, 로컬 개발 서비스, 사내 레지스트리, 민감한 관리 엔드포인트에는 명시적인 예외를 두고 로그로 확인하는 것이 안전합니다.
단순한 환경 변수 방식은 Docker 데몬 하나에 적용하기 쉽지만, Docker Desktop VM·컨테이너 내부 도구·여러 레지스트리를 함께 다룰 때 설정이 흩어지고 누락된 호스트를 찾기 어렵습니다. 반대로 일부 GUI 프록시 도구는 브라우저 연결에는 편리해도 TUN 라우팅, DNS 가로채기, 규칙별 로그가 부족해 “브라우저는 되는데 Docker만 실패”하는 상황을 설명하기 어렵습니다. 이 글에서 살펴본 것처럼 Clash는 TUN과 YAML 규칙으로 Docker Hub·CDN·사내 레지스트리를 분리하고, 실제 연결 로그로 경로를 검증할 수 있다는 장점이 있으므로, 환경에 맞는 클라이언트와 코어를 선택해 사용해 보세요.