Tailscale 속도 느릴 때 직접 연결 확인 및 해결 방법
Tailscale 연결이 직접 연결인지 DERP 릴레이인지 확인하는 명령어와 UDP 차단 및 NAT 문제를 해결하여 속도를 최적화하는 방법을 설명합니다. 직접 연결이 되지 않는 원인을 파악하고 회선 속도에 근접한 성능을 확보하는 구체적인 가이드를 제공합니다.
Tailscale이 느린 이유: 직접 연결 대신 릴레이 사용
Tailscale 연결이 공유 DERP 서버를 통해 릴레이되면 속도가 느려지며, 직접 연결될 때는 회선 속도에 근접합니다. 직접 연결은 암호화된 WireGuard 패킷을 한 기기에서 다른 기기로 바로 전달하므로 두 인터넷 회선이 허용하는 최대 속도로 동작합니다. 반면 릴레이 연결은 모든 패킷을 제3의 기기를 거쳐 전송하므로, 해당 기기의 지연 시간과 할당된 대역폭의 영향을 받습니다. 이로 인한 성능 저하 정도는 릴레이 서버에 따라 다릅니다. Tailscale은 자사의 DERP 서버가 "일반적으로 직접 연결보다 느리다"고 명시하며, 사용자가 직접 운영하는 피어 릴레이는 "DERP보다 보통 빠르며", Tailscale의 초기 테스트 결과 "직접 연결에 근접한" 처리량을 보였다고 설명합니다. Tailscale의 성능 관련 페이지는 이 일반적인 규칙을 한 줄로 요약합니다: "직접 연결은 거의 항상 더 낮은 지연 시간과 더 높은 처리량을 제공합니다."
애플리케이션 내부에서는 이러한 차이를 확인할 수 없습니다. 파일 복사가 느리거나 SSH 세션이 지연되는 현상으로만 나타납니다. 따라서 첫 번째 작업은 현재 어떤 방식으로 연결되어 있는지 확인하는 것입니다. 두 개의 명령어를 사용하면 1분 이내에 이를 파악할 수 있으며, 그 이후의 과정은 원인을 해결하는 단계입니다. 조정 서버와 WireGuard 데이터 평면은 별개의 시스템이며 데이터 평면만이 실제 데이터를 전송한다는 점을 이해하는 것이 문제 해결의 시작입니다.
직접 연결과 릴레이 연결을 구분하는 두 가지 명령어
측정을 시작하기 전에 피어(peer)로 트래픽을 일부 전송하십시오. Tailscale은 필요에 따라 경로를 생성하므로, 오늘 통신한 적이 없는 피어는 아직 경로 협상을 마치지 않았을 수 있으며, 이 경우 오래된 정보를 읽게 될 수 있습니다. 피어의 tailnet 주소로 ping 또는 curl을 한 번만 실행하면 충분합니다.
tailscale status결과는 각 피어 정보가 표시된 줄의 끝에서 확인할 수 있습니다.
100.113.160.82 device-a tagged-devices linux active; offers exit node; direct 203.0.113.9:41641
100.104.93.78 device-b you@ android active; relay "tor"direct 뒤에 주소와 포트가 표시된다면 패킷이 해당 주소로 직접 전달되고 있다는 의미입니다. relay "tor"는 DERP(designated encrypted relay for packets) 서버를 나타내며, 이는 Tailscale의 릴레이 서버 중 하나로 해당 피어로 향하는 모든 패킷이 이 서버를 거쳐 전달됨을 뜻합니다. 세 번째 값인 peer-relay에 대해서는 다음 섹션에서 다룹니다.
tailscale ping device-b정상적인 연결은 릴레이 상태로 시작하여 직접 연결로 전환됩니다. 두 기기가 협상하는 동안 첫 패킷은 가장 가까운 DERP 서버를 통과하며, 이후 경로가 자동으로 변경됩니다.
pong from device-b (100.113.160.82) via DERP(tor) in 51ms
pong from device-b (100.113.160.82) via DERP(tor) in 48ms
pong from device-b (100.113.160.82) via 203.0.113.9:41641 in 35ms--until-direct의 기본값이 true이므로 실행은 여기서 멈춥니다. 직접 연결이 불가능한 연결은 다음과 같이 표시되며, pong 대신 문장이 출력됩니다.
pong from device-b (100.104.93.78) via DERP(tor) in 53ms
pong from device-b (100.104.93.78) via DERP(tor) in 60ms
direct connection not established마지막 줄이 결과입니다. 이는 Tailscale이 시도할 수 있는 모든 프로브를 보냈음에도 직접 경로를 찾지 못했음을 의미합니다. 첫 번째 직접 경로에서 멈추지 않고 릴레이 경로를 계속 관찰하려면 tailscale ping --until-direct=false -c 20 device-b를 실행하여 지연 시간(latency)의 변화를 확인하십시오. 릴레이 경로는 사용자가 제어할 수 없는 서버를 거쳐 두 개의 인터넷 경로가 결합된 형태이므로, 일반적으로 더 높은 수치와 더 큰 변동 폭을 보입니다.
tailscale status에서 peer-relay는 무엇을 의미합니까?
peer relay는 직접 연결이 불가능할 때 tailnet 내의 다른 구성원을 위해 트래픽을 중계하는 귀하의 장비입니다. 이 장비는 사용자가 선택한 UDP 포트에서 대기하며, tailscaled 데몬은 DERP보다 이 장비를 우선적으로 사용합니다. tailscale status은 이러한 연결을 표시하며, peer-relay, 그리고 tailscale ping은 중계 장비의 엔드포인트를 출력합니다:
pong from device-b (100.97.143.93) via peer-relay(203.0.113.42:40000:vni:1) in 4ms
direct connection not established이 내용을 주의 깊게 읽으십시오. 여전히 직접 연결은 아니므로 실행 결과는 direct connection not established로 끝납니다. 변경된 점은 중계 주체입니다. 공인 IP 주소와 충분한 대역폭을 가진 VPS는 공유 DERP 노드보다 귀하의 트래픽을 중계하기에 훨씬 적합하며, 이것이 서버를 임대하는 사용자에게 중요한 이유입니다. 공인 엔드포인트가 확보된 장비에서 다음과 같이 활성화하십시오:
sudo tailscale set --relay-server-port=400000 포트를 지정하면 사용하지 않는 임의의 포트가 선택되며, 빈 문자열을 입력하면 중계 서버가 비활성화됩니다. 그런 다음 tailnet 정책 파일의 tailscale.com/cap/relay 기능을 사용하여 클라이언트 장치에 사용 권한을 부여하십시오:
{
"grants": [
{
"src": ["tag:us-east-vpc"],
"dst": ["tag:us-east-relays"],
"app": {
"tailscale.com/cap/relay": []
}
}
]
}중계 장치와 클라이언트 장치 모두 Tailscale 1.86 이상 버전이 필요하므로, 정책 파일을 수정하기 전에 각 장치에서 tailscale version 명령어로 버전을 확인하십시오. 데몬이 연결을 시도하는 순서를 기억해 두는 것이 좋습니다. 데몬은 먼저 직접 연결을 시도합니다. 실패할 경우 사용할 수 있는 peer relay를 찾습니다. 중계 장비가 없으면 DERP로 대체합니다. DERP는 두 장비가 최초로 통신을 협상하는 채널 역할을 하므로 완전히 배제되지는 않습니다.
원인 1: UDP를 차단하는 송신(egress) 방화벽
Tailscale 문서에는 연결이 릴레이 상태로 유지되는 두 가지 이유가 명시되어 있으며, 첫 번째는 UDP 차단입니다. 다음 명령어로 장비에 직접 확인하십시오.
tailscale netcheck보고서는 여기서 잘리며, 가장 상단에 있는 필드가 모든 것을 결정합니다.
Report:
* UDP: true
* IPv4: yes, 203.0.113.9:41641
* IPv6: no
* MappingVariesByDestIP: false
* PortMapping:
* Nearest DERP: DallasUDP: false는 얻을 수 있는 가장 강력한 단서입니다. 이는 장비가 Tailscale의 프로브 서버로 UDP 패킷을 보낼 수 없음을 의미하며, Tailscale CLI 참조 문서에 따르면 이 경우 "Tailscale이 지점 간(point-to-point) 연결을 생성할 가능성은 낮습니다." 트래픽은 TCP 포트 443을 통해 DERP로 전달됩니다. 이러한 폴백(fallback) 기능 때문에 장비는 연결되어 있고, 접근 가능하며, 모든 바이트가 릴레이되고 있어 정상적으로 보입니다. netcheck는 특정 연결이 아닌 네트워크 전체를 설명하므로, 규칙을 변경하기 전에 tailscale status 또는 tailscale ping을 사용하여 피어가 실제로 사용하는 경로를 확인하십시오.
두 가지 아웃바운드 규칙이 문서화되어 있습니다. 첫 번째는 "내부 장비가 :41641에서 *:*로 UDP를 시작하도록 허용"하는 것으로, 이는 WireGuard 트래픽 자체를 의미합니다. 두 번째는 "내부 장비가 *:3478으로 UDP를 시작하도록 허용"하는 것으로, 이는 장비가 자신의 공인 IP 주소와 포트를 학습하는 데 사용하는 프로토콜인 STUN(session traversal utilities for NAT)을 의미합니다. 목적지에는 와일드카드를 사용하십시오. Tailscale은 시간이 지남에 따라 릴레이 서버를 추가하므로, 수동으로 작성한 주소 목록은 1년 이내에 잘못된 정보가 됩니다.
임대 서버에서 흔히 발생하는 원인은 엄격한 송신 정책입니다. 이는 하드닝된 이미지에서 상속받았거나 제공업체가 상위 네트워크에서 적용한 정책일 수 있습니다. 먼저 기본 아웃바운드 정책을 확인하십시오.
sudo ufw status verbose
sudo nft list rulesetDefault: deny (incoming), allow (outgoing)은 정상이며 문제가 되지 않습니다. 기본 아웃바운드 정책이 deny로 설정되어 있고 TCP 443과 DNS만 허용하는 짧은 허용 목록을 가진 경우, TCP 443을 통한 DERP 경로는 이 구멍을 통과하지만 직접 경로는 통과하지 못하므로 서버가 계속 릴레이 상태에 머물게 됩니다. 이러한 규칙이 실제로 어디에 존재하는지는 해당 장비가 내부적으로 iptables를 사용하는지 nftables를 사용하는지에 따라 다르며, 잘못된 설정을 수정하는 것은 아무런 변화를 주지 못하는 흔한 원인입니다.
인바운드 측면도 중요합니다. VPS는 공인 IP 주소를 가지고 있으므로 연결의 쉬운 절반이 될 수 있습니다. 방화벽이 tailscaled이 수신 대기하는 포트에서 인바운드 UDP를 허용하면, 까다로운 가정용 라우터 뒤에 있는 피어들도 별도의 트릭 없이 서버에 도달할 수 있습니다. 실제로 사용 중인 포트를 찾으십시오.
sudo ss -lunp | grep tailscaled
sudo ufw allow 41641/udp41641은 기본 정적 포트입니다. randomizeClientPort 설정이 활성화된 테일넷(tailnet)은 클라이언트가 임의의 포트를 선택하게 하므로, 이 경우 본 페이지의 값이 아닌 ss 출력에서 실제 포트 번호를 확인하십시오. 그 후 제공업체의 제어판을 확인하십시오. 대부분의 호스트는 서버 내부 방화벽과 별도로 네트워크 방화벽을 운영하며, ufw으로 추가한 규칙은 해당 네트워크 방화벽에 아무런 영향을 주지 않습니다.
원인 2: 양쪽 또는 한쪽 끝의 하드 NAT
두 번째로 문서화된 원인은 하드 NAT입니다. NAT(network address translation)는 라우터가 내부 사설 주소를 공인 주소로 재작성하는 과정입니다. 일반적인 라우터는 통신 대상과 관계없이 특정 내부 소켓에 대해 동일한 공인 포트를 유지하며, 이를 endpoint independent mapping이라고 합니다. 반면 하드 NAT는 목적지마다 서로 다른 공인 포트를 할당하므로, STUN 서버로부터 확인한 주소를 피어(peer)가 그대로 사용할 수 없습니다. Tailscale은 이 상태를 netcheck에서 MappingVariesByDestIP: true로 보고합니다.
한쪽이 하드 NAT인 경우는 통신이 가능합니다. 상대방의 공인 엔드포인트가 안정적이라면 하드 NAT 뒤에 있는 장비가 접속을 시도하여 경로를 형성할 수 있기 때문입니다. 양쪽 모두 하드 NAT인 경우에는 양측 모두 상대방의 포트를 예측할 수 없으므로 통신에 실패합니다.
공인 IPv4 주소를 사용하는 VPS라면 이 항목은 일반적으로 false로 표시되어야 합니다. 공인 주소를 사용한다고 해서 NAT가 전혀 없는 것은 아닙니다. 예를 들어 AWS는 인터넷 게이트웨이가 인스턴스의 공인 IPv4 주소와 사설 주소 간에 일대일 NAT를 수행한다고 명시합니다. 이와 같은 정적 일대일 매핑은 통신 대상과 관계없이 동일한 포트를 유지하므로 하드 NAT가 아니며, 해당 항목은 여전히 false으로 표시됩니다. 만약 임대한 서버에서 true로 표시된다면, 제공자의 네트워크 장비가 목적지마다 다른 포트를 할당하고 있는 것이며, 서버 내부의 방화벽 규칙으로는 이를 해결할 수 없습니다. 이 경우 안정적인 공인 엔드포인트를 가진 장비에 피어 릴레이를 배치하거나, 워크로드를 다른 곳으로 이전해야 합니다. 이러한 상황에서는 서브넷 라우터로부터 사설 대역을 광고하는 방식이 효과적입니다. 네트워크 내의 모든 장치로 가는 경로를 확보할 필요 없이, 네트워크로 진입하는 하나의 안정적인 경로만 확보하면 되기 때문입니다.
왜 출구 노드를 사용하면 Tailscale이 실제보다 느려 보이는가
출구 노드는 두 번째 홉(hop)이므로, 사용자는 그 원인을 터널 탓으로 돌리곤 합니다. 출구 노드를 선택하면 요청은 노트북을 떠나 터널을 거쳐 VPS에 도달하고, 다시 VPS에서 공용 인터넷으로 나간 뒤, 응답이 동일한 경로를 통해 돌아옵니다. VPS에 대한 연결이 아무리 완벽하게 직접적이라 해도 전체 속도는 VPS 자체의 업링크 속도를 넘을 수 없으며, 추가된 물리적 거리는 모든 페이지 로드 시 지연으로 나타납니다.
두 구간을 분리하여 측정하십시오. 출구 노드를 끄고, VPS의 tailnet 주소를 대상으로 터널 자체의 성능을 테스트하십시오:
sudo tailscale set --exit-node=
sudo apt install -y iperf3
iperf3 -s클라이언트에서 해당 tailnet 주소로 iperf3 -c 100.113.160.82을 실행하십시오. 그 결과값이 터널의 성능입니다. 이제 sudo tailscale set --exit-node=100.113.160.82로 출구 노드를 다시 켜고 공용 인터넷을 대상으로 일반적인 속도 테스트를 수행하십시오. 그 결과값은 터널과 VPS의 업링크를 합친 성능입니다. 첫 번째 수치는 양호한데 두 번째 수치가 나쁘다면, Tailscale의 문제가 아니며 출구 노드 자체의 네트워크 및 규모를 확인해야 합니다. 어떤 노드를 선택했는지 확실하지 않다면 tailscale exit-node list를 통해 현재 제공되는 노드 정보를 확인할 수 있습니다.
CPU는 출구 노드의 성능 한계를 결정하는 또 다른 요소입니다. Tailscale은 코어 수가 많은 것보다 클럭 속도가 높은 최신 CPU 세대를 선호할 것을 권장하므로, vCPU가 더 많은 요금제라고 해서 반드시 더 빠른 것은 아닙니다. 혼잡한 공유 호스트 환경에서는 약속된 CPU 성능을 온전히 보장받지 못하며, 노이즈 이웃으로 인한 스틸 타임(steal time)이 발생하면 사용자 측의 변경 사항이 없어도 시간대에 따라 처리량이 변동하게 됩니다.
단 하나의 튜닝 설정: rx-udp-gro-forwarding
Tailscale은 단 하나의 Linux 설정을 문서화하고 있으며, 이는 트래픽을 전달하는 장비, 즉 exit node와 subnet router에 적용됩니다. 일반 클라이언트는 이 설정으로 이득을 얻지 못합니다. 이 설정은 Tailscale 1.54 이상 버전과 Linux 커널 6.2 이상이 필요하므로, 설정을 변경하기 전에 두 가지 모두 확인하십시오:
tailscale version
uname -r요건이 충족되었다면, 인터넷과 연결된 인터페이스에서 UDP GRO(generic receive offload) 포워딩을 활성화하십시오:
NETDEV=$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")
sudo ethtool -K $NETDEV rx-udp-gro-forwarding on rx-gro-list off설정이 적용되었는지 확인하십시오:
ethtool -k $NETDEV | grep -E 'rx-udp-gro-forwarding|rx-gro-list'rx-udp-gro-forwarding: on 및 rx-gro-list: off이 출력되어야 합니다. 이 설정이 도움이 되는 이유는 Tailscale의 트래픽이 UDP 기반이기 때문입니다. 커널이 전달 경로상에서 작은 UDP 패킷들을 병합(coalesced)된 상태로 유지하게 하면, 데몬은 동일한 바이트 수에 대해 더 적고 큰 세그먼트를 처리하게 됩니다. ethtool -K 설정은 재부팅 시 유지되지 않으므로 영구적으로 적용해야 합니다. networkd-dispatcher를 사용하는 시스템의 경우 다음과 같이 하십시오:
printf '#!/bin/sh\n\nethtool -K %s rx-udp-gro-forwarding on rx-gro-list off \n' "$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")" | sudo tee /etc/networkd-dispatcher/routable.d/50-tailscale
sudo chmod 755 /etc/networkd-dispatcher/routable.d/50-tailscale스크립트를 수동으로 한 번 실행하고 종료 상태가 0인지 확인하십시오. 포워딩 노드는 우선 IP 포워딩이 활성화되어 있어야 하며, 이는 별도의 설정 항목이자 별개의 실패 원인이 될 수 있습니다:
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conftailscale0 인터페이스는 어떤 MTU를 사용합니까?
이 값을 추측하지 말고 장비에서 직접 확인하십시오:
ip link show tailscale0출력값 중 mtu 항목이 실제 터널이 사용하는 값이며, 이더넷 인터페이스가 보고하는 1500보다 낮습니다. 이는 버그가 아니라 의도된 설계입니다. 터널 내부로 전송되는 모든 패킷은 캡슐화됩니다. IPv4는 20바이트, IPv6는 40바이트의 외부 IP 헤더, 8바이트의 UDP 헤더, 그리고 32바이트의 WireGuard 프레이밍 및 인증 태그가 추가됩니다. 이 모든 데이터는 실제 경로가 수용할 수 있는 크기 내에 들어가야 합니다. Tailscale은 PPPoE 연결, 일부 모바일 네트워크, IPv6 터널 등 1500바이트 패킷을 온전히 전달하지 못하는 링크에서도 통신이 가능하도록 충분히 낮은 값을 선택합니다.
MTU 문제의 증상은 명확하므로 단순히 속도가 느리다는 이유만으로 진단해서는 안 됩니다. SSH는 반응이 빠르고 ping 명령도 잘 작동하지만, 대용량 전송이나 큰 HTTPS 페이지를 불러올 때 속도가 느려지는 것이 아니라 아예 멈춰버립니다. 이러한 패턴은 크기가 너무 큰 패킷이 어딘가에서 폐기되고 있으며, 송신자에게 이를 알리는 ICMP 메시지가 돌아오지 않음을 의미합니다. tailscale0 MTU를 1500에 가깝게 높이는 것은 이미 맞지 않는 패킷을 더 크게 만들어 상황을 악화시킬 뿐입니다. 근본적인 해결책은 이분 탐색으로 실제 경로 MTU를 찾고 트래픽을 전달하는 라우터에서 TCP MSS를 클램핑하는 것입니다. 연결 방식은 이 과정에 영향을 주지 않으며, 릴레이 경로와 직접 경로 모두 동일한 인터페이스 MTU를 사용합니다.
FAQ
Tailscale 연결이 직접 연결인지 릴레이 연결인지 어떻게 확인합니까?
tailscale status를 실행하고 피어 행의 끝부분을 확인합니다. direct 203.0.113.9:41641은 직접 연결을 의미하며, relay "tor"는 모든 패킷이 해당 DERP 서버를 거쳐 전달됨을 의미하고, peer-relay는 패킷이 사용자의 tailnet 내 다른 장비를 거쳐 전달됨을 의미합니다. 다른 방법으로 tailscale ping <peer>을 실행해 보십시오. 정상적인 경로는 DERP로 시작하여 일반 주소와 포트가 포함된 pong을 출력하지만, 릴레이 경로는 실행이 종료될 때까지 DERP pong만 출력하고 direct connection not established로 끝납니다. Tailscale은 요청이 있을 때만 경로를 생성하므로, 먼저 피어에게 트래픽을 일부 전송한 뒤 확인하십시오.
VPS에서 직접 연결이 전혀 되지 않는 이유는 무엇입니까?
VPS에서 tailscale netcheck을 실행합니다. 만약 UDP: false가 출력된다면 아웃바운드 UDP가 차단된 상태입니다. 이는 주로 송신 방화벽 때문이며, 이 경우 Tailscale은 지점 간 연결이 어려울 것으로 판단합니다. tailscale status 또는 tailscale ping을 통해 피어가 DERP에 있는지 확인하십시오. DERP는 TCP 443 포트를 사용하므로 해당 장비는 여전히 연결된 것처럼 보입니다. 모든 목적지로 향하는 41641 포트의 아웃바운드 UDP와 모든 목적지로 향하는 3478 포트의 아웃바운드 UDP를 허용하십시오. 서버 내부 방화벽뿐만 아니라 클라우드 제공업체의 네트워크 방화벽도 확인해야 합니다. 이 둘은 별개의 제어 영역이므로 ufw 규칙은 제공업체의 방화벽에 영향을 주지 않습니다.
릴레이된 Tailscale 연결이 직접 연결보다 보안이 떨어집니까?
아닙니다. DERP 서버는 복호화할 수 없는 WireGuard 패킷을 단순히 전달할 뿐입니다. 암호화 키는 사용자의 장치에서 생성되며 장치 외부로 유출되지 않기 때문입니다. 릴레이 사용 시 발생하는 비용은 지연 시간과 처리량의 문제이지 기밀성의 문제가 아닙니다. 조정 서버(coordination server)가 제어하는 것은 어떤 장치가 서로를 인식할 수 있는지에 대한 정보이며, 키 자료와 연결 메타데이터의 분리에 대해 이해한 뒤 자체 호스팅 범위를 결정하는 것이 좋습니다.
rx-udp-gro-forwarding 설정은 모든 장치에 도움이 됩니까?
아닙니다. 이 설정은 다른 장치를 위해 트래픽을 전달하는 Linux 장치, 즉 exit node나 subnet router를 위해 문서화된 기능입니다. 자신의 피어와만 통신하는 노트북이나 서버는 이 설정으로 얻는 이점이 없습니다. 또한 Tailscale 1.54 이상 버전과 Linux 커널 6.2 이상이 필요하므로 먼저 tailscale version과 uname -r를 확인하십시오. ethtool -K 설정은 재부팅 시 초기화되므로 영구적으로 적용하려면 별도의 조치가 필요합니다.
Tailscale이 일반 WireGuard보다 느립니까?
둘 다 트래픽 암호화에 WireGuard를 사용합니다. Tailscale은 일반 WireGuard에서 수동으로 수행해야 하는 연결 설정을 자동화하며, 이 설정 과정에서 때때로 릴레이를 거치게 됩니다. 일반 WireGuard에는 릴레이가 없으므로 직접 연결되거나 아예 연결되지 않습니다. 따라서 동일한 조건에서 비교해야 하며, tailscale status이 direct을 출력할 때만 Tailscale의 성능을 측정하십시오. 비교를 위해 직접 설정한 버전을 원한다면 직접 구축하는 WireGuard 서버는 약 40줄의 설정이 필요하며, 두 방식의 차이점은 두 접근 방식의 비교에서 확인할 수 있습니다.