冲突从哪来
三处会互相干扰
默认网关抢占:两套工具都想写默认路由,后启动的覆盖先启动的,表现为原出口工具突然失效。
策略路由回环:加速流量注入后若被另一套规则再次捕获,形成回环,表现为连接无响应或吞吐塌陷。
DNS 劫持:两套进程各写一份解析配置,容器内解析结果与宿主机预期不一致,表现为偶发解析失败。
下表是本页的核心结论:五类场景各自的典型现状、核心痛点、快连做法与预期结果。结果列描述的是配置生效后的稳定状态,不承诺具体性能数值。
| 场景 | 典型现状 | 核心痛点 | 快连做法 | 结果 |
|---|---|---|---|---|
| 宿主机已有出口 | 已跑着网关 / 软路由 / 其他加速进程 | 抢默认网关、路由回环、DNS 互相污染 | 容器内建独立 netns + 策略路由,main 表只读 | 并存 |
| Kubernetes 集群 | 多租户 Pod 共用节点网络 | 一个节点一套出口,无法按工作负载授权 | DaemonSet 注入,按 namespace / label 打标分流 | 按租户隔离 |
| CI 构建 | 构建机拉取 npm / PyPI / Maven 依赖频繁超时 | 拉取慢导致流水线超时,重试推高成本 | 构建容器单独挂载加速 sidecar,构建机不受影响 | 构建提速 |
| 多出口审计 | 要求记录每条流量的归属与出口选择 | 全局加速无法解释「走了哪个出口」 | 规则命中即产生可导出的决策记录(仅元数据) | 可审计 |
| 合规与隐私 | 要求零日志、密钥不落盘、数据不出境 | 日志含访问明细、配置留存凭据 | 白名单日志 + 内存加密 + 关闭 core dump | 可交付审计 |
五类场景可以叠加使用:例如集群场景中同时满足合规要求时,DaemonSet 挂载的规则文件与注入方式不变,只是额外开启决策元数据导出。各场景的完整部署顺序见 部署文档。
宿主机上已经跑着网关、软路由或另一套加速进程,是最常见的接入前提。关键不在于「选哪个」,而在于快连是否去动宿主机的路由与 DNS。
三处会互相干扰
默认网关抢占:两套工具都想写默认路由,后启动的覆盖先启动的,表现为原出口工具突然失效。
策略路由回环:加速流量注入后若被另一套规则再次捕获,形成回环,表现为连接无响应或吞吐塌陷。
DNS 劫持:两套进程各写一份解析配置,容器内解析结果与宿主机预期不一致,表现为偶发解析失败。
宿主机零改动
独立 netns:快连在容器自身的网络命名空间内建立独立命名空间,宿主机路由表不参与。
main 表只读:策略路由只写入容器自己的表,宿主机 main 表与默认网关保持原样。
DNS 分域:容器内使用独立的解析配置,宿主机原有 DNS 设置不被改写。
把规则默认值设为 direct,先保证宿主机原有出口在容器内也是默认行为,再显式声明需要加速的部分。这样即使规则写漏,也不会把不该走加速的流量送出去。
default: direct
rules:
# 1) 内网与云元数据永远直连,不进入加速链路
- cidr: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
action: direct
- cidr: "169.254.0.0/16"
action: direct
# 2) 明确标记为需要加速的工作负载
- label: "kl.route=proxy"
action: proxy
protocol: hysteria2
# 3) 镜像仓库直接拉取,不做额外处理
- domain: ["registry.npmjs.org", "pypi.org"]
action: direct
全局加速客户端工作在宿主机命名空间内,需要修改本机默认网关与 DNS,因此与既有出口工具必然冲突。快连只在容器内部建立独立的 netns 与策略路由表,宿主机路由零改动,两者可以长期并存。隔离原理见 隔离架构,规则维度的完整优先级见 分流规则优先级。
# 执行前记录宿主机默认网关
ip route show default
# 启动加速容器
docker run -d --name kl-proxy \
--cap-add NET_ADMIN --device /dev/net/tun \
-v /opt/kl/rules.yaml:/etc/kl/rules.yaml:ro \
registry.kiualanad.com.cn/kl/kl-proxy:v2.4.1 \
--config /etc/kl/rules.yaml --mode container
# 再执行一次,输出应与启动前完全一致
ip route show default
# 容器内确认使用的是自己的策略路由表
docker exec kl-proxy ip rule show
集群里的问题是出口粒度:节点级的全局配置无法区分租户。做法是用 DaemonSet 在每个节点注入快连,再由命名空间与标签决定每个工作负载走哪个出口。
DaemonSet 只负责注入快连进程与规则文件;真正的分流决策来自业务 Pod 上的 kl.route 标签。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kl-proxy
namespace: kl-system
labels:
app: kl-proxy
spec:
selector:
matchLabels:
app: kl-proxy
template:
metadata:
labels:
app: kl-proxy
spec:
initContainers:
- name: rules-sync
image: registry.kiualanad.com.cn/kl/kl-proxy:v2.4.1
command: ["sh", "-c", "cp /etc/kl-base/rules.yaml /shared/rules.yaml"]
volumeMounts:
- name: rules
mountPath: /shared
containers:
- name: kl-proxy
image: registry.kiualanad.com.cn/kl/kl-proxy:v2.4.1
args: ["--config", "/shared/rules.yaml", "--mode", "node"]
securityContext:
capabilities:
add: ["NET_ADMIN"]
volumeMounts:
- name: rules
mountPath: /shared
volumes:
- name: rules
configMap:
name: kl-rules
同一集群内不同租户用不同标签表达出口需求,规则文件负责把标签映射到出口:
配置 · 业务 Pod 与 namespace 标签# 命名空间级标签:整个租户走同一条出口
kubectl label namespace team-a kl.route=proxy --overwrite
# 工作负载级标签:单个 Deployment 单独指定
kubectl patch deployment api-gateway -n team-a -p \
'{"spec":{"template":{"metadata":{"labels":{"kl.route":"proxy"}}}}}'
kl.route 标签,规则文件用 namespace 维度匹配。适合整个租户出口一致的情况,改动面小,适合先做试点。spec.template.metadata.labels 上打 kl.route 标签,规则文件用 label 维度匹配。适合同一租户内不同工作负载走不同出口。kubectl rollout restart 让 DaemonSet 重新加载,不需要重建业务 Pod。多租户集群中一个节点通常运行多个租户的 Pod。节点级配置会让该节点上所有 Pod 共用同一出口,无法满足「按工作负载授权」的要求,也无法在审计时区分租户归属。应始终使用 namespace 级标签(或工作负载级标签)表达意图,把节点级配置留作无租户差异的内部集群使用。
构建机的依赖拉取是超时与重试成本的主要来源。把加速挂到构建容器上而不是构建机上,构建机本身的路由与缓存目录都不受影响。
# 加速容器与构建容器共用同一个 network 命名空间
docker run -d --name kl-build-proxy \
--network container:builder \
--cap-add NET_ADMIN --device /dev/net/tun \
-v /opt/kl/rules.yaml:/etc/kl/rules.yaml:ro \
registry.kiualanad.com.cn/kl/kl-proxy:v2.4.1 \
--config /etc/kl/rules.yaml --mode container
# 在构建容器内验证拉取命中加速
docker exec builder sh -c 'curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://registry.npmjs.org/'
# 构建结束后销毁,构建机上不留残留
docker rm -f kl-build-proxy
用 services 声明加速 sidecar,把凭据放进 variables 并在流水线内注入,不写进 .gitlab-ci.yml:
stages:
- build
build:app:
stage: build
image: node:20-bookworm
services:
- name: registry.kiualanad.com.cn/kl/kl-proxy:v2.4.1
alias: kl-proxy
command: ["--config", "/etc/kl/rules.yaml", "--mode", "container"]
variables:
KL_RULES: /etc/kl/rules.yaml
before_script:
- # 凭据由 CI 变量注入,仅存在于容器内存,不写入仓库
- test -n "$KL_SUBSCRIPTION"
- sh -c "curl -fsS https://registry.npmjs.org/ -o /dev/null"
script:
- npm ci
- npm run build
after_script:
- # 任务结束即销毁,容器内不留可复用副本
- rm -f .npmrc
artifacts:
when: always
expire_in: 1 day
paths: [dist/]
加速 sidecar 与构建任务同生命周期:任务开始时创建,任务结束时随作业容器一起销毁。它不安装在构建机上,不写入构建缓存目录,也不进入流水线产物。因此流水线重跑、缓存复用、产物下载这三个环节都不会把加速状态或凭据带出本次构建。凭据的存放方式见 零日志隐私栈。
当环境里同时存在多条出口时,需要回答的不是「流量走了哪条链路」,而是「依据哪条规则走了哪条链路」。后者只能由规则命中产生的决策记录给出。
用出口标识(egress)把命名空间映射到具体出口,配置里不出现任何访问明细:
default: direct
outbounds:
hk: { protocol: hysteria2, server: "hk.example.net:443" }
sg: { protocol: tuic, server: "sg.example.net:443" }
direct: { protocol: direct }
rules:
- namespace: team-a
action: proxy
egress: hk
- namespace: team-b
action: proxy
egress: sg
- cidr: ["10.0.0.0/8", "169.254.0.0/16"]
action: direct
audit:
decision_log: true # 仅导出决策元数据
终端 · 查看出口归属
# 查看各命名空间当前命中的出口
docker exec kl-proxy kl-ctl status
# 导出决策记录用于审计(仅规则 ID / 命中维度 / 动作 / 时间戳)
docker exec kl-proxy kl-ctl export --type decision --last 24h > decisions.jsonl
导出的决策记录只含规则 ID、命中维度、动作与时间戳四个字段,不含访问域名、目标 IP、会话内容与账号标识。它回答「依据哪条规则走了哪个出口」,不回答「用户访问了什么」,因此可以在不记录访问明细的前提下交付审计。字段定义与导出边界见 审计与合规边界。
合规评审关心的不是「有没有日志」,而是「日志里有什么、密钥在哪里、崩溃时会不会落盘」。快连对这四个问题都有可验证的答案。
docker exec kl-proxy sh -c 'ulimit -c' 的验证方法,输出 0 即为生效;同时说明 core_pattern 指向与持久卷未挂载两项检查。详见 崩溃转储关闭。可以,两者并存。快连VPN 在容器网络命名空间内建立独立 netns 与策略路由表,main 表只读,不修改宿主机默认网关,也不接管宿主机 DNS。宿主机原有出口照常工作,容器内流量由容器自己的规则决定走哪条链路。
不会。加速以 sidecar 形式与构建容器一起启动,只在构建阶段存在,任务结束即整体销毁。订阅凭据只存在于容器内存加密区域,不写入构建缓存目录、不写入日志、不写入流水线产物,构建机上不留下可复用的副本。
按命名空间配置。多租户环境下一个节点上会运行多个租户的 Pod,节点级配置会让所有租户共用同一出口,无法满足按工作负载授权的要求。应使用 namespace 级标签,让 DaemonSet 注入后按 namespace/label 打标分流,实现按租户隔离。