在 Kubernetes 集群里,只要 Pod 有了 IP,理论上它就能访问集群内任何其他 Pod,外部来的流量也不受限制。这种默认全通的网络模型在测试环境很方便,但放到生产环境就是裸奔:一旦某个应用被入侵,攻击者可以横向移动到数据库、消息队列等敏感服务。Kubernetes 提供的 NetworkPolicy(网络策略)就是解决这个问题的官方方案,它允许你以声明式的方式定义哪些流量可以进、哪些流量可以出。本文将从原理讲起,逐步展开各种实战配置。

一、NetworkPolicy 的工作原理与前置条件
首先要理解一点:NetworkPolicy 本身只是一个 API 对象、一份声明,Kubernetes 自身的 kube-proxy 组件并不会执行它。真正干活的是 CNI 网络插件(Container Network Interface)。这意味着没有支持策略的 CNI 插件,你写的任何 NetworkPolicy 都只是摆设,Pod 之间照样全通,而且不会报错。
目前主流的支持 NetworkPolicy 的 CNI 插件包括 Calico、Cilium、Weave Net、Antrea 等,而 Flannel、较老版本的某些网络方案则不支持。所以在动手配置之前,先确认集群用的是哪个 CNI。查看方法很简单:
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave|flannel'
如果输出里能看到 calico-node 或 cilium 这类 Pod,说明策略大概率能生效。另外要注意,NetworkPolicy 是命名空间级别的资源,它只影响所选中的 Pod,不会影响 Service、节点本身。策略的生效逻辑可以概括为两条:一是 Pod 之间的流量是允许还是拒绝,取决于 Pod 是否被某个策略选中;二是只要 Pod 被任意一个策略选中,那么没有被任何规则明确允许的流量就全部拒绝。
还有一个新手容易忽略的细节:NetworkPolicy 是叠加关系,不是覆盖关系。如果 Pod A 同时被两个策略选中,那么两个策略允许的流量加在一起才是它的白名单,不会出现后面的策略覆盖前面的情况。
二、基础配置:默认拒绝与白名单放行
最常见的用法是先做一个默认拒绝全部的策略,再逐条放行需要的流量,这是安全上推荐的最小权限模式。下面是一个默认拒绝所有入站流量的策略:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: prod
spec:
podSelector: {} # 空的选择器,选中命名空间内所有 Pod
policyTypes:
- Ingress
# 没有 ingress 字段,意味着拒绝所有入站流量
这个策略的关键在于 podSelector: {},空的花括号表示选中 prod 命名空间下的全部 Pod,而 policyTypes 声明了 Ingress 但没有定义任何 ingress 规则,效果就是一刀切拒绝。同理,把 policyTypes 换成 Egress 就是默认拒绝所有出站流量。
在默认拒绝的基础上,再针对具体应用放行。比如一个 Web 应用只允许接收来自 nginx ingress 控制器的 80 端口流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-from-ingress
namespace: prod
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 80
这里有个重点:当 namespaceSelector 和 podSelector 写在同一个列表项里时,是并且关系,即流量来源必须同时满足命名空间和 Pod 两个条件。如果分成两个列表项写,就变成了或者关系,范围会大很多,这是实践中最常见的配置错误之一。
另外,Kubernetes 从 1.22 开始会自动为每个命名空间打上 kubernetes.io/metadata.name 标签,所以可以直接用它来匹配命名空间,不需要自己手动维护标签,稳定且省事。
三、进阶实战:出站控制与 IP 段限制
只管入站往往不够,出站同样重要。比如应用 Pod 需要访问同一命名空间的 Redis 和外部 DNS,其他出站一律禁止,可以有效减少数据外泄的风险。示例如下:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-redis-dns
namespace: prod
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
注意这里专门放行了 kube-dns 的 53 端口。很多人配完默认拒绝出站后发现服务解析不了域名,就是因为忘了 DNS 流量也被挡住了,这是排障时的高频问题。
除了标签选择器,还可以用 ipBlock 按 CIDR 限制来源或目标。比如允许访问某个固定网段的数据库集群:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-to-db-cidr
namespace: prod
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.8.0/24
except:
- 10.0.8.5/32
ports:
- protocol: TCP
port: 3306
需要提醒的是,ipBlock 匹配的是 Pod 出栈时的 IP。由于很多 CNI 插件(比如开启了 NAT 或者使用了 overlay 网络)在数据包离开 Pod 后会做地址转换,ipBlock 的行为在不同插件下可能有差异,生产使用前建议先在测试环境验证,或者优先使用 Calico 的 GlobalNetworkPolicy 等扩展资源来处理复杂的 IP 需求。
四、策略不生效的排查思路
配置完成后如果发现策略没起作用,可以按下面的顺序逐层排查。
第一步确认 CNI 插件是否支持 NetworkPolicy。用 Flannel 这类不支持策略的插件时,yaml 能正常 apply,kubectl 也不报错,但策略完全不生效,这是最隐蔽的坑。第二步检查标签是否匹配。可以用 kubectl get pods -n prod --show-labels 查看 Pod 的真实标签,确认 podSelector 和 namespaceSelector 写的标签确实存在,大小写和拼写错误很常见。
第三步分析策略叠加的影响。前面提过策略是叠加的,可能你只想放行 80 端口,但另一个旧策略放行了全部端口,叠加后等于没限制。可以用下面的命令列出某个命名空间下所有策略,逐一核对:
kubectl get networkpolicy -n prod kubectl describe networkpolicy allow-web-from-ingress -n prod
第四步在 Pod 内做连通性验证,常用工具是 netshoot 或 busybox:
kubectl run test --rm -it --image=nicolaka/netshoot -n prod -- /bin/bash # 在容器内测试连通性 curl -m 3 http://web.prod.svc.cluster.local nc -zv -w 3 10.244.1.12 6379
如果测试 Pod 自身也在默认拒绝策略的范围内,注意先给它放行测试通道,否则测什么都是不通,容易误判。
总的来说,NetworkPolicy 的配置思路遵循三步:先默认拒绝,再按业务最小化放行,最后持续验证。把网络策略纳入应用发布的标准流程,配合 GitOps 管理策略文件,集群的横向安全边界才算真正建立起来。
KubernetesNetworkPolicy网络策略修改时间:2026-09-12 02:28:34