Docker 컨테이너 VPN 라우팅 시 포트 사라짐 해결법
Gluetun을 사용하여 컨테이너를 VPN으로 라우팅할 때 포트가 사라지는 원인을 설명합니다. 네트워크 네임스페이스 공유로 인해 발생하는 문제를 해결하고, 정상적으로 작동하는 Docker Compose 설정 파일과 올바른 포트 매핑 방법을 상세히 안내합니다.
Docker 컨테이너를 VPN으로 라우팅할 때 포트가 사라지는 이유
Docker 컨테이너를 VPN으로 라우팅하려면 한 컨테이너에 터널을 연결한 뒤, network_mode: "service:gluetun"를 사용하여 다른 컨테이너들을 해당 컨테이너의 네트워크 네임스페이스에 연결해야 합니다. 이 연결 과정에서 많은 사용자가 혼란을 겪습니다. 연결된 컨테이너는 더 이상 자체 네트워크를 가지지 않으므로, 해당 컨테이너의 게시된 포트와 Docker 서비스 이름도 함께 사라집니다. 대신 VPN 컨테이너에서 포트를 게시하면, 다른 컨테이너들은 VPN 컨테이너의 이름을 통해 해당 애플리케이션에 접근할 수 있습니다.
연결된 컨테이너에 ports: 블록을 그대로 두면 Docker는 컨테이너 생성을 거부합니다.
Error response from daemon: conflicting options: port publishing and the container type network mode여기서 사용하는 도구는 Gluetun입니다. 이 컨테이너는 WireGuard나 OpenVPN을 통해 상용 VPN(가상 사설망) 제공업체에 연결되며 자체 방화벽을 포함하고 있습니다. 2026년 10월 기준 최신 릴리스는 v3.41.3입니다. 예제에서는 Mullvad와 WireGuard를 사용하므로, 해당 제공업체의 계정과 키가 필요합니다. 만약 직접 소유한 하드웨어에서 터널을 종료하고 싶다면, VPS에서 직접 WireGuard 서버 운영하기를 통해 반대편 종단점을 구축하거나, Docker에서 wg-easy 사용하기를 통해 웹 인터페이스로 관리할 수 있습니다.
network_mode: "service:gluetun"의 실제 동작 방식
모든 Docker 컨테이너는 기본적으로 고유한 네트워크 네임스페이스를 할당받습니다. 여기에는 자체 인터페이스, 라우팅 테이블, 방화벽 규칙, 리스닝 소켓이 포함됩니다. service: 모드는 이 과정을 건너뛰고 gluetun의 네임스페이스 안에서 컨테이너를 시작합니다. 하나의 네임스페이스를 공유한다는 것은 하나의 IP 주소를 사용한다는 의미이며, 이는 다음 6가지 사항을 변화시킵니다.
- 애플리케이션은 고유 주소를 갖지 않습니다. 애플리케이션의 주소는 곧 gluetun의 주소가 됩니다.
- 애플리케이션은 어떤 Docker 네트워크에도 연결되지 않으므로, 서비스 이름이 등록되지 않으며 이름 해석도 불가능합니다. 다른 컨테이너들은 반드시
gluetun를 사용해야 합니다. - 같은 네임스페이스 안의 컨테이너들은
localhost을 통해 서로 통신합니다. - 하나의 네임스페이스 안에 있는 두 컨테이너는 동일한 포트에서 리스닝할 수 없습니다. Gluetun 문서에서는 이에 대해 우회 방법이 없다고 명시하고 있습니다.
- 권한(Capabilities)은 네임스페이스가 아닌 컨테이너에 귀속됩니다. Gluetun은 터널 인터페이스를 생성하므로
NET_ADMIN와/dev/net/tun권한을 보유합니다. 하지만 연결된 컨테이너는 이를 상속받지 않습니다. - Compose는 한 서비스가
network_mode과networks을 동시에 설정한 파일을 거부합니다. gluetun을 네트워크에 연결하면, 애플리케이션은 그 네트워크를 함께 사용하게 됩니다.
gluetun을 재시작하면 여기에 연결된 모든 컨테이너의 연결이 끊어집니다. 이는 문서화된 동작 방식이며, 연결 실패 시 gluetun이 프로세스를 종료하는 대신 컨테이너 내부에서 VPN 프로세스를 재시작하는 이유이기도 합니다. 사용자가 직접 gluetun을 재시작하거나 재생성한 후에는, gluetun에 연결된 컨테이너들도 함께 재시작하십시오.
작동하는 compose 파일
services:
gluetun:
image: qmcgaw/gluetun:v3
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- SERVER_CITIES=Amsterdam
- TZ=Europe/Amsterdam
env_file:
- ./gluetun.env
volumes:
- ./gluetun:/gluetun
ports:
- 127.0.0.1:8080:8080/tcp
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun"
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
- WEBUI_PORT=8080
volumes:
- ./qbittorrent:/config
- ./downloads:/downloads
depends_on:
gluetun:
condition: service_healthy
restart: unless-stopped:v3 태그는 v3 시리즈의 최신 안정화 릴리스입니다. :latest 태그는 master 브랜치의 마지막 커밋을 가리키며, 이는 개발 중인 최신 버전입니다. 따라서 디버깅을 원치 않는 운영 환경의 서버라면 :v3 버전을 고정하여 사용하십시오.
WEBUI_PORT=8080은 게시된 포트와 일치해야 합니다. qBittorrent는 gluetun의 네임스페이스 내부에서 바인딩되며, 게시 규칙은 호스트 트래픽을 해당 네임스페이스의 8080 포트로 전달하기 때문입니다. 두 값 중 하나라도 다르게 설정하면 포트는 응답하지 않습니다. 127.0.0.1:8080:8080는 웹 인터페이스를 호스트의 루프백 주소에 유지합니다. 단순히 8080:8080만 설정하면 모든 인터페이스에 게시되고 자체 방화벽 규칙을 작성하게 되는데, 이것이 바로 Docker 게시 포트가 ufw를 우회하는 방식입니다.
서비스를 시작한 뒤 다음 순서로 확인하십시오:
docker compose up -d
docker compose ps
docker compose logs gluetun | tail -30docker compose ps를 실행하면 gluetun은 healthy로, qbittorrent는 running으로 표시되어야 합니다. 그 다음 네임스페이스 내부에서 나가는 주소를 확인하십시오. 이 확인 과정이 나머지 모든 동작의 정상 여부를 결정합니다:
docker run --rm --network=container:gluetun alpine:3.22 sh -c "apk add wget && wget -qO- https://ipinfo.io"해당 JSON의 ip 필드에는 VPN 제공업체의 주소가 표시되어야 합니다. 만약 서버 자신의 주소가 표시된다면, 애플리케이션이 터널 내부에 있지 않은 상태이므로 이후의 모든 동작은 설명과 다르게 작동할 것입니다.
키를 compose 파일 외부에 보관하기
gluetun.env은 자격 증명을 담고 있으며, git 저장소에 포함되지 않아야 합니다.
WIREGUARD_PRIVATE_KEY=wOEI9rqqbDwnN8/Bpp22sVz48T71vJ4fYmFWujulwUU=
WIREGUARD_ADDRESSES=10.64.222.21/32두 값 모두 제공업체의 계정 영역에서 생성한 WireGuard 설정 파일에서 가져옵니다. 파일 모드를 600으로 설정하십시오. 이 방식이 제공하는 이점을 명확히 이해해야 합니다. 키가 저장소에 포함되지는 않지만, docker inspect gluetun는 Docker 소켓에 접근할 수 있는 누구에게나 모든 환경 변수를 출력할 수 있습니다. Docker Compose의 환경 파일 및 보안 정보에서 더 강력한 보안 옵션을 다룹니다.
터널 외부의 컨테이너가 내부 컨테이너와 통신하는 방법
양방향 통신이 가능하며, 각 방향마다 다른 이름을 사용합니다. 두 컨테이너는 Docker 네트워크를 공유해야 합니다. 연결된 컨테이너는 자체 네트워크가 없으므로 gluetun의 네트워크를 사용하게 됩니다. Docker Compose 네트워크 연결 방식에서 기본 설정을 다룹니다.
외부에서 내부로 통신할 때는 gluetun의 이름과 애플리케이션이 수신 대기 중인 포트를 사용하십시오. 리버스 프록시 컨테이너는 gluetun:8080를 통해 qBittorrent 웹 인터페이스에 접근합니다. 컨테이너 간 트래픽은 Docker 네트워크 내부에서만 처리되며 호스트 포트를 거치지 않으므로, 이를 위해 별도의 ports: 항목을 설정할 필요는 없습니다.
내부에서 외부로 통신할 때는 다른 컨테이너의 서비스 이름(예: postgres:5432)을 사용하십시오. gluetun은 v3.41 버전부터 네임스페이스 내부에서 다른 컨테이너 이름을 해석할 수 있습니다. 이름 해석이 되지 않는다면 해당 버전 이상으로 고정하십시오.
gluetun의 방화벽은 연결 허용 대상을 결정합니다. gluetun 자체 Docker 네트워크에서 발생하는 트래픽은 허용됩니다. 서로 다른 서브넷에 있는 클라이언트, LAN에 연결된 노트북, 또는 별도의 브리지 네트워크에 있는 컨테이너는 gluetun의 방화벽과 라우팅을 통해 반환 경로가 허용되어야 합니다.
FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24문서에 명시된 의미는 정확합니다. 이는 Gluetun과 해당 네트워크 스택을 공유하는 컨테이너가 접근할 수 있도록 허용된 서브넷을 쉼표로 구분하여 나열한 것입니다.
인터넷에서 들어오는 인바운드 연결은 별개의 문제입니다. 토렌트 클라이언트의 피어는 VPN 측에서 들어오므로, 호스트에서 6881 포트를 공개해도 아무런 효과가 없습니다. 서비스 제공업체로부터 포트 포워딩을 받아야 하며, 해당 포트를 VPN 서버 측에서 허용하도록 FIREWALL_VPN_INPUT_PORTS에 기재해야 합니다. 이 예시에서 사용된 Mullvad는 포트 포워딩을 지원하지 않으므로, 이를 지원하는 제공업체가 필요합니다. 이는 Docker Compose로 구축된 미디어 스택에서 가장 흔히 누락되는 부분입니다.
킬 스위치: 터널 연결이 끊어질 때 발생하는 일
이 패턴은 장애 발생 시 그 복잡함에 상응하는 가치를 증명합니다. 연결된 컨테이너는 Gluetun의 인터페이스와 라우팅 테이블을 공유합니다. Gluetun의 방화벽은 터널 외부의 일반적인 인터넷 트래픽을 차단하는 동시에, VPN 서버 엔드포인트, 로컬 Docker 트래픽 및 설정된 예외 사항은 허용합니다. 이 방화벽이 바로 터널이 끊어졌을 때 일반 인터페이스로 연결이 전환되는 것을 방지하는 핵심입니다. 클라이언트가 재연결을 시도하는 동안 패킷이 일반 인터페이스로 유출되는 틈은 존재하지 않습니다.
Gluetun은 자신의 연결 상태를 감시합니다. 1분마다 HEALTH_ICMP_TARGET_IPS에 지정된 주소로 ICMP 에코(ping)를 보내며, 기본값은 1.1.1.1,8.8.8.8입니다. ICMP가 허용되지 않는 환경이라면, 이 간단한 확인 절차는 일반 텍스트 UDP DNS 쿼리로 대체됩니다. 5분마다 HEALTH_TARGET_ADDRESSES(기본값 cloudflare.com:443,github.com:443)를 대상으로 전체 TCP 및 TLS(transport layer security) 다이얼을 시도합니다. 이 과정이 실패하면 컨테이너 내부에서 VPN을 재시작하고 다음과 같이 로그를 남깁니다.
WARN [vpn] restarting VPN because it failed to pass the healthcheck: periodic check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout이러한 순서를 염두에 두고 연결된 컨테이너의 로그를 확인하십시오. 애플리케이션 내부의 connection refused, operation not permitted, i/o timeout와 같은 줄은 터널이 죽었기 때문에 발생하는 결과이지 원인이 아닙니다. Gluetun 문서에서도 이 점을 명확히 밝히고 있는데, 많은 사용자가 결과만 보고 이를 원인으로 착각하여 몇 시간씩 헛수고를 하기 때문입니다.
HEALTH_RESTART_VPN=on는 기본 설정이며 켜두어야 합니다. 특정 장애를 디버깅하는 동안에만 잠시 끄십시오. 이 옵션을 끄면 터널이 끊어졌을 때 자동으로 복구되지 않고 그대로 죽어버리기 때문입니다.
순서 지정: 터널이 연결되기 전에 스택이 시작되지 않도록 방지
이 이미지에는 Docker healthcheck가 포함되어 있습니다:
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=3 CMD /gluetun-entrypoint healthcheck이 명령어는 gluetun의 짧은 수명 복사본을 실행하여 실행 중인 gluetun의 상태 서버인 http://127.0.0.1:9999/에 질의합니다. 터널이 정상 작동하면 200 OK을 반환합니다. 터널이 끊기면 오류 문자열과 함께 500 Internal server error을 반환하며, Docker는 3회 연속 실패 시 컨테이너를 unhealthy 상태로 표시합니다.
condition: service_healthy은 이를 기다리는 설정입니다. 단순히 depends_on: [gluetun]를 사용하면 컨테이너가 시작되기만을 기다리는데, 이는 핸드셰이크가 완료되기 수 초 전에 발생합니다. 따라서 애플리케이션은 네트워크가 연결되지 않은 상태에서 시작되어 첫 연결 시도부터 실패하는 경우가 많습니다. Docker Compose의 Healthcheck에서 구문과 타이밍 필드에 대해 자세히 다룹니다.
사용자가 자주 겪는 한계가 하나 있습니다. Compose는 스택을 시작할 때 해당 조건을 평가합니다. gluetun이 나중에 unhealthy 상태가 되더라도 애플리케이션을 중지하거나 재시작하지는 않습니다. 대신 gluetun 내부의 자동 복구 기능이 이 상황을 처리하며, 이것이 컨테이너 자체가 아닌 VPN 프로세스를 재시작하는 이유입니다.
설정을 신뢰하기 전에 DNS 유출 여부를 확인하십시오
DNS(domain name system)는 올바르게 구성된 터널에서도 발생하는 유출 경로입니다. Gluetun은 네임스페이스 내부에서 자체 리졸버를 실행하며, 기본적으로 DNS_UPSTREAM_RESOLVER_TYPE=dot 및 DNS_UPSTREAM_RESOLVERS=cloudflare을 통해 Cloudflare로 DoT(DNS over TLS) 쿼리를 전달합니다. 이 설정을 그대로 두면 조회 요청이 암호화되어 터널을 통해 전송됩니다.
이 설정을 로컬 리졸버로 지정하여 무력화하는 설정이 DNS_UPSTREAM_PLAIN_ADDRESSES입니다. 사용자는 이름 해석이 실패할 때 라우터나 ISP의 리졸버가 응답하도록 하려고 이 설정을 변경하곤 합니다. Gluetun 문서에서는 Docker 브리지나 LAN의 리졸버로 쿼리를 보내면 VPN 터널 외부로 유출된다고 경고합니다. 공개 평문 리졸버를 사용하면 터널을 통해 쿼리가 전달되기는 하지만, VPN 제공자가 이를 읽거나 수정할 수 있습니다. 동일한 실수의 WireGuard 버전은 WireGuard 터널에서 DNS 해석이 중단되는 경우에서 다룹니다.
테스트하려면 gluetun에서 SHADOWSOCKS=on 및 SHADOWSOCKS_PASSWORD를 설정하고, 8388:8388/tcp 및 8388:8388/udp을 게시한 다음, UDP DNS를 터널링하도록 구성된 Shadowsocks 클라이언트로 연결하여 DNS 유출 테스트를 실행하십시오. 결과에는 홈 라우터가 아닌 VPN 제공자나 Cloudflare가 표시되어야 합니다. Gluetun 자체 문서에서는 네임스페이스 내부의 리졸버가 최종 응답 서버가 아닌 로컬 캐싱 중개자 역할을 하므로 일부 유출 테스트에서 이상한 결과가 보고될 수 있다고 경고합니다. 다른 국가나 본인의 ISP 리졸버가 표시된다면 이를 실제 유출 신호로 간주하십시오.
VPN 사이드카와 함께 Tailscale 추가하기 및 우선순위 결정
Tailscale은 자신의 장비에 접근하기 위해 WireGuard 기반으로 구축된 오버레이 네트워크이며, 관리 경로를 유지하기 위해 VPN 제공업체의 서비스와 함께 실행하는 경우가 많습니다. 네임스페이스를 공유하면 방화벽 규칙도 공유하게 되므로, 두 서비스는 호환되는 방화벽 및 라우팅 설정을 갖추어야 합니다. Tailscale 문서에 명시된 기본 동작은 다음과 같습니다. Tailscale은 오버레이 네트워크로 작동하며, Tailscale을 실행 중인 장치 간의 트래픽만 라우팅하고 공용 인터넷 트래픽에는 관여하지 않습니다.
따라서 결과는 설정 하나에 따라 달라집니다.
- 별도 컨테이너에서 실행되는 Tailscale(기본 설정): 애플리케이션의 아웃바운드 트래픽을 전혀 보지 않습니다. 모든 트래픽은 Gluetun이 처리합니다. Tailscale 컨테이너 내의 프로세스는 공유 Docker 네트워크를 통해
gluetun:8080에서 애플리케이션에 접근하며, 해당 연결을 tailnet 클라이언트에 노출하려면 포트 포워딩이나 프록시가 필요합니다. network_mode: "service:gluetun"를 사용하여 Gluetun의 네임스페이스에 연결된 Tailscale: 기본 사용자 공간(userspace) 네트워킹 모드에서TS_USERSPACE가 켜져 있으면, tailscaled는 인터페이스를 생성하지 않고 SOCKS5 또는 HTTP 프록시로 작동하므로 라우팅을 변경할 수 없습니다. 애플리케이션이 명시적으로 해당 프록시를 사용하지 않는 한, 일반적인 아웃바운드 트래픽은 여전히 Gluetun을 통해 나갑니다.- 동일한 환경에서
TS_USERSPACE=false사용: Tailscale은 네임스페이스에 포함되지 않는 자체/dev/net/tun장치와 권한이 필요합니다. tailscaled는 터널 장치를 생성하며, Linux에서는 일반적으로100.64.0.0/10에 대한 단일 경로 대신 개별 IPv4 피어 /32 경로를 설치합니다.TS_USERSPACE=false설정만으로는 집계 경로(aggregate route)가 활성화되지 않습니다. 다른 노드의 서브넷 경로는--accept-routes를TS_EXTRA_ARGS에 전달해야만 설치됩니다.TS_ROUTES은 반대 방향으로 작동합니다. 즉, 이 컨테이너가 도달할 수 있는 서브넷을 tailnet의 나머지 부분에 광고하며, 이는tailscale set --advertise-routes=과 동일한 역할을 하고 여기에는 경로를 추가하지 않습니다. 공용 트래픽은 여전히 Gluetun을 통해 나갑니다. - 종료 노드(exit node)가 선택된 Gluetun 네임스페이스 내의 커널 네트워킹,
sudo tailscale set --exit-node=<exit-node-ip>: Tailscale은 해당 네임스페이스에 기본 경로를 설치하지만, Gluetun의 방화벽이 해당 경로를 차단할 수 있습니다. 별도의 컨테이너에서 종료 노드를 선택한다고 해서 애플리케이션의 경로가 변경되지는 않으며, 사용자 공간 모드는 프록시를 통해 명시적으로 전송된 트래픽만 처리합니다. Gluetun 네임스페이스 내에서 경쟁하는 기본 경로를 결합하지 마십시오. 기본 경로는 하나여야 하며, 소유자도 하나여야 합니다.
광고된 경로가 목적이며, 장비 단독이 아닌 박스 뒤의 전체 사설 네트워크에 접근하고자 한다면 VPS에서 Tailscale 서브넷 라우터 실행하기를 참조하십시오. 이 문서에서는 경로 승인, IP 포워딩, 그리고 TS_ROUTES만으로는 해결되지 않는 클라이언트 측 플래그 설정을 다룹니다.
Tailscale의 목적이 경로가 아닌 관리용 URL 제공이라면, tailscale serve 및 tailscale funnel을 사용하여 로컬 HTTP 서비스 앞에 HTTPS를 배치하고 funnel을 통해서만 공용 인터넷에 노출할 수 있습니다. Tailscale v1.104.5의 HTTP 리버스 프록시는 Tailscale 컨테이너가 공유 Docker 네트워크를 통해 접근할 수 있는 경우 http://gluetun:8080를 직접 수락합니다. 로컬호스트가 아닌 대상에는 http:// 스킴을 포함하십시오. Gluetun의 네트워크 네임스페이스를 공유하고 http://127.0.0.1:8080를 대상으로 지정하는 방법도 가능합니다.
한 가지 부작용은 Tailscale이 터널 내부에서 실행될 때 나타납니다. 피어들은 VPN 제공업체의 주소를 보게 되므로, 릴레이로 더 자주 전환될 수 있습니다. 이러한 현상이 발생하면 tailscale status는 피어 옆에 direct 대신 relay "..."을 표시합니다. 연결은 작동하지만 속도는 더 느려집니다. 오버레이 네트워크만 필요한 경우라면 일반 WireGuard와 Tailscale의 차이점을 먼저 살펴보는 것이 좋습니다.
무엇이 문제이며, 어떤 메시지가 나타나는가
Docker가 애플리케이션 컨테이너 생성을 거부합니다. Error response from daemon: conflicting options: port publishing and the container type network mode은 연결된 서비스에 ports: 블록이 여전히 남아 있음을 의미합니다. 이를 gluetun으로 옮기십시오.
Compose가 파일 전체를 거부합니다. 서비스는 network_mode과 networks을 동시에 설정할 수 없습니다. 네트워크를 gluetun에 설정하십시오.
다른 컨테이너가 애플리케이션을 해석(resolve)할 수 없습니다. 연결된 컨테이너가 어떤 네트워크에도 참여하지 않았고 이름을 등록하지 않았기 때문에 curl: (6) Could not resolve host: qbittorrent는 정상적인 동작입니다. gluetun과 포트를 사용하십시오.
두 번째로 연결된 컨테이너가 시작되지 않습니다. 동일한 네임스페이스 내의 두 프로세스는 동일한 포트에 바인딩할 수 없으며, 실패한 프로세스는 주소가 이미 사용 중이라는 오류를 보고합니다. 애플리케이션의 내부 포트를 변경하거나 두 번째 gluetun을 실행하십시오.
gluetun을 건드린 후 애플리케이션의 네트워크가 끊겼습니다. gluetun을 재시작하거나 다시 생성하면 gluetun에 연결된 모든 컨테이너의 연결이 끊어집니다. 해당 컨테이너들을 재시작하십시오.
작은 페이지는 로드되지만 큰 페이지는 멈춥니다. 이는 MTU(maximum transmission unit) 문제입니다. 터널은 오버헤드를 추가하며, 경로상의 특정 지점에서 오류 메시지 없이 크기가 큰 패킷을 폐기합니다. WIREGUARD_MTU를 확인하십시오. v3.41.3의 기본값은 1320이며, 경로상 더 작은 패킷이 필요하다면 이 값을 낮추십시오. 이 설정이 지정되지 않은 경우 개발용 이미지는 경로 MTU 탐색(path MTU discovery)을 사용합니다.
gluetun이 정상 상태(healthy)가 되지 않습니다. 시작 검사 시 첫 번째 의심 요소인 WARN [vpn] restarting VPN because it failed to pass the healthcheck: startup check: dialing: dial tcp4: lookup cloudflare.com: i/o timeout이 표시됩니다. 키가 만료되었는지, 서버 목록이 오래되었는지, 호스트 방화벽이 아웃바운드 UDP를 차단하고 있는지 확인하십시오.
FAQ
Gluetun 뒤에서 컨테이너의 포트 포워딩이 작동하지 않는 이유는 무엇입니까?
network_mode: "service:gluetun"은 컨테이너를 Gluetun의 네트워크 네임스페이스 안으로 배치하기 때문입니다. 하나의 네임스페이스는 하나의 IP 주소와 하나의 포트 대역을 가집니다. 애플리케이션은 계속 대기 상태를 유지하지만, 포트 게시 규칙은 해당 네임스페이스를 소유한 컨테이너에 적용되어야 합니다. ports: 목록을 Gluetun 서비스로 옮기십시오. 연결된 서비스에 그대로 두면 Docker는 해당 규칙을 생성하지 않습니다: Error response from daemon: conflicting options: port publishing and the container type network mode.
VPN 터널 내부의 컨테이너에 외부 컨테이너에서 어떻게 접근합니까?
Gluetun의 서비스 이름과 애플리케이션이 대기 중인 포트를 사용하십시오. 예: gluetun:8080. 연결된 컨테이너는 자체적인 Docker 네트워크를 가지지 않으므로 해당 컨테이너의 이름으로는 주소 해석이 되지 않습니다. 컨테이너 간 통신을 위해 포트를 게시할 필요는 없습니다. 반대로, 네임스페이스 내부의 컨테이너가 외부 컨테이너에 접근할 때는 Gluetun v3.41 이상 버전에서 서비스 이름을 사용하십시오. 예: postgres:5432. LAN에 있는 노트북과 같이 다른 서브넷에 있는 클라이언트가 접근하려면 Gluetun의 방화벽과 라우팅에서 반환 경로가 허용되어야 합니다. 해당 서브넷을 FIREWALL_OUTBOUND_SUBNETS에 추가하십시오.
VPN 연결이 끊어지면 Gluetun이 킬 스위치(kill switch) 역할을 합니까?
네, 방화벽이 활성화된 경우 그렇습니다. 연결된 컨테이너는 Gluetun의 인터페이스와 경로를 공유하며, 방화벽은 터널 외부의 일반 인터넷 트래픽을 차단합니다. 이때 VPN 서버 엔드포인트, 로컬 Docker 트래픽, 설정된 예외 사항은 허용됩니다. Gluetun은 종료되는 대신 내부적으로 VPN을 재시작하며 WARN [vpn] restarting VPN because it failed to pass the healthcheck를 로그에 남깁니다. Gluetun 자체가 재시작되면 연결된 모든 컨테이너의 네트워크가 끊기기 때문입니다.
Tailscale과 Gluetun을 같은 스택에서 사용할 때, 어떤 것이 아웃바운드 트래픽을 처리합니까?
Tailscale을 통해 명시적으로 트래픽을 보내지 않는 한, Gluetun이 일반적인 아웃바운드 트래픽을 처리합니다. Tailscale은 기본적으로 tailnet 내의 장치 간 트래픽만 라우팅하며 공용 트래픽은 건드리지 않습니다. 컨테이너 이미지의 기본 사용자 공간(userspace) 모드에서는 인터페이스를 생성하지 않으므로 라우팅에 영향을 줄 수 없습니다. TS_USERSPACE=false을 사용하면 Linux는 일반적으로 집계된 100.64.0.0/10 경로 대신 개별 IPv4 피어 /32 경로를 가져오며, --accept-routes를 통해 TS_EXTRA_ARGS을 추가한 경우에만 다른 노드의 서브넷 경로를 가져옵니다. TS_ROUTES은 이 컨테이너의 서브넷을 tailnet에 알릴 뿐 로컬 경로를 추가하지는 않습니다. sudo tailscale set --exit-node=<exit-node-ip>로 출구 노드(exit node)를 선택하면 Tailscale의 커널 네트워킹을 사용하는 네임스페이스에만 기본 경로가 설치됩니다. 별도의 컨테이너는 애플리케이션의 경로를 변경하지 않으며, 사용자 공간 모드는 프록시를 통해 전송된 트래픽만 처리합니다. 두 제품을 쌓아서 사용하기보다 하나를 선택하여 기본 경로를 관리하게 하십시오.