HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 概览

    • K8s 实战学习实验室
    • 服务访问清单
    • K8s-Lab 学习总纲、仓库评估与专家路线图
  • 课程正文

    • 环境验证与第一课:认识你的真实集群
    • 第二课:kubectl apply 之后,到底发生了什么
    • 第三课:调度器如何选节点,为什么 Pod 会 Pending
    • 第四课:Kubernetes 网络、协议分层、VXLAN/IPIP/WireGuard 原理与排障
    • 第五课:NetworkPolicy、零信任网络与流量边界
    • 第六课:身份、认证、授权、准入与 ServiceAccount / RBAC 原理
    • 第七课:ConfigMap 与 Secret 注入模型、更新机制与安全边界
    • 第八课:存储持久化、PV / PVC / StorageClass 与 NFS 原理
    • 第九课:StatefulSet、Headless Service、稳定身份与存储原理
    • 第十课:探针、滚动更新、优雅终止与 PDB 原理
    • 第十一课:requests / limits、QoS、OOM 与驱逐原理
    • 第十二课:HPA、自动扩缩容、指标链路与副本伸缩原理
    • 第十三课:Service、EndpointSlice、kube-proxy、CoreDNS 与服务发现原理
    • 第十四课:Ingress-nginx、反向代理、Host / Path、NodePort 与北南向流量原理
    • 第十五课:HTTPS、TLS、SNI、证书信任与 Ingress 终止原理
    • 第十六课:cert-manager、Ingress 自动签发、证书生命周期与 ACME 工作流原理
    • 第十七课:ACME、Let's Encrypt、HTTP-01 / DNS-01、Orders / Challenges 与生产限制原理
    • 第十八课:大模型全生态,从数据到训练到部署到治理原理
    • 第十九课:大模型数据集、清洗、标注、切分、版本管理与质量治理原理
    • 第二十课:大模型训练、SFT、LoRA、Checkpoint、Adapter 与模型产物原理
    • 第二十一课:大模型推理、量化、KV Cache、vLLM、吞吐/延迟与部署发布链路原理
  • 实验操作记录

    • 本次仓库审查操作记录与命令原理
    • 本轮操作记录:环境验证、集群基线盘点与故障样本采集
    • 本轮操作记录:kubectl apply 主链路实验
    • 本轮操作记录:调度实验与 Pending 排查
    • 本轮操作记录:Kubernetes 网络原理、协议对比与调试实验
    • 本轮操作记录:NetworkPolicy 与零信任网络实验
    • 本轮操作记录:身份、认证、授权、准入实验
    • 本轮操作记录:ConfigMap 与 Secret 注入、更新与安全边界实验
    • 本轮操作记录:存储持久化、PV / PVC / StorageClass 与 NFS 实验
    • 本轮操作记录:StatefulSet、Headless Service 与稳定身份实验
    • 本轮操作记录:探针、滚动更新、优雅终止与 PDB 实验
    • 本轮操作记录:资源模型、QoS、OOM 与 CPU 节流实验
    • 本轮操作记录:HPA 自动扩缩容实验
    • 本轮操作记录:Service、EndpointSlice、CoreDNS 与服务发现排障实验
    • 本轮操作记录:Ingress-nginx、NodePort 与北南向流量实验
    • 本轮操作记录:HTTPS、TLS、自签证书与 Ingress 实验
    • 本轮操作记录:cert-manager 安装、CA 签发与 Ingress 自动证书实验
    • 本轮操作记录:ACME staging、HTTP-01 失败样本与排障实验
    • 本轮操作记录:大模型全生态与基础原理科普文撰写
    • 本轮操作记录:大模型数据集样本与治理文档编写
    • 本轮操作记录:大模型训练与模型产物概念文撰写
    • 本轮操作记录:大模型推理与服务发布概念文撰写

本轮操作记录:NetworkPolicy 与零信任网络实验

本轮目标

上一轮已经讲清了:

  • Service
  • DNS
  • kube-proxy
  • CNI
  • VXLAN / IPIP / WireGuard

这一轮顺着网络主线继续往下,目标变成:

把“网络怎么通”变成“网络该不该通”。

因此,这轮工作的重点是:

  1. 先看集群里已有的 NetworkPolicy 现状
  2. 再创建隔离实验环境
  3. 逐步验证:
    • 默认全通
    • default-deny-ingress
    • allow-same-namespace
    • namespaceSelector
    • default-deny-egress
    • allow DNS
    • allow business traffic
  4. 最后把实验结果整理成第五课

Step 1: 查看集群里已经有哪些策略

实际命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl get networkpolicy -A -o wide

为什么先看现状

因为我不希望新实验建立在“未知现状”上。

如果集群里已经有很多策略,你不先看清楚:

  • 容易误把旧策略效果当成新实验效果
  • 容易在错误 namespace 做实验

我看到了什么

已有策略包括:

  • argocd 中多条组件自带策略
  • gitea 中 PostgreSQL 相关策略
  • prod 中 default-deny-ingress
  • prod 中 allow-same-namespace

我得到的结论

当前集群已经存在真实的策略案例,这说明:

  • Calico 的 NetworkPolicy 能力是活的
  • 集群并不是“只有教学文件,没有现实策略”

但为了避免和已有环境混淆,我仍然决定创建专门实验 namespace。


Step 2: 读取已有文档和 API 能力

实际命令

sed -n '1,240p' phase-2/03-ingress-networkpolicy.md
KUBECONFIG=~/.kube/config-k8s-lab kubectl get ns --show-labels
KUBECONFIG=~/.kube/config-k8s-lab kubectl api-resources | rg 'networkpol|globalnetworkpolicy|networkset|globalnetworkset'

为什么看这三类内容

现有文档

为了保证本轮实验和仓库主线一致。

namespace label

因为 namespaceSelector 会直接依赖 namespace label。

API 资源

为了确认你的集群里除了标准 NetworkPolicy 外,是否还有 Calico 扩展策略资源。

我看到的关键信息

API 里同时存在:

  • networking.k8s.io/v1 NetworkPolicy
  • crd.projectcalico.org/v1 NetworkPolicy
  • GlobalNetworkPolicy
  • NetworkSet
  • GlobalNetworkSet

我得到的结论

这说明:

  • 标准策略能讲
  • Calico 扩展能力也存在

所以第五课里我可以很清楚地区分:

  • 标准语义
  • Calico 增强语义

Step 3: 为什么要创建两个 namespace

新增对象

我新建了:

  • np-demo
  • np-external

以及:

  • np-demo/web + web-svc
  • np-demo/same-ns-client
  • np-external/cross-ns-client

对应文件在:

  • manifests/05-networkpolicy/

为什么至少要两个 namespace

因为如果只有一个 namespace,你最多只能演示:

  • 同 namespace 放行/拒绝

但你讲不清:

  • 跨 namespace 流量
  • namespaceSelector
  • 零信任里的环境边界

为什么还要同 namespace 客户端

因为我要同时对比:

  • 同 namespace 客户端
  • 跨 namespace 客户端

这样你才能真正理解:

  • 哪条策略只影响同 namespace
  • 哪条策略影响跨 namespace

Step 4: apply 基础实验对象,但先不加策略

实际命令

KUBECONFIG=~/.kube/config-k8s-lab kubectl apply \
  -f manifests/05-networkpolicy/00-namespace-np-demo.yaml \
  -f manifests/05-networkpolicy/01-namespace-np-external.yaml \
  -f manifests/05-networkpolicy/10-web-deployment.yaml \
  -f manifests/05-networkpolicy/11-web-service.yaml \
  -f manifests/05-networkpolicy/20-same-ns-client.yaml \
  -f manifests/05-networkpolicy/21-cross-ns-client.yaml

为什么先只创建工作负载

因为我要先拿到“默认网络行为”的基线。

如果你一上来就把策略一起 apply 了,后面就不知道:

  • 默认到底是通还是不通
  • 是策略生效了,还是本来就不通

这一步体现的工程原则

做实验时,先建立 baseline,再逐步改单个变量。


Step 5: 等待工作负载 Ready

实际命令

kubectl -n np-demo rollout status deployment/web --timeout=120s
kubectl -n np-demo wait --for=condition=Ready pod/same-ns-client --timeout=120s
kubectl -n np-external wait --for=condition=Ready pod/cross-ns-client --timeout=120s

为什么这一步不能跳

因为如果 Pod 还没 ready,你去测连接:

  • 失败了也不知道是策略问题还是 Pod 没起来

这就是典型的变量污染。

我得到的结果

所有基础对象都准备就绪,可以开始做网络访问实验。


Step 6: 先验证默认全通

实际命令

kubectl -n np-demo get pods,svc -o wide
kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc | head -n 1 && echo same-ns=success'
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local | head -n 1 && echo cross-ns=success'
kubectl -n np-external exec cross-ns-client -- nslookup web-svc.np-demo.svc.cluster.local

为什么这组命令同时测三件事

同 namespace HTTP

验证最基本路径。

跨 namespace HTTP

验证默认是否真的跨 namespace 全通。

跨 namespace DNS

把“解析”和“连接”分开验证。

结果

我看到了:

  • same-ns=success
  • cross-ns=success
  • nslookup 正常返回 10.107.52.92

我得到的结论

默认情况下,这两个 namespace 之间没有隔离。

这为后面的 default-deny 实验建立了非常清晰的对照组。


Step 7: 应用 default-deny-ingress

实际命令

kubectl apply -f manifests/05-networkpolicy/30-default-deny-ingress.yaml

为什么先从 ingress deny 开始

因为它是最经典、最基础的一刀。

这条策略的意义是:

  • 先把目标 namespace 保护起来

应用后怎么验证

我执行了:

kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc >/dev/null && echo same-ns=success || echo same-ns=blocked'
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo cross-ns=success || echo cross-ns=blocked'
kubectl -n np-demo get networkpolicy

结果

我看到了:

  • same-ns=blocked
  • cross-ns=blocked
  • 且 wget 都是超时

我得到的结论

default-deny-ingress 一旦选中 np-demo 的所有 Pod:

  • 同 namespace 也不再天然被允许
  • 跨 namespace 更不会被允许

这和很多人的直觉相反,但正好体现了策略语义:

  • 被选中的方向,默认不再全通

Step 8: 应用 allow-same-namespace

实际命令

kubectl apply -f manifests/05-networkpolicy/31-allow-same-namespace.yaml

然后再次测试:

kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc >/dev/null && echo same-ns=success || echo same-ns=blocked'
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo cross-ns=success || echo cross-ns=blocked'
kubectl -n np-demo get networkpolicy -o yaml

为什么还要看完整 YAML

因为我要把“策略是累加的”这件事写进文档,而不是只凭现象说。

看完整 YAML 可以确认:

  • default-deny-ingress 还在
  • allow-same-namespace 也在

结果

我看到了:

  • same-ns=success
  • cross-ns=blocked

我得到的结论

这完美证明了:

  • default-deny 没有被覆盖
  • 新增规则只是往允许集合里加了一项

这就是标准 NetworkPolicy 的“并集允许”模型。


Step 9: 动态给 namespace 打标签,再用 namespaceSelector 放行

实际命令

kubectl label namespace np-external access=allowed --overwrite
kubectl apply -f manifests/05-networkpolicy/32-allow-from-labeled-namespace.yaml

然后验证:

kubectl get ns np-external --show-labels
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo cross-ns=success || echo cross-ns=blocked'
kubectl -n np-demo get networkpolicy

为什么先 label namespace

因为 namespaceSelector 不是按 namespace 名字直接匹配,而是按:

  • namespace 的 label

这点必须在实验里让用户真正看见。

结果

我看到了:

  • np-external 标签变成 access=allowed
  • cross-ns=success

我得到的结论

这说明:

  • namespace 身份边界可以通过 label 动态表达
  • namespaceSelector 的规则不是死写死记,而是可治理的

这在平台设计里非常重要。


Step 10: 进入 egress 实验,先加 default-deny-egress

实际命令

kubectl apply -f manifests/05-networkpolicy/40-default-deny-egress-client.yaml

然后验证:

kubectl -n np-external exec cross-ns-client -- nslookup kubernetes.default.svc.cluster.local
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo http=success || echo http=blocked'
kubectl -n np-external get networkpolicy -o wide

为什么这里先测 DNS

因为 egress deny 最容易先打挂的就是 DNS。

这是生产里极高频的坑。

结果

我看到了:

nslookup

失败:

connection timed out; no servers could be reached

wget

失败,但错误不是 timeout,而是:

bad address 'web-svc.np-demo.svc.cluster.local'

我得到的结论

这里有两个非常关键的教学点:

  1. DNS 本身也是网络流量,会被 egress policy 拦住
  2. 应用访问失败不一定是业务端口被挡,也可能是名字根本解析不出来

这一步对建立排障分层特别重要。


Step 11: 只恢复 DNS

实际命令

kubectl apply -f manifests/05-networkpolicy/41-allow-dns-egress-client.yaml

然后测试:

kubectl -n np-external exec cross-ns-client -- nslookup web-svc.np-demo.svc.cluster.local
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo http=success || echo http=blocked'
kubectl -n np-external get networkpolicy -o yaml

为什么只恢复 DNS,而不一起恢复 HTTP

因为我要让“DNS 和业务流量是两种独立白名单”这个事实变得非常清楚。

结果

我看到了:

  • nslookup 恢复成功
  • wget 仍然 timeout

我得到的结论

DNS 白名单恢复后:

  • 名字能解了
  • 但业务流量仍出不去

这正好说明:

  • DNS 放行 ≠ 应用流量放行

Step 12: 再恢复业务 HTTP 出站

实际命令

kubectl apply -f manifests/05-networkpolicy/42-allow-web-egress-client.yaml

然后验证:

kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo http=success || echo http=blocked'
kubectl -n np-external get networkpolicy
kubectl get networkpolicy -A | rg 'np-demo|np-external'

为什么最后还看全局策略列表

因为我要把这轮实验最终形成的“策略全景”固化下来,方便教学总结:

  • 目标端有哪些 ingress 策略
  • 源端有哪些 egress 策略

结果

我看到:

  • http=success
  • np-demo 中 3 条 ingress 相关策略
  • np-external 中 3 条 egress 相关策略

我得到的结论

到这里,完整零信任实验链条已经闭环:

  • 默认全通
  • ingress default deny
  • ingress allow same namespace
  • ingress allow labeled namespace
  • egress default deny
  • egress allow dns
  • egress allow web

Step 13: 这轮为什么没有直接使用 Calico 的 GlobalNetworkPolicy

你的集群确实支持:

  • GlobalNetworkPolicy

我在 API 资源里也确认到了。

但这一轮我故意没有一上来就用它,原因是:

先把标准语义吃透更重要

如果你连标准 NetworkPolicy 都还没吃透,就上:

  • 显式 deny
  • order
  • global scope

只会把认知复杂度抬太高。

工程上也应该先掌握最小通用能力

标准 networking.k8s.io/v1 是:

  • 更通用
  • 更容易迁移
  • 更容易面试和团队沟通的基础能力

所以这一轮是故意先打牢地基。


Step 14: 将结果写成第五课文档

基于这一轮所有实验和证据,我写入了:

  • 05-第五课-NetworkPolicy与零信任网络.md

这份文档里,我重点讲清了:

  • 默认全通
  • 被策略选中后才进入隔离状态
  • 顶层 podSelector 的真正含义
  • 标准 NetworkPolicy 的“累加允许”模型
  • Ingress / Egress 的方向语义
  • DNS 为什么必须单独考虑
  • namespaceSelector / podSelector 的组合语义
  • 标准策略和 Calico 扩展策略的区别

本轮命令清单

KUBECONFIG=~/.kube/config-k8s-lab kubectl get networkpolicy -A -o wide
sed -n '1,240p' phase-2/03-ingress-networkpolicy.md
KUBECONFIG=~/.kube/config-k8s-lab kubectl get ns --show-labels
KUBECONFIG=~/.kube/config-k8s-lab kubectl api-resources | rg 'networkpol|globalnetworkpolicy|networkset|globalnetworkset'

KUBECONFIG=~/.kube/config-k8s-lab kubectl apply \
  -f manifests/05-networkpolicy/00-namespace-np-demo.yaml \
  -f manifests/05-networkpolicy/01-namespace-np-external.yaml \
  -f manifests/05-networkpolicy/10-web-deployment.yaml \
  -f manifests/05-networkpolicy/11-web-service.yaml \
  -f manifests/05-networkpolicy/20-same-ns-client.yaml \
  -f manifests/05-networkpolicy/21-cross-ns-client.yaml

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo rollout status deployment/web --timeout=120s
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo wait --for=condition=Ready pod/same-ns-client --timeout=120s
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external wait --for=condition=Ready pod/cross-ns-client --timeout=120s

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo get pods,svc -o wide
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc ...'
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local ...'
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external exec cross-ns-client -- nslookup web-svc.np-demo.svc.cluster.local

KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/30-default-deny-ingress.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/31-allow-same-namespace.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl label namespace np-external access=allowed --overwrite
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/32-allow-from-labeled-namespace.yaml

KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/40-default-deny-egress-client.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/41-allow-dns-egress-client.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/42-allow-web-egress-client.yaml

KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo get networkpolicy
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo get networkpolicy -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external get networkpolicy -o wide
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external get networkpolicy -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl get networkpolicy -A | rg 'np-demo|np-external'

本轮最重要的结论

结论一:默认全通是现实,不是理论

我已经用跨 namespace 客户端验证过了。

结论二:策略一旦选中 Pod,该方向就进入“默认拒绝 + 允许列表”

这是 NetworkPolicy 最核心的语义。

结论三:标准 NetworkPolicy 是“允许并集”,不是顺序覆盖

这一点已经通过 default-deny-ingress + allow-same-namespace 实验验证。

结论四:Egress 不只是“能不能访问业务”,还直接决定 DNS 会不会活着

这点已经通过 nslookup 和 wget bad address 的真实现象验证。

结论五:真正的连接放行,要同时考虑源侧 egress 和目标侧 ingress

只看一边是不够的。


下一步建议

继续推进时,最自然的下一课应该进入“身份和权限”:

  • ServiceAccount
  • RBAC
  • ConfigMap / Secret
  • API 访问身份

因为网络边界只是平台安全的一部分。

你要走向专家,就必须把:

  • 网络边界
  • 身份边界
  • 权限边界

三条线一起串起来。*** End Patch

Prev
本轮操作记录:Kubernetes 网络原理、协议对比与调试实验
Next
本轮操作记录:身份、认证、授权、准入实验