K8s 网络策略与出站控制:云原生 CNI、Calico、出网代理和 Cilium 怎么选
目录
在采用扁平网络的公有云 Kubernetes 集群里,Pod 直接拿 VPC 子网的私网 IP。只要集群没开网络策略,同一个 VPC 里的任意虚拟机都能绕过负载均衡器直连 Pod 端口;反过来,一旦某个带数据源代理或 Webhook 功能的 Pod(例如监控面板、低代码平台或带回调接口的业务服务)被拿下,它也能直接充当跳板,向集群内的 Vault(:8200)、节点 Kubelet(:10250)、同 VPC 的无密码 Redis(:6379)以及云主机元数据服务(169.254.169.254)发起探测。
给这类 Pod 加出入站限制时,很快会遇到一个选型问题:EKS 这类托管集群自带的 VPC CNI 已经内置了基于 eBPF 的原生 NetworkPolicy 支持,到底够不够用?如果团队同时维护多个云厂商的 Kubernetes 集群,还要配合 External Secrets Operator(ESO)和准入控制器做统一的 Policy as Code,要不要引入 Cilium?
答案取决于需求停在「封堵内网横向移动」,还是要进一步管住「按域名放行出站流量」与「跨云策略统一」。
标准 NetworkPolicy 能管住什么,管不住什么#
Kubernetes 原生的 networking.k8s.io/v1 NetworkPolicy 工作在三层和四层,选择器只有三种:podSelector、namespaceSelector 和 ipBlock(CIDR 网段),再配合协议与端口。
如果目标只是阻断 Pod 在内网横向探测,标准 NetworkPolicy 已经覆盖了主要场景:
- 限制入站来源:例如只允许
monitoring命名空间内部的组件访问 Prometheus(:9090)和 Alertmanager(:9093),阻断其他命名空间或同 VPC 虚拟机直连未加认证的管理接口(如POST /-/quit或/api/v2/silences)。 - 封禁私网出站与元数据地址:在
Egress规则里用ipBlock放行0.0.0.0/0,同时在except里排除10.0.0.0/8、172.16.0.0/12、192.168.0.0/16和169.254.0.0/16。这样 Pod 仍然能发公网 HTTPS 请求,但无法触达集群内未显式放行的 Service、同 VPC 的数据库或节点 IMDS。
不过在托管集群里直接写标准 NetworkPolicy,有两类底层细节容易踩空,另有三件事是标准 API 本身做不到的。
云集群上用标准 NetworkPolicy 的两个细节#
第一个细节是 CNI 的策略开关默认可能是关着的。Kubernetes API Server 只负责把 NetworkPolicy 对象存进 etcd,不负责拦截流量;如果底层 CNI 没开策略控制器,kubectl apply 会正常返回成功,但规则不会生效。以 AWS EKS 的托管插件 vpc-cni 为例,虽然节点上的 aws-node DaemonSet 默认已经跑着 aws-eks-nodeagent 容器,但 kube-system/amazon-vpc-cni ConfigMap 里的 enable-network-policy-controller 默认是 "false",容器启动参数也是 --enable-network-policy=false,要在插件配置里显式打开才会开始下发 PolicyEndpoint。
第二个细节是 VPC 扁平网络里的源 IP 重叠与控制面代理路径:
- 当 Ingress 使用 AWS ALB 的
target-type: ip模式时,ALB 的弹性网卡(ENI)落在公有子网里,直接向 Pod 端口发包。如果图省事在NetworkPolicy里用ipBlock放行整个公有子网 CIDR(例如10.0.0.0/20),而同一个公有子网里恰好还跑着其他虚拟机,这些虚拟机照样能绕过 ALB 直连 Pod。此时要么只放行 ALB ENI 的/32地址,要么开启 VPC CNI 的ENABLE_POD_ENI=true,用SecurityGroupPolicy按安全组 ID 放行。 - 日常运维如果习惯用
kubectl get --raw "/api/v1/namespaces/monitoring/services/prometheus-operated:9090/proxy/..."查指标,请求包是由 EKS 控制面的跨账号 ENI 发往 Pod 的,不属于集群内任何 Namespace 的 Pod。入站策略如果只写了podSelector,这条代理路径就会超时(改用走宿主机本机 Kubelet 的kubectl port-forward则不受影响,因为 VPC CNI 默认放行本节点宿主机与 Pod 的通信)。
标准 NetworkPolicy 管不住的三件事#
一旦需求超出固定 IP 和同集群 Label,标准 NetworkPolicy 就会触及边界:
- 管不住动态 IP 的外部域名(FQDN Egress)。很多合法出站目标在 CDN 或云厂商动态负载均衡后面:监控面板要把告警发往
discord.com、从 Cloudflare 后面的opensearch.example.com查日志;ESO 要连secretsmanager.ap-northeast-1.amazonaws.com拉取密钥;业务 Pod 要调外部合作方的 HTTPS 接口。标准NetworkPolicy不认域名,一旦为了放行这些接口而打开公网:443,攻击者就能借httpbin.org或自建公网 HTTPS 端点把容器里的数据带出去。 - 没有原生的集群级兜底策略和 Audit 模式。标准
NetworkPolicy是 Namespace 级别的,且只有强制执行(Enforce)一种状态。要想一条规则封死全集群所有 Namespace 访问169.254.169.254,或者在上线前先观察一周「哪些合法流量会被误杀」,都得依赖各家 CNI 自己扩展的 CRD。 - 各云厂商的扩展 CRD 互不通用。AWS VPC CNI 在 v1.21.0(2025-12-15 发布)引入了
ClusterNetworkPolicy(networking.k8s.aws/v1alpha1),GKE Dataplane V2 提供了FQDNNetworkPolicy(networking.gke.io/v1alpha3),自建集群又是另一套。如果团队同时维护多个云的 Kubernetes 集群,试图做一套统一的 Policy as Code,策略模板就会按云厂商分叉。
四种方案逐项对照:云原生 CNI、Calico、出网代理与 Cilium#
把「云厂商原生 CNI」「独立出网代理」「Calico 开源版」和「Cilium 开源版」放在一起对比,各自的优缺点和能力边界如下:
| 对比维度 | 云厂商原生 CNI(以 AWS VPC CNI 为例) | 原生 CNI + 显式出网代理(Envoy / Squid) | Calico 开源版(Policy-only 或 CNI) | Cilium 开源版(Chaining 或 CNI) |
|---|---|---|---|---|
| 数据面实现 | AWS 自研 eBPF(aws-eks-nodeagent,v1.14.0 于 2023-08-29 引入) |
保持原生 CNI,集群内部署独立代理 Deployment | iptables / nftables 或 eBPF(可挂在 VPC CNI 之上只跑 Felix) | eBPF(TC / Socket 层挂载,加可选的节点级 DNS 与 Envoy 代理) |
| 三四层隔离与防内网探测 | 支持标准 NetworkPolicy |
支持标准 NetworkPolicy |
支持标准 NetworkPolicy 与 NetworkPolicy 优先级 |
支持标准 NetworkPolicy 与身份感知策略 |
| 按域名放行出网(FQDN) | 开源插件不支持(需买 AWS Network Firewall 等云服务) | 支持(按 HTTP Host 或 TLS SNI 匹配域名白名单) |
开源版不支持(域名策略属于商业版 Calico Enterprise / Cloud) | 开源版原生支持(toFQDNs,通过内置 DNS 代理动态学习 IP) |
| 集群级策略与优先级 | ClusterNetworkPolicy(支持 Admin / Baseline 两级) |
靠 Kyverno 为每个 Namespace 复制标准策略 | GlobalNetworkPolicy(支持数值优先级与显式 Deny) |
CiliumClusterwideNetworkPolicy(支持显式 egressDeny / ingressDeny) |
| 策略观察模式(Audit Mode) | 原生支持 NETWORK_POLICY_ENFORCING_MODE=standard 与日志记录 |
代理层访问日志可单独做 dry-run | 开源版仅支持 Log 动作,Staged Policy 属商业版 |
原生支持 Policy Audit Mode 与 Hubble 实时丢包流观测 |
| 七层协议过滤(HTTP 路径等) | 不支持 | 仅对明文 HTTP 有效,HTTPS 需做 TLS 解密 | 开源版需配合 Istio / Dikastes | 内置节点级 Envoy 支持 HTTP / gRPC / Kafka 规则 |
| 跨云 CRD 一致性 | 仅限单一云(绑定 networking.k8s.aws) |
高(只用标准 K8s NetworkPolicy 加通用代理配置) |
高(projectcalico.org/v3 跨云一致,但缺开源 FQDN) |
高(cilium.io/v2 在 AWS、GCP、自建 K3s 通用) |
| 存量集群引入与维护成本 | 最低(托管插件改一行参数即可) | 低(普通业务 Pod,不动节点内核与 CNI) | 中(需自己维护 DaemonSet 与版本升级) | 中高(Chaining 模式需关注 CNI 升级兼容,替换模式需重建节点) |
这四种方案在落地时的具体优缺点如下:
云厂商原生 CNI(AWS VPC CNI)#
优点是零额外组件和托管维护。在 EKS 1.35 加 Amazon Linux 2023(内核 6.12)的环境下,aws-eks-nodeagent 容器本来就随 vpc-cni 部署在每台节点上,开启策略不需要装任何新 Operator,也不会改变现有的 VPC IP 分配方式。
缺点是能力停在三四层和单云边界。它解决不了动态域名出网白名单,扩展 CRD 也只能留在 AWS 用。
原生 CNI 加显式出网代理(Envoy 或 Squid)#
优点是不动底层网络数据面。做法是用标准 NetworkPolicy 把业务 Pod 的公网出站全部封死,只允许它们连集群内 egress-proxy 命名空间的 Envoy 或 Squid Pod;只有这个代理 Pod 允许访问公网 :443,并在代理配置里按 TLS SNI 或 HTTP Host 校验域名白名单(比如只放行 discord.com 和 *.amazonaws.com)。这套做法只用标准 Kubernetes API,跨云通用,升级集群时不存在 CNI 兼容性风险。
缺点是侵入应用配置。每个需要出网的组件都得支持并配置 HTTPS_PROXY / NO_PROXY 环境变量;遇到忽略系统代理环境变量的 SDK 或二进制工具,改造起来比较烦琐。
Calico 开源版#
优点是策略表达力成熟、支持显式 Deny 与数字优先级。Calico 的 GlobalNetworkPolicy 在写复杂的多租户隔离规则时很直观,而且在 EKS 上支持「只装 Felix 策略引擎、保留 AWS VPC CNI 分配 IP」的叠加装法。
缺点是开源版没有 toFQDNs。很多人选型时容易混淆 Calico 开源版和商业版:Calico 官方文档里基于域名的出站策略属于 Calico Enterprise / Calico Cloud 付费功能。如果核心诉求是开源方案下的域名级出站控制,装 Calico 开源版解决不了问题。
Cilium 开源版#
优点是把「跨云统一 CRD」「开源 toFQDNs 域名控制」「Hubble 可观测性」和「跨集群 ClusterMesh」合在了一起:
- 透明的域名级出站白名单:Cilium 在开源版里直接提供
toFQDNs。Pod 照常发起 DNS 请求和 HTTPS 连接,无需配HTTPS_PROXY;Cilium 的 DNS Proxy 拦截:53响应,把域名解析出的 IP 动态写入该 Pod 的 eBPF 放行表。 - 跨云 Policy as Code 底座:不管是自建 K3s、AWS EKS 还是其他云集群,同一份
CiliumClusterwideNetworkPolicy和CiliumNetworkPolicy都能用 GitOps 统一同步;而在 GCP GKE 上,官方的 GKE Dataplane V2 底层本来就是 Google 托管的 Cilium。 - 排障与审计体验好:配合 Hubble CLI 或 UI,可以直接按 Pod 或判决结果(
DROPPED/AUDIT)实时看哪条连接被哪条策略拦了,省去了翻节点日志或抓包的麻烦。
缺点也很实在,主要集中在存量集群的接入复杂度和七层代理开销:
- 存量云集群的接入模式有取舍:新建集群从第一天就用 Cilium 最干净;但对于已经在跑业务的存量 EKS 集群,如果用 Cilium 替换
vpc-cni,需要重规划 Pod IP 管理并全量滚动重建节点;如果用 CNI Chaining 模式(cni.chainingMode=aws-cni,让vpc-cni管 IP、Cilium 管策略),虽然不用换 IP,但每次升级 EKS 控制面或vpc-cni插件前,都得先在测试集群验证链式挂载有没有兼容性问题。 - 开启七层 HTTP 策略会拉起节点级 Envoy:一旦在
CiliumNetworkPolicy里写了rules.http(按 URL Path 或 Method 过滤),Cilium 会把对应流量透明劫持到节点上的 Envoy 进程。这不仅增加每台节点的常驻内存开销,一旦 Envoy 重载或异常,经过它的长连接(如 WebSocket、gRPC)还会跟着抖动;而且对外访问的 HTTPS 流量本身是加密的,节点 Envoy 在不配证书解密的前提下也看不到加密包里的 HTTP 路径。 - DNS 代理与 TTL 边界:
toFQDNs要配合显式放行:53的rules.dns才能工作,因为只有挂了rules.dns的端口规则,Cilium 才会把 DNS 请求导进内置的 DNS Proxy 记录域名与 IP 的映射。如果应用绕过集群 DNS(比如直连外部 DoH 或写死 IP),或者上游 CDN 极短 TTL 切换 IP 时连接刚好跨过缓存过期窗口,偶尔需要调大toFQDNs.minTTL。
什么时候该留在原生 CNI,什么时候值得引入 Cilium#
把上面的权衡收拢成一条决策路径:
或大量使用动态域名出站白名单"] -->|"否:单云且只需三四层隔离"| A1["留在云厂商原生 CNI
(如开启 VPC CNI NetworkPolicy)"] Q1 -->|"是"| Q2["需要出网的组件是否只有一两个
且都支持配置 HTTPS_PROXY"] Q2 -->|"是,不想动节点 CNI"| A2["原生 CNI + 集群内 Envoy/Squid 出网代理"] Q2 -->|"否,组件多或要求透明拦截"| Q3["集群是新建还是存量生产"] Q3 -->|"新建集群 / K3s"| A3["直接用 Cilium 作为主 CNI"] Q3 -->|"存量 EKS 集群"| A4["Cilium Chaining 模式(aws-cni)
仅开 L3/L4 + toFQDNs + Audit"]
留在云厂商原生 CNI 的场景#
如果集群都在同一家云上(例如全在 AWS EKS),且眼前的任务只是「把监控组件和业务 Namespace 隔开、禁止 Pod 访问私网和 IMDS」,直接启用 vpc-cni 自带的网络策略就够了。
在 Terraform 里给 aws_eks_addon 加上 configuration_values 即可开启:
resource "aws_eks_addon" "vpc_cni" {
cluster_name = module.eks.cluster_name
addon_name = "vpc-cni"
resolve_conflicts_on_create = "OVERWRITE"
resolve_conflicts_on_update = "OVERWRITE"
configuration_values = jsonencode({
enableNetworkPolicy = "true"
nodeAgent = {
enablePolicyEventLogs = "true"
}
})
}
开启后,一条标准的 NetworkPolicy 就能把 Grafana 这类面板的出站锁在集群内 Prometheus 和公网 HTTPS 上,封死它去探测内网 Vault(:8200)、节点 Kubelet(:10250)或内网 Redis 的路径:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-grafana-egress
namespace: monitoring
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: grafana
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- podSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values: ["prometheus", "alertmanager"]
ports:
- protocol: TCP
port: 9090
- protocol: TCP
port: 9093
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 169.254.0.0/16
ports:
- protocol: TCP
port: 443
值得引入 Cilium 的三个条件#
当架构演进触及下面三个条件中的任意两个时,继续用「标准 NetworkPolicy 加手工打补丁」会比引入 Cilium 更累:
- 多云 Kubernetes 集群需要同一套 Policy as Code。团队同时管着 AWS EKS、其他公有云 Kubernetes 和自建 K3s,希望在同一个 GitOps 仓库里用 Kyverno 或 OPA 配合同一套网络策略 CRD 管理所有集群的基线,而不是为每个云写一套专属 CRD。
- 多个组件需要透明的域名级出站白名单(
toFQDNs)。除了监控告警发往discord.com、查opensearch.example.com,集群里的 ESO 还要连云厂商的 Secrets Manager 域名同步密钥,业务服务还要调几十个外部合作方域名,且不希望到处维护HTTPS_PROXY。 - 需要全局策略观察期(Audit Mode)和可视化排障(Hubble)。存量业务之前没有网络策略,直接切默认拒绝风险太高,需要先开 Audit 模式跑完至少一次完整发布周期,看着 Hubble 的丢包统计确认没有误伤再切强制执行。
两种落地做法对比:CNI Chaining 模式与完全替换 CNI#
决定引入 Cilium 之后,立刻会面临第二个分水岭:是用它**完全替换(Standalone)集群原有的 CNI,还是以链式调用(Chaining)**模式挂在现有 CNI 后面?
两者在职责边界、数据面性能与运维风险上的对比如下:
| 对比维度 | CNI Chaining 模式(以 AWS VPC CNI 为例) | 完全替换 CNI 模式(云 ENI IPAM 或 Overlay) |
|---|---|---|
| 网络生命周期与 IPAM | 云厂商 CNI 负责申请 IP、建 veth 网卡并配路由;Cilium 仅在网卡就绪后挂载 eBPF | Cilium 独占整个生命周期:自主申请 IP、创建 veth、配置路由 |
| kube-proxy 替代 | 保留 kube-proxy(链式模式下通常关闭 kubeProxyReplacement) |
可彻底关闭并卸载 kube-proxy(开启 kubeProxyReplacement=true) |
| eBPF 性能加成 | 依赖主 CNI 的网络栈,无法使用激进的 eBPF Host Routing 直接转发 | 纯净 eBPF 数据面,支持 Host Routing 绕过 iptables,支持 XDP 加速 |
| 云平台 IAM 权限 | Cilium 无需任何云平台 VPC 或 EC2 管理权限 | 云 ENI 模式下 Cilium Operator 必须持有申请和挂载 ENI 的高权限 |
| 存量集群迁移成本 | 较低(存量节点无需排水,下发 DaemonSet 即可对后续 Pod 生效) | 较高(存量节点无法平滑热切,需新建节点池逐步排水迁移) |
| 长期升级与维护隐患 | 存在两套 CNI 插件打架风险(如升级时 conflist 配置文件被覆盖) | 单一网络责任主体,无插件链冲突,但需自理底层 CNI 的所有运维 |
Chaining 模式的运行机制与潜在隐患#
在 Chaining 模式下,节点上的 /etc/cni/net.d/ 会维护一个插件列表文件(如 10-aws.conflist)。Kubelet 创建 Pod 时,先调用主 CNI(如 aws-cni)在宿主机和容器之间建立 veth 对并分配 VPC IP;主 CNI 返回成功后,紧接着调用 cilium-cni。Cilium 此时不碰 IPAM,只负责把预编译好的 eBPF 程序挂载到该 veth 网卡上,从而实现网络策略检查与流量采集。
这种模式在存量集群上落地的代价最小,但有两个长期隐患需要关注:
- 配置覆盖引发的创建卡顿:当托管集群升级控制面或升级主 CNI 插件版本时,云厂商的自动化控制器有时会重新下发默认的 CNI 配置文件,把包含 Cilium 的
.conflist覆盖回单插件的.conf。一旦发生覆盖,新创建的 Pod 就不会挂载 Cilium 的 eBPF 程序,甚至出现 Pod 停留在ContainerCreating状态。 - eBPF 极致性能无法解锁:由于网卡拓扑和内核路由由主 CNI 决定,Cilium 无法启用
bpf.masquerade=true或 bypass 宿主机网络栈的 Host Routing 快速路径,所有出入站流量依然要老老实实走一遍内核 netfilter 和路由查找。
完全替换模式的适用边界#
在完全替换模式下,Cilium 成为集群内唯一的 CNI。如果是在 AWS 环境并采用 ipam: eni 模式,Cilium Operator 会直接通过 AWS SDK 调用 EC2 接口为节点申请辅助 ENI 和私网 IP,直接给 Pod 分配 VPC 原生地址。
这种模式架构清晰,能完整享受 eBPF 替代 kube-proxy、Socket 层负载均衡和绕过内核栈直接转发的性能优势,也不会发生多插件配置覆盖的事故。但它的落地门槛很高:对于已有业务在跑的生产集群,无法原地换掉正在分配 IP 的 CNI,必须通过新建独立节点组、逐批驱逐工作负载的方式滚动迁移;同时 Cilium Operator 需要配置独立的云厂商 IAM Role,授予 ec2:CreateNetworkInterface、ec2:AttachNetworkInterface 等一系列高权限。
云上使用 Cilium ENI 模式的四个生产配置细节#
如果决定从第一天起就完全替换原生 CNI(使用 ipam: mode: eni),根据 Isovalent 官方指南 以及业界在大规模生产集群上的落地经验,有四个关键配置直接决定了后续的稳定性和运维成本:
- 开启前缀委派(Prefix Delegation)避免 API 限流:在默认的二级 IP 模式下,每个 Pod 都需要独立申请一个私网 IP,这会导致 Cilium Operator 高频调用 AWS EC2 API,在集群突发扩容时极易触发
RequestLimitExceeded限流错误。配置eni.awsEnablePrefixDelegation: true后,Cilium 会一次性向 AWS 申请/28规格的 IPv4 前缀(每段包含 16 个 IP),大幅降低 API 调用频率,同时显著提高单个节点的 Pod 容纳密度(参考 AWS 官方前缀配置指南)。 - 开启空闲 IP 自动释放(
awsReleaseExcessIPs)防止子网耗尽:如果不打开eni.awsReleaseExcessIPs: true,在业务高峰扩容、随后缩容后,节点已经释放的前缀不会自动退还给 VPC 子网,导致少量节点长期霸占大量子网 IP,甚至引发 VPC IP 枯竭。 - 给节点配置初始化污点防 Pod 抢跑:在节点加入集群时,Kubelet 会先启动,而 Cilium Agent 挂载 eBPF 与配置 IPAM 需要短暂时间。在节点引导参数中配置污点
node.cilium.io/agent-not-ready:NoSchedule,由 Cilium 初始化就绪后自动移除,防止业务 Pod 在网络就绪前被调度上去导致崩溃。 - 存量集群采用 Blue/Green 节点池平滑迁移:对于已有业务在跑的集群,不要尝试在存量节点上原地切换 CNI。成熟的迁移方式是给原有节点打上标签并给旧 CNI(如
aws-node)设置节点亲和性,新建独立的节点组部署 Cilium,通过分批驱逐工作负载平滑过渡。
另一个常见分歧:要不要直接上 Istio 或 Linkerd 这类 Service Mesh?#
在讨论出网控制和网络隔离时,另一个经常被拿出来比较的选项是 Service Mesh。之所以很多团队会在 Cilium 和 Istio / Linkerd 之间犹豫,是因为 Cilium 官方也在主推「Cilium Service Mesh(无 Sidecar 模式)」,两者在「出网域名白名单」、「mTLS 加密」和「L7 访问控制」上看起来功能非常相似。
把它们放在一起对比,底层分工和代价模型其实有很大差异:
| 维度 | Cilium(CNI / 内核层) | 传统 Sidecar 模式(Istio / Linkerd) | 新一代 Sidecarless 模式(Istio Ambient) |
|---|---|---|---|
| 控制点与主战场 | 内核与网络层(L3/L4 + eBPF),可选拉起节点级 Envoy | 应用网络层(L7 为主),每个 Pod 强制注入 Sidecar 容器 | 分层网络:节点级 ztunnel(L4)+ 按需独立 Waypoint Pod(L7) |
| 域名出站管控(防 SSRF) | toFQDNs 拦截 DNS :53,动态放行 IP,应用完全无感 |
依赖 ServiceEntry + Egress Gateway,依赖 iptables 劫持到 Envoy |
类似传统 Istio,但四层拦截由 ztunnel 统一处理 |
| mTLS 与身份粒度 | WireGuard/IPsec 透明加密;Mutual Auth 主要做握手鉴权 | 原生 X.509 证书绑定 SPIFFE ID,每条连接应用层鉴权与加密 | ztunnel 原生处理应用间 mTLS,证书由控制面按需轮换 |
| 资源与性能损耗 | 极低(不进用户态代理);开七层策略时每节点常驻一个 Envoy | 较高,Pod 数量多时存在显著的内存放大(每个 Pod 几十上百 MB) | 低到中等,节点 ztunnel 常驻内存小,按需拉起 Waypoint |
| 微服务高级治理 | 支持基础路由与 Ingress,但非核心强项 | 行业标杆(权重切流、故障注入、分布式追踪头注入) | 继承传统 Istio 完整的微服务治理能力 |
选型侧重点:出站管控 vs 深度应用治理#
如果团队的核心痛点只是「封堵内网横向探测(SSRF)」和「透明放行特定外部域名(如各类外部 API、云端密钥管理服务或通知服务)」,优先选 Cilium 会轻松得多:
- 没有 Sidecar 内存税:无需在每个业务 Pod 里常驻一个 Envoy 容器,集群即便有几千个 Pod 也不用承受巨大的内存开销放大。
- 协议透明无侵入:Cilium 的
toFQDNs本质上还是在内核层根据 DNS 响应动态维护 IP 过滤规则,不会破坏非 HTTP 协议或长连接(如数据库长连接、Redis、gRPC 等)的原本语义。
什么时候必须引入 Istio 或 Linkerd?只有当业务需求进入了深水区的应用层治理:
- 严格的端到端应用身份鉴权:要求调用链上的每一跳都必须由 X.509 证书携带 SPIFFE ID,在七层严格校验「只有服务 A 的身份才能调服务 B 的特定 URL 路径」;
- 复杂的微服务流量调度:需要根据 HTTP Header 灰度切流、做跨集群金丝雀发布、或者主动注入故障与请求重试;
- 分布式链路追踪上下文透传:希望在不需要应用代码配合的前提下,由代理层自动串联 Trace ID。
两者在现代云原生架构中并不互斥。业界非常多的大规模实践是让 Cilium 负责底座(跨云网络打平、高性能 eBPF 路由、节点级防 SSRF 与 toFQDNs 域名出网),在上层核心业务链路上按需挂载 Istio Ambient 或 Linkerd 负责应用级 mTLS 与流量治理。
存量云集群引入 Cilium 的两个避坑选择#
如果评估下来决定引入 Cilium,落地时克制使用它的功能范围,能避开绝大部分稳定性隐患。
存量 EKS 用 Chaining 模式,关掉原生 CNI 的策略开关#
在存量 EKS 集群上,按 Cilium 官方的 AWS CNI Chaining 指南部署 cni.chainingMode=aws-cni 时,Pod 的网卡创建和 VPC IP 分配仍然归 AWS vpc-cni 管,Cilium 只往容器的 veth 网卡上挂 eBPF 程序。
这里有一条硬前提:保持 AWS vpc-cni 自己的 enableNetworkPolicy = "false"。不要同时打开 vpc-cni 的 aws-eks-nodeagent 策略执行和 Cilium 的策略执行,两个 eBPF 策略引擎同时挂在同一张网卡上会让排障变得非常困难。
只用三四层加 toFQDNs,不拿节点 Envoy 做七层 SSRF 防护#
用 Cilium 防 SSRF 和数据外带时,把范围控制在 L3/L4 加 DNS Proxy(toFQDNs) 这一层性价比最高:
- 应用层自身的白名单(例如 Grafana 的
[security] data_source_proxy_whitelist、禁止在线安装非预期插件、收紧 ServiceAccount RBAC)照常在应用配置里做; - 网络层用
CiliumClusterwideNetworkPolicy的egressDeny一行封死所有非系统 Pod 访问169.254.169.254/32和节点 Kubelet 端口; - 对需要出公网的组件,用
CiliumNetworkPolicy的toFQDNs精确锁死目标域名,不开启rules.http七层路径过滤,从而避开节点级 Envoy 代理的内存与连接开销:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: grafana-egress-fqdn
namespace: monitoring
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: grafana
egress:
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
- toEndpoints:
- matchExpressions:
- key: app.kubernetes.io/name
operator: In
values: ["prometheus", "alertmanager"]
toPorts:
- ports:
- port: "9090"
- port: "9093"
- toFQDNs:
- matchName: "discord.com"
- matchName: "opensearch.example.com"
- matchName: "hc-ping.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
配合 Kyverno 或 OPA 做 Policy as Code 时,两边的职责边界也很清楚:准入控制器负责校验静态声明(例如镜像签名、runAsNonRoot、限制业务 Namespace 的 ESO ExternalSecret 只能引用对应环境的 SecretStore),并在新建 Namespace 时自动生成基线 CiliumNetworkPolicy;Cilium 负责在运行时按身份和域名执行出入站拦截。
小结#
只在一个云厂商的集群里做基础的三四层隔离和防内网探测时,开启云厂商原生 CNI 的 NetworkPolicy 开关(如 AWS VPC CNI 的 enableNetworkPolicy)就能拦住 Pod 扫集群内部服务、同 VPC 私网和云主机元数据,不需要为此引入 Cilium。等到团队面对跨多云集群的 Policy as Code 统一治理、需要用 toFQDNs 透明管控大量动态域名出站(如 ESO 拉云端密钥、监控发外部通知、业务调第三方接口),或者需要 Audit 模式与 Hubble 流量观测时,Cilium 才真正省事。在存量云集群落地时,优先采用 CNI Chaining 模式并只启用三四层加 toFQDNs,把应用层路径收敛交给应用自身配置和准入控制,能兼顾安全边界与运行稳定性。
相关文章#
- AWS EKS 从 VPC CNI 迁移到 Cilium eBPF 独立 ENI 模式
- K8s 应用从 Vault 取密钥:Agent sidecar、init-only、VSO、ESO 和 CSI 怎么选
- K8s Service 访问链路:域名如何解析到 ClusterIP,再转发到 Pod
- K3s 从 Flannel 换到 Cilium 之后冒出来的三个问题
- Oracle Cloud K3s 迁移到 Cilium:一次把网络、密钥和状态数据都翻出来的升级
- Cilium ClusterMesh 实战:连接两个 K3s 集群的跨云服务发现
参考资料#
- Amazon VPC CNI now supports Kubernetes Network Policies:2023-08-29 AWS VPC CNI v1.14 原生支持 NetworkPolicy 的发布说明与架构解析
- amazon-vpc-cni-k8s v1.21.0 Release Notes:2025-12-15 引入
ClusterNetworkPolicy支持 - Configure Kubernetes Network Policies in Amazon VPC CNI:
enableNetworkPolicy、nodeAgent参数与限制说明 - Assign more IP addresses to Amazon EKS nodes with prefixes:AWS EKS 前缀委派机制与计算公式
- Cilium Documentation: AWS ENI IPAM:Cilium 在 AWS 上的 ENI IPAM 模式与参数配置
- Cilium Documentation: DNS-Based Policies (
toFQDNs):rules.dns拦截机制与minTTL配置说明 - Cilium Documentation: AWS VPC CNI Chaining:在存量 EKS 上复用 VPC CNI IP 分配的链式部署方式
- Isovalent Blog: EKS BYOCNI and Cilium:在 Amazon EKS 上以原生 CNI 模式部署 Cilium 的架构与实践
- Isovalent Blog: How to Deploy Cilium and Egress Gateway in EKS:在 EKS 上部署 Cilium 并通过 Egress Gateway 管控出网流量
- GKE Dataplane V2 overview:Google Cloud 基于 Cilium 的托管数据面说明
- Calico Enterprise: Domain-based policy:Calico 域名级策略在商业版中的支持说明