上一篇 下一篇 分享链接 返回 返回顶部

K8s 双 AZ 在香港机房的落地部署:Calico 与 Cilium 深度对比、eBPF 加速实测、Pod 安全边界与零信任网络

发布人:Minchunlin 发布时间:2025-09-26 10:39 阅读量:955

风从冷通道吹过来,戴着耳机也挡不住风扇的“喷气”声。我站在 A 机房(将军澳 AZ-A)和 B 机房(荃湾 AZ-B)之间的专线光纤熔接架前,手机上的 hubble observe 不时刷出跨 AZ 的流量。这个夜里我要把一套 Kubernetes 双 AZ 集群彻底切到 eBPF 数据面,顺便把 CalicoCilium 做一次同场对比。故事从一个“看似简单”的跨 AZ 网络策略开始,最终落在 性能、稳定性与安全边界 的平衡点上。

本文完全基于我的线下落地过程撰写,包含硬件参数、完整部署与优化、踩坑复盘,以及可直接套用的配置与脚本。

目标与总体设计

目标

在香港两地机房(双 AZ)部署高可用 K8s;2) 同时验证 Calico(BGP/隧道) vs Cilium(eBPF+kube-proxy 替代) 的带宽/延迟/CPU 占用;3) 落地 Pod 安全边界(PSA+Kyverno/CNP),实现零信任网络策略;4) 给出生产级优化与故障排查清单。

网络基线

  • 两个 AZ 通过 L3 专线 互通(RTT ≈ 0.4–0.6 ms),不做 L2 延伸。
  • 每个 AZ 内部叶脊架构,ToR 交换机支持 BGPECMP
  • 允许在边界对接运营商 BGP(出网)或 Anycast VIP(入口)。

选型结论(先剧透)

若追求极致网络性能/可观测简化转发层,**Cilium(eBPF + kube-proxy 替代 + Hubble)**明显更优。

若环境已有稳定的 BGP 生态或需要保守演进Calico BGP 无隧道也能打到不错水平。

机房与节点参数(真实可复用)

硬件拓扑

AZ 节点角色 数量 机型 CPU 内存 系统盘 数据盘 网卡 备注
AZ-A 控制面 + etcd 3 Dell R6525 AMD EPYC 7543P (32C) 128 GB 2 x 960 GB SSD (RAID1) 2 x 1.92 TB NVMe 2 x 25GbE (Intel E810) 与 AZ-B 互通 L3 专线
AZ-A Worker 6 Dell R6525 AMD EPYC 7543P (32C) 256 GB 2 x 960 GB SSD (RAID1) 2 x 3.84 TB NVMe 2 x 25GbE (Intel/Mellanox) SR-IOV 可选
AZ-B 控制面 + etcd 2 Dell R6525 AMD EPYC 7543P (32C) 128 GB 2 x 960 GB SSD (RAID1) 2 x 1.92 TB NVMe 2 x 25GbE 3+2 的 etcd 布局
AZ-B Worker 6 Dell R6525 AMD EPYC 7543P (32C) 256 GB 2 x 960 GB SSD (RAID1) 2 x 3.84 TB NVMe 2 x 25GbE

为什么控制面 3+2? 实测在 2 个 AZ 下,5 节点 etcd(AZ-A 3,AZ-B 2)在 AZ 间链路成熟且低抖动时相对稳妥,容忍一个 AZ 内少量节点维护而不丢 Quorum。

软件版本与区划

OS:Rocky Linux 9.x(推荐)。如果你被环境约束为 CentOS 7,见文末“兼容路径与注意事项”。

内核:5.14+(Cilium eBPF 替代 kube-proxy 建议 ≥ 5.10)。

Kubernetes:v1.30.x(kubeadm)。

容器运行时:containerd 1.7+

集群网段规划:

  • Pod CIDR:10.244.0.0/15(AZ-A: /16,AZ-B: /16 切片)
  • Service CIDR:10.96.0.0/16
  • Node 网段:AZ-A 10.10.0.0/24,AZ-B 10.20.0.0/24
  • MTU:1500(若隧道/Geneve,建议设置为 1450/1440

基础系统与内核优化(所有节点)

# 1) 关闭 Swap
swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab

# 2) 基本内核参数(/etc/sysctl.d/99-k8s.conf)
cat > /etc/sysctl.d/99-k8s.conf <<'EOF'
net.ipv4.ip_forward=1
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.all.arp_announce=2
net.core.rmem_max=134217728
net.core.wmem_max=134217728
net.core.somaxconn=65535
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_rmem=4096 87380 134217728
net.ipv4.tcp_wmem=4096 65536 134217728
net.ipv4.tcp_congestion_control=bbr
vm.max_map_count=262144
fs.file-max=2097152
EOF
sysctl --system

# 3) 时间同步(Chrony)
dnf -y install chrony && systemctl enable --now chronyd

# 4) containerd
dnf -y install containerd && mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
# 调整为 systemd cgroup
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl enable --now containerd

# 5) kube 工具
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
EOF

dnf -y install kubelet kubeadm kubectl && systemctl enable kubelet

MTU 提示:跨 AZ 若走隧道(VXLAN/Geneve),请将节点 mtu 调整至 1450/1440,并在 CNI 中同步配置,否则会出现 碎片/黑洞 与 TCP 性能骤降。

使用 kubeadm 初始化集群

控制面的 kubeadm 配置(示例开启/关闭 kube-proxy 两种)

# kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.30.4
networking:
  podSubnet: 10.244.0.0/15
  serviceSubnet: 10.96.0.0/16
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
# 若使用 Cilium 替代 kube-proxy,请设为 "none" 并不部署 kube-proxy
mode: "iptables"   # 或者:mode: "none"
# 初始化(仅在第一个控制面节点)
kubeadm init --config kubeadm-config.yaml --upload-certs

# 管理端 kubeconfig
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

# 记录 join 命令用于其他控制面/工作节点加入
kubeadm token create --print-join-command

方案一:Calico(BGP / 隧道)部署与优化

1. 安装 Tigera Operator

kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yaml

2. 自定义 Calico 安装(BGP 无隧道优先)

# custom-resources.yaml (Calico)
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  variant: Calico
  registry: docker.io
  calicoNetwork:
    mtu: 1500          # 若跨 AZ 走 IPIP/VXLAN,请降到 1450/1440
    ipPools:
    - cidr: 10.244.0.0/15
      encapsulation: None     # 首选无隧道(BGP)
      natOutgoing: Enabled
      nodeSelector: all()
---
apiVersion: operator.tigera.io/v1
kind: BGPConfiguration
metadata:
  name: default
spec:
  asNumber: 65001
  logSeverityScreen: Info
---
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: az-a-tor
spec:
  peerIP: 10.10.0.254        # AZ-A ToR 或 RR
  asNumber: 65000
---
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: az-b-tor
spec:
  peerIP: 10.20.0.254        # AZ-B ToR 或 RR
  asNumber: 65000
kubectl apply -f custom-resources.yaml

Calico 要点

若不具备 BGP,次选 VXLANIPIP,但务必校正 MTU

跨 AZ 可能出现 非对称路由,请禁用严格 RPF(见上文 sysctl)。

出网 SNAT 建议通过 边界网关 统一做,避免节点 SNAT 端口耗尽。

方案二:Cilium(eBPF + kube-proxy 替代)部署与优化

1. 安装 cilium CLI 与 Hubble(可选)

# 管理机
curl -L --remote-name https://github.com/cilium/cilium/releases/latest/download/cilium-linux-amd64.tar.gz
sudo tar xzf cilium-linux-amd64.tar.gz -C /usr/local/bin cilium

# 安装 Hubble CLI(可选)
curl -L --remote-name https://github.com/cilium/hubble/releases/latest/download/hubble-linux-amd64.tar.gz
sudo tar xzf hubble-linux-amd64.tar.gz -C /usr/local/bin hubble

2. 使用 Helm 安装 Cilium(启用 eBPF、关闭 kube-proxy)

helm repo add cilium https://helm.cilium.io/
helm repo update

helm upgrade --install cilium cilium/cilium \
  --namespace kube-system \
  --set kubeProxyReplacement=true \
  --set k8sServiceHost=<APISERVER_VIP> \
  --set k8sServicePort=6443 \
  --set bpf.masquerade=true \
  --set bpf.hostRouting=true \
  --set tunnel=disabled \
  --set autoDirectNodeRoutes=true \
  --set ipv4.enabled=true \
  --set ipv6.enabled=false \
  --set routingMode=native \
  --set devices='{ens3f0,ens3f1}' \
  --set hubble.enabled=true \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true

无隧道前提:底层 L3 可达 & ToR 具备正确路由。若必须隧道,改为:--set tunnel=vxlan --set mtu=1450

3. Cilium BGP 控制器(跨 AZ 公网/私网播告)

# cilium-bgp.yaml(简化示例)
apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeeringPolicy
metadata:
  name: bgp-to-tor
spec:
  nodeSelector:
    matchLabels:
      bgp: "true"
  virtualRouters:
  - localASN: 65001
    serviceSelector:
      matchLabels: {}
    neighbors:
    - peerAddress: 10.10.0.254/32
      peerASN: 65000
    - peerAddress: 10.20.0.254/32
      peerASN: 65000
kubectl apply -f cilium-bgp.yaml

4. 开启 eBPF 加速的观测与流量抓取

# 查看数据面健康
cilium status
# 实时观测 L3~L7 流量
hubble observe --since 1m --follow
# BPF maps 与 policy 统计
cilium bpf ipcache list
cilium policy get --verbose

双 AZ 亲测数据:Calico vs Cilium(eBPF)

基于相同业务 Pod(HTTP/GRPC,128B~64KB 负载),跨 AZ 测试 10 分钟平均值。

场景 方案 隧道 平均 RTT (ms) 吞吐 (Gbps) Node CPU (net) 备注
AZ 内 Calico 无隧道(BGP) 0.09 22.8 18% 25GbE 单向
AZ 内 Cilium 无隧道(eBPF) 0.08 24.3 14% kube-proxy 替代
跨 AZ Calico VXLAN 0.62 12.1 31% MTU=1450
跨 AZ Calico 无隧道(BGP) 0.58 18.4 22% 依赖 ToR 路由
跨 AZ Cilium 无隧道(eBPF) 0.55 21.7 16% 观测最稳

结论:Cilium eBPF跨 AZ 下的 吞吐与 CPU 占用均优于 Calico(尤其相对 VXLAN 场景)。若环境允许 无隧道直路由,收益更明显。

业务调度与高可用设计

反亲和与拓扑均衡

# Deployment 片段
spec:
  replicas: 6
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels: { app: web }
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values: [web]
        topologyKey: kubernetes.io/hostname

PDB

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  minAvailable: 4
  selector:
    matchLabels:
      app: web

入口与出网

Ingress:每 AZ 各一组 LB/Nginx Ingress(或 Cilium LB/IPAM),ExternalTrafficPolicy=Local 减少回程。

出网:边界网关统一 SNAT;若必须节点 SNAT,调大 nf_conntrack_maxip_local_port_range

Pod 安全边界:从 PSA 到 L7 策略

1. 启用 Pod Security Admission(集群级)

# 限制命名空间的默认安全级别(baseline/restricted)
apiVersion: core/v1
kind: Namespace
metadata:
  name: prod
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: baseline

2. Kyverno 策略(必须非 root、丢弃特权能力、禁用 hostPath)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-pods
spec:
  validationFailureAction: enforce
  background: true
  rules:
  - name: require-runAsNonRoot
    match:
      resources:
        kinds: [Pod]
    validate:
      message: "Pods must set runAsNonRoot=true"
      pattern:
        spec:
          securityContext:
            runAsNonRoot: true
  - name: drop-capabilities
    match:
      resources: { kinds: [Pod] }
    validate:
      message: "Drop NET_RAW and ALL unnecessary capabilities"
      pattern:
        spec:
          containers:
          - securityContext:
              capabilities:
                drop: ["ALL", "NET_RAW"]
  - name: deny-hostpath
    match:
      resources: { kinds: [Pod] }
    validate:
      message: "hostPath is not allowed"
      deny:
        conditions:
        - key: "{{ request.object.spec.volumes[].hostPath }}"
          operator: NotEquals
          value: null

3. Cilium L7 策略(细粒度到 HTTP 方法/路径)

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: web-l7
  namespace: prod
spec:
  endpointSelector:
    matchLabels: { app: web }
  ingress:
  - fromEndpoints:
    - matchLabels: { app: api }
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: GET
          path: "/v1/health"
        - method: POST
          path: "/v1/orders"
  egress:
  - toFQDNs:
    - matchName: api.payments.example.com
    toPorts:
    - ports:
      - port: "443"
        protocol: TCP

默认拒绝:为每个命名空间添加 DefaultDeny(CNP/NP),让“只放行业务需要”的策略成为常态。

故障与坑位复盘(真现场)

  1. MTU 黑洞:跨 AZ 走 VXLAN 忘记降 MTU,HTTP 长连接偶发超时。修复:节点、Calico/Cilium 统一 MTU=1450/1440,必要时在边界做 MSS Clamping
  2. 非对称路由 & RPF:BGP 多路径导致回程从另一 AZ 走,内核严格 RPF 丢包。修复rp_filter=0,或在 ToR 上做策略路由保持会话对称。
  3. SNAT 端口耗尽:节点 SNAT 出网消耗本地端口,峰值 64K 不足。修复:统一网关 SNAT;或调大 ip_local_port_range=1024 65535、开启 nf_conntrack 扩容。
  4. BPF map 内存不足(Cilium):高并发 FQDN/Service 导致 map 膨胀。修复bpf.mapDynamicSizeRatio 合理调大,或拆分命名空间与服务粒度。
  5. 内核版本不兼容:CentOS 7 默认 3.x 内核对 eBPF 支持弱。修复:ELRepo 升级到 kernel-ml ≥ 5.4;但建议直接 Rocky/Alma 9。
  6. kube-proxy 残留:切换到 kubeProxyReplacement=true 时忘记禁用 kube-proxy DaemonSet。修复:先 mode: none,再部署 Cilium。
  7. 观测盲点:仅靠 L3 监控看不出 L7 行为。修复:启用 Hubble,并为关键命名空间开启 L7 可视化与审计。

性能与稳定性优化清单

网络层

IRQ 绑核:将高流量网卡队列通过 irqbalance pin 到独立 CPU 核心。

NIC 调优:适度开启 GRO/LRO(视业务 rps),合理的 rx-usecs 中断合并。

BBR 拥塞控制:跨 AZ RTT 更友好,注意与中间盒兼容。

系统层

nf_conntrack_max ≥ 4M(视规模),net.netfilter.nf_conntrack_buckets 成比例。

容器镜像预热 & 私有镜像仓库(就近香港节点)。

Cilium 专属

kubeProxyReplacement=truebpf.hostRouting=truetunnel=disabled(可达则首选)。

hubble 打开流量审计,问题排查快人一步。

Calico 专属

首选 BGP 无隧道,ToR/RR 规划清晰;隧道时务必统一 MTU。

结合 FelixConfiguration 控制 conntrack/路由刷新频率。

兼容:被迫使用 CentOS 7 时的注意点(不推荐)

# 1) 升级内核(ELRepo)
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install -y kernel-ml
grub2-set-default 0 && reboot

# 2) 安装 containerd(从二进制或第三方仓库),kube 组件锁在 1.26~1.28 更稳。
# 3) 关闭 firewalld,转用 iptables,注意 nft 与老内核兼容坑。

建议尽快迁移至 Rocky/Alma 9,以原生支持 eBPF 特性。

验证脚本与示例

iPerf3 跨 AZ 压测

# AZ-A Pod 作为 server
kubectl -n bench run iperf-s --image=networkstatic/iperf3 -- -s
kubectl -n bench expose pod iperf-s --port 5201

# AZ-B Pod 作为 client
kubectl -n bench run iperf-c --image=networkstatic/iperf3 -- -c iperf-s.bench.svc.cluster.local -t 60 -P 4

Hubble 观察业务流

hubble observe --from-pod prod/web-* --to-pod prod/api-* --protocol http --since 5m

默认拒绝网络策略(命名空间级别)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: prod
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

凌晨 5 点,机房的灯终于不那么刺眼了。Grafana 的曲线稳定在我想要的区间,跨 AZ 的延迟与吞吐都比昨晚更漂亮。Cilium 的 eBPF 数据面让转发路径清爽、观测更直观;而 Calico BGP 给了我保守迁移的“后路”。我把最后一个 DefaultDeny 策略 apply 下去,想起这套系统以后还要扛更多未知的流量和风险——但至少现在,我们有了 清晰的安全边界可复制的部署手册,以及一份“熬夜后依然干净的网络拓扑”

目录结构
全文