目录
Please enable Javascript to view the contents

开发环境代理配置——从原理到实践

 ·  ☕ 9 分钟

重要

代理的本质是"中间人转发"——客户端不直接连目标,而是通过代理服务器中转。不同代理模式决定了哪些流量被拦截、哪些工具需要额外配置。

核心认知:不是所有工具都遵守系统代理设置。 Git、Docker、APT 等底层工具默认不读系统代理变量,需要单独配置或在网络层(TUN 模式)拦截。

1. 代理原理

1.1 代理在协议栈中的位置

1
2
3
应用层 (HTTP/HTTPS)     ← HTTP 代理(能看请求头)、SOCKS5(不关心协议)
传输层 (TCP/UDP)        ← SOCKS5 代理(转发 TCP/UDP 连接)
网络层 (IP)             ← TUN 模式(拦截所有 IP 包)
代理类型工作层能代理的协议是否需要应用支持典型端口
HTTP 代理应用层HTTP/HTTPS需要应用设置 http_proxy10800
SOCKS5 代理传输层任意 TCP/UDP需要应用设置 all_proxy1088
TUN 模式网络层所有 IP 流量不需要——创建虚拟网卡,系统级拦截无端口

1.2 HTTP 代理 vs SOCKS5 代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HTTP 代理:
  客户端 → 代理服务器: CONNECT github.com:443 HTTP/1.1
  代理服务器 → GitHub:443(TCP 连接)
  之后:客户端 ⟷ 代理 ⟷ GitHub(透明转发 TLS 流量)

SOCKS5 代理:
  客户端 → 代理服务器: SOCKS5 握手(协商认证方式)
  客户端 → 代理服务器: CONNECT 到 github.com:443
  代理服务器 → GitHub:443(TCP 连接)
  之后:客户端 ⟷ 代理 ⟷ GitHub(透明转发任意协议)

关键区别:

HTTP 代理SOCKS5
协议感知理解 HTTP,可做缓存、过滤不关心上层协议
UDP 支持不支持(仅 TCP)支持 UDP
认证方式Basic Auth用户名/密码、GSSAPI
DNS 解析代理服务器端解析可客户端解析也可服务端解析

1.3 环境变量机制

1
2
3
4
http_proxy=http://127.0.0.1:10800    # HTTP 请求走此代理
https_proxy=http://127.0.0.1:10800   # HTTPS 请求走此代理
all_proxy=socks5://127.0.0.1:1088    # 所有协议走此代理(SOCKS5)
no_proxy=.service.rd,localhost       # 这些域名/网段不走代理

环境变量的工作原理:库层面的拦截。curlwget、Python requests、Node.js axios 等网络库在发起连接前检查这些环境变量,如果设置了就通过代理连接而非直连。

但 Git、Docker CLI、APT 等工具的底层网络库不自动读取这些变量——它们通过自己的配置文件或系统服务管理来设置代理。

2. TUN 模式:从应用层代理到网络层拦截

2.1 系统代理的局限

系统代理(System Proxy)通过操作系统设置 HTTP_PROXY / HTTPS_PROXY,但只有遵守系统代理 API 的应用才会使用。以下工具默认不走系统代理

工具原因
Git使用 libcurl,但不读系统代理设置
Docker daemonsystemd 服务,环境变量在 unit file 中独立配置
APT/etc/apt/apt.conf.d/proxy.conf,不读环境变量
ping / traceroute网络层工具,不使用 HTTP/SOCKS 代理

2.2 TUN 模式原理

TUN 是 Linux 内核的三层虚拟网络设备。代理客户端(如 Clash)创建一个 TUN 网卡,修改系统路由表,将所有(或部分)IP 流量导向 TUN 设备。代理客户端从 TUN 设备读取 IP 包,解析出 TCP/UDP 连接,再通过代理服务器转发。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
                    ┌─────────────────┐
  应用程序发出 IP 包  │                 │
         ↓           │   路由表决策     │
  ┌──────────┐       │                 │
  │ TUN 网卡  │←────│ 目标 IP 匹配规则  │
  │ (utun/clash0)    │ → 发给 TUN 设备  │
  └────┬─────┘       │                 │
       ↓             └─────────────────┘
  Clash 进程(用户态)
       │ 解析 IP 包 → 还原 TCP/UDP 连接
  通过 SOCKS5/HTTP 代理发出
  代理服务器 → 目标服务器

TUN 模式不依赖应用的代理设置——它是在网络层拦截所有 IP 包,因此对应用透明。

2.3 几种代理模式对比

模式原理覆盖范围对应用透明配置复杂度适用场景
系统代理设置 HTTP_PROXY 等环境变量遵守系统代理 API 的应用浏览器、curl 等
TUN 模式虚拟网卡 + 路由表劫持所有 IP 流量Git、Docker、终端全代理
TUN + Fake-IPTUN 拦截 DNS 请求,Clash 返回假 IP,根据假 IP 反查域名TUN 模式 + DNS 本地加速需要域名规则匹配、高 DNS 延迟场景
透明代理(Redirect)iptables REDIRECT 将流量转到代理端口所有 TCP 连接网关级代理(路由器)

TUN 模式可以替代所有工具的单独代理配置——它走的是路由层面,应用完全无感。代价是系统路由被修改,需要确保规则准确,否则可能导致内网流量也被劫持。

2.4 问题:TUN 下 DNS 为什么慢

TUN 模式下,每个新连接的第一步是 DNS 解析——应用需要知道 github.com 的 IP 才能发起 TCP 连接。

1
2
3
  应用 → DNS 请求 → TUN 设备 → Clash 进程 → 代理服务器 → 8.8.8.8
                                    这段走代理,延迟 100-500ms

DNS 请求本身是 UDP 包,大小不到 100 字节,但因为走了代理链路(Clash→代理服务器→公共 DNS),延迟通常在 100-500ms。每个新域名都要等这一下,体感就是"打开网页卡一下才加载"。

2.5 尝试:缓存真实 IP 行不行?

直观想法:Clash 把解析到的真实 IP 缓存下来,下次同一个域名直接返回缓存的 IP,不就快了吗?

不行,两个致命问题:

问题一:CDN 域名对应海量 IP,且频繁变动。

1
2
3
4
5
github.com 一次 DNS 解析可能返回:
  140.82.112.3
  140.82.113.4
  140.82.114.5
  ...(CDN 边缘节点可能有数百个不同 IP,按地理位置轮换)

这些 IP 的变化频率是分钟级。缓存意味着要维护一套 TTL、刷新、淘汰机制——复杂度和延迟不比直接走代理低。

问题二:更致命的——丢掉域名后无法精准匹配规则。

Clash 的规则核心是域名维度(DOMAIN-SUFFIXDOMAIN-KEYWORD)。如果 DNS 返回的是真实 IP,当应用后续向这个 IP 发包时,TUN 交给 Clash 的就只是一个裸 IP 包——没有域名信息

1
2
3
4
5
缓存真实 IP 方案的死结:
  DNS 阶段:Clash 知道域名 github.com → 返回真实 IP 140.82.112.3
  IP 包阶段:TUN 交给 Clash 一个 IP 包,目标 = 140.82.112.3
            Clash 只能看到 140.82.112.3,不知道这是 github.com
            → 只能用 GEOIP 或 IP-CIDR 匹配,永远无法达到域名规则的精度

缓存真实 IP 等于在 DNS 这一步丢弃了域名——而域名正是后续规则匹配最需要的信息。这就是为什么必须引入 Fake-IP。

2.6 方案:Fake-IP——把域名编码进 IP 里

既然问题根源是"IP 包阶段丢失了域名",Fake-IP 的思路就是把域名信息编码回 IP 包里——生成一个假的 IP,建立 假 IP ↔ 域名 的映射,让 Clash 从假 IP 反查出域名。

工作流程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
TUN + Fake-IP:
  应用 → DNS 请求 github.com
           → TUN → Clash 拦截
           → 立即返回假 IP 198.18.0.5(~0ms)
           → 记录映射 198.18.0.5 ↔ github.com
  应用 → TCP 连接 198.18.0.5:443
           → TUN → Clash 收到 IP 包
           → 目标 IP = 198.18.0.5 → 反查哈希表 → github.com
           → 匹配域名规则(DOMAIN-SUFFIX,github.com,PROXY)
           → 走代理发出 ✓

与普通 TUN 的对比:

普通 TUNTUN + Fake-IP
DNS 延迟100-500ms(走代理链路)~0ms(本地返回)
IP 包阶段只有 IP,无域名 → 只能 IP 规则反查得域名 → 可继续用域名规则
适用场景规则以 IP-CIDR 为主时够用规则以域名为主时必备

映射关系谁维护: Clash 进程在内存中维护一张哈希表 map[假IP]域名。写入:处理 DNS 请求时。读取:收到 TUN 设备的 IP 包时。进程退出即消失,重启后重建。

为什么不能脱离 TUN: Fake-IP 需要 TUN 同时拦截两个点——① DNS 请求(返回假 IP)、② 后续 IP 包(根据假 IP 反查域名)。没有 TUN,一个都拦不到。

2.7 正确性:如何保证只给需要代理的域名返假 IP?

这里有一个关键约束:Fake-IP 不能对所有 DNS 都返回假 IP。否则内网域名(git.service.rd)也拿到假 IP,应用连到假 IP 上,Clash 反查后发现规则是 DIRECT——但此时已经晚了一步,因为应用已经向假 IP 发起连接了。

Clash 的做法:在返回假 IP 之前,先跑规则匹配。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Clash 收到 DNS 请求(如 github.com A 记录)
  ├── 先匹配规则
  ├── 命中 PROXY 规则
  │      → 返回假 IP(如 198.18.0.5)
  │      → 记录映射 198.18.0.5 ↔ github.com
  │      → 后续 IP 包到达时反查得 github.com → 走代理
  └── 命中 DIRECT 规则(或无 PROXY 匹配)
         → **不返回假 IP**
         → 转发 DNS 请求到上游 DNS(如 223.5.5.5)
         → 返回真实 IP,不参与 Fake-IP 体系

只有命中代理规则的域名才进 Fake-IP 体系。内网域名命中 DIRECT → 正常 DNS 解析 → 拿到真实 IP → 直连,整个过程与 Fake-IP 无关。

规则配置示例:

1
2
3
4
5
6
rules:
  - DOMAIN-SUFFIX,service.rd,DIRECT       # 内网 → 真实 IP,不返假 IP
  - DOMAIN-SUFFIX,github.com,PROXY        # 外网 → 返假 IP
  - IP-CIDR,10.0.0.0/8,DIRECT
  - GEOIP,CN,DIRECT                       # 兜底国内
  - MATCH,PROXY                           # 兜底外网

极端错误:如果 GEOIP,CN,DIRECT 写在域名规则之前,且 github.com 恰好解析到国内 CDN 节点,则 GitHub 被错误直连。解决:域名规则永远放在 IP 规则之前。

2.8 假 IP 从哪来,会冲突吗?

Clash 从 RFC 2544 保留的基准测试网段 198.18.0.0/15 中分配。这个 /15 提供 198.18.0.0 ~ 198.19.255.255 约 13 万个地址,专用于网络设备基准测试,不分配给任何公网或内网

分配方式:每个新域名按递增序号分配——github.com198.18.0.1google.com198.18.0.2,以此类推。重启清零重新分配。

网段冲突可能原因
10.0.0.0/8不冲突不在同一网段
172.16.0.0/12不冲突不在同一网段
192.168.0.0/16不冲突不在同一网段
198.18.0.0/15极罕见RFC 保留段,全球无 ISP/企业生产使用

极端情况可切到 198.51.100.0/24(RFC 5737 文档/测试保留段)。

3. 各工具代理配置

3.1 Git

需求:GitHub 走代理,内网 Git 仓库直连。

按域名配置(推荐):

1
2
# 仅 github.com 走代理
git config --global http.https://github.com.proxy http://127.0.0.1:10808

这样只有 GitHub 走代理,其他域名直连,无需 no_proxy

环境变量方式:

1
2
export https_proxy=http://127.0.0.1:10800/
export no_proxy=".service.rd .icos.city 127.0.0.1 localhost"

仓库级别禁用代理:

1
git config --local --add http.proxy ""

3.2 Docker Daemon

Docker daemon 是 systemd 服务,代理必须配置在 systemd unit file 中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
sudo mkdir -p /etc/systemd/system/docker.service.d/
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf << 'EOF'
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:10800"
Environment="HTTPS_PROXY=http://127.0.0.1:10800"
Environment="NO_PROXY=localhost,127.0.0.1,reg.chebai.org,.service.rd"
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker

验证:

1
2
3
systemctl show --property=Environment docker
# 或
docker info | grep -i proxy

Docker daemon 代理影响的是 docker pull(daemon 从 registry 拉取镜像),不影响容器内部网络。容器内部需要单独配置代理或者宿主机开 TUN 模式。

3.3 容器内代理

构建镜像时如果需要走代理(如 apt install):

1
2
3
4
# 仅构建阶段使用,不写入最终镜像
ARG HTTP_PROXY
ARG HTTPS_PROXY
RUN export http_proxy=$HTTP_PROXY && apt update
1
docker build --build-arg HTTP_PROXY=http://127.0.0.1:10800 .

3.4 APT

1
2
3
4
sudo tee /etc/apt/apt.conf.d/proxy.conf << 'EOF'
Acquire::http::Proxy "http://127.0.0.1:10800";
Acquire::https::Proxy "http://127.0.0.1:10800";
EOF

3.5 Minikube:国内镜像加速

无需代理,直接用国内 registry mirror:

1
minikube start --registry-mirror=https://registry.docker-cn.com

本质:Docker daemon 拉取镜像时先尝试 mirror 地址,失败才回退到原始 registry。比代理方案更稳定,因为国内 mirror 网络链路更短。

4. 故障排查

4.1 代理不生效

1
2
3
4
5
6
7
8
# 确认代理是否可达
curl -x http://127.0.0.1:10800 -I https://github.com

# 确认环境变量在当前 shell 生效
env | grep -i proxy

# 确认 Git 代理配置
git config --global --list | grep proxy

4.2 内网也走了代理

常见原因:no_proxy 格式不对。

1
2
3
4
5
# 正确:点号前缀匹配所有子域名
no_proxy=".service.rd,localhost"

# 错误:不带点号只匹配精确域名,不匹配 git.service.rd
no_proxy="service.rd,localhost"

4.3 开了 TUN 之后内网不通

TUN 模式劫持了所有流量。需要在 Clash 规则中排除内网网段:

1
2
3
4
5
6
7
# Clash 配置
rules:
  - IP-CIDR,10.0.0.0/8,DIRECT
  - IP-CIDR,172.16.0.0/12,DIRECT
  - IP-CIDR,192.168.0.0/16,DIRECT
  - GEOIP,CN,DIRECT
  - MATCH,PROXY

5. 总结

场景推荐方案原因
浏览器访问 GitHub系统代理 + SwitchyOmega最轻量
Git clone GitHubgit config http.https://github.com.proxy按域名配置,内网不受影响
VS Code / JetBrains 全代理TUN 模式终端、插件、Git 一次性覆盖
Docker pull gcr.iodaemon proxy 或 minikube registry mirrordaemon 层面配置
CI/CD 构建--build-arg 传代理不写入镜像,构建完即失效
全局开发环境TUN 模式 + 内网网段 DIRECT 规则一劳永逸,对应用透明

选择代理方案的决策树:

1
2
3
4
5
6
需要代理的工具数量?
  ├── 1-2 个工具 → 各自单独配置(最可控)
  └── 5+ 个工具 → TUN 模式(一劳永逸)
       └── 同时需要访问内网?
            ├── 是 → TUN + 内网 DIRECT 规则
            └── 否 → TUN 全局代理

参考链接

分享

Hex
作者
Hex
CloudNative Developer