K8s 双 AZ 在香港机房的落地部署:Calico 与 Cilium 深度对比、eBPF 加速实测、Pod 安全边界与零信任网络
风从冷通道吹过来,戴着耳机也挡不住风扇的“喷气”声。我站在 A 机房(将军澳 AZ-A)和 B 机房(荃湾 AZ-B)之间的专线光纤熔接架前,手机上的 hubble observe 不时刷出跨 AZ 的流量。这个夜里我要把一套 Kubernetes 双 AZ 集群彻底切到 eBPF 数据面,顺便把 Calico 和 Cilium 做一次同场对比。故事从一个“看似简单”的跨 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 交换机支持 BGP 和 ECMP。
- 允许在边界对接运营商 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-B10.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,次选
VXLAN或IPIP,但务必校正 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_max 与 ip_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),让“只放行业务需要”的策略成为常态。
故障与坑位复盘(真现场)
- MTU 黑洞:跨 AZ 走 VXLAN 忘记降 MTU,HTTP 长连接偶发超时。修复:节点、Calico/Cilium 统一 MTU=1450/1440,必要时在边界做 MSS Clamping。
- 非对称路由 & RPF:BGP 多路径导致回程从另一 AZ 走,内核严格 RPF 丢包。修复:
rp_filter=0,或在 ToR 上做策略路由保持会话对称。 - SNAT 端口耗尽:节点 SNAT 出网消耗本地端口,峰值 64K 不足。修复:统一网关 SNAT;或调大
ip_local_port_range=1024 65535、开启nf_conntrack扩容。 - BPF map 内存不足(Cilium):高并发 FQDN/Service 导致 map 膨胀。修复:
bpf.mapDynamicSizeRatio合理调大,或拆分命名空间与服务粒度。 - 内核版本不兼容:CentOS 7 默认 3.x 内核对 eBPF 支持弱。修复:ELRepo 升级到
kernel-ml≥ 5.4;但建议直接 Rocky/Alma 9。 - kube-proxy 残留:切换到
kubeProxyReplacement=true时忘记禁用 kube-proxy DaemonSet。修复:先mode: none,再部署 Cilium。 - 观测盲点:仅靠 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=true,bpf.hostRouting=true,tunnel=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 下去,想起这套系统以后还要扛更多未知的流量和风险——但至少现在,我们有了 清晰的安全边界、可复制的部署手册,以及一份“熬夜后依然干净的网络拓扑”
