Docker 개발 환경에서는 컨테이너마다 HTTP_PROXY, HTTPS_PROXY, ALL_PROXY 환경 변수를 넣는 방식이 가장 먼저 떠오릅니다. 하지만 이미지 빌드 단계, 패키지 매니저, 서드파티 바이너리, 자식 프로세스까지 모든 요청이 환경 변수를 제대로 따르는 것은 아닙니다. 컨테이너가 늘어날수록 예외 설정도 함께 늘어나고, 프록시를 사용하지 않아야 하는 사내 서비스까지 잘못 우회하는 문제가 생깁니다.
이때 Docker 호스트에서 실행 중인 Clash 또는 Mihomo를 투명 프록시로 구성하면 애플리케이션 내부 설정을 최소화할 수 있습니다. Docker 브리지 네트워크에서 외부로 나가는 TCP 연결을 호스트의 Clash 포트로 리다이렉트하고, Clash 규칙에 따라 직접 연결 또는 프록시 경유를 선택하는 구조입니다. 이 글에서는 Linux Docker 호스트를 기준으로 네트워크 구조, redir 기반 설정, DNS 처리, 컨테이너별 예외, 이미지 다운로드와 패키지 설치 문제, 프록시 루프를 단계적으로 점검합니다.
Docker 투명 프록시의 전체 구조
일반적인 Docker 컨테이너는 docker0 또는 사용자 정의 브리지 네트워크를 통해 호스트의 NAT를 거쳐 외부로 통신합니다. 컨테이너가 외부 서버에 접속할 때 패킷은 대략 다음 경로를 통과합니다.
- 컨테이너가 대상 도메인 또는 IP로 연결을 시작합니다.
- Docker 브리지 인터페이스가 패킷을 호스트 네트워크 네임스페이스로 전달합니다.
- 호스트의 방화벽 규칙이 패킷을 검사하고 필요한 경우 Clash의 투명 프록시 포트로 리다이렉트합니다.
- Clash가 원래 목적지를 복원하고 도메인, IP, 규칙 제공자에 따라 프록시 또는 직접 연결을 선택합니다.
- 응답은 같은 연결 상태를 따라 컨테이너로 반환됩니다.
핵심은 컨테이너 내부의 127.0.0.1과 Docker 호스트의 127.0.0.1이 서로 다르다는 점입니다. 컨테이너에서 127.0.0.1:7890에 접속하면 호스트의 Clash가 아니라 해당 컨테이너 자신을 가리킵니다. 따라서 투명 프록시 포트는 호스트의 Docker 브리지 주소나 모든 인터페이스에 바인딩해야 합니다. 동시에 관리 API 포트까지 외부에 노출하면 안 되므로, 프록시 수신 포트와 외부 컨트롤러 포트를 분리해야 합니다.
| 구성 요소 | 권장 역할 | 주의할 점 |
|---|---|---|
| Clash/Mihomo | 호스트에서 실행되는 투명 프록시 | 프록시 포트는 Docker 브리지에서 접근 가능해야 함 |
| Docker bridge | 컨테이너 트래픽의 진입점 | 사용자 정의 네트워크를 쓰면 인터페이스와 대역을 별도로 확인 |
| iptables 또는 nftables | 컨테이너의 TCP 연결을 Clash로 리다이렉트 | Clash 자체 트래픽과 로컬 네트워크는 제외해야 함 |
| Clash DNS | 도메인 조회와 fake-ip 또는 실제 IP 처리 | 컨테이너 DNS와 호스트 DNS의 경로를 함께 점검 |
Clash 호스트 수신 포트 설정
Mihomo를 호스트에서 실행한다면 먼저 Docker 브리지에서 접근할 수 있는 리스너를 활성화합니다. 가장 단순한 방법은 redir-port를 사용하는 것입니다. 이 포트는 Linux의 리다이렉트 규칙과 함께 사용되며, 일반 HTTP 프록시처럼 요청 헤더에 프록시 정보를 넣지 않아도 원래 목적지를 추적할 수 있습니다.
redir-port: 7892 mixed-port: 7890 allow-lan: true bind-address: '*' external-controller: 127.0.0.1:9090 secret: 'change-this-secret' mode: rule log-level: info lan-allowed-ips: - 192.168.0.0/16 - 172.16.0.0/12 - 10.0.0.0/8
allow-lan: true와 bind-address: '*'는 Docker 브리지에서 Clash 포트로 접근하기 위해 필요할 수 있습니다. 다만 이 설정은 모든 네트워크 인터페이스에서 포트를 열 수 있으므로 호스트 방화벽으로 접근 범위를 제한해야 합니다. 외부 컨트롤러는 관리 명령을 수행할 수 있는 API이므로 반드시 127.0.0.1에만 바인딩하고 강력한 시크릿을 사용하세요. Docker 컨테이너에 API 포트 9090을 공개하는 것은 투명 프록시 구성에 필요하지 않습니다.
호스트의 Docker 브리지 주소가 172.17.0.1이라면 컨테이너에서 이 주소의 7892 포트에 TCP 연결이 가능한지 먼저 확인합니다. 사용자 정의 브리지 네트워크를 사용하는 경우 게이트웨이는 달라질 수 있으므로 docker network inspect 네트워크이름으로 실제 서브넷과 게이트웨이를 확인해야 합니다.
redir-port 기반 TCP 투명 프록시를 안정화한 뒤, UDP와 더 복잡한 라우팅이 필요할 때 TUN을 별도로 검토하는 편이 안전합니다.Docker 브리지 트래픽 리다이렉트
Clash 포트를 준비했다면 Docker 브리지에서 나가는 TCP 패킷을 7892로 보냅니다. 아래 예시는 Docker 기본 브리지인 docker0과 일반적인 컨테이너 대역을 기준으로 한 개념 설정입니다. 배포판의 방화벽 관리 방식과 Clash 버전에 따라 iptables 또는 nftables 문법이 다를 수 있으므로, 운영 환경에서는 기존 규칙을 백업한 후 적용하세요.
# Clash가 사용하는 사용자 정의 체인 iptables -t nat -N CLASH_DOCKER 2>/dev/null || true iptables -t nat -F CLASH_DOCKER # 사설망, 브리지 게이트웨이, 멀티캐스트는 프록시하지 않음 iptables -t nat -A CLASH_DOCKER -d 127.0.0.0/8 -j RETURN iptables -t nat -A CLASH_DOCKER -d 10.0.0.0/8 -j RETURN iptables -t nat -A CLASH_DOCKER -d 172.16.0.0/12 -j RETURN iptables -t nat -A CLASH_DOCKER -d 192.168.0.0/16 -j RETURN iptables -t nat -A CLASH_DOCKER -d 224.0.0.0/4 -j RETURN # Clash 프로세스가 만든 패킷은 다시 가로채지 않도록 제외 iptables -t nat -A CLASH_DOCKER -m owner --uid-owner clash -j RETURN # 나머지 TCP 연결을 redir-port로 전달 iptables -t nat -A CLASH_DOCKER -p tcp -j REDIRECT --to-ports 7892 # docker0에서 들어오는 컨테이너 트래픽에 체인 연결 iptables -t nat -A PREROUTING -i docker0 -p tcp -j CLASH_DOCKER
위 규칙에서 --uid-owner clash의 사용자 이름은 실제 Clash 프로세스를 실행하는 Linux 사용자와 일치해야 합니다. systemd 서비스가 다른 사용자로 실행되거나 컨테이너 안에서 Clash가 실행되는 경우에는 UID를 확인해 수정해야 합니다. 또한 Docker Compose가 만든 네트워크는 br-xxxxxxxx 형태의 별도 인터페이스를 만들 수 있으므로, 해당 네트워크도 같은 체인에 연결해야 합니다.
리다이렉트 규칙을 적용하기 전에 다음 항목을 기록해 두면 장애 복구가 쉽습니다.
ip addr로docker0와br-*인터페이스의 주소를 확인합니다.docker network inspect로 컨테이너 서브넷과 게이트웨이를 확인합니다.ss -lntp로 Clash가 실제로7892포트를 열고 있는지 확인합니다.- iptables의 기존
DOCKER,DOCKER-USER체인을 무작정 삭제하지 않습니다. - 재부팅 후에도 규칙이 필요한 경우 배포판의 영구 방화벽 설정에 등록합니다.
DNS와 도메인 기반 라우팅
TCP 연결만 리다이렉트해도 IP 주소로 직접 접속하는 요청은 처리할 수 있지만, 개발 도구 대부분은 먼저 DNS 조회를 수행합니다. 컨테이너가 호스트의 로컬 DNS나 회사 네트워크 DNS를 직접 사용하면 도메인 조회가 프록시 경로와 분리될 수 있습니다. 이 때문에 Git 저장소, 패키지 저장소, 컨테이너 레지스트리에서 간헐적인 연결 실패나 잘못된 IP가 발생할 수 있습니다.
개발 환경에서는 fake-ip가 도메인 기반 규칙과 잘 맞습니다. 다만 컨테이너 내부 DNS가 Clash DNS를 사용하도록 연결되어 있어야 하며, Docker의 내장 DNS 주소인 127.0.0.11을 단순히 호스트 주소로 오해하면 안 됩니다. 가장 먼저 일반적인 도메인 조회와 실제 외부 연결을 따로 테스트하세요.
dns: enable: true listen: 0.0.0.0:1053 enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16 fake-ip-filter: - '*.lan' - '*.local' - 'localhost.ptlogin2.qq.com' - 'time.*.com' nameserver: - https://1.1.1.1/dns-query - https://8.8.8.8/dns-query fallback: - tls://1.1.1.1:853 - tls://8.8.8.8:853
Docker Compose 서비스에서 DNS를 명시적으로 지정해야 한다면 Clash가 바인딩된 호스트 주소를 사용합니다. 그러나 이 경우 DNS 포트가 Docker 브리지에서 접근 가능한지, 방화벽이 UDP와 TCP 1053을 허용하는지 확인해야 합니다. DNS 요청을 강제로 리다이렉트하는 방법은 환경마다 충돌 가능성이 높으므로, 먼저 Compose의 dns 옵션으로 특정 테스트 서비스에만 적용하는 것을 권장합니다.
fake-ip-range에 속하는 주소를 사설망 예외 규칙으로 잘못 제외하지 마세요. 198.18.0.0/15는 fake-ip에 사용되는 특수 대역입니다. 이 주소가 일반 인터넷 주소라고 가정해 직접 연결 규칙을 추가하면 도메인 매핑이 깨지고 컨테이너 연결이 타임아웃될 수 있습니다.서비스별 예외와 안전한 개발 규칙
모든 Docker 트래픽을 프록시로 보내는 것이 항상 최선은 아닙니다. 데이터베이스, 사내 Kubernetes API, 로컬 레지스트리, 호스트의 개발 서버는 직접 연결해야 합니다. 이런 대상은 프록시 규칙에서 먼저 제외해야 하며, 그렇지 않으면 인증서 검증 실패, 지연 증가, 클러스터 내부 주소 접근 실패가 발생합니다.
rules: # Docker 및 사내 네트워크는 먼저 직접 연결 - IP-CIDR,172.17.0.0/16,DIRECT - IP-CIDR,172.18.0.0/16,DIRECT - IP-CIDR,10.20.0.0/16,DIRECT - DOMAIN-SUFFIX,corp.example,DIRECT - DOMAIN,registry.local,DIRECT # 개발 도구와 외부 저장소는 프록시 그룹으로 전달 - DOMAIN-SUFFIX,github.com,Developer-Proxy - DOMAIN-SUFFIX,githubusercontent.com,Developer-Proxy - DOMAIN-SUFFIX,npmjs.org,Developer-Proxy - DOMAIN-SUFFIX,pypi.org,Developer-Proxy - DOMAIN-SUFFIX,docker.io,Developer-Proxy - MATCH,DIRECT
규칙은 위에서 아래로 평가되므로 사설망과 로컬 레지스트리 예외를 외부 저장소 규칙보다 앞에 배치해야 합니다. MATCH,DIRECT를 마지막에 둘지 프록시 그룹으로 둘지는 환경 정책에 따라 결정합니다. 기본값을 직접 연결로 두면 누락된 도메인은 프록시를 우회하고, 기본값을 프록시로 두면 새로 추가된 외부 의존성도 안정적으로 접근할 가능성이 높아집니다. 민감한 사내 서비스가 있는 환경에서는 명시적인 DOMAIN과 IP-CIDR 예외를 충분히 작성한 뒤 기본값을 선택하세요.
서비스별로 다른 경로가 필요하다면 Docker Compose의 네트워크를 분리하는 것도 유용합니다. 예를 들어 외부 패키지를 다운로드하는 빌드 컨테이너와 사내 데이터베이스에만 접근하는 애플리케이션 컨테이너를 별도 네트워크에 배치할 수 있습니다. 그 후 인터페이스별 방화벽 체인 또는 컨테이너 서브넷별 규칙을 적용하면 애플리케이션 코드나 이미지에 프록시 환경 변수를 넣지 않고도 정책을 구분할 수 있습니다.
이미지 다운로드와 연결 오류 진단
투명 프록시를 적용한 뒤 가장 먼저 실패하는 작업은 Docker 이미지 다운로드일 수 있습니다. 중요한 점은 일반 컨테이너 트래픽과 Docker 데몬 트래픽의 경로가 다를 수 있다는 사실입니다. docker pull은 사용자의 셸이 아니라 Docker 데몬이 수행하므로, 데몬이 호스트 프로세스로 실행된다면 컨테이너 리다이렉트 규칙의 대상이 아닐 수 있습니다. 이 경우 Docker 데몬 자체에 프록시를 설정하거나, 레지스트리 도메인에 대한 호스트 라우팅을 별도로 구성해야 합니다.
| 증상 | 가능한 원인 | 확인 방법 |
|---|---|---|
컨테이너는 연결되지만 docker pull만 실패 | Docker 데몬이 리다이렉트 체인 밖에 있음 | Docker 서비스 환경 변수와 데몬 로그 확인 |
| 도메인은 조회되지만 HTTPS가 타임아웃 | TCP 리다이렉트 누락 또는 Clash 포트 미수신 | ss, 방화벽 카운터, Clash 연결 목록 확인 |
| 사내 DB가 프록시로 전송됨 | 사설망 예외가 없거나 규칙 순서가 잘못됨 | 연결 상세에서 매칭 규칙과 목적지 확인 |
| Clash CPU 사용량이 계속 증가 | Clash 자체 연결이 다시 리다이렉트되는 루프 | 프로세스 UID 예외와 NAT 체인 확인 |
| 일부 패키지만 인증서 오류 | 중간 프록시 인증서, fake-ip, 시스템 CA 문제 | 컨테이너 시간과 CA 번들, DNS 응답 확인 |
테스트는 한 번에 하나씩 진행합니다. 먼저 컨테이너에서 getent hosts example.com으로 DNS 응답을 확인하고, 다음으로 curl -v https://example.com을 실행해 TCP와 TLS 단계를 확인합니다. 그 다음 Clash 대시보드의 연결 목록에서 해당 도메인, 매칭된 규칙, 사용된 프록시 그룹을 확인하세요. 연결이 대시보드에 전혀 나타나지 않는다면 방화벽 리다이렉트 또는 인터페이스 선택이 문제일 가능성이 큽니다. 연결은 나타나지만 실패한다면 DNS, 노드, 인증서, MTU를 순서대로 점검하는 편이 효율적입니다.
프록시 루프는 특히 주의해야 합니다. 컨테이너의 패킷을 Clash로 보냈는데 Clash가 업스트림 프록시와 통신할 때 생성한 패킷까지 다시 같은 리다이렉트 체인에 들어가면, 연결이 계속 중첩되어 결국 타임아웃이 발생합니다. Clash 프로세스의 UID를 반환 규칙에 등록하고, 프록시 서버의 IP와 호스트의 로컬·사설 대역을 명시적으로 제외하세요. 규칙을 수정한 뒤에는 기존 연결을 모두 종료하고 새 연결로 재시험해야 이전 상태의 오해를 피할 수 있습니다.
운영 전 최종 점검
개발용 투명 프록시는 빠르게 적용할 수 있지만, 재부팅과 네트워크 변경을 고려하지 않으면 쉽게 깨집니다. 운영 또는 팀 공용 개발 서버에 적용하기 전에는 다음 점검을 완료하세요.
- Clash 설정 파일의 백업과 문법 검사를 완료합니다.
- 프록시 포트는 Docker 브리지에서만 접근 가능하게 하고 관리 API는 로컬에 제한합니다.
- iptables 또는 nftables 규칙의 적용 순서와 재부팅 후 복원 방식을 문서화합니다.
- Docker 기본 브리지와 Compose 사용자 정의 브리지를 모두 테스트합니다.
- 사내망, 로컬호스트, 데이터베이스, 레지스트리, DNS 서버의 직접 연결 정책을 명시합니다.
- Git, npm, pip, Maven, Docker Registry 등 실제 사용하는 저장소를 각각 테스트합니다.
- Clash 연결 목록과 시스템 로그에서 루프, 재시도, DNS 실패가 없는지 확인합니다.
- UDP가 필요한 서비스는 TCP 투명 프록시만으로 해결되지 않는다는 점을 팀에 알립니다.
가장 안정적인 도입 순서는 테스트용 Docker 네트워크 하나에만 규칙을 연결하고, 단순한 curl과 패키지 다운로드를 검증한 뒤 서비스 범위를 넓히는 것입니다. 컨테이너마다 환경 변수를 반복하지 않으면서도 외부 개발 리소스에는 일관된 프록시 정책을 적용할 수 있고, 사내 서비스는 명확한 예외 규칙으로 직접 연결할 수 있습니다. 구성을 완성한 뒤에는 사용하는 Docker 네트워크 대역, Clash 리스너, 방화벽 규칙, 예외 도메인을 팀 문서에 남겨 두면 새 프로젝트를 시작할 때 같은 문제를 반복하지 않게 됩니다.
Clash로 트래픽을 완전히 제어하세요
Windows, macOS, Linux, Android, iOS 지원. 유연한 규칙, 간단한 설정.