Kubernetes Tips:NetworkPolicy 从“默认拒绝”到可运营的落地路径
一步步上线 NetworkPolicy:先做 ingress 默认拒绝,再做 DNS/关键依赖 egress 白名单,避免“一刀切”把业务打挂。
NetworkPolicy 是 Kubernetes 里非常高 ROI 的隔离能力:能显著降低横向移动与爆炸半径。但它也很容易在上线第一天就把业务“全断网”,原因通常是:
- 没理解 NetworkPolicy 的默认行为(它是 allow-list)
- egress 没规划好(DNS、监控、追踪、外部依赖全要走)
- 标签体系不稳定(policy 选不中或选错对象)
这篇 tips 给你一个可运营的推进顺序。
前置条件:你的 CNI 是否真的执行 NetworkPolicy
NetworkPolicy 的执行依赖 CNI 实现:
- 有的 CNI 默认支持并执行
- 有的需要显式启用
- 有的对某些规则存在限制
上线之前先确认平台能力,否则你写了 policy 可能“看起来有”,实际不生效。
NetworkPolicy 的核心语义:选中后“默认拒绝”
很多人误以为 NetworkPolicy 是“加一条 deny 规则”。实际更像这样:
- 如果某个 Pod 在某个方向(Ingress/Egress)没有被任何 policy 选中,那么该方向 默认允许。
- 一旦有 policy 选中了该 Pod 且包含该方向,则该方向 默认拒绝,只允许 policy 明确放行的流量。
这就是为什么“默认拒绝”通常是:
podSelector: {}(选中命名空间内所有 Pod)policyTypes: [Ingress]或[Egress]
第 1 步:先做 namespace 级别的 Ingress 默认拒绝
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: app
spec:
podSelector: {}
policyTypes:
- Ingress
这会阻止 app 命名空间内 Pod 的所有入站流量,除非后续显式放行。
上线建议:
- 先在非核心命名空间试点
- 或只对某个 app label 生效(更小爆炸半径)
第 2 步:放行来自 ingress controller/gateway 的流量
例如允许 ingress-nginx 命名空间过来的流量访问 app=api 的 8080:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress
namespace: app
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080
注意:namespaceSelector 依赖命名空间标签,确认你的集群确实有这些 label。
第 3 步:如果服务之间互调,先放行“同命名空间”
微服务常见需要 namespace 内互通:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: app
spec:
podSelector: {}
policyTypes: [Ingress]
ingress:
- from:
- podSelector: {}
这一步的意义是:先让业务恢复互通,再逐步细化到“谁能访问谁”(按 label 收敛)。
第 4 步:Egress 要当成迁移项目来做(DNS 先行)
Egress 是最容易把业务打挂的一环,因为你很可能不知道依赖到底有多少:
- DNS(必需)
- 指标/日志/追踪(Prometheus/OTel/日志收集)
- 外部 API(支付、短信、身份等)
- 证书、时间同步、镜像仓库(视环境而定)
最推荐的 egress 第一步:先放行 DNS。
一个更“紧”的 DNS 放行方式是只放行 kube-system 里 CoreDNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: app
spec:
podSelector: {}
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
不同集群 CoreDNS 的 label 可能不同,按实际改。
外部访问:ipBlock 很实用,但要认清局限
NetworkPolicy 支持 ipBlock:
egress:
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
注意点:
- 它是 IP 维度,不是 DNS 维度;外部 SaaS IP 可能变动
- 很多团队会引入统一 egress proxy/NAT,维持可控出口
调试与排障:先确认“哪些 policy 选中了我”
当你发现“突然不通”:
kubectl get netpol -n app
kubectl describe netpol -n app <name>
kubectl exec -n app -it <pod> -- sh
如果 Pod 内没有工具,建议用 ephemeral container 来跑 curl/dig/nc,这样不污染生产镜像且符合真实网络命名空间。
很多时候你会发现:
- 不是 policy 写错,而是 label 没打上
- 或者某个“宽松 allow” policy 让流量仍然放行(policy 的效果是 union)
平台常见的“基础放行集”
很多平台最终都会沉淀一组“每个 namespace 都需要”的 policy:
- 允许 ingress/gateway 进来
- 允许 monitoring 抓
/metrics - 允许 egress DNS
把这些做成模板/基线,会让运营成本大幅下降。
Checklist
- 确认 CNI 真正执行 NetworkPolicy
- 先做 ingress 默认拒绝,再逐步放行入口
- egress 从 DNS 开始,逐步补齐依赖白名单
- 维持稳定的 label 体系(policy 依赖 labels)
- 试点/渐进上线,避免“一刀切”
参考链接
FAQ
Q: 为什么策略不生效? A: 需要支持 NetworkPolicy 的 CNI 插件;没有 provider 时策略会被忽略。
Q: DNS 怎么放行? A: 添加 egress 到 kube-dns/CoreDNS(TCP/UDP 53),并限定 namespace 或 label。
Q: 默认就是隔离吗? A: 只有被策略选择的 Pod 才会被隔离;没有被选择的 Pod 仍然是默认放行。