NetworkPolicy — 用标签画出来的防火墙
一句话总结
Kubernetes 的默认设置是“所有 Pod 都能与所有 Pod 通信”,而 NetworkPolicy 是唯一能够按 Pod 维度扭转该默认行为的标准手段。
为什么需要了解这些
假设一个 Pod 已被攻破。在默认配置的集群中,该 Pod 可以直接连接数据库、支付服务和内部管理 API,因为防火墙只位于集群外部边界,内部则是一片平坦的开放区域。入侵不止于一个 Pod,而是向周围扩散的现象称为横向移动。
NetworkPolicy 会在这片开放区域中筑起围栏。但它与传统防火墙的筑墙方式不同:目标不是通过 IP,而是通过标签选择。Pod 销毁并重新创建后 IP 会改变,标签却保持不变。策略如果不写 IP,而是描述“标签为 app=api 的 Pod”,那么无论自动扩缩容把 Pod 增加到十个,还是 Pod 迁移到其他节点,策略都依然有效。
工作原理
理解三条规则,就能解释大多数行为。
第一,策略附着在接收方。 podSelector 选择策略适用的 Pod,ingress.from 选择允许向这些 Pod 发送流量的来源。若要允许从 web 到 api 的流量,策略中的 podSelector 必须选择 api。把这个方向写反,是最常见的错误。
第二,策略采用白名单。 一旦出现至少一条选择某个 Pod 的策略,该 Pod 对应方向的流量就会变成“只允许策略中列出的内容”。因此,默认拒绝通过一条不含任何规则的策略来表示。
spec:
podSelector: {} # 네임스페이스의 모든 파드
policyTypes: [Ingress] # 인그레스에 대해
# ingress 규칙 없음 → 전부 거부
第三,from 数组中的各项是 OR,同一项内的选择器是 AND。 这是最微妙的一点。
from:
- namespaceSelector: {matchLabels: {purpose: monitoring}}
podSelector: {matchLabels: {app: prom}} # ← 같은 항목: AND
表示“monitoring 命名空间中的 prom Pod”;但如果再添加一个连字符,把它们拆成两个数组项,就会变成“monitoring 命名空间中的所有 Pod,或者任意命名空间中的 prom Pod”。一个缩进层级就能彻底改变含义。
出站策略还有一个陷阱。启用出站默认拒绝后,DNS 会最先失效。 Pod 为了把服务名称解析为 IP,必须查询 kube-system 中的 CoreDNS,而该路径也会被阻断。因此,出站默认拒绝始终需要配套 DNS 例外规则。而且 DNS 不只使用 UDP 53;响应较大时会切换到 TCP 53,所以两种协议都必须开放。
还可以通过 ipBlock 开放 IP 网段,其方式是用 cidr 广泛允许,再用 except 排除较小范围。不过,在集群内部通信中使用 IP,会失去基于标签策略的优势,因此最好只用于办公网段、外部网关等集群外部地址。
生产现场中的常见情况
第一,如果 CNI 不支持,策略只是装饰。 NetworkPolicy 对象虽然能创建,但真正执行拦截的是 CNI 插件。在 Flannel 等不支持 NetworkPolicy 的 CNI 中,无论创建多少策略都不会产生效果。作者的家庭实验室使用 eBPF 模式的 Cilium,因此还能使用 L7 规则和 Hubble 观测。本实验环境是 Pod 之间不流动真实流量的模拟集群,所以评分依据不是数据包是否被丢弃,而是策略是否准确编写,以及是否正确理解选择器语义。 可以在这里掌握语法与含义,再到具备 CNI 的环境中通过 Hubble 或 calicoctl 验证实际拦截。
第二,先部署默认拒绝造成事故。 这是实际发生过的故障。运维团队未经测试就在生产环境应用默认拒绝,却遗漏了 DNS 允许规则。所有服务发现都失败,readiness probe 也随之连锁失效。正确顺序是:先部署全部允许策略,最后再添加默认拒绝。
第三,标签规范。 策略只能通过标签选择目标,因此没有标签的 Pod 或命名空间会成为策略盲区。选择命名空间时常用的 kubernetes.io/metadata.name 是 Kubernetes 自动添加到每个命名空间的标签,使用起来很方便。
下一次实验要做什么
在一个命名空间中放置 web、api、db 三个 Pod,从入站默认拒绝开始,依次创建只允许 web 访问 api 的 8080 端口的策略、出站默认拒绝与 DNS 例外、允许来自其他命名空间的监控流量、允许 IP 网段并设置排除项,以及在一条策略中同时包含双向规则的综合练习。最后,用 JSON 绘制哪些策略适用于哪些 Pod 的地图。