Kubernetes 网络策略全解:从 NetworkPolicy 到 GlobalNetworkPolicy 实战
导读 :在默认情况下,K8S 集群中所有 Pod 之间是完全互通 的------任何 Pod 都可以访问任何其他 Pod 的任何端口。这在开发测试环境没问题,但在生产环境中是巨大的安全风险。本文将从零开始,结合真实的生产级 YAML 和验证命令,系统讲解 K8S 网络策略的三种控制维度(ipBlock / namespaceSelector / podSelector),对比 K8S 原生 NetworkPolicy 与 Calico 增强 GlobalNetworkPolicy 的差异,并提供可直接落地的最佳实践方案。
一、为什么需要网络策略?
1.1 默认网络模型的隐患
K8S 集群安装完成后,所有 Pod 之间的网络通信没有任何限制:
┌─────────────────────────────────────────────────┐
│ K8S 默认网络模型 │
│ │
│ Pod A (前端) ──→ Pod B (数据库) ✅ 可访问 │
│ Pod C (日志) ──→ Pod B (数据库) ✅ 可访问 │
│ Pod D (任意) ──→ Pod B (数据库) ✅ 可访问 │
│ │
│ ⚠️ 任何 Pod 都可以访问任何端口,无安全隔离 │
└─────────────────────────────────────────────────┘
这意味着:
- 开发环境的 Pod 可以直连生产数据库
- 被入侵的 Pod 可以横向渗透到整个集群
- 敏感服务(支付、认证)暴露给所有 Pod
1.2 NetworkPolicy 解决什么?
NetworkPolicy 是 K8S 原生的网络策略资源,用于控制 Pod 之间以及 Pod 与外部之间的网络通信规则:
┌──────────────────────────────────────────────────┐
│ 启用 NetworkPolicy 后 │
│ │
│ Pod A (前端) ──→ Pod B (数据库) ✅ 允许 │
│ Pod C (日志) ──→ Pod B (数据库) ✅ 允许 │
│ Pod D (任意) ──→ Pod B (数据库) ❌ 拒绝 │
│ │
│ ✅ 精细化控制:谁可以访问谁、访问哪些端口 │
└──────────────────────────────────────────────────┘
1.3 前提条件:CNI 插件必须支持
关键前提:NetworkPolicy 的实际执行依赖 CNI 网络插件。不是所有 CNI 都支持:
| CNI 插件 | 是否支持 NetworkPolicy |
|---|---|
| Flannel | 不支持(需配合 Calico 等策略组件) |
| Calico | 完整支持 |
| Cilium | 完整支持(还支持 L7 策略) |
| Weave Net | 支持 |
重要 :如果你使用 Flannel 作为 CNI,编写 NetworkPolicy 不会生效!需要切换到 Calico 或 Cilium。这也是很多初学者踩过的坑------NetworkPolicy 写了但"没有效果"。
二、三大主流 CNI 插件对比
| 特性 | Flannel | Calico | Cilium |
|---|---|---|---|
| 性能 | 中等(VXLAN 封装开销) | 高(三层路由,无封装或 VXLAN) | 极高(eBPF 内核加速) |
| 网络策略 | 不支持 | L3/L4 强策略 | L3/L4/L7(HTTP/gRPC) |
| 安全 | 弱 | 中(IPsec 可选) | 强(eBPF + WireGuard) |
| 可观测性 | 差 | 一般 | 强(内置 Hubble) |
| 复杂度 | 低(一键部署) | 中 | 较高(内核 5.10+) |
| 适用规模 | 小集群(< 50 节点) | 中大型(50-500 节点) | 任意规模 |
| 适用场景 | 开发/测试、快速搭建 | 生产集群、多租户隔离 | 高性能网关、服务网格 |
三、Flannel 切换 Calico 实战
如果你的集群当前使用 Flannel,需要先完全卸载再安装 Calico,否则会产生网络冲突。
3.1 卸载 Flannel
bash
# 1. 删除 Flannel 的 K8S 资源
kubectl delete -f kube-flannel-v0.27.0.yml
# 2. 所有节点清理 Flannel 残留文件和配置
rm -rf /run/flannel/subnet.env \
/opt/cni/bin/flannel \
/etc/cni/net.d/10-flannel.conflist \
/var/lib/cni/
# 3. 删除 Flannel 创建的虚拟网卡
ip link delete cni0
ip link delete flannel.1
# 4. 重启 kubelet
systemctl restart kubelet.service
# 5. 确认节点变为 NotReady(预期行为)
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master231 NotReady control-plane,master 17d v1.23.17
worker232 NotReady <none> 17d v1.23.17
worker233 NotReady <none> 17d v1.23.17
注意 :以上操作需要在所有节点上执行。
3.2 安装 Calico
bash
# 1. 所有节点导入 Calico 镜像(master 节点完成后 scp 到其他节点)
bash batch-load-calico-v3.5.2-images.sh 14
# 2. 安装 Tigera Calico Operator
kubectl create -f tigera-operator.yaml
# 3. 配置 Calico 网络参数
cat custom-resources.yaml
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
calicoNetwork:
ipPools:
- blockSize: 24 # 每个 Node 分配的 IP 段大小
cidr: 10.100.0.0/16 # Pod 网段(需与 kubeadm init 一致)
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
---
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
name: default
spec: {}
# 4. 应用配置
kubectl create -f custom-resources.yaml
# 5. 验证 Calico 组件正常运行
kubectl get pods -n calico-system -o wide
NAME READY STATUS RESTARTS AGE IP
calico-kube-controllers-76d5c7cfc-tjg8c 1/1 Running 0 48s 10.100.141.2
calico-node-v79vw 1/1 Running 0 49s 10.0.0.231
calico-node-vzhfk 1/1 Running 0 49s 10.0.0.232
calico-node-wls9l 1/1 Running 0 49s 10.0.0.233
calico-typha-6887f66bf8-jbxfn 1/1 Running 0 45s 10.0.0.232
calico-typha-6887f66bf8-w5ggv 1/1 Running 0 49s 10.0.0.231
# 6. 确认节点恢复 Ready
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master231 Ready control-plane,master 17d v1.23.17
worker232 Ready <none> 17d v1.23.17
worker233 Ready <none> 17d v1.23.17
四、NetworkPolicy 核心概念
4.1 策略模型
NetworkPolicy 的核心思想是白名单模型:
没有 NetworkPolicy 的时候,所有流量都允许;一旦创建了 NetworkPolicy,则只有明确允许的流量才能通过,其余全部拒绝。
4.2 资源结构
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: <策略名称>
namespace: <策略所在命名空间> # ⚠️ 只在同一个命名空间内生效
spec:
podSelector: {} # 选择本命名空间中哪些 Pod 应用此策略
policyTypes: # 策略类型:入站/出站/双向
- Ingress
- Egress
ingress: # 入站规则列表
- from: # 流量来源(3种匹配方式)
- ipBlock: {}
- namespaceSelector: {}
- podSelector: {}
ports:
- port: <端口号>
protocol: TCP
egress: # 出站规则列表
- to: # 流量目标
- ipBlock: {}
- namespaceSelector: {}
- podSelector: {}
ports:
- port: <端口号>
protocol: TCP
4.3 三种流量匹配方式
这是 NetworkPolicy 最重要的概念,所有的策略都围绕这三种匹配方式展开:
| 匹配方式 | 作用范围 | 典型场景 |
|---|---|---|
| podSelector | 同一命名空间内的 Pod | 同命名空间微服务间访问控制 |
| namespaceSelector | 其他命名空间 | 跨命名空间隔离(如 dev/staging/prod) |
| ipBlock | 外部 IP 地址段 | 限制外部访问来源(如办公网/VPN) |
4.4 podSelector 详解
podSelector 有两种匹配模式:
yaml
# 模式一:空选择器 {} → 匹配命名空间内所有 Pod
spec:
podSelector: {} # 本命名空间的每个 Pod 都受此策略影响
# 模式二:指定标签 → 只匹配特定 Pod
spec:
podSelector:
matchLabels:
apps: xiuxian # 只影响带有 apps=xiuxian 标签的 Pod
五、NetworkPolicy 实战案例
5.1 环境准备
yaml
# 准备两个命名空间和测试 Pod
# 01-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: net-demo
---
apiVersion: v1
kind: Namespace
metadata:
name: net-target
---
# net-demo 命名空间的测试 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-v1
namespace: net-demo
spec:
replicas: 1
selector:
matchLabels:
apps: xiuxian
template:
metadata:
labels:
apps: xiuxian
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1
ports:
- containerPort: 80
---
# net-target 命名空间的目标 Pod(被访问方)
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-v2
namespace: net-target
spec:
replicas: 1
selector:
matchLabels:
apps: xiuxian
template:
metadata:
labels:
apps: xiuxian
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v2
ports:
- containerPort: 80
5.2 案例一:基于 ipBlock 限制外部访问
场景:只允许特定 IP 段(如 10.0.0.0/8 内网)访问目标 Pod。
yaml
# network-policy-ipblock.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-ipblock
namespace: net-target
spec:
podSelector:
matchLabels:
apps: xiuxian # 作用于标签为 apps=xiuxian 的 Pod
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/8 # 只允许 10.0.0.0/8 网段访问
except: # 排除以下网段
- 10.0.1.0/24 # 10.0.1.x 被排除
ports:
- port: 80
protocol: TCP
bash
kubectl apply -f network-policy-ipblock.yaml -n net-target
# 验证策略生效
kubectl get networkpolicy -n net-target
NAME POD-SELECTOR AGE
policy-ipblock apps=xiuxian 10s
效果:
- 10.0.0.0/8 网段的请求 → 允许访问 80 端口
- 10.0.1.0/24 网段的请求 → 被拒绝(被 except 排除)
- 其他网段的请求 → 被拒绝
注意 :ipBlock 中
except的优先级高于cidr。
5.3 案例二:基于 namespaceSelector 的跨命名空间隔离
场景:只允许特定命名空间的 Pod 访问目标 Pod。
yaml
# network-policy-ns.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-namespace
namespace: net-target
spec:
podSelector:
matchLabels:
apps: xiuxian
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: # 按命名空间标签匹配
matchLabels:
access: allowed # 只允许带有 access=allowed 标签的命名空间
ports:
- port: 80
protocol: TCP
bash
# 1. 给 net-demo 命名空间打上"允许访问"标签
kubectl label namespace net-demo access=allowed
# 2. net-target 不打标签(或打其他标签)
# 默认情况下命名空间没有 access 标签
# 3. 应用策略
kubectl apply -f network-policy-ns.yaml -n net-target
效果:
- net-demo 命名空间的 Pod(有
access=allowed标签) → 允许访问 - 其他命名空间的 Pod → 被拒绝
5.4 案例三:基于 podSelector 的同命名空间精细控制
场景:在同一命名空间内,只允许特定 Pod 访问目标 Pod。
yaml
# network-policy-pod.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-pod
namespace: net-demo
spec:
podSelector:
matchLabels:
apps: xiuxian # 作用于 xiuxian Pod
policyTypes:
- Ingress
ingress:
- from:
- podSelector: # 只允许同命名空间内特定 Pod 访问
matchLabels:
role: frontend # 只有 role=frontend 的 Pod 能访问
ports:
- port: 80
protocol: TCP
5.5 综合案例:多规则组合
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-combined
namespace: net-target
spec:
podSelector:
matchLabels:
apps: xiuxian
policyTypes:
- Ingress
- Egress # 同时控制入站和出站
ingress:
- from:
- namespaceSelector:
matchLabels:
access: allowed # 来自允许的命名空间
- ipBlock:
cidr: 10.0.0.0/8 # 或来自内网 IP 段
ports:
- port: 80
protocol: TCP
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8 # 只允许访问内网
ports:
- port: 3306
protocol: TCP # 如:只允许出站访问数据库
六、GlobalNetworkPolicy(Calico 增强)
6.1 为什么需要 GlobalNetworkPolicy?
K8S 原生 NetworkPolicy 有一个致命局限 :它只在同一个命名空间内生效。如果你需要跨命名空间的统一网络策略(比如"所有命名空间的 Pod 都禁止访问外部网络"),就需要逐个命名空间创建策略,管理成本极高。
Calico 提供了 GlobalNetworkPolicy(GNP) 作为增强方案,支持集群级别的全局网络策略。
6.2 NetworkPolicy vs GlobalNetworkPolicy
| 对比项 | NetworkPolicy (K8S 原生) | GlobalNetworkPolicy (Calico) |
|---|---|---|
| 生效范围 | 单个命名空间 | 整个集群(跨命名空间) |
| 管理方式 | kubectl | calicoctl |
| 命名空间选择 | 只能选本命名空间的 Pod | 可以指定任意命名空间 |
| 动作类型 | Allow(隐式)/ Deny(全拒绝) | Allow / Deny / Pass |
| 优先级 | 无 | 支持指定优先级(order 字段) |
| 协议深度 | L3/L4 | L3/L4 |
| eBPF offload | 不支持 | 支持 |
| 出站规则 | 支持 | 支持 |
6.3 GNP 的三种动作类型
这是 GNP 相比 NP 最核心的差异:
| 动作 | 含义 | 典型场景 |
|---|---|---|
| Allow | 明确允许通过 | 开放特定端口访问 |
| Deny | 明确拒绝(K8S 原生 NP 不支持) | 封禁特定 IP 或端口 |
| Pass | 跳过此规则,交给下一条规则判断 | 实现复杂的规则链 |
关键区别:K8S 原生 NetworkPolicy 只有"白名单"模式(Allow),无法实现"黑名单"模式(Deny)。Calico GNP 同时支持 Allow 和 Deny,可以实现更精细的控制。
6.4 GNP 实战:全局拒绝特定端口的出站流量
yaml
# global-network-policy.yaml
# 使用 calicoctl apply -f global-network-policy.yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: deny-egress-db
spec:
order: 100 # 优先级,数字越小优先级越高
selector: all() # 作用于所有 Pod(全局生效)
types:
- Egress
egress:
- action: Deny # 拒绝(K8S 原生 NP 做不到!)
destination:
ports:
- 3306 # 拒绝所有出站到 3306 的流量
- action: Deny
destination:
ports:
- 6379 # 拒绝所有出站到 6379 的流量
- action: Allow # 其余出站流量放行
bash
# 使用 calicoctl 应用 GNP
calicoctl apply -f global-network-policy.yaml
# 查看已创建的 GNP
calicoctl get globalnetworkpolicy
NAME ORDER SELECTOR
deny-egress-db 100 all()
6.5 GNP 实战:基于命名空间的全局隔离
yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: namespace-isolation
spec:
order: 200
selector: all()
types:
- Ingress
ingress:
# 允许同一命名空间内的 Pod 互访
- action: Allow
source:
selector: all()
namespaceSelector:
name: "" # 同一命名空间
# 允许来自 DNS 的查询
- action: Allow
protocol: UDP
destination:
ports:
- 53
# 其余入站流量全部拒绝
- action: Deny
6.6 calicoctl 与 kubectl 的关系
| 对比维度 | kubectl | calicoctl |
|---|---|---|
| 归属 | K8S 官方原生客户端 | Calico 专属运维工具 |
| 管理范围 | 所有 K8S 原生资源 | Calico CRD(IPPool、BGP、GNP 等) |
| 策略类型 | 仅 NetworkPolicy | NetworkPolicy + GlobalNetworkPolicy |
| 生效范围 | 单个命名空间 | 命名空间级 + 集群全局 |
| 语法 | K8S 标准 YAML | 与 kubectl 语法一致,零学习成本 |
bash
# 安装 calicoctl(推荐 kubectl 插件方式)
wget https://github.com/projectcalico/calico/releases/download/v3.25.2/calicoctl-linux-amd64 \
-O /usr/local/bin/kubectl-calico
chmod +x /usr/local/bin/kubectl-calico
# 验证版本(客户端必须与集群 Calico 版本一致)
kubectl calico version
Client Version: v3.25.2
Cluster Version: v3.25.2
Cluster Type: typha,kdd,k8s,operator,bgp,kubeadm
# 查看 BGP 状态
calicoctl node status
Calico process is running.
IPv4 BGP status
+--------------+-------------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+--------------+-------------------+-------+----------+-------------+
| 10.0.0.232 | node-to-node mesh | up | 08:52:20 | Established |
| 10.0.0.233 | node-to-node mesh | up | 08:52:20 | Established |
+--------------+-------------------+-------+----------+-------------+
七、生产环境最佳实践
7.1 网络策略设计原则
原则1:默认拒绝 → 最小权限开放
├── 集群级别:所有入站默认拒绝
├── 命名空间级别:按需开放跨命名空间访问
└── Pod 级别:只开放必要端口
原则2:从外到内分层控制
├── 外部 → 集群:Ingress/LoadBalancer 层控制
├── 集群内跨命名空间:namespaceSelector 控制
└── 命名空间内:podSelector 精细控制
原则3:必须放行 DNS
└── UDP 53 端口必须始终开放,否则 Pod 无法解析域名
7.2 推荐的基线策略
第一步:全局默认拒绝所有入站(使用 Calico GNP)
yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: default-deny-ingress
spec:
order: 1000 # 低优先级(兜底规则)
selector: all()
types:
- Ingress
ingress:
- action: Deny
第二步:放行 DNS(必须!)
yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-dns
spec:
order: 500
selector: all()
types:
- Egress
egress:
- action: Allow
protocol: UDP
destination:
ports:
- 53
- action: Allow
protocol: TCP
destination:
ports:
- 53
- action: Allow
destination:
namespaceSelector: name == "kube-system"
selector: k8s-app == "kube-dns"
第三步:按业务逐个开放访问
yaml
# 例如:允许前端命名空间访问后端命名空间的 8080 端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: backend
spec:
podSelector:
matchLabels:
apps: backend-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
team: frontend
ports:
- port: 8080
protocol: TCP
7.3 命名空间标签规划
bash
# 按环境打标签
kubectl label namespace production env=prod
kubectl label namespace staging env=staging
kubectl label namespace development env=dev
# 按业务团队打标签
kubectl label namespace payment team=payment
kubectl label namespace user-service team=user
# 按访问权限打标签
kubectl label namespace monitoring access=allowed
kubectl label namespace logging access=allowed
八、网络策略排障指南
8.1 网络策略不生效的常见原因
| 原因 | 排查方法 | 解决方案 |
|---|---|---|
| CNI 不支持 NetworkPolicy | kubectl get pods -n calico-system |
切换到 Calico/Cilium |
| 策略写在了错误的命名空间 | 检查 metadata.namespace | 确保 NP 在目标 Pod 所在命名空间 |
| DNS 未放行 | Pod 内 nslookup 失败 |
始终放行 UDP/TCP 53 端口 |
| podSelector 标签不匹配 | kubectl get pods --show-labels |
检查标签是否一致 |
| 多条策略冲突 | 列出所有 NetworkPolicy | 检查是否有其他策略覆盖 |
8.2 排障命令速查
bash
# 查看命名空间内的所有网络策略
kubectl get networkpolicy -n <namespace>
# 查看策略详细信息
kubectl describe networkpolicy <name> -n <namespace>
# 查看 Calico 全局网络策略
calicoctl get globalnetworkpolicy
# 查看 Calico 节点 BGP 状态
calicoctl node status
# 进入 Pod 测试网络连通性
kubectl exec -it <pod-name> -n <namespace> -- curl <target-ip>:<port>
# 查看 Pod 的 IP
kubectl get pods -o wide -n <namespace>
九、面试高频考点速查
| 问题 | 答案要点 |
|---|---|
| K8S 默认网络模型? | 所有 Pod 完全互通,没有任何隔离 |
| NetworkPolicy 的前提条件? | CNI 插件必须支持(如 Calico/Cilium),Flannel 不支持 |
| NetworkPolicy 的默认行为? | 没有 NP = 全部允许;有 NP = 只允许明确放行的流量 |
| 三种流量匹配方式? | podSelector (Pod 标签)、namespaceSelector (命名空间标签)、ipBlock(IP 段) |
| GNP 和 NP 的核心区别? | GNP 跨命名空间全局生效 ,支持 Deny 动作和优先级 |
| GNP 三种动作? | Allow (允许)、Deny (拒绝)、Pass(跳过交给下条规则) |
| 为什么必须放行 DNS? | Pod 依赖 DNS 进行服务发现,阻断 DNS 会导致集群内部域名无法解析 |
| Calico 的 BGP 模式? | node-to-node mesh(默认全互联)或 Route Reflector(大规模) |
| calicoctl 版本要求? | 客户端版本必须与集群 Calico 版本严格一致 |
十、总结
本文从三个层次系统讲解了 K8S 网络策略:
- 基础概念:理解 K8S 默认的"全互通"模型和 NetworkPolicy 的白名单机制,明确 CNI 插件支持是前提条件
- NetworkPolicy 实战:掌握 ipBlock(IP 段)、namespaceSelector(命名空间标签)、podSelector(Pod 标签)三种匹配方式的实际运用
- GlobalNetworkPolicy 进阶:理解 Calico GNP 的跨命名空间全局能力、Allow/Deny/Pass 三种动作、优先级机制
生产建议:
- 必须使用支持 NetworkPolicy 的 CNI(推荐 Calico),Flannel 无法生效
- 采用"默认拒绝 + 最小权限开放"的策略设计原则
- 始终放行 DNS(UDP/TCP 53),否则集群服务发现将全部失效
- 命名空间标签要提前规划,便于 namespaceSelector 策略管理
- 对于跨命名空间的统一管控,使用 Calico GNP 替代逐个命名空间创建 NP
环境信息:
- Kubernetes: v1.23.17
- CNI: Calico v3.25.2
- 部署方式: kubeadm
- calicoctl: v3.25.2