Skip to content

Commit 8349fc3

Browse files
committed
feat(tailscale): improve derp startup diagnostics
1 parent f278504 commit 8349fc3

9 files changed

Lines changed: 433 additions & 50 deletions

File tree

config/network/tailscale/derp/.env.example

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# 构建与镜像版本。启用 --verify-clients 时,建议这里和 tailscaled 使用同一版本线。
22
TAILSCALE_VERSION=latest
3+
GO_BUILD_IMAGE=golang:1.26-alpine
4+
5+
# 可选:Alpine 包管理镜像源。若 `apk add` 很慢,可改成你本地更快的镜像,例如
6+
# https://mirrors.aliyun.com/alpine 或 https://mirrors.tuna.tsinghua.edu.cn/alpine
7+
ALPINE_MIRROR=https://dl-cdn.alpinelinux.org/alpine
8+
9+
# 可选:Go 模块下载代理。若构建阶段访问 proxy.golang.org 超时,可改成你本地可达的镜像。
10+
# GOPROXY=https://proxy.golang.org,direct
11+
# GOSUMDB=sum.golang.org
12+
GOPROXY=https://goproxy.cn,direct
13+
GOSUMDB=sum.golang.google.cn
314

415
# 必填:DERP 对外公布的公网 IP。manual cert 模式会以它作为证书主机名与 certdir 文件名。
516
DERP_PUBLIC_IP=
@@ -15,7 +26,8 @@ TS_EXTRA_ARGS=--accept-routes=false --accept-dns=false
1526

1627
# DERP / STUN 监听端口。derper 使用 host network,因此这些端口需要直接在宿主机防火墙放行。
1728
DERP_PORT=443
18-
DERP_HTTP_PORT=80
29+
# IP-only + manual cert 路线,不依赖 80/tcp 做 ACME,先直接关掉
30+
DERP_HTTP_PORT=-1
1931
DERP_STUN_PORT=3478
2032

2133
# 可选:证书目录。若目录中不存在 <DERP_PUBLIC_IP>.crt/.key,derper 会自动生成自签 IP 证书。
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
certs/

config/network/tailscale/derp/Dockerfile.derper

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
# 通过构建时版本参数把 derper 固定到明确的 Tailscale 版本,方便和 tailscaled 对齐。
2-
FROM golang:1.24-alpine AS build
1+
ARG GO_BUILD_IMAGE=golang:1.26-alpine
2+
FROM ${GO_BUILD_IMAGE} AS build
33

4+
# 通过构建时版本参数把 derper 固定到明确的 Tailscale 版本,方便和 tailscaled 对齐。
5+
ARG ALPINE_MIRROR=https://dl-cdn.alpinelinux.org/alpine
46
ARG TAILSCALE_VERSION=latest
7+
ARG GOPROXY=https://proxy.golang.org,direct
8+
ARG GOSUMDB=sum.golang.org
9+
10+
# 通过 build args 暴露 Go 模块下载配置,便于在 proxy.golang.org 不可达时切换镜像站。
11+
ENV GOPROXY=${GOPROXY}
12+
ENV GOSUMDB=${GOSUMDB}
513

14+
# 只替换镜像源域名,保留基础镜像自带的 Alpine 版本路径,避免把 3.x 仓库误切到其它发行线。
15+
RUN sed -i "s#https\?://dl-cdn.alpinelinux.org/alpine#${ALPINE_MIRROR}#g" /etc/apk/repositories
616
RUN apk add --no-cache git
717
RUN go install tailscale.com/cmd/derper@${TAILSCALE_VERSION}
818

config/network/tailscale/derp/README.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ cp config/network/tailscale/derp/.env.example config/network/tailscale/derp/.env
3939
- `DERP_PUBLIC_IP`
4040
- `TS_AUTHKEY`
4141

42+
如果你在当前网络里构建 `Dockerfile.derper` 时访问 `proxy.golang.org` 超时,还建议一起补上:
43+
44+
- `GO_BUILD_IMAGE`
45+
- `ALPINE_MIRROR`
46+
- `GOPROXY`
47+
- `GOSUMDB`
48+
49+
例如:
50+
51+
```dotenv
52+
GO_BUILD_IMAGE=golang:1.26-alpine
53+
ALPINE_MIRROR=https://mirrors.aliyun.com/alpine
54+
GOPROXY=https://goproxy.cn,direct
55+
GOSUMDB=sum.golang.google.cn
56+
```
57+
4258
3. 确认宿主机防火墙已放行:
4359

4460
- `TCP 80`
@@ -71,13 +87,62 @@ docker compose --env-file config/network/tailscale/derp/.env.local -f config/net
7187

7288
```powershell
7389
./config/network/tailscale/derp/start.ps1
90+
./config/network/tailscale/derp/start.ps1 build --no-cache
7491
./config/network/tailscale/derp/start.ps1 logs --tail 100
7592
./config/network/tailscale/derp/start.ps1 ps
7693
./config/network/tailscale/derp/start.ps1 pull
7794
./config/network/tailscale/derp/start.ps1 config
7895
./config/network/tailscale/derp/start.ps1 -DryRun
7996
```
8097

98+
## 构建阶段网络说明
99+
100+
`Dockerfile.derper` 会在构建阶段执行:
101+
102+
```text
103+
go install tailscale.com/cmd/derper@<version>
104+
```
105+
106+
如果你看到类似下面的错误:
107+
108+
```text
109+
Get "https://proxy.golang.org/...": dial tcp ...:443: i/o timeout
110+
```
111+
112+
通常不是 `derper` 本身坏了,而是当前容器网络访问 Go 模块代理超时。此时优先调整:
113+
114+
- `GO_BUILD_IMAGE`
115+
- `ALPINE_MIRROR`
116+
- `GOPROXY`
117+
- `GOSUMDB`
118+
119+
模板已经把这些值透传到 build args,无需再改 Dockerfile。
120+
121+
如果你卡在的是:
122+
123+
```text
124+
RUN apk add --no-cache git
125+
```
126+
127+
通常说明 Alpine 包镜像访问慢或不稳定。此时优先把 `ALPINE_MIRROR` 改成你本地更快的镜像,例如:
128+
129+
```dotenv
130+
ALPINE_MIRROR=https://mirrors.aliyun.com/alpine
131+
```
132+
133+
如果你看到类似下面的错误:
134+
135+
```text
136+
requires go >= 1.26.1 (running go 1.24.x)
137+
```
138+
139+
说明 `derper@latest` 的最低 Go 版本已经高于当前 builder。此时优先把 `GO_BUILD_IMAGE` 提升到
140+
更高版本,例如:
141+
142+
```dotenv
143+
GO_BUILD_IMAGE=golang:1.26-alpine
144+
```
145+
81146
## 证书行为
82147

83148
默认 `DERP_CERTS_DIR=./certs`。第一轮启动时:
@@ -124,10 +189,146 @@ DERP_PUBLIC_IP=203.0.113.10 TS_AUTHKEY=tskey-example docker compose -f config/ne
124189

125190
客户端侧排查:
126191

192+
- `tailscale status`
127193
- `tailscale netcheck`
128194
- `tailscale ping <另一台设备>`
129195
- `tailscale debug derp`
130196

197+
## 排查与常见故障
198+
199+
### 1. 先确认 DERP 容器本身是否正常
200+
201+
```powershell
202+
./config/network/tailscale/derp/start.ps1 ps
203+
./config/network/tailscale/derp/start.ps1 logs --tail 200
204+
```
205+
206+
正常时至少应满足:
207+
208+
- `tailscaled-auth``derper` 都是 `Up`
209+
- `derper` 日志里没有 `tailscaled.sock``verify-clients`、证书加载相关 fatal 错误
210+
211+
### 2. 再确认客户端是否已经收到新的 DERP 配置
212+
213+
```bash
214+
tailscale status
215+
tailscale netcheck
216+
```
217+
218+
重点看:
219+
220+
- 客户端是否在线、目标设备是否在线
221+
- `tailscale netcheck` 里是否能看到你的自定义 DERP region
222+
223+
如果这里完全看不到你的自定义 DERP,优先检查:
224+
225+
- tailnet policy 是否真的保存成功
226+
- `HostName` / `DERPPort` / `STUNPort` 是否和容器配置一致
227+
- 宿主机 `TCP 80``TCP 443``UDP 3478` 是否放行
228+
229+
### 3. `tailscale ping` 为什么在加了 DERP 配置后反而超时
230+
231+
像下面这种:
232+
233+
```text
234+
tailscale ping macmini
235+
ping "100.x.x.x" timed out
236+
```
237+
238+
最常见不是“DERP 配上了但没被用”,而是“客户端现在需要走中继,但可用中继不可达”。
239+
240+
优先怀疑这几类问题:
241+
242+
1. `OmitDefaultRegions: true`
243+
影响:你把官方 DERP fallback 关掉了,但自建 DERP 还没完全可用
244+
建议:在正式验证通过前,先保持 `OmitDefaultRegions: false`
245+
2. `HostName` / `DERP_PUBLIC_IP` / 证书主机名不一致
246+
影响:客户端看到 DERP 节点,但 TLS 校验或节点匹配失败
247+
3. `InsecureForTests` 缺失或被改掉
248+
影响:IP-only + 自签证书路线下,客户端不会信任这个 DERP
249+
4. `tailscaled-auth` 在 ACL 中看不到要使用 DERP 的客户端
250+
影响:`--verify-clients` 会拒绝这些连接
251+
5. 自建 DERP 端口没放行
252+
影响:客户端无法连上 `443/tcp``3478/udp`
253+
254+
### 4. 怎么快速判断是不是 DERP 故障
255+
256+
按这个顺序最省时间:
257+
258+
```bash
259+
tailscale status
260+
tailscale netcheck
261+
tailscale ping <另一台设备>
262+
tailscale debug derp
263+
```
264+
265+
辅助看 server 侧:
266+
267+
```powershell
268+
./config/network/tailscale/derp/start.ps1 logs --tail 200
269+
```
270+
271+
经验判断:
272+
273+
- `tailscale ping` 直连成功
274+
说明当前不需要 DERP,不能说明 DERP 配错
275+
- `tailscale ping` 显示 `via DERP`
276+
说明当前正在走中继
277+
- `tailscale ping` 持续 timeout
278+
说明这次连接既没直连成功,也没通过可用 DERP 建立中继
279+
280+
### 5. 关于 `No home relay server`
281+
282+
如果客户端日志或诊断里出现:
283+
284+
```text
285+
No home relay server
286+
```
287+
288+
它通常意味着客户端当前没有可用 DERP 可选。这个场景最值得先查:
289+
290+
- `OmitDefaultRegions` 是否过早设成 `true`
291+
- 自建 DERP 是否真的可达
292+
- policy 是否下发到客户端
293+
294+
### 6. 推荐上线顺序
295+
296+
为了避免“刚加 DERP 就把联通性打断”,推荐顺序是:
297+
298+
1. 先保持 `OmitDefaultRegions: false`
299+
2. 先确认 `tailscale netcheck` 能看到你的自定义 DERP
300+
3. 再观察 `tailscale ping` 是否能在需要时 `via DERP`
301+
4. 最后如果你确实想强制只用自建 DERP,再把 `OmitDefaultRegions` 切到 `true`
302+
303+
### 7. 当前日志里最常见的两类硬故障
304+
305+
如果你看到 `derper` 容器反复重启,并且日志里有:
306+
307+
```text
308+
listen tcp :80: bind: address already in use
309+
```
310+
311+
说明不是 DERP 协议本身坏了,而是宿主机 `80/tcp` 已经被别的服务占用。对当前这套
312+
IP-only + manual cert 模板来说,最简单的处理是:
313+
314+
- 如果你不需要 `80/tcp`,把 `.env.local` 里的 `DERP_HTTP_PORT` 改成 `-1`
315+
- 或者把占用 `80/tcp` 的现有服务停掉
316+
317+
如果你看到 `tailscaled-auth` 日志里有:
318+
319+
```text
320+
invalid key: unable to validate API key
321+
```
322+
323+
说明当前 `TS_AUTHKEY` 无效、过期,或者填错了。这会导致 `tailscaled-auth` 无法成功加入
324+
tailnet,`--verify-clients` 也就失去作用。处理方式是:
325+
326+
- 在 Tailscale Admin 里重新生成一个可用的 auth key
327+
- 更新 `.env.local` 里的 `TS_AUTHKEY`
328+
- 重新执行 `./config/network/tailscale/derp/start.ps1 up`
329+
330+
如果改了 key 之后依旧反复重启,再考虑清理当前 compose 的 state volume 后重启。
331+
131332
## 注意事项
132333

133334
- `--verify-clients` 只能限制到 tailnet 级,不能进一步细分“同 tailnet 内哪些设备允许走这个 DERP”

config/network/tailscale/derp/compose.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ services:
2323
context: .
2424
dockerfile: Dockerfile.derper
2525
args:
26+
GO_BUILD_IMAGE: ${GO_BUILD_IMAGE:-golang:1.26-alpine}
27+
ALPINE_MIRROR: ${ALPINE_MIRROR:-https://dl-cdn.alpinelinux.org/alpine}
2628
TAILSCALE_VERSION: ${TAILSCALE_VERSION:-latest}
29+
GOPROXY: ${GOPROXY:-https://proxy.golang.org,direct}
30+
GOSUMDB: ${GOSUMDB:-sum.golang.org}
2731
restart: unless-stopped
2832
depends_on:
2933
- tailscaled-auth

0 commit comments

Comments
 (0)