Qingular

NetworkPolicy 网络策略

·CKAk8s练习

CKA Domain 3 — Kubernetes NetworkPolicy 配置与管理(Ingress、Egress、隔离策略)

← 返回 CKA 练习目录

概述

NetworkPolicy 是 Kubernetes 中用于控制 Pod 级别的网络访问规则的资源。它通过标签选择器和命名空间选择器来定义哪些 Pod 可以相互通信。

重要前提:NetworkPolicy 需要 CNI 网络插件的支持(如 Calico、Cilium、Weave Net 等)。如果 CNI 不支持,NetworkPolicy 规则不会生效。

资源结构

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector:          # 选择策略作用的 Pod
    matchLabels:
      app: my-app
  policyTypes:          # 策略类型(Ingress / Egress)
    - Ingress
    - Egress
  ingress:              # 入站规则
    - from:
        - ipBlock:
            cidr: 10.0.0.0/16
        - namespaceSelector:
            matchLabels:
              project: my-project
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 80
  egress:               # 出站规则
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
      ports:
        - protocol: TCP
          port: 443

Ingress 规则配置

控制入站(进入 Pod)的流量。

允许特定命名空间的 Pod 访问

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-namespace
  namespace: target-ns
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: source-ns   # 命名空间标签

允许特定 Pod 标签访问

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-pod-label
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: frontend
      ports:
        - protocol: TCP
          port: 8080

组合选择器(同时匹配命名空间和 Pod 标签)

  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: dev
          podSelector:             # 同一规则内的多个选择器是 AND 关系
            matchLabels:
              role: frontend

Egress 规则配置

控制出站(从 Pod 出去)的流量。

允许访问特定外部 CIDR

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-external
spec:
  podSelector:
    matchLabels:
      app: my-app
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/8
      ports:
        - protocol: TCP
          port: 443
        - protocol: TCP
          port: 80

常见策略模式

1. Deny-All Ingress(拒绝所有入站)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
spec:
  podSelector: {}           # 匹配命名空间中的所有 Pod
  policyTypes:
    - Ingress
  # ingress 为空列表,拒绝所有入站流量

2. Deny-All Egress(拒绝所有出站)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
spec:
  podSelector: {}
  policyTypes:
    - Egress
  # egress 为空列表,拒绝所有出站流量

3. Allow-All Ingress(允许所有入站)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - {}   # 空规则匹配所有来源

4. 允许特定命名空间访问

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-specific-ns
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              environment: production
      ports:
        - port: 80

5. 基于 IP 段的访问控制

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-ip-range
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 192.168.1.0/24
            except:               # 排除特定 IP
              - 192.168.1.5/32
      ports:
        - protocol: TCP
          port: 443

关键概念与注意事项

概念说明
podSelector选择策略作用的 Pod,{} 匹配命名空间中所有 Pod
policyTypes声明规则类型,即使 ingress/egress 为空也要指定
namespaceSelector按命名空间标签选择来源/目标
ipBlock按 CIDR 选择来源/目标 IP 段
规则内多个选择器AND 关系(必须同时匹配)
规则间多个选择器OR 关系(匹配任意一个即可)

测试 NetworkPolicy

# 创建一个测试 Pod
kubectl run test-pod --image=busybox --rm -it --restart=Never -- sh

# 测试连通性
wget -qO- http://target-svc:80

# 查看 NetworkPolicy
kubectl get networkpolicy
kubectl describe networkpolicy my-policy

🧪 完整操作实例:配置 NetworkPolicy 实现租户隔离

场景描述

默认拒绝所有入站流量,然后只允许带特定标签的 Pod 之间通信,模拟多租户隔离场景。

前置条件

  • 集群使用支持 NetworkPolicy 的 CNI 插件(Calico、Cilium 等)
  • kubectl 已配置好集群访问

操作步骤

Step 1: 创建两个命名空间并部署 Pod

# 创建两个命名空间模拟不同租户
kubectl create namespace tenant-a
kubectl create namespace tenant-b
# 预期输出:namespace/tenant-a created

# 在 tenant-a 中部署带标签的 Pod
kubectl run pod-a --image=nginx:1.25 --labels="app=web,tenant=a" -n tenant-a
# 预期输出:pod/pod-a created

# 在 tenant-b 中部署两个不同角色的 Pod
kubectl run pod-b1 --image=nginx:1.25 --labels="app=web,tenant=b" -n tenant-b
kubectl run pod-b2 --image=nginx:1.25 --labels="app=api,tenant=b" -n tenant-b
# 预期输出:pod/pod-b1 created

# 验证 Pod 状态
kubectl get pods -n tenant-a -o wide
kubectl get pods -n tenant-b -o wide
# 预期输出:所有 Pod 处于 Running 状态

Step 2: 创建 Deny-All Ingress NetworkPolicy

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: tenant-a
spec:
  podSelector: {}
  policyTypes:
    - Ingress
EOF
# 预期输出:networkpolicy.networking.k8s.io/deny-all-ingress created

# 同样对 tenant-b 也应用拒绝策略
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: tenant-b
spec:
  podSelector: {}
  policyTypes:
    - Ingress
EOF
# 预期输出:networkpolicy.networking.k8s.io/deny-all-ingress created

Step 3: 验证入站流量被拒绝

# 在 tenant-b 中启动测试 Pod,尝试访问 tenant-a 的 pod-a
kubectl run test-pod --image=busybox:1.28 -n tenant-b --rm -it --restart=Never -- sh
# 在测试 Pod 内执行:
wget -qO- --timeout=5 http://pod-a.tenant-a
# 预期输出:wget: download timed out(连接被拒绝)

# 退出测试容器后,检查 NetworkPolicy
kubectl get networkpolicy --all-namespaces
# NAMESPACE   NAME              POD-SELECTOR   AGE
# tenant-a    deny-all-ingress   <none>         1m
# tenant-b    deny-all-ingress   <none>         1m

Step 4: 创建允许带特定标签 Pod 访问的策略

# 在 tenant-a 中创建策略,只允许标签为 app=web 的 Pod 访问
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-web-to-web
  namespace: tenant-a
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: web
      ports:
        - protocol: TCP
          port: 80
EOF
# 预期输出:networkpolicy.networking.k8s.io/allow-web-to-web created

Step 5: 验证带标签 Pod 可以访问

# 在 tenant-b 的 pod-b1(标签 app=web)中测试访问 tenant-a 的 pod-a(标签 app=web)
kubectl exec pod-b1 -n tenant-b -- wget -qO- --timeout=5 http://pod-a.tenant-a
# 预期输出:nginx 欢迎页面 HTML(策略放行,因为双方都有 app=web 标签)

# 在 tenant-b 的 pod-b2(标签 app=api)中测试访问 tenant-a 的 pod-a
kubectl exec pod-b2 -n tenant-b -- wget -qO- --timeout=5 http://pod-a.tenant-a
# 预期输出:wget: download timed out(策略拒绝,因为 pod-b2 标签是 app=api 而非 app=web)

Step 6: 使用临时诊断 Pod 全面测试

# 创建诊断 Pod 测试不同场景
kubectl run test-pod -n tenant-a --image=nicolaka/netshoot --rm -it --restart=Never -- sh

# 在诊断 Pod 内逐项测试:
# 测试 1:访问同命名空间的 pod-a(应成功)
curl -m 5 http://pod-a.tenant-a
# 预期输出:HTML 内容

# 测试 2:访问 tenant-b 的 pod-b1(取决于策略配置,可能被拒绝)
curl -m 5 http://pod-b1.tenant-b
# 预期输出:连接超时(如 tenant-b 未放行此 Pod)

验证结果

# 查看所有 NetworkPolicy
kubectl get networkpolicy --all-namespaces -o wide
# NAMESPACE   NAME                POD-SELECTOR   AGE
# tenant-a    deny-all-ingress     <none>         5m
# tenant-a    allow-web-to-web     app=web        3m
# tenant-b    deny-all-ingress     <none>         5m

# 查看具体策略详情
kubectl describe networkpolicy allow-web-to-web -n tenant-a
# 确认 Ingress 规则中包含 podSelector: app=web

# 清理资源
kubectl delete namespace tenant-a tenant-b

考试提示