跨网的Kubernetes集群:从零构建混合云架构

跨越公网的Kubernetes集群:从零构建混合云架构

一个真实的生产案例:如何让内网Kubernetes集群与远程GPU节点协同工作


📖 目录

  1. 背景:为什么需要混合集群
  2. 架构抽象:理解网络的层次
  3. 核心挑战:跨公网的三大难题
  4. 解决方案:双向隧道设计
  5. 深入组件:技术栈详解
  6. 实战配置:从理论到实践
  7. 运维实践:监控与故障排查
  8. 经验总结:踩坑与优化

1. 背景:为什么需要混合集群

1.1 真实场景

想象这样一个场景:

  • 你有一个内网Kubernetes集群(172.1.x.x),部署着公司的核心服务
  • 公司采购了一台高性能GPU服务器,但只能放在远程机房
  • GPU服务器有公网IP. ..),但与内网集群隔离
  • 你希望:
    • GPU节点能运行Kubernetes Pod,享受K8s的调度和管理
    • 内网服务(MinIO、MongoDB)能被GPU节点访问
    • 主集群能管理GPU节点(kubectl logs、exec等)
    • 所有通信安全加密

这就是典型的混合云场景:内网主集群 + 公网边缘节点。

1.2 为什么不简单?

如果两个集群在同一内网,一切都很简单。但跨公网后:

复制代码
❌ 问题1:内网集群无法直接访问GPU节点的私有IP
❌ 问题2:GPU节点无法直接访问内网服务(防火墙/NAT)
❌ 问题3:Kubernetes组件期望节点在同一网络(Kubelet、CNI等)

我们需要一套完整的网络架构来解决这些问题。


2. 架构抽象:理解网络的层次

在深入技术细节前,先建立一个清晰的抽象模型。

2.1 三层网络模型

Kubernetes混合集群的网络可以抽象为三层:
访问
底层支撑
基础设施层 (Infrastructure Layer)
物理网络(公网/内网)
隧道技术(VPN/SSH/Konnectivity)
容器网络(Flannel/Calico)
管理层 (Control Plane)
kubectl命令
API Server ↔ Kubelet通信
日志收集、监控、调度
应用层 (Application Layer)
MinIO对象存储
MongoDB数据库
用户业务Pod

理解这三层很重要:

  • 应用层关心"能不能访问服务"
  • 管理层关心"能不能管理节点"
  • 基础设施层负责"打通网络通道"

2.2 节点拓扑图

我们的实际拓扑:
内网集群 192.168.0.0/16
Master-1

192.168.0.1

• API Server

• Scheduler

• etcd
Worker-1

192.168.0.2

• MinIO

• Ingress
Worker-2

192.168.0.3

• MongoDB

• 业务Pod
内网交换机/路由器
公网网关 NAT
互联网

公网
GPU节点

8.1.2.3

远程机房

• Kubelet

• GPU Pod

• 边缘应用

关键观察:

  1. 主集群3个节点,都在内网(192.168.0.x)
  2. GPU节点独立,只有公网IP(80.2.3.x)
  3. 两侧无法直接通信

3. 核心挑战:跨公网的三大难题

挑战1:双向通信的不对称性

问题描述:

复制代码
主集群 → GPU节点:❌ 内网无法主动连接公网节点的私有端口
GPU节点 → 主集群:❌ 公网无法访问内网私有IP

技术根源:

  • 内网有NAT,外部无法主动发起连接
  • 防火墙策略限制入站流量
  • 没有VPN或专线

影响:

  • kubectl logs/exec 无法工作(需要API Server → Kubelet)
  • GPU Pod无法访问MinIO/MongoDB(需要反向访问内网)

挑战2:Kubernetes原生假设被打破

Kubernetes设计时假设所有节点在同一扁平网络

yaml 复制代码
# Kubernetes期望的理想状态
node1: 192.168.0.1  ──┐
node2: 192.168.0.2  ──┼─→ 任意节点互相可达
node3: 192.168.0.3  ──┘

但我们的现实:

yaml 复制代码
# 实际状态
master: 192.168.0.230     ──→ GPU节点不可达
worker: 192.168.0.122     ──→ GPU节点不可达  
gpu:    8.1.3.4(公) + 10.x.x.x(私) ──→ 内网不可达

打破的假设:

  1. Pod IP直接路由(CNI期望)
  2. 节点IP互通(Kubelet心跳)
  3. Service网络可达(kube-proxy期望)

挑战3:安全与性能的平衡

安全需求:

  • 数据必须加密(公网传输)
  • 身份验证(防止中间人攻击)
  • 最小权限原则

性能需求:

  • 低延迟(AI推理实时性)
  • 高带宽(大模型传输)
  • 稳定性(避免频繁重连)

传统VPN方案的问题:

  • WireGuard: 上级防火墙限制UDP,无法稳定运行
  • OpenVPN: TCP-over-TCP性能差
  • IPsec: 配置复杂,维护成本高

4. 解决方案:双向隧道设计

我们的核心思路:用不同的隧道技术分别解决单向通信问题

4.1 整体架构图

GPU节点(公网)
主集群(内网)
管理平面

Konnectivity Tunnel

gRPC over TLS
数据平面

SSH Tunnel (tun0)

10.10.0.2 → 10.10.0.1
控制连接
暴露
反向访问
数据连接
⨩ API Server
⨩ Konnectivity Server
⨩ MinIO
⨩ MongoDB
⨩ SSH Server

10.10.0.1
⨨ Kubelet:10250
⨨ Konnectivity Agent
⨨ 应用Pod

(访问内网)
⨨ SSH Client

tun0
互联网

公网、双向TCP

4.2 设计原则

原则1: 单向隧道,双向解决

与其建一个复杂的双向隧道,不如:

  • Konnectivity: 专门解决 Master → GPU (管理流量)
  • SSH Tunnel: 专门解决 GPU → Master (数据流量)

每个隧道只做一件事,更简单、更可靠。

原则2: 谁需要谁主动

  • Master需要管理GPU → Konnectivity由GPU主动连接Master
  • GPU需要访问内网服务 → SSH由GPU主动连接Master

原则3: 分层独立
容器网络
Pod IP路由
使用Flannel VXLAN
管理层
kubectl, logs
使用Konnectivity
应用层
MinIO, MongoDB
使用SSH隧道

每层互不干扰,可独立升级。


5. 深入组件:技术栈详解

现在我们深入每个核心组件,理解它们如何工作。

5.1 Konnectivity: Kubernetes官方隧道方案

什么是Konnectivity?

Konnectivity是Kubernetes官方提供的网络代理解决方案,专门用于:

  • API Server访问Kubelet(kubectl logs, exec)
  • API Server访问Pod(kubectl port-forward)
  • API Server访问Service(aggregated API)

核心理念: 反向代理 + 长连接

架构组成

Kubelet (GPU:10250) Konnectivity Agent (GPU) Konnectivity Server API Server 用户/kubectl Kubelet (GPU:10250) Konnectivity Agent (GPU) Konnectivity Server API Server 用户/kubectl 步骤1: Agent主动建立连接 保持心跳 步骤2: 执行 kubectl logs 检测Pod在GPU节点 查看egress-selector配置 查找gpu-ampere01 的Agent连接 全程加密,无需内网端口暴露 gRPC长连接 连接成功 GET /api/pods/gpu-pod-123/log 转发请求(通过UDS) gRPC请求 HTTP: GET /containerLogs/... 日志数据 响应数据 返回响应 显示日志

工作流程详解

假设你执行 kubectl logs gpu-pod-123

步骤1: kubectl → API Server

bash 复制代码
GET /api/v1/namespaces/default/pods/gpu-pod-123/log

步骤2: API Server检测到Pod在GPU节点,查看Egress配置

yaml 复制代码
# /etc/kubernetes/egress-selector-configuration.yaml
egressSelections:
- name: cluster
  connection:
    proxyProtocol: GRPC
    transport:
      uds:
        udsName: /etc/srv-konnectivity-server/konnectivity-server.socket

步骤3: API Server将请求转发给Konnectivity Server(通过Unix Socket)

步骤4 : Konnectivity Server在已建立的gRPC长连接中找到gpu-ampere01的Agent

步骤5: 通过长连接发送请求到Agent

步骤6 : Agent在GPU节点本地访问 http://localhost:10250/containerLogs/...

步骤7: 响应原路返回

关键优势:

  • ✅ Agent主动连接Server,无需内网端口暴露
  • ✅ 长连接复用,避免频繁握手
  • ✅ TLS加密,安全可靠
  • ✅ Kubernetes官方支持,无缝集成
配置要点

Agent配置 (GPU节点):

yaml 复制代码
args:
- --logtostderr=true
- --ca-cert=/etc/srv-konnectivity-server-ca/ca.crt
- --proxy-server-host=kubernetes  # 关键:使用DNS名而非IP
- --proxy-server-port=8132
- --agent-identifiers=host=gpu-ampere01

# 关键技巧:hostAliases解决证书验证
hostAliases:
- ip: "114.1.2.3"         # 主集群公网IP
  hostnames:
  - kubernetes               # 匹配证书DNS SAN

为什么用hostAliases?

API Server证书的Subject Alternative Names (SAN)是:

复制代码
DNS: kubernetes
DNS: kubernetes.default
IP:  192.168.0.230  # 内网IP

没有公网IP 114.1.2.3!如果Agent直接连接IP,TLS验证会失败:

复制代码
x509: cannot validate certificate for 114.1.2.3 because it doesn't contain any IP SANs

通过hostAliases,我们让Agent通过DNS名kubernetes连接,然后将其解析为公网IP,完美!

5.2 SSH隧道: 数据平面的反向通道

虽然Konnectivity很强大,但它是单向的(Master → GPU)。GPU节点上的应用如何访问内网的MinIO、MongoDB呢?

答案:SSH反向隧道

SSH隧道原理

SSH隧道(SSH Tunneling)是利用SSH协议建立加密通道,转发流量的技术。

正向隧道 (Local Port Forwarding):

bash 复制代码
# 本地8080 → 远程服务器的3000
ssh -L 8080:localhost:3000 user@remote-server

反向隧道 (Remote Port Forwarding):

bash 复制代码
# 远程服务器8080 → 本地3000
ssh -R 8080:localhost:3000 user@remote-server

动态隧道 (Dynamic Port Forwarding / SOCKS代理):

bash 复制代码
# 本地1080作为SOCKS5代理
ssh -D 1080 user@remote-server

我们使用的是 TUN设备隧道,创建虚拟网卡:

我们的SSH Tunnel配置

Master节点 (SSH Server):

bash 复制代码
# /etc/ssh/sshd_config
PermitRootLogin no
PermitTunnel yes           # 允许TUN设备

GPU节点 (SSH Client):

bash 复制代码
# 建立隧道
ssh -o ServerAliveInterval=30 \
    -o ServerAliveCountMax=3 \
    -w 0:0 \                    # TUN设备编号
    root@114.1.2.3

# 创建后的网络配置
ifconfig tun0 10.10.0.2 netmask 255.255.255.0  # GPU侧
ifconfig tun0 10.10.0.1 netmask 255.255.255.0  # Master侧

# 添加路由
ip route add 192.168.0.0/16 via 10.10.0.1 dev tun0  # GPU节点

效果:

复制代码
GPU节点访问 192.168.0.233:31017 (MongoDB)
    ↓
查路由表 → 匹配 192.168.0.0/16
    ↓
经由 tun0 (10.10.0.1)
    ↓
SSH隧道加密传输
    ↓
Master节点解密,转发到 192.168.0.233:31017
    ↓
返回数据原路返回
AutoSSH保活

SSH连接可能断开(网络波动、防火墙),使用AutoSSH自动重连:

bash 复制代码
# systemd service
[Service]
ExecStart=/usr/bin/autossh -M 0 \
    -o "ServerAliveInterval=30" \
    -o "ServerAliveCountMax=3" \
    -o "ExitOnForwardFailure=yes" \
    -w 0:0 root@114.1.2.3

Restart=always
RestartSec=10

5.3 Flannel VXLAN: Pod网络的跨公网扩展

Kubernetes Pod之间需要通信,这是由CNI(Container Network Interface)插件负责的。

Flannel基础

Flannel是轻量级的CNI插件,支持多种后端:

  • VXLAN: 在UDP上构建虚拟L2网络
  • Host-gw: 直接路由(要求同一L2)
  • IPSec: 加密的VXLAN

我们使用 VXLAN over 公网

VXLAN工作原理

VXLAN (Virtual Extensible LAN) 将L2以太网帧封装在UDP包中:
Flannel处理
原始Pod数据包

───────────────

Eth Header | IP Header | Payload

Pod1 → Pod2
Flannel VXLAN 封装
VXLAN封装后的包

────────────────────────────────────────

外层Eth | 外层IP | UDP:8472 | VXLAN | 内层包

Host1 → Host2 | Pod1→Pod2
关键点:

  1. 外层IP是节点IP(可以是公网IP)

  2. UDP端口8472(VXLAN默认)

  3. 内层IP是Pod IP(10.244.x.x)

关键点:

  1. 外层IP是节点IP(可以是公网IP)
  2. UDP端口8472(VXLAN默认)
  3. 内层IP是Pod IP(10.244.x.x)
跨公网的VXLAN挑战

问题1: 公网IP vs 私有IP

Flannel默认使用节点的内网IP作为VTEP(VXLAN Tunnel Endpoint)。但GPU节点有公网IP!

解决: 使用annotation指定公网IP:

yaml 复制代码
apiVersion: v1
kind: Node
metadata:
  name: gpu-ampere01
  annotations:
    flannel.alpha.coreos.com/public-ip: "8.1.2.3"  # 公网IP

问题2: MTU设置

VXLAN封装增加50字节头部:

复制代码
原始以太网: 1500 MTU
+ VXLAN封装: 50字节
= 需要: 1550 MTU

但GPU节点通过公网,MTU通常是1500。解决:

yaml 复制代码
# Flannel ConfigMap
net-conf.json: |
  {
    "Network": "10.244.0.0/16",
    "Backend": {
      "Type": "vxlan",
      "VNI": 1,
      "MTU": 1320  # 保守值,避免分片
    }
  }

问题3: FDB(Forwarding Database)同步

Flannel需要知道"Pod IP在哪个节点"。通过Kubernetes API的Node对象和Flannel的backend实现:

bash 复制代码
# 查看FDB
bridge fdb show dev flannel.1

# 输出示例
00:00:00:00:00:00 dst 8.1.2.3 self permanent  # GPU节点

5.4 技术栈总结

现在你应该理解了每个组件的职责:

组件 作用 方向 流量类型 协议
Konnectivity API Server访问Kubelet Master → GPU 管理流量 gRPC/TLS
SSH Tunnel GPU访问内网服务 GPU → Master 数据流量 SSH/TCP
Flannel VXLAN Pod间通信 双向 容器流量 UDP/8472

6. 实战配置:从理论到实践

理论讲完了,现在手把手配置。

6.1 环境准备

主集群节点:

bash 复制代码
# Master-1
IP: 192.168.0.230 (内网) / 114.1.2.3 (公网)
OS: Ubuntu 22.04
K8s: v1.28.2

# Worker-1
IP: 192.168.0.122 (内网)

# Worker-2
IP: 192.168.0.233 (内网)

GPU节点:

bash 复制代码
IP: 8.1.2.3 (公网) / 10.x.x.x (内网,不可用)
OS: Ubuntu 22.04
GPU: NVIDIA A100

6.2 步骤1: 部署Konnectivity Server

1. 生成证书(如果需要):

bash 复制代码
# 在Master节点,使用集群已有的CA
cd /etc/kubernetes/pki

# API Server证书已有kubernetes作为DNS SAN
openssl x509 -in apiserver.crt -text -noout | grep DNS
# 输出: DNS:kubernetes, DNS:kubernetes.default, ...

2. 创建Konnectivity Server Deployment:

yaml 复制代码
# konnectivity-server.yaml
apiVersion: v1
kind: Pod
metadata:
  name: konnectivity-server
  namespace: kube-system
spec:
  hostNetwork: true
  containers:
  - name: konnectivity-server-container
    image: registry.k8s.io/kas-network-proxy/proxy-server:v0.0.32
    command:
    - /proxy-server
    - --logtostderr=true
    - --v=3
    - --server-port=0                         # 禁用HTTP
    - --agent-port=8132                        # Agent连接端口
    - --health-port=8133
    - --admin-port=8134
    - --cluster-cert=/etc/kubernetes/pki/ca.crt
    - --cluster-key=/etc/kubernetes/pki/ca.key
    - --server-cert=/etc/kubernetes/pki/apiserver.crt
    - --server-key=/etc/kubernetes/pki/apiserver.key
    - --mode=grpc
    - --uds-name=/etc/srv-konnectivity-server/konnectivity-server.socket
    volumeMounts:
    - mountPath: /etc/kubernetes/pki
      name: k8s-certs
      readOnly: true
    - mountPath: /etc/srv-konnectivity-server
      name: uds-socket
  volumes:
  - name: k8s-certs
    hostPath:
      path: /etc/kubernetes/pki
  - name: uds-socket
    emptyDir: {}

3. 配置API Server:

yaml 复制代码
# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
  containers:
  - command:
    - kube-apiserver
    # ... 其他参数 ...
    - --egress-selector-config-file=/etc/kubernetes/egress-selector-configuration.yaml
    volumeMounts:
    - mountPath: /etc/kubernetes/egress-selector-configuration.yaml
      name: egress-selector-config
      readOnly: true
    - mountPath: /etc/srv-konnectivity-server
      name: konnectivity-uds
  volumes:
  - hostPath:
      path: /etc/kubernetes/egress-selector-configuration.yaml
      type: FileOrCreate
    name: egress-selector-config
  - hostPath:
      path: /etc/srv-konnectivity-server
      type: DirectoryOrCreate
    name: konnectivity-uds

4. Egress Selector配置:

yaml 复制代码
# /etc/kubernetes/egress-selector-configuration.yaml
apiVersion: apiserver.k8s.io/v1beta1
kind: EgressSelectorConfiguration
egressSelections:
- name: cluster
  connection:
    proxyProtocol: GRPC
    transport:
      uds:
        udsName: /etc/srv-konnectivity-server/konnectivity-server.socket

6.3 步骤2: 部署Konnectivity Agent

在GPU节点加入集群后,部署Agent:

yaml 复制代码
# konnectivity-agent.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: konnectivity-agent
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: konnectivity-agent
  template:
    metadata:
      labels:
        k8s-app: konnectivity-agent
    spec:
      hostAliases:
      - ip: "114.1.2.3"          # Master公网IP
        hostnames:
        - kubernetes                # 匹配证书DNS SAN
      nodeSelector:
        kubernetes.io/hostname: gpu-ampere01
      containers:
      - name: konnectivity-agent
        image: registry.k8s.io/kas-network-proxy/proxy-agent:v0.0.32
        args:
        - --logtostderr=true
        - --v=3
        - --ca-cert=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        - --proxy-server-host=kubernetes
        - --proxy-server-port=8132
        - --agent-identifiers=host=gpu-ampere01
        - --service-account-token-path=/var/run/secrets/tokens/konnectivity-agent-token
        volumeMounts:
        - mountPath: /var/run/secrets/tokens
          name: konnectivity-agent-token
      serviceAccountName: konnectivity-agent
      volumes:
      - name: konnectivity-agent-token
        projected:
          sources:
          - serviceAccountToken:
              path: konnectivity-agent-token
              audience: system:konnectivity-server

创建ServiceAccount和RBAC:

yaml 复制代码
apiVersion: v1
kind: ServiceAccount
metadata:
  name: konnectivity-agent
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:konnectivity-agent
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: konnectivity-agent
  namespace: kube-system

6.4 步骤3: 配置SSH隧道

Master节点:

bash 复制代码
# 1. 配置SSH Server
vi /etc/ssh/sshd_config

# 添加
PermitTunnel yes
PermitRootLogin禁止-password  # 仅密钥登录

systemctl restart sshd

# 2. 配置网络(隧道建立后)
ip addr add 10.10.0.1/24 dev tun0
ip link set tun0 up

GPU节点:

bash 复制代码
# 1. 生成SSH密钥
ssh-keygen -t ed25519 -C "gpu-tunnel"

# 2. 复制公钥到Master
ssh-copy-id root@114.1.2.3

# 3. 创建systemd service
cat > /etc/systemd/system/tunnel-to-master.service <<EOF
[Unit]
Description=SSH Tunnel to Master Cluster
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/bin/autossh -M 0 \\
    -o "ServerAliveInterval=30" \\
    -o "ServerAliveCountMax=3" \\
    -o "StrictHostKeyChecking=no" \\
    -o "ExitOnForwardFailure=yes" \\
    -w 0:0 root@114.1.2.3
    
ExecStartPost=/bin/sleep 2
ExecStartPost=/sbin/ip addr add 10.10.0.2/24 dev tun0
ExecStartPost=/sbin/ip link set tun0 up
ExecStartPost=/sbin/ip route add 192.168.0.0/16 via 10.10.0.1 dev tun0

Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# 4. 启动服务
systemctl enable tunnel-to-master
systemctl start tunnel-to-master

# 5. 验证
ping 192.168.0.233  # 应该能通

6.5 步骤4: 配置Flannel

修改Flannel ConfigMap:

bash 复制代码
kubectl edit cm kube-flannel-cfg -n kube-flannel-system
json 复制代码
{
  "Network": "10.244.0.0/16",
  "Backend": {
    "Type": "vxlan",
    "VNI": 1,
    "Port": 8472,
    "MTU": 1320
  }
}

GPU节点加annotation:

bash 复制代码
kubectl annotate node gpu-ampere01 \
  flannel.alpha.coreos.com/public-ip=8.1.2.3  \
  --overwrite

6.6 验证

1. 验证Konnectivity:

bash 复制代码
# 查看Agent状态
kubectl get pods -n kube-system -l k8s-app=konnectivity-agent

# 查看Server日志
kubectl logs -n kube-system konnectivity-server | grep "Register backend"
# 应看到: "Register backend for agent" agentID="gpu-ampere01"

# 测试kubectl logs
kubectl logs <gpu-pod-name>  # 应该成功

2. 验证SSH隧道:

bash 复制代码
# GPU节点
ping 192.168.0.233
curl http://192.168.0.233:31017  # MongoDB

3. 验证Flannel:

bash 复制代码
# 查看FDB
bridge fdb show dev flannel.1 | grep 8.1.2.3 

# Pod间通信测试
kubectl run test --image=busybox --command -- sleep 3600
kubectl exec test -- ping <gpu-pod-ip>

7. 运维实践:监控与故障排查

7.1 监控指标

Konnectivity监控:

bash 复制代码
# Prometheus指标
curl http://localhost:8134/metrics

# 关键指标
konnectivity_network_proxy_server_ready  # Server就绪
konnectivity_network_proxy_agent_open_connections  # Agent连接数
konnectivity_network_proxy_agent_dial_failure_count  # 连接失败

SSH隧道监控:

bash 复制代码
# 检查隧道状态
systemctl status tunnel-to-master

# 检查tun0接口
ip -s link show tun0  # 查看流量统计

# 持续监控
watch -n 5 'ip addr show tun0; echo "---"; ip route show | grep tun0'

7.2 常见问题排查

问题1: kubectl logs超时

症状:

bash 复制代码
$ kubectl logs gpu-pod
Error from server: Get "https://192.168.0.122:10250/...": dial tcp 192.168.0.122:10250: i/o timeout

排查:

bash 复制代码
# 1. 检查Konnectivity Agent状态
kubectl get pods -n kube-system -l k8s-app=konnectivity-agent
# 应该是Running

# 2. 查看Agent日志
kubectl logs -n kube-system konnectivity-agent-xxx
# 查找 "Connected" 或错误信息

# 3. 检查Server日志
kubectl logs -n kube-system konnectivity-server
# 查找 "Register backend"

# 4. 检查UDS socket
ls -lh /etc/srv-konnectivity-server/konnectivity-server.socket
# 应该存在

常见原因:

  • UDS socket未创建 → 检查Server配置
  • Agent连接失败 → 检查证书、端口、防火墙
  • API Server未配置egress-selector → 检查manifest
问题2: GPU Pod无法访问MinIO

症状:

bash 复制代码
# 在GPU Pod内
curl http://192.168.0.233:30090
# 超时

排查:

bash 复制代码
# 在GPU节点宿主机
# 1. 检查tun0
ip addr show tun0
# 应有 10.10.0.2

# 2. 检查路由
ip route | grep 192.168
# 应有: 192.168.0.0/16 via 10.10.0.1 dev tun0

# 3. Ping测试
ping 192.168.0.233
# 应该通

# 4. 检查SSH隧道
systemctl status tunnel-to-master
ps aux | grep ssh

常见原因:

  • SSH隧道断开 → 重启service
  • 路由未生效 → 手动添加路由
  • 防火墙拦截 → 检查iptables
问题3: Pod间无法通信

症状:

bash 复制代码
# Master节点Pod ping GPU节点Pod
ping 10.244.5.10
# 不通

排查:

bash 复制代码
# 1. 检查Flannel annotation
kubectl get node gpu-ampere01 -o yaml | grep public-ip
# 应该是公网IP

# 2. 检查FDB
bridge fdb show dev flannel.1 | grep 8.1.2.3 

# 3. 抓包验证
tcpdump -i eth0 udp port 8472 -nn
# 发送ping时应看到VXLAN包

# 4. 检查MTU
ip link show flannel.1
# MTU应该是1320或更小

8. 经验总结:踩坑与优化

8.1 我们踩过的坑

坑1: 证书SAN不匹配

问题: Agent连接Server时报错:

复制代码
x509: cannot validate certificate for 114.1.2.3 because it doesn't contain any IP SANs

原因: API Server证书没有公网IP的SAN

解决 : 使用hostAliases将DNS名(kubernetes)映射到公网IP

教训: 理解TLS证书验证机制,灵活运用DNS

坑2: WireGuard UDP被拦截

问题: 最初使用WireGuard,频繁断线

原因: 上级防火墙严格限制UDP流量

解决: 改用基于TCP的SSH隧道

教训: 生产环境的网络策略可能很复杂,方案要适应现实

坑3: MTU分片导致性能下降

问题: 大文件传输速度很慢

原因: VX LAN封装 + SSH隧道导致MTU过小,频繁分片

解决:

  1. 降低Flannel MTU到1320
  2. 优化应用层,避免大包

教训: 跨公网注意MTU设置

8.2 性能优化建议

1. 连接复用

Konnectivity使用长连接,避免频繁建立TLS握手:

yaml 复制代码
# Agent配置
- --keepalive-time=1h
- --sync-interval=1s

2. 监控告警

关键指标告警:

yaml 复制代码
# Prometheus Rules
- alert: KonnectivityAgentDown
  expr: up{job="konnectivity-agent"} == 0
  for: 5m
  
- alert: SSHTunnelDown
  expr: node_network_up{device="tun0"} == 0
  for: 2m

3. 日志级别

生产环境降低日志级别:

复制代码
--v=3  # 开发环境
--v=1  # 生产环境(减少IO)

8.3 安全建议

  1. SSH密钥管理: 使用ed25519密钥,定期轮换
  2. 证书过期: 监控API Server证书有效期
  3. 网络隔离: SSH隧道仅允许特定IP段
  4. 审计日志: 开启Kubernetes审计,记录跨网访问

8.4 架构演进方向

短期(已完成):

  • ✅ 基于SSH + Konnectivity的混合方案
  • ✅ Flannel VXLAN跨公网

中期(规划中):

  • ⏳ Konnectivity UDS配置完成,实现完整管理面
  • ⏳ 使用Cilium替换Flannel(eBPF性能优化)
  • ⏳ WireGuard over TCP(如新版本支持)

长期(愿景):

  • 🎯 服务网格(Istio/Linkerd)统一管理
  • 🎯 专线/SD-WAN替代公网隧道
  • 🎯 边缘自治(离线也能运行)

总结

我们从零构建了一个跨公网的Kubernetes混合集群,核心要点:

架构精华

复制代码
┌────────────────────────────────────────────┐
│  理念: 分层解耦,单向隧道双向互补          │
│                                            │
│  管理面: Konnectivity (Master → GPU)       │
│  数据面: SSH Tunnel   (GPU → Master)       │
│  容器网络: Flannel VXLAN (跨公网Pod通信)   │
└────────────────────────────────────────────┘

关键技术

  1. Konnectivity: Kubernetes官方方案,gRPC长连接,反向代理
  2. SSH Tunnel: TUN设备,加密可靠,AutoSSH保活
  3. Flannel VX LAN: UDP封装,Public IP annotation,MTU优化
  4. 证书技巧: hostAliases绕过IP SAN限制

适用场景

适合:

  • 边缘计算(GPU/IoT设备)
  • 混合云(公有云+私有云)
  • 多数据中心互联
  • 临时节点扩容

不适合:

  • 延迟敏感应用(<10ms要求)
  • 超大规模(>100节点建议专线)
  • 合规要求禁止公网传输

学习路线

如果你想深入,建议按此顺序学习:

  1. 基础: Kubernetes网络模型(Service、Pod网络、DNS)
  2. 进阶: CNI插件原理(Flannel、Calico)
  3. 高级: Konnectivity源码、gRPC协议
  4. 实战: 在测试环境复现本文配置

参考文档

相关推荐
bepeater12342 小时前
使用Kubernetes部署Spring Boot项目
spring boot·容器·kubernetes
终生成长者3 小时前
Kubernetes常用操作与概念总结--从服务器导出mongo数据,并下载到本地
服务器·容器·kubernetes
高可用架构3 小时前
LangChain创始人:Agent 连接沙箱的两种模式(附深度架构解析)
架构·langchain
whatever who cares4 小时前
Java Web 架构全组件详解
java·前端·架构
运维行者_6 小时前
深入解析 Docker 监控:核心指标完整清单
运维·服务器·网络·数据库·docker·容器·eureka
礼拜天没时间.6 小时前
容器网络配置——从互联到自定义桥接
运维·网络·docker·容器·centos
无心水6 小时前
2025,一路有你!
java·人工智能·分布式·后端·深度学习·架构·2025博客之星
中草药z7 小时前
【Linux】拆解 Linux 容器化核心:Namespace 隔离 + cgroups 资源控制,附 LXC 容器生命周期实战
运维·docker·容器·虚拟化·namespace·lxc·cgroups