你是园区物业,园里有两栋重要建筑:一栋是 AI 推理中心,一栋是数据库档案室。每天,外部客户要进园区办事——他们必须走唯一的访客大门(Ingress),由前台按来访目的指引到对应的楼栋。至于楼栋里谁能进、谁不能进,前台一概不管,那是另一套规则说了算:每栋楼门口有门禁系统(NetworkPolicy),只认访客胸牌上的标签。没装门禁的楼,任何人随便进;装了门禁的楼,没有白名单一律免谈。 这个故事完整地道出了 K8s 网络策略的本质:Ingress 管“外面的人怎么到正确的楼”,NetworkPolicy 管“楼与楼之间谁有资格走动”。对 AI 工程师来说,你的模型服务、向量库、训练平台往往共享同一个集群,默认情况下它们之间“门洞大开”——任何 Pod 都能互访。只有同时理解这两套机制,才能把推理链路锁进白名单,真正做到零信任。

岗位关联

掌握 Ingress 与 NetworkPolicy,是 AI 工程师走向生产环境安全交付的必修课:既能对外正确暴露模型 API,又能对内守住数据库与模型权重。

目录
  1. 可视化演示
  2. 网络策略入门
  3. 策略核心字段
  4. 规则匹配机制
  5. 七层路由Ingress
  6. 隔离方案实践
  7. 后端实现原理
  8. 排错与验证
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

网络策略入门

园区落成那天,物业贴出一则公告:除明确标注“禁止入内”的区域,所有建筑一律开放。刚入驻时大家觉得方便,但很快发现问题:数据中心的机房、财务室的档案柜,跟食堂、茶水间一样谁都能进。这就是 K8s 集群的默认语义——默认放行:没有策略约束的地方,所有 Pod 可以自由互访。

AI 团队的生产集群可不敢这么敞着:模型权重、向量特征库、训练数据都是核心资产,一旦某个测试 Pod 被攻破,攻击者就能在集群内横向移动。K8s 给出的标准答案是 NetworkPolicy——一份由 API Server 统一管理的“门禁规则清单”,用标签选择器圈定哪些楼栋(Pod)需要装上电磁锁。

NetworkPolicy 是命名空间级别的资源,真正执行它的是 CNI 插件。它的作用对象是“一批具有相同标签的 Pod”,而不是某个 IP 地址。当至少一条策略选中某 Pod 后,该 Pod 的流量立即从“默认放行”切换为“默认拒绝,仅放行显式声明的流量”;没有被任何策略选中的 Pod 依旧全通。这是整个 K8s 网络隔离最核心的语义,理解它,后面的一切都顺理成章。

一句话记住:门禁系统不是装上就全区安全,而是“哪栋楼装了,哪栋楼才安全”。NetworkPolicy 是精准点名,不是全局结界——想保护数据库,就必须专门写一条选中数据库标签的策略。

Decision(pod,c)={ALLOW,P=∅ALLOW,⋁P∈P⋁r∈Pmatch(r,c)=TrueDENY,otherwise\text{Decision}(\text{pod}, c) =\begin{cases}\displaystyle \text{ALLOW}, & \mathcal{P} = \varnothing \\[6pt]\displaystyle \text{ALLOW}, & \bigvee_{\mathbf{P} \in \mathcal{P}} \bigvee_{r \in \mathbf{P}} \text{match}(r, c) = \text{True} \\[8pt]\displaystyle \text{DENY}, & \text{otherwise}\end{cases}
一句话记住这个公式:没人点名(无策略)就放行;有人点名(有策略),就必须在规则里找到你的名字。
  1. 收集策略集合先把集群里所有选中该 Pod 的 NetworkPolicy 收集起来,构成待判定的策略集合。
    P={ P∣selects(P,pod) }\mathcal{P} = \{\, \mathbf{P} \mid \text{selects}(\mathbf{P}, \text{pod}) \,\}
  2. 空集默认放行集合为空意味着没有任何策略点名这栋楼,流量一律放行,这是默认语义。
    P=∅  ⇒  ALLOW\mathcal{P} = \varnothing \;\Rightarrow\; \text{ALLOW}
  3. 双重或运算集合非空时,遍历所有策略的所有规则逐条匹配,任一规则命中,整个式子为真即放行。
    ⋁P∈P⋁r∈Pmatch(r,c)\bigvee_{\mathbf{P} \in \mathcal{P}} \bigvee_{r \in \mathbf{P}} \text{match}(r, c)
  4. 兜底拒绝全部规则都没命中时,或运算结果为假,连接被拒绝。
    ¬match(r,c),  ∀r  ⇒  DENY\neg \text{match}(r, c),\; \forall r \;\Rightarrow\; \text{DENY}

策略核心字段

一张门禁卡要生效,得写清楚三件事:管哪栋楼(作用对象)、管进门还是出门(方向)、谁有资格(白名单)。NetworkPolicy 的 YAML 恰好也由这三块组成:podSelector、policyTypes、以及 ingress/egress 规则数组。

以“数据库门禁”为例:spec.podSelector.matchLabels 写 app: db,圈定这栋“数据库楼”;policyTypes 声明这次只管入站方向;然后 ingress 数组里写从哪些来源、哪些端口可以进来。

细节上,podSelector 只能选同命名空间内的 Pod,是“楼内点名”;policyTypes 是数组,可写 Ingress、Egress 或都写,不写时默认只对规则中出现的方向生效;ingress 定义“允许谁进来”,egress 定义“允许本 Pod 出去找谁”。要特别注意:当对应方向的规则数组为空时,该方向就是完全拒绝——空白名单等于谁都别进。

就像门禁卡上“楼栋编号 + 进出方向 + 白名单”三要素缺一不可,写策略时也要三连问:管谁?哪个方向?放行谁?少一个,门禁要么管错了楼,要么干脆没通电。

match(pod,S)=⋀(k,v)∈S[ pod.labels[k]=v ]\text{match}(\text{pod}, S) = \displaystyle \bigwedge_{(k, v) \in S} \big[\, \text{pod.labels}[k] = v \,\big]
标签选择器就像门禁读卡器:胸牌上每个字段都要对上,一个不对就锁死。
  1. 键值对展开把 YAML 里的 matchLabels 读成若干键值对,组成一个选择器集合。
    S={ (k1,v1),(k2,v2),… }S = \{\, (k_1, v_1), (k_2, v_2), \dots \,\}
  2. 单键取值比对对每个键,从 Pod 的标签字典里取出对应值,与期望值做全等判断。
    pod.labels[ki]=vi\text{pod.labels}[k_i] = v_i
  3. 合取收口所有键的比对结果用逻辑“与”串起来,任何一个不等,整体匹配失败。
    ⋀i[ pod.labels[ki]=vi ]\bigwedge_{i} \big[\, \text{pod.labels}[k_i] = v_i \,\big]

规则匹配机制

白名单可以写得很灵活:按楼栋整栋放行(整个命名空间)、按工牌精确到人(特定标签的 Pod)、按访客来源地圈范围(IP 网段)。门禁系统支持三种名单格式,对应 NetworkPolicy 的三种匹配器。

from/to 规则里支持三类来源或目标:podSelector 匹配同一命名空间内的 Pod;namespaceSelector 匹配整个命名空间(可以再叠加 podSelector 精确到人);ipBlock 按 CIDR 网段匹配,常用来放行集群外的运维机器网段。

组合时最容易踩坑的是“与或”关系:from 列表内部是“或”,任一来源命中即通过;而同一个 rule 里嵌套的 namespaceSelector 与 podSelector 是“与”,要求“既来自这个命名空间,又是这个标签的 Pod”。ports 字段限定协议与端口,不写则放行全部端口。把“或”当成“与”,策略就会悄悄变宽,这是安全评审最常抓的漏洞。

门禁名单再多,归结起来一句话:要么“这个楼的人都能进”,要么“这个工牌的人能进”,要么“这片区域的人能进”;三种条件叠加时,先想清楚哪些是“或”、哪些是“与”。

allowed(src,port)=⋁r∈R((⋁s∈r.frommatch(s,src))∧(port∈r.ports))\text{allowed}(\text{src}, \text{port}) = \bigvee_{r \in R} \Big( \Big( \bigvee_{s \in r.\text{from}} \text{match}(s, \text{src}) \Big) \wedge \big( \text{port} \in r.\text{ports} \big) \Big)
来源任选其一、端口必须命中、规则之间任一满足——这就是门禁白名单的完整判定。
  1. 规则集合遍历一条策略内的所有规则是“或”关系,先定义规则集合。
    R={ r1,r2,… },⋁r∈R(⋯ )R = \{\, r_1, r_2, \dots \,\}, \quad \bigvee_{r \in R} (\cdots)
  2. 来源选择器或每个规则内的 from 列表也是“或”,任一来源选择器命中,来源条件即通过。
    ⋁s∈r.frommatch(s,src)\bigvee_{s \in r.\text{from}} \text{match}(s, \text{src})
  3. 端口白名单校验请求目标端口必须出现在规则声明的端口集合内。
    port∈r.ports\text{port} \in r.\text{ports}
  4. 与或组合判定来源通过且端口通过,该规则命中;规则之间任一命中即整体放行。
    (⋁smatch(s,src))∧(port∈r.ports)\Big(\bigvee_{s} \text{match}(s, \text{src})\Big) \wedge \big(\text{port} \in r.\text{ports}\big)

七层路由Ingress

访客走到园区大门口,前台问三个问题:你找谁(host)、去哪个窗口(path)、找哪个部门(backend)。前台只负责把访客带到正确的楼门口,绝不检查他在楼里有没有权限——那是门禁的活。

Ingress 就是 K8s 的“前台”,工作在七层(应用层),依据 host 和 path 把外部 HTTP/HTTPS 流量路由到不同的 Service。它完全看不到集群内部的 Pod 间流量,也从不做访问控制;真正的认证、限流要交给网关插件或 NetworkPolicy 配合完成。

技术实现上,Ingress 资源只是一份声明,真正干活的是 Ingress Controller(如 nginx-ingress、traefik),它 watch 这份声明并动态生成反向代理配置。多路径前缀可以灵活分发业务:把 /api 路由给 AI 推理服务的 Service,把 /admin 路由给管理后台。当多条规则同时命中时,路径最长优先——写得越具体,优先级越高。

前台指路永远只到“楼门口”,楼里怎么走是门禁的事。Ingress 管七层路由、NetworkPolicy 管三层可达,一个对外、一个对内,互相补位,才构成完整的园区访问体系。

r∗=arg⁡max⁡r∈R(∣r.host∣+∣r.path∣),s.t.    r.host=req.host  ∧  req.path 命中 r.pathr^* = \arg\max_{r \in \mathbf{R}} \big( |r.\text{host}| + |r.\text{path}| \big), \qquad \text{s.t.} \;\; r.\text{host} = \text{req}.\text{host} \;\wedge\; \text{req}.\text{path} \text{ 命中 } r.\text{path}
Ingress 的路由规则像快递分拣:地址写得越详细(host + path 越长),越优先被命中。
  1. 候选规则过滤先从所有路由规则里筛出 host 与请求一致的候选项,这是前提条件。
    r.host=req.hostr.\text{host} = \text{req}.\text{host}
  2. 路径前缀匹配请求路径必须落在规则声明的路径前缀内,才算这条规则覆盖该请求。
    req.path∈prefix(r.path)\text{req}.\text{path} \in \text{prefix}(r.\text{path})
  3. 最长优先仲裁多个候选同时命中时,取 host 加 path 长度最大的一条,转发到对应 Service。
    r∗=arg⁡max⁡r∈R(∣r.host∣+∣r.path∣)r^* = \arg\max_{r \in \mathbf{R}} \big( |r.\text{host}| + |r.\text{path}| \big)

隔离方案实践

安全团队接管园区后推行零信任:所有楼栋默认锁门,任何人进门先回答三个问题——你是谁、去哪栋楼、干什么(什么端口)。

落到 AI 推理集群,推荐的隔离三步走:第一步,数据库和向量库套“默认拒绝 + 白名单”,只放行推理服务的访问;第二步,控制 AI 服务之间的通信,例如前端 Web 只能访问推理服务,推理服务只能访问特征库;第三步,全集群推广默认拒绝,所有正常通信都显式声明。

具体配置手法:先为关键服务写一条“锚点策略”——选中目标标签但规则数组为空,它负责把该方向设为默认拒绝;再叠加白名单策略。因为多条策略对同一 Pod 是“或”关系,白名单会放开锚点的拒绝,语义等价于“除了白名单,其余一律拒绝”。这个组合拳是生产环境最常用的模式,也是 AI 服务上生产前最该做的一次安全加固。

零信任不是买一把好锁,而是让每一段路都有人查岗:发送方出站查一次,接收方入站再查一次,双向都过才放行。这也是 AI 服务上生产时最值得抄的作业。

allow(A→B,p)=egress(A,B,p)⏟发送方出站链  ∧  ingress(B,A,p)⏟接收方入站链\text{allow}(A \to B, p) = \underbrace{\text{egress}(A, B, p)}_{\text{发送方出站链}} \;\wedge\; \underbrace{\text{ingress}(B, A, p)}_{\text{接收方入站链}}
零信任的完整链路是“双人查岗”:源侧出站放行、目标侧入站放行,两边都同意才通过。
  1. 发送方出站检查源 Pod 的 egress 策略必须明确允许到目标 Pod 的访问。
    egress(A,B,p)=True\text{egress}(A, B, p) = \text{True}
  2. 接收方入站检查目标 Pod 的 ingress 策略必须明确允许来自源 Pod 的访问。
    ingress(B,A,p)=True\text{ingress}(B, A, p) = \text{True}
  3. 双向合取裁决两个条件都是真,整条链路才放行;任何一环为假,连接被切断。
    egress∧ingress  ⇒  ALLOW\text{egress} \wedge \text{ingress} \;\Rightarrow\; \text{ALLOW}

后端实现原理

门禁的按钮、电磁锁、感应器是物业统一安装的;K8s 里,把 NetworkPolicy 翻译成真正“锁”的,是 CNI 插件——Calico、Cilium、Weave 等。

YAML 只是愿望清单,CNI 负责把它编译成内核数据通路上的规则。最常见的实现是 iptables/ipset:每个策略派生出独立的规则链,数据包依次经过这些链,命中规则返回 ACCEPT,否则一路落到默认 DROP。

Calico 在宿主机 FORWARD 链上挂载一组 KUBE-NWPLCY 链,按源/目标 Pod 分流检查;Cilium 则用 eBPF 把策略直接编译进内核虚拟机,绕过 iptables 的性能瓶颈,还能顺带做七层流量观测。无论哪种实现,最终裁决语义一致:源侧出站链 ACCEPT 且目标侧入站链 ACCEPT,才真正放行。

对应用开发者,CNI 是黑盒;但排错时要记住:门禁锁是物业装的,不是住户随手买的。查规则之前,先确认这套集群装的到底是哪家“门禁品牌”——比如 Flannel 默认不支持 NetworkPolicy,写了也白写。

verdict(A→B)=ChainAegress=ACCEPT⏟源侧策略链  ∧  ChainBingress=ACCEPT⏟目标侧策略链\text{verdict}(A \to B) = \underbrace{\text{Chain}_{A}^{\text{egress}} = \text{ACCEPT}}_{\text{源侧策略链}} \;\wedge\; \underbrace{\text{Chain}_{B}^{\text{ingress}} = \text{ACCEPT}}_{\text{目标侧策略链}}
数据包从 A 到 B,要过两道门禁:A 楼出门刷一次卡,B 楼进门再刷一次卡。
  1. 数据包进入内核Pod 发出的数据包经过虚拟网卡进入宿主机,交到 netfilter 框架手里。
    packet:A→FORWARD\text{packet}: A \to \text{FORWARD}
  2. 源侧出站链裁决CNI 按源 Pod 插入的 egress 链先检查一次,不通过直接丢弃。
    ChainAegress=ACCEPT\text{Chain}_{A}^{\text{egress}} = \text{ACCEPT}
  3. 目标侧入站链裁决数据包到达目标 Pod 前,再过一遍目标侧 ingress 链。
    ChainBingress=ACCEPT\text{Chain}_{B}^{\text{ingress}} = \text{ACCEPT}
  4. 双链合取转发两条链都返回 ACCEPT,数据包才被最终转发给目标 Pod。
    ACCEPT∧ACCEPT\text{ACCEPT} \wedge \text{ACCEPT}

排错与验证

访客刷卡被拒,物业排查三步:门禁有没有通电(CNI 正常吗)、胸牌信息对不对(标签匹配吗)、白名单里有没有你(规则放行了吗)。

K8s 里排查网络连通性遵循同一思路:先确认网络插件与网络策略都就绪,再确认 Service 能解析出后端端点,最后定位是不是 NetworkPolicy 拦截。

常用工具链:kubectl get networkpolicy 看策略清单;kubectl describe networkpolicy 看详细规则;kubectl get pods --show-labels 核对标签;进入源 Pod 用 curl/wget 实测连通性;kubectl get endpoints 确认 Service 后端 Pod 健康;还不行就上 CNI 的观测工具,如 Cilium Hubble 的流量拓扑图,能直接看到某条连接被哪条策略拒绝。

网络故障排查像查门禁:用公式把链路拆成“解析—端点—策略”三段,逐段验证,半小时内准能定位是哪道门没开——而不是在错误的一层反复猜。

reachable(A,B,p)=DNS(B)⏟服务名解析∧Endpoints(B,p)⏟后端端点∧Policy(A,B,p)⏟网络策略裁决\text{reachable}(A, B, p) = \underbrace{\text{DNS}(B)}_{\text{服务名解析}} \wedge \underbrace{\text{Endpoints}(B, p)}_{\text{后端端点}} \wedge \underbrace{\text{Policy}(A, B, p)}_{\text{网络策略裁决}}
网络不通就像快递丢件:先查地址能不能解析,再查收件人是否存在,最后查门卫放不放行。
  1. 解析层验证服务名能否解析出 ClusterIP,失败属于 DNS 层问题。
    DNS(B):svc-name↦ClusterIP\text{DNS}(B): \text{svc-name} \mapsto \text{ClusterIP}
  2. 端点层验证Service 背后是否有健康的后端 Pod 端口,失败属于调度层问题。
    Endpoints(B,p)≠∅\text{Endpoints}(B, p) \neq \varnothing
  3. 策略层验证NetworkPolicy 是否放行这条连接,失败属于安全层问题。
    Policy(A,B,p)=ALLOW\text{Policy}(A, B, p) = \text{ALLOW}
  4. 连乘定位故障三个条件任何一个为假,整条链路就不通,故障落在对应那层。
    DNS∧Endpoints∧Policy=False  ⇒  断连\text{DNS} \wedge \text{Endpoints} \wedge \text{Policy} = \text{False} \;\Rightarrow\; \text{断连}

AI 应用场景

AI 推理服务数据库白名单

阶段:生产部署与安全加固

解决的问题:大模型推理要查特征库与向量库,但训练平台和调试 Web 控制台不应直连数据库。传统做法靠网络安全组手工配子网隔离,新服务扩容时容易漏配或放太宽。用 NetworkPolicy 给数据库打上 app=vector-db 标签,只放行来自 app=llm-infer 的 6333 端口访问,其余来源一律拒绝。

具体案例:某智能客服团队把 Milvus 向量库与 LLM 推理服务部署在同一集群。上线时配置“默认拒绝 + LLM 白名单”,训练 Web 控制台再无法直连向量库;同时用 Ingress 开放 /v1/chat 路径给外部 API 网关,内部通信被锁死在白名单内,模型权重与用户特征数据得到双层保护。

多租户模型仓库隔离

阶段:多团队共享 GPU 集群

解决的问题:算法 A、B 组共用一个 GPU 集群,没有隔离时,B 组的 Pod 可以直接 curl A 组未发布的实验模型服务,存在模型权重与实验数据泄露风险。按命名空间划分租户,再配“跨命名空间默认拒绝,只放行统一网关”的策略。

具体案例:某公司两个算法组共享 K8s 集群,每组独立 Namespace,根命名空间部署 ingress-nginx 网关。NetworkPolicy 规定“只允许来自网关 Pod 标签的入站流量”,实验服务不再被其他组直接探测到;新模型上线走网关灰度,审计时直接导出策略清单作为安全证据。

模型上线前的零信任演练

阶段:CI/CD 与安全合规

解决的问题:合规审计要求模型推理链路遵循最小权限,但团队靠口头约定,拿不出任何证据。落地“全链路策略 + 流量可视化”后,审计可直接对照 NetworkPolicy 清单与流量拓扑图逐条核对。

具体案例:金融风控模型上线前,安全团队用 Cilium Hubble 生成流量拓扑,发现一个测试 Job 偷偷访问生产特征库,流水线直接拦截该任务,并把“默认拒绝”设为新命名空间的初始策略,从此任何新服务想通信都必须显式申请白名单。

Python 演示

python93 行
class LabelSelector:    """标签选择器: 键的取值全部相等才算匹配成功"""     def __init__(self, labels):        self.labels = labels     def matches(self, pod):        return all(pod.labels.get(k) == v for k, v in self.labels.items())  class NetworkPolicy:    """模拟一个 NetworkPolicy 对象, 包含入站与出站规则"""     def __init__(self, name, pod_selector, ingress=None, egress=None):        self.name = name        self.pod_selector = pod_selector        self.ingress = ingress or []   # 每项: {'peers': [...], 'ports': [...]}        self.egress = egress or []     def selects(self, pod):        return self.pod_selector.matches(pod)  class Pod:    def __init__(self, name, labels):        self.name = name        self.labels = labels  def rule_hit(peer, port, rules):    """规则之间是或关系, 来源与端口同时命中才算通过"""    for rule in rules:        peer_ok = any(s.matches(peer) for s in rule['peers'])        if peer_ok and port in rule['ports']:            return True    return False  def is_allowed(src, dst, port, policies):    """默认放行; 一旦有策略选中, 则入站出站都要显式通过"""    dst_policies = [p for p in policies if p.selects(dst)]    if dst_policies:        ingress_rules = [r for p in dst_policies for r in p.ingress]        if not rule_hit(src, port, ingress_rules):            return False    src_policies = [p for p in policies if p.selects(src)]    if src_policies:        egress_rules = [r for p in src_policies for r in p.egress]        if not rule_hit(dst, port, egress_rules):            return False    return True  def print_matrix(pods, policies, port):    print('%-8s' % 'src\\dst', end='')    for b in pods:        print(' %-8s' % b.name, end='')    print()    for a in pods:        print('%-8s' % a.name, end='')        for b in pods:            mark = 'Y' if is_allowed(a, b, port, policies) else 'N'            print(' %-8s' % mark, end='')        print()  def main():    pods = [        Pod('web', {'app': 'web'}),        Pod('model', {'app': 'model'}),        Pod('db', {'app': 'db'}),    ]    db_deny = NetworkPolicy(        'db-default-deny',        LabelSelector({'app': 'db'}),        ingress=[{'peers': [LabelSelector({'app': 'model'})], 'ports': [5432]}],    )    web_egress = NetworkPolicy(        'web-egress-only-model',        LabelSelector({'app': 'web'}),        egress=[{'peers': [LabelSelector({'app': 'model'})], 'ports': [8080, 5432]}],    )     print('=== 场景一: 未配置策略 ===')    print_matrix(pods, [], 5432)    print('\n=== 场景二: 数据库白名单 ===')    print_matrix(pods, [db_deny], 5432)    print('\n=== 场景三: 零信任全链路 ===')    print_matrix(pods, [db_deny, web_egress], 5432)  if __name__ == '__main__':    main()
目的:用纯 Python 模拟 K8s NetworkPolicy 的判定语义,对比“无策略、数据库白名单、零信任全链路”三种场景下 Pod 间的连通矩阵。
  1. return all(pod.labels.get(k) == v for k, v in self.labels.items())标签选择器的核心:所有键值对全部相等才算匹配,get 取不到键时天然判不等,实现“一票否决”。
  2. dst_policies = [p for p in policies if p.selects(dst)]找出所有选中目标 Pod 的策略;列表为空就等同于“没人点名这栋楼”,默认放行。
  3. if not rule_hit(src, port, ingress_rules):有策略选中目标,但入站规则没命中来源,直接拒绝,这就是“默认拒绝 + 显式放行”的落地。
  4. peer_ok = any(s.matches(peer) for s in rule['peers'])白名单内的多个来源是“或”关系,任何一个匹配就算来源通过,对应 from 列表语义。
  5. if peer_ok and port in rule['ports']:来源与端口必须同时命中才放行,对应“与或组合判定”公式。
  6. ingress=[{'peers': [LabelSelector({'app': 'model'})], 'ports': [5432]}],数据库门禁只认 model 的工牌、只放行 5432 端口,其余一律拦截。
  7. print_matrix(pods, [db_deny, web_egress], 5432)叠加两类策略后,输出矩阵中 web→db 变 N,完整呈现零信任全链路效果。

常见误区

误区我以为只要集群里有人写了 NetworkPolicy,所有 Pod 就自动被保护起来了,不用再管别的。

正解NetworkPolicy 只作用于被其标签选择器选中的 Pod。没被任何策略选中的 Pod 依旧全通,就像园区只有装了门禁的楼才受管控,没装门禁的楼随便进。想保护数据库,必须专门写一条选中数据库标签的策略;想让全网默认拒绝,必须为每个命名空间都铺上锚点策略。集群里“有策略”不等于“每个 Pod 有策略”。

为何常见:把“集群存在策略”误当成“所有 Pod 已被策略覆盖”,忽略了 selectors 的精准点名机制。

误区我把 Ingress 的访问限制配得很严,集群内部的 Pod 间通信就不用管了吧?

正解Ingress 只处理从集群外部进来的 HTTP/HTTPS 流量,是七层路由;集群内部 Pod 之间互访的数据包根本不经过它,它完全看不见。内部隔离必须靠 NetworkPolicy 这类三层策略。就像大门前台只知道把访客带到楼门口,楼里谁和谁走动,前台一概不知,那是楼栋门禁的职责。

为何常见:把对外网关当成内部防火墙,混淆了“外部入口”与“内部隔离”两个不同层次的问题。

误区我只给 Pod 配了 egress 规则限制它访问外部 IP,结果业务直接报 DNS 解析失败,这也太奇怪了。

正解一旦有策略选中 Pod,其 egress 方向就变成“默认拒绝,只放行显式规则”。DNS 查询要访问 CoreDNS 的 53 端口(通常 UDP,部分场景 TCP),这条流量不在你的白名单里,自然被拦。解法是在 egress 里显式放行 kube-system 命名空间下 DNS 服务的 53 端口。这就像楼里规定“只能去 A 楼”,可你还想去物业办公室(DNS)问路,当然被门禁拦住。

为何常见:人们只盯着业务端口,忘了 DNS 是 Pod 通信的隐性依赖,出站策略一收紧,第一个崩的往往是解析。

误区我配了数据库的 ingress 默认拒绝,为什么数据库还能往外连别的服务?策略是不是坏了?

正解入站和出站是相互独立的两个方向。policyTypes 声明了哪些方向生效,只写 ingress 规则,不代表 egress 被约束。数据库要真正隔离,需要同时配置入站(谁可以进来)和出站(它可以去谁那里)两套规则。真正的零信任方案里,发送方走 egress、接收方看 ingress,双向都要过。

为何常见:把单方向规则当成双向防火墙策略,忽略了 NetworkPolicy 的方向显式声明机制。

误区外部请求经过 Ingress 转发到后端 Pod,这属于“公网流量”,NetworkPolicy 应该管不着吧?

正解在 K8s 里没有特殊通道——Ingress 控制器本身也是一个 Pod,它转发到后端 Pod 时,数据包同样要接受 NetworkPolicy 检查。如果后端 Pod 配了默认拒绝入站,就必须在 from 里显式放行 ingress-nginx 控制器的 Pod 标签,否则外部用户会看到 502/504 超时。这就像前台把你送到楼门口,楼栋门禁照样要验你的证。

为何常见:以为外部流量走的是“特殊通道”,忽略了 K8s 内所有流量本质上都是 Pod 到 Pod 的事实。

面试题

Q1. 一个 Pod 没有被任何 NetworkPolicy 选中时,它的入站流量会怎样?

单选题 · easy

  • A.入站流量全部被拒绝
  • B.入站流量默认全放行← 正确答案
  • C.仅放行同命名空间流量
  • D.仅放行集群内部流量

答案:B。没有被任何策略点名的 Pod,如同没装门禁的楼,流量默认全放行。只有在至少一条策略选中它后,才切换为“默认拒绝 + 显式放行”。

Q2. 要使“数据库 Pod 默认拒绝全部入站”,正确做法是?

单选题 · mid

  • A.只创建匹配数据库标签的空规则策略← 正确答案
  • B.创建默认允许全部入站的兜底策略
  • C.在命名空间上打禁止入站的标签
  • D.删除数据库 Pod 的所有访问入口

答案:A。用 podSelector 选中数据库 Pod,policyTypes: [Ingress],且 ingress 规则数组为空,就是“空白名单等于谁都别进”。B 会放行所有流量,C 的命名空间标签不参与判定,D 是人为破坏而非策略。

Q3. NetworkPolicy 的 from/to 支持哪几种来源或目标选择方式?

多选题 · mid

  • A.podSelector 选择命名空间内 Pod← 正确答案
  • B.namespaceSelector 选择整个命名空间← 正确答案
  • C.ipBlock 按 CIDR 匹配 IP 地址段← 正确答案
  • D.serviceAccount 选择服务账号

答案:ABC。from/to 支持 podSelector、namespaceSelector、ipBlock 三类匹配器,D 的 serviceAccount 选择器不是核心 API 的标准能力。门禁名单只有“楼内点名、整楼放行、区域圈定”三种格式。

Q4. 多条 NetworkPolicy 同时选中同一个 Pod 时,流量判定规则是?

单选题 · mid

  • A.只执行最先创建的一条策略
  • B.多条策略取交集才允许放行
  • C.多条策略是或关系任一放行← 正确答案
  • D.以标签选择器最长的为准

答案:C。多策略对同一 Pod 是“或”关系,只要任一策略的任一规则命中就放行。这也是“锚点拒绝 + 白名单放宽”组合能生效的根本原因——白名单策略会把锚点的拒绝“打开一个口子”。

Q5. Ingress 资源工作在 OSI 模型的第几层?

单选题 · easy

  • A.第二层数据链路层转发帧
  • B.第三层网络层转发数据包
  • C.第四层传输层转发 TCP 连接
  • D.第七层应用层路由 HTTP 流量← 正确答案

答案:D。Ingress 依据 host 和 path 路由 HTTP/HTTPS 流量,属于应用层(第七层)能力。它像园区前台按“找谁、办什么事”来指路,而 NetworkPolicy 工作在网络层,按 IP 与端口做可达性控制。

Q6. 给 Pod 配置了严格的 egress 规则后,发现业务无法解析域名,可能的原因是?

多选题 · hard

  • A.未放行 DNS 服务的 53 端口← 正确答案
  • B.未放行 CoreDNS 命名空间访问← 正确答案
  • C.DNS 请求自动豁免策略检查
  • D.未同时放行 UDP 与 TCP 协议← 正确答案

答案:ABD。egress 收紧后,所有出站流量都要显式放行,DNS 查询也不例外。需要放行到 CoreDNS 所在命名空间(通常 kube-system)的 53 端口,且常常需要同时放行 UDP 和 TCP。C 是错误认知——DNS 没有“免检通道”。

Q7. Pod 默认拒绝入站后,从 Ingress 控制器转发到该 Pod 的流量会?

单选题 · hard

  • A.完全不受影响,走独立公网通道
  • B.会被拦截,需放行控制器来源← 正确答案
  • C.自动放行,因为来自网关服务
  • D.只放行且仅放行 HTTP 请求

答案:B。Ingress 控制器本质也是一个 Pod,它到后端 Pod 的流量同样过 NetworkPolicy。后端默认拒绝入站时,必须在 from 里放行控制器 Pod 的标签(如 app=ingress-nginx),否则外部访问会超时。K8s 里没有绕过策略的“特殊通道”。

Q8. 未配置任何 NetworkPolicy 时,集群内 Pod 间流量的默认行为是?

单选题 · mid

  • A.所有跨命名空间流量被拒绝
  • B.所有集群外部的流量被拒绝
  • C.全部流量默认放行不设限← 正确答案
  • D.全部流量默认拒绝零信任

答案:C。K8s 网络模型的默认语义是“未配置即全通”,就像园区公告“没标注禁止入内的建筑一律开放”。默认拒绝不是凭空生效的,必须靠策略主动声明,这正是零信任方案需要逐个命名空间铺策略的原因。

Q9. 关于 NetworkPolicy 与 CNI 插件的关系,正确的说法是?

单选题 · mid

  • A.所有 CNI 插件都强制支持网络策略
  • B.由 kubelet 独立执行,与 CNI 无关
  • C.需 CNI 实现支持,例如 Calico 等← 正确答案
  • D.由内核自动执行,无需插件参与

答案:C。NetworkPolicy 的 YAML 由 API Server 存储,但真正把它翻译成 iptables/eBPF 规则的是 CNI 插件。Flannel 默认不支持,Calico、Cilium、Weave 等才具备该能力。就像门禁规则是物业定的,但电磁锁必须由专业门禁厂商来安装。

Q10. 零信任方案中,一条完整链路 "A→B" 要放行必须满足哪些条件?

多选题 · easy

  • A.A 的出站策略允许访问 B← 正确答案
  • B.B 的入站策略允许来自 A← 正确答案
  • C.只要 Ingress 网关放行即可
  • D.A 与 B 必须属于同一命名空间

答案:AB。零信任要求双向查岗:A 的 egress 放行、B 的 ingress 放行,两个条件同时满足才通过。C 混淆了外部路由与内部隔离;D 错误,跨命名空间访问可以用 namespaceSelector 显式放行,并不强制同域。