当微服务在 Kubernetes 里横向扩散,传统 kube-proxy 的 iptables 规则只能做服务发现和负载均衡,却对”谁可以访问谁”一无所知。Cilium 网络策略基于 eBPF 在内核层实现细粒度访问控制,让 Zero-Trust(零信任)不再是一句口号。本文从安装到 L7 策略,带你用 Cilium 给集群装上”看得见的防火墙”。
一、iptables 为什么管不了”信任”
默认情况下,Kubernetes 的 Pod 网络是扁平的:任何 Pod 都能访问任何 Pod。kube-proxy 用 iptables 做 NAT 和转发,但它工作在 L3/L4 且只关心”怎么把包送到后端”,不关心”该不该送”。CNI 插件如 Calico 可以补 NetworkPolicy,但传统实现仍依赖 iptables 链式匹配,规则一多就线性扫描、性能陡降。
更深层的问题是:在 Kubernetes 里 Pod IP 是临时的,每次调度、扩缩容都会换一批新地址。你写在云安全组或宿主机 iptables 里的”放行 10.244.3.0/24″几乎一上线就过时。传统基于 IP 的防火墙,本质是给一个时刻在变形的对象画固定边界,天然失效。这也是为什么零信任架构强调”以身份为中心”而非”以网络位置为中心”。
Cilium 的思路完全不同:它用 eBPF 程序直接挂载在内核的网络栈钩子上,绕过 iptables 的繁冗链条,在 socket 层就能识别身份(基于 Pod 标签、ServiceAccount 乃至 HTTP 路径),既快又细。这与我们之前在 eBPF 内核观测 中用到的高效数据面思路一脉相承。
二、用 Helm 安装 Cilium(替换 kube-proxy)
最干净的方式是让 Cilium 以 kube-proxy 替代模式运行,连 iptables 规则都省了。下面是一组生产可用的安装参数:
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.16.0 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set k8sServiceHost=$KUBERNETES_SERVICE_HOST \
--set k8sServicePort=$KUBERNETES_SERVICE_PORT \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set securityContext.privileged=true
安装后,用自带的健康检查确认数据面就绪:
cilium status --wait
# ✓ Encapsulation: Disabled
# ✓ Controller Status: 34/34 healthy
# ✓ KubeProxyReplacement: Proxy replacement enabled
切换到 kube-proxy 替代模式后,服务负载均衡从 iptables 的随机匹配升级为 eBPF 的 Maglev 一致性哈希,连接跟踪不再依赖全局 conntrack 表,在大连接数下能显著降低内核开销与尾延迟。对高并发入口服务而言,这是性能与安全的双重收益。
三、Cilium 的身份模型:用标签而非 IP 寻址
Cilium 的核心抽象是”端点身份(Endpoint Identity)”。每个 Pod 启动时被分配一个基于标签推导出的数字身份,策略匹配的是这个身份,而不是随时会变的 IP。换句话说,后端扩容到 20 个 Pod、IP 全换,只要标签不变,网络策略完全不用动。这恰好补上了传统防火墙在动态环境下的短板。
也正因为身份与 IP 解耦,微分段(micro-segmentation)才真正可行:你可以把”订单服务””支付服务””用户服务”按业务语义隔离,而不是按”10.244.5.0/24 这个网段”隔离。安全策略第一次变得和架构图一致。
四、默认拒绝:先给命名空间上锁
Zero-Trust 的第一步是”默认什么都不信”。先对一个命名空间下发 deny-all 策略,把东西向流量全部切断,再按需放行:
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: default-deny-ingress
spec:
endpointSelector: {}
ingress: []
这条 Clusterwide 策略对所有端点生效(endpointSelector: {}),ingress: [] 表示不放行任何入向流量。此时 Pod 之间彻底隔离,连通性测试会全部超时——这正是我们想要的底色。
五、L3/L4 策略:按标签放行
现在放行”前端可以访问后端的 8080 端口”。Cilium 用标签选择器表达意图,比原生 NetworkPolicy 更直观:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: frontend-to-backend
namespace: shop
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
io.kubernetes.pod.namespace: shop
toPorts:
- ports:
- port: "8080"
protocol: TCP
在真实多租户场景里,这一步还能叠加命名空间级隔离:不同租户的前端即便都叫 frontend,只要 io.kubernetes.pod.namespace 不同,就互不可达。身份维度的组合让”同标签跨租户”这类经典越权被天然阻断。
下表对比了三类主流网络策略方案的落地差异:
| 方案 | 数据面 | L7 支持 | 可观测性 |
|---|---|---|---|
| 原生 NetworkPolicy + iptables | iptables 链式 | 无 | 弱 |
| Calico | iptables / eBPF 可选 | 部分(需 Istio) | 中 |
| Cilium | eBPF | 原生 HTTP/gRPC/Kafka | 强(Hubble) |
六、L7 策略:在 HTTP 层精细管控
真正的零信任要能识别”方法 + 路径”。比如只允许 GET /api/products,拒绝 POST /api/admin。Cilium 通过 Envoy 代理注入实现 L7 过滤:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: backend-l7
namespace: shop
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/api/products"
加上 L7 规则后,不匹配的 HTTP 请求会直接被 Cilium 在内核层拒绝,后端根本收不到报文。下面是 Cilium 与传统方案的对比要点:
| 维度 | Cilium | 传统 iptables CNI |
|---|---|---|
| 规则匹配位置 | 内核 eBPF 钩子 | netfilter 链 |
| 大规模规则性能 | 接近 O(1) | 线性扫描 |
| 身份来源 | 标签 / SA / L7 头 | 仅 IP/端口 |
| 运维复杂度 | 中 | 低 |
七、可观测性:用 Hubble 看清每一个连接
策略生效后是黑盒?Cilium 内置的 Hubble 把每个连接的五元组、策略判决、延迟都导出成流日志。本地排障一条命令即可:
hubble observe --namespace shop --follow \
--protocol http \
--to-pod backend
# Oct 01 10:22:31.123 frontend backend http-request GET /api/products -> 200
# Oct 01 10:22:33.456 frontend backend http-request POST /api/admin -> FORWARDED (dropped by policy)
Hubble UI 还能画出服务依赖拓扑图,配合 Prometheus + Grafana 监控 可以把策略拒绝数做成告警,让零信任策略的每一次拦截都看得见。
八、常见误区与排障
落地时最容易踩的坑有几个。第一,DNS 被拦:开了 deny-all 却忘了放行到 kube-dns 的 53 端口,结果所有解析超时,业务”假死”。务必单独加一条放行进 CoreDNS 的策略。第二,L7 代理资源:Envoy sidecar 要吃掉 CPU 和内存,如果不设 requests/limits,高峰期可能 OOM 把 Pod 拖垮。第三,hostPort / 节点本地流量:Cilium 默认不管宿主机网络命名空间,节点级暴露的服务要单独用 host policy 处理。第四,升级顺序:Cilium 自身升级建议先升控制面再升数据面,并利用其滚动重启能力,避免一次性把所有节点 eBPF 程序清空。
排障时优先用 cilium policy get 看哪些端点匹配了哪条规则,再用 hubble observe 确认报文是在哪一层被 drop 的。绝大多数”连不通”都是 deny-all 太狠或标签写错,而不是 Cilium 本身的问题。
九、生产落地检查清单
| 检查项 | 建议 |
|---|---|
| 渐进启用 | 先单命名空间 deny-all,验证业务后再全集群 |
| DNS 放行 | 务必加放行进 kube-dns 的 53 端口,否则解析全挂 |
| L7 代理资源 | Envoy 需预留 CPU/内存,避免 OOM |
| 策略即代码 | 策略随 Git 入库,走 GitHub Actions 评审发布 |
| 灰度验证 | 用 Hubble observe 先观察再收紧,防止误杀 |
十、与 Nginx / Caddy 的边界
注意分工:Nginx 反向代理 管的是”南北向”入口流量(外部用户 → 集群),而 Cilium 管的是”东西向”Pod 间流量(集群内部)。二者互补,不互相替代。若需要七层入口能力,可叠加 Caddy 或 Gateway API 处理 TLS 终止。纵深防御,才是 Zero-Trust 的本意。
Cilium 把 eBPF 的工程红利真正落到了网络安全上:默认拒绝打底、标签身份寻址、L7 精细管控、Hubble 全程可视。当你的集群规模上来、合规要求变严,这套组合拳会比”全通 + 祈祷”稳得多。它不只是一个网络插件,而是把零信任从 PPT 里的原则,变成了集群里可验证、可观测、可审计的真实防线。




