K8s 网络通关手册:Ingress-Nginx 生产级路由 + Calico 三种隧道模式 + NetworkPolicy 白名单实战

K8s 网络通关手册:Ingress-Nginx 生产级路由 + Calico 三种隧道模式 + NetworkPolicy 白名单实战

摘要 :容器网络是 Kubernetes 最容易"配了却不生效"的一块。本文把三件事讲透:第一 ,Ingress 作为七层入口,从控制器工作流程到 pathType、主机名匹配的判定规则,再用 7 组可直接复制的生产规则(多域名、多路径、路径裁剪、TLS 强制跳转、限流、跨域、白名单、灰度)落地;第二 ,容器网络从 Docker 单机 veth pair 讲到跨主机 Underlay/Overlay,横向对比 Flannel、Calico、Weave、Macvlan,并拆解 Calico 的 IPIP 与 VXLAN 封装差异、Always/Never/CrossSubnet 三种模式的选型与切换;第三,NetworkPolicy 的隔离模型与四类选择器,配合 10 组实验验证"谁能访问谁"。全文按"先原理、后实操"组织,所有命令与 YAML 验证过可直接执行,末尾附高频踩坑清单。

实验环境 :Kubernetes v1.30(兼容 1.26--1.30)、ingress-nginx v1.11.2、Calico(BGP/IPIP 可切换)、MetalLB 提供 LoadBalancer。示例域名 laoma.cloud、集群节点网段 10.1.8.0/24、Pod 网段 10.224.0.0/16,请按自己的环境替换。


一、先搞懂 Ingress:它到底解决什么问题

1.1 Ingress 的定位

Ingress 可为 Service 提供外部可访问的 URL、对流量做负载均衡、终止 SSL/TLS,以及基于名称的虚拟托管。真正干活的是 Ingress 控制器(Ingress Controller),它通常借用负载均衡器、边缘路由器或其他前端来承载流量。

一句话理解层级关系:

  • Service:四层抽象,解决"一组 Pod 怎么被稳定访问";
  • Ingress:七层抽象,解决"不同域名/路径怎么分给不同 Service";
  • Ingress Controller:真正跑着的反向代理(本文用 Nginx)。

Ingress 本身不会随意公开端口或协议。要把 HTTP/HTTPS 以外的服务开放到 Internet,仍然应该用 NodePort 或 LoadBalancer 类型的 Service。

1.2 为什么必须单独部署控制器

Ingress 控制器不像 kube-controller-manager 里的其他控制器那样随集群自动启动,它是独立部署的工作负载。Kubernetes 官方目前支持和维护 AWS、GCE、Nginx 三种实现,本次实验使用 ingress-nginx。

1.3 ingress-nginx 的五步工作流程

这是理解后面所有"改了 YAML 却没生效"问题的根子:

  1. 先部署 Ingress Controller 实体(相当于前端的 Nginx);
  2. 再创建 Ingress 资源(相当于一份 Nginx 配置声明);
  3. Controller 与 kube-apiserver 交互,动态获取集群中的全部 Ingress 规则,解析后生成 proxy 配置;
  4. 把生成的配置写进 ingress proxy Pod 里,如果 Pod 跑的是 Nginx,就落到 /etc/nginx/nginx.conf;
  5. reload 服务生效。

记住一句话:Ingress 本质是七层 HTTP/HTTPS 代理配置,不是流量本身。


二、Ingress 的三条匹配铁律(先看规则,再写 YAML)

2.1 pathType:三种路径类型,不写就过不了校验

Ingress 中每个路径都必须有对应的路径类型,未明确设置 pathType 的路径无法通过合法性检查。

类型 匹配方式 说明
Prefix 基于 / 分隔的 URL 前缀匹配 区分大小写,按路径元素逐个比对;/aaa/bb 不匹配 /aaa/bbb
Exact 精确匹配 URL 路径 区分大小写;/foo 不匹配 /foo/
ImplementationSpecific 匹配方法取决于 IngressClass 具体实现可当作 Prefix 或 Exact 处理

高频判定示例,考试和实战都爱问:

类型 路径 请求路径 是否匹配
Prefix / 所有路径 是
Exact /foo /foo 是
Exact /foo /foo/ 否
Prefix /aaa/bb /aaa/bbb 否(按元素切分,不是字符串前缀)
Prefix /aaa/bbb /aaa/bbbxyz 否(字符串前缀不匹配)
Prefix /aaa/bbb /aaa/bbb/ccc 是(匹配子路径)
Prefix /、/aaa /aaa/ccc 是(匹配更长的 /aaa)
Prefix /aaa /ccc 否(走默认后端)
混合 /foo(Prefix) + /foo(Exact) /foo 是(优选 Exact)

2.2 多重匹配:最长优先,同长比类型

多条路径命中同一请求时:匹配路径最长者优先 ;若长度相同,Exact 优先于 Prefix。

2.3 主机名匹配:精确 vs 通配

主机名可以精确匹配(foo.bar.com),也可以用通配符(*.foo.com)。通配符只覆盖一个 DNS 标签。

主机 Host 头部 是否匹配
*.foo.com bar.foo.com 是(同后缀)
*.foo.com baz.bar.foo.com 否(跨了两个标签)
*.foo.com foo.com 否(标签数不够)

2.4 IngressClass:控制器多了怎么区分

Ingress 可以由不同控制器实现,每个 Ingress 应当通过 ingressClassName 引用一个 IngressClass。把某个 IngressClass 标记为集群默认后,未写 ingressClassName 的 Ingress 会自动归到它名下。

bash 复制代码
# 标记为集群默认 IngressClass
root@master30:~# kubectl patch ingressclass nginx -p '{"metadata":{"annotations":{"ingressclass.kubernetes.io/is-default-class":"true"}}}'

对应的 YAML 形态:

bash 复制代码
root@master30:~# vim ingressclass.yaml
apiVersion: networking.k8s.io/v1                   # IngressClass API版本
kind: IngressClass                                 # 资源类型:IngressClass
metadata:
  name: nginx                                      # IngressClass名称,对应ingress-nginx控制器
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"  # 标记为集群默认IngressClass,Ingress不写ingressClassName时自动选用
spec:
  controller: k8s.io/ingress-nginx                 # 绑定的ingress-nginx控制器标识

踩坑提醒 :spec.ingressClassName 一定别漏写,否则规则可能落到别的控制器上,表现为"YAML 对但就是不生效"。

2.5 没有 rules 的 Ingress:defaultBackend 兜底

一个 Ingress 可以不写任何 rules,只指定一个默认后端。此时所有发往该入口 IP 的流量都会转给 defaultBackend 列出的 Service------相当于 Nginx 里那个"兜底 server"。

bash 复制代码
root@master30:~# vim default-backend-ingress.yaml
apiVersion: networking.k8s.io/v1        # Ingress 资源API版本
kind: Ingress                           # 资源类型:Ingress七层流量规则
metadata:
  name: test-ingress                    # Ingress资源名称
spec:
  ingressClassName: nginx               # 引用IngressClass,名字对应前面IngressClass的metadata.name(nginx)
  defaultBackend:                       # 默认后端:所有不匹配任何路径/域名的流量,全部转发到这里
    service:
      name: webapp01                    # 目标Service名称
      port:
        number: 80                      # Service端口

三个使用要点:

  1. 只写 defaultBackend 的 Ingress 不区分域名和路径,任何 Host 头都能命中,适合做统一的 404/维护页兜底;
  2. 与带 rules 的 Ingress 并存时,只有 rules 全部匹配失败才落到 defaultBackend;
  3. defaultBackend 与 rules 写在同一个 Ingress 里同样合法,前者负责兜底、后者负责分流。

三、部署 ingress-nginx(前置条件 + 四段式资源)

3.1 部署前提

  1. 需要外部入口地址,本次环境用 MetalLB 提供 LoadBalancer 类型的 Service;
  2. 必须已经部署一个 Ingress 控制器实现,即 ingress-nginx。

3.2 版本对应关系

Ingress-NGINX v1.10--v1.11 系列适配 Kubernetes 1.26--1.30,底层 Nginx 版本 1.25.x,Alpine 3.19--3.20。选版本先对齐集群版本,别只看"最新"。

3.3 部署与镜像替换

bash 复制代码
# 下载离线包并解压
root@master30:~# wget https://laoma.cloud/class/course-materials/softwares/stage03/ingress-nginx-controller-v1.11.2.tar.gz
root@master30:~# tar -xf ingress-nginx-controller-v1.11.2.tar.gz

# 查看资源用到的镜像
root@master30:~# grep image: ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml|uniq
        image: registry.k8s.io/ingress-nginx/controller:v1.11.2@sha256:d5f8217feeac4887cb1ed21f27c2674e58be06bd8f5184cacea2a69abaf78dce
        image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.3@sha256:a320a50cc91bd15fd2d6fa6de58bd98c1bd64b9a6f926ce23a600d87043455a3

# 去掉 sha256 摘要并替换镜像仓库,便于内网离线拉取
root@master30:~# sed -ir 's#@sha256.*##;s/registry.k8s.io/hub.laoma.cloud/' \
  ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml

# 部署
root@master30:~# kubectl apply -f ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml

3.4 deploy.yaml 到底创建了哪四类东西

  1. 独立命名空间 ingress-nginx,与其他业务隔离;
  2. ConfigMap:把控制器通用配置统一成对象管理。创建 Pod 时绑定 ConfigMap,Pod 内应用可直接引用,常用于设置环境变量、命令行参数、生成配置文件;
  3. RBAC 授权:创建 ServiceAccount、ClusterRole、Role、RoleBinding、ClusterRoleBinding,让控制器有权监听集群内 Ingress 变化;
  4. ingress-controller 工作负载:把新增 Ingress 转成代理配置的执行者。

3.5 验收部署结果

bash 复制代码
root@master30:~# kubectl get all -n ingress-nginx

重点关注三行信息:

  • ingress-nginx-controller Pod 为 Running;
  • ingress-nginx-controller Service 类型是 LoadBalancer ,并拿到了 EXTERNAL-IP(本例 10.1.8.40),后面所有域名都解析到这个地址;
  • 两个 admission-create / admission-patch Job 状态为 Completed,它们是做 Webhook 证书初始化的,跑完即退,正常现象。

四、7 组生产级 Ingress 规则实战

4.0 环境准备:两个站点 + 两个 Service

bash 复制代码
# 命名空间与上下文
root@master30:~# kubectl create ns ingress
root@master30:~# kubectl config set-context --current --namespace ingress

# webapp01:两个副本
root@master30:~# kubectl create deployment webapp01 --image=hub.laoma.cloud/library/httpd --replicas=2
root@master30:~# kubectl get pod -L app

# 写入各自的主页内容,便于区分是哪个 Pod 应答
root@master30:~# kubectl exec -it webapp01-57b8567f7c-kq56c -- bash -c "echo hello webapp01 pod1 > htdocs/index.html"
root@master30:~# kubectl exec -it webapp01-57b8567f7c-vzqj7 -- bash -c "echo hello webapp01 pod2 > htdocs/index.html"
root@master30:~# kubectl expose deployment webapp01 --port=80 --target-port=80

# webapp02:额外造一个 games 子目录,用来验证路径分流
root@master30:~# kubectl create deployment webapp02 --image=hub.laoma.cloud/library/httpd --replicas=2
root@master30:~# kubectl get pod | grep webapp02

root@master30:~# kubectl exec -it webapp02-598c447b5b-4j76n -- bash -c "
echo hello webapp02 pod1 > htdocs/index.html
mkdir htdocs/games
echo hello webapp02 game1 > htdocs/games/index.html"

root@master30:~# kubectl exec -it webapp02-598c447b5b-dddwh -- bash -c "
echo hello webapp02 pod2 > htdocs/index.html
mkdir htdocs/games
echo hello webapp02 game2 > htdocs/games/index.html"

root@master30:~# kubectl expose deployment webapp02 --port=80 --target-port=80

4.1 规则一:多域名虚拟主机(一个域名一个后端)

场景:多个业务域名,各自转发到自己的 Service。

bash 复制代码
root@master30:~# vim multi-host-ingress.yaml
apiVersion: networking.k8s.io/v1        # Ingress API版本
kind: Ingress                           # 资源类型:Ingress
metadata:
  name: multi-host-ingress              # Ingress名称:多域名主机Ingress
spec:
  ingressClassName: nginx               # 指定使用名称为nginx的IngressClass,交给ingress-nginx控制器处理
  rules:                                # 流量规则列表
  - host: webapp01.laoma.cloud          # 域名1:访问这个域名
    http:
      paths:
      - path: /                         # 匹配根路径 /
        pathType: Prefix                # 前缀匹配:以 / 开头全部命中
        backend:
          service:
            name: webapp01              # 转发到service webapp01
            port:
              number: 80                # service端口80
  - host: webapp02.laoma.cloud          # 域名2:访问这个域名
    http:
      paths:
      - path: /                         # 匹配根路径 /
        pathType: Prefix
        backend:
          service:
            name: webapp02              # 转发到service webapp02
            port:
              number: 80

root@master30:~# kubectl apply -f multi-host-ingress.yaml
root@master30:~# kubectl describe ingress multi-host-ingress

验证:客户端把两个域名都指向 EXTERNAL-IP,然后直接 curl 域名(80 端口默认,无需带端口号)。

bash 复制代码
echo '10.1.8.40 webapp01.laoma.cloud' >> /etc/hosts
echo '10.1.8.40 webapp02.laoma.cloud' >> /etc/hosts

curl webapp01.laoma.cloud
curl webapp01.laoma.cloud
curl webapp02.laoma.cloud
curl webapp02.laoma.cloud

预期输出在 hello webapp01 pod1 / hello webapp01 pod2 之间交替,说明 Ingress 后面还做了一层轮询负载均衡。

bash 复制代码
root@master30:~# kubectl delete ingress multi-host-ingress

4.2 规则二:同一域名多路径分流

场景 :一个域名下,不同路径转发给不同微服务(/games 走 webapp02)。

bash 复制代码
root@master30:~# vim multi-path-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-path-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            port:
              number: 80
      - path: /games
        pathType: Prefix
        backend:
          service:
            name: webapp02
            port:
              number: 80

root@master30:~# kubectl apply -f multi-path-ingress.yaml
root@master30:~# kubectl describe ingress multi-path-ingress

验证 :/ 命中 webapp01,/games/ 命中 webapp02,靠的就是"最长路径优先"。

bash 复制代码
echo '10.1.8.40 www.laoma.cloud' >> /etc/hosts

curl www.laoma.cloud
curl www.laoma.cloud
curl www.laoma.cloud/games/
curl www.laoma.cloud/games/
bash 复制代码
root@master30:~# kubectl delete ingress multi-path-ingress

4.3 规则三:路径裁剪(等价 Nginx 的 proxy_pass /)

场景 :前端访问 /api/xxx,后端只认 /xxx,需要把前缀剥掉。这是生产环境最常见的一条重写规则。

bash 复制代码
root@master30:~# vim path-trimming-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-trimming-ingress
  annotations:
    # 开启正则匹配,(.*) 捕获分组才会生效
    nginx.ingress.kubernetes.io/use-regex: "true"
    # 路径重写:$1捕获(.*)里的内容,把捕获部分拼到/后面
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /webapp01/(.*)
        # 正则路径必须用 ImplementationSpecific
        pathType: ImplementationSpecific
        backend:
          service:
            name: webapp01
            port:
              number: 80
      - path: /webapp02/(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: webapp02
            port:
              number: 80

root@master30:~# kubectl apply -f path-trimming-ingress.yaml
root@master30:~# kubectl describe ingress path-trimming-ingress

验证与效果对照:

  • 客户端请求 www.laoma.cloud/webapp01/ → 后端收到 /;命中 webapp01 首页。
  • 客户端请求 www.laoma.cloud/webapp02/ → 后端收到 /,命中 webapp02 首页。
bash 复制代码
curl www.laoma.cloud/webapp01/
curl www.laoma.cloud/webapp01/
curl www.laoma.cloud/webapp02/
curl www.laoma.cloud/webapp02/

三个必须避开的坑:

  1. 不写 use-regex: "true",(.*) 会被当普通字符串,路径匹配不上;
  2. 路径写成 /webapp01(.*) 少了斜杠,会连 /webapp01xxx 一起匹配;
  3. 后端如果是 Web 应用,自身重定向到 /login 这类绝对路径时,浏览器会跳出前缀导致 404,需要后端配置 base path 或补 proxy-redirect 类注解。
bash 复制代码
root@master30:~# kubectl delete ingress path-trimming-ingress

4.4 规则四:绑定 TLS 证书 + 强制 HTTPS

第一步:生成自签证书并落成 Secret(生产环境换成正式证书即可,Secret 用法一致)。

bash 复制代码
root@master30:~# openssl genrsa -out www.key 2048
root@master30:~# openssl req -new -key www.key -out www.csr \
  -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=www.laoma.cloud/emailAddress=webadmin@laoma.cloud"
root@master30:~# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt

root@master30:~# kubectl create secret tls www-tls --cert=./www.crt --key=./www.key

第二步:Ingress 里挂证书并强制跳转。这是生产标配:访问 80 端口也自动跳到 443。

bash 复制代码
root@master30:~# vim tls-ingress.yaml
apiVersion: networking.k8s.io/v1                          # Ingress API版本
kind: Ingress                                             # 资源类型:Ingress
metadata:
  name: tls-ingress                                       # Ingress名称:TLS HTTPS加密 + 强制跳转
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"      # 开启HTTP自动跳HTTPS
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"# 强制HTTPS跳转,即使后端服务本身是http
spec:
  ingressClassName: nginx                                 # 使用nginx这个IngressClass,交给ingress-nginx控制器
  tls:                                                    # TLS证书配置块
  - hosts:
      - www.laoma.cloud                                   # 证书对应的域名
    secretName: www-tls                                   # 存放证书的Secret名称(提前建好)
  rules:
  - host: www.laoma.cloud                                 # 访问域名
    http:
      paths:
      - path: /                                           # 匹配所有根路径
        pathType: Prefix                                  # 前缀匹配
        backend:
          service:
            name: webapp01                                # 转发后端服务webapp01
            port:
              number: 80                                  # 后端端口80

root@master30:~# kubectl apply -f tls-ingress.yaml
root@master30:~# kubectl describe ingress tls-ingress

describe 输出里要能看到 TLS: www-tls terminates www.laoma.cloud 这一行,才算证书挂对了。

验证 :-L 跟随 301/302/307/308 重定向,-k 跳过自签证书校验。

bash 复制代码
curl -Lk http://www.laoma.cloud/
curl -Lk http://www.laoma.cloud/
curl -k https://www.laoma.cloud/
curl -k https://www.laoma.cloud/
bash 复制代码
root@master30:~# kubectl delete ingress tls-ingress

4.5 规则五:限流防刷 + 超时优化

场景:防 CC 攻击、保护后端慢接口。这两个注解作用在 Ingress 上,对同一 Ingress 的所有后端生效。

bash 复制代码
root@master30:~# vim limit-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: limit-ingress
  annotations:
    # 单 IP 最大并发连接数
    nginx.ingress.kubernetes.io/limit-connections: "50"
    # 单 IP 每秒请求数
    nginx.ingress.kubernetes.io/limit-rps: "20"
    # 连接后端超时(秒)
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
    # 后端响应超时,长任务接口要放大
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    # 向后端发送请求超时
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            port:
              number: 80

4.6 规则六:跨域配置(前后端分离必备)

bash 复制代码
root@master30:~# vim cors-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cors-ingress
  annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"     # 开启CORS跨域支持
    nginx.ingress.kubernetes.io/cors-allow-origin: "*"  # 允许所有来源域名访问(*代表不限)
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET,POST,PUT,DELETE,OPTIONS"  # 允许的请求方法
spec:
  ingressClassName: nginx
  rules:
  - host: api.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            port:
              number: 80

生产建议把 cors-allow-origin: "*" 收窄成明确的前端域名,避免接口被任意站点调用。

4.7 规则七:白名单、真实 IP、缓存、灰度、错误页

白名单(管理后台、内部系统只放行公司内网):

bash 复制代码
root@master30:~# vim whitelist-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whitelist-ingress
  annotations:
    # 只放行 10.1.8.0/24 与本地回环
    nginx.ingress.kubernetes.io/whitelist-source-range: "10.1.8.0/24,127.0.0.1/32"
spec:
  ingressClassName: nginx
  rules:
  - host: admin.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            port:
              number: 80

真实客户端 IP 透传(后端日志想拿到真实来源):

bash 复制代码
root@master30:~# vim realip-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: realip-ingress
  annotations:
    # 开启传递 X-Forwarded-For 请求头,把客户端真实IP传给后端Pod里的服务
    nginx.ingress.kubernetes.io/x-forwarded-for: "true"
    # 信任哪些网段传来的 XFF 头
    nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "10.0.0.0/8"
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            port:
              number: 80

静态资源缓存:

bash 复制代码
root@master30:~# vim cache-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cache-ingress
  annotations:
    # 开启Ingress-Nginx代理缓存功能(总开关)
    nginx.ingress.kubernetes.io/proxy-cache: "true"
    # 缓存规则:状态码200、302的响应,缓存有效期10分钟
    nginx.ingress.kubernetes.io/proxy-cache-valid: "200 302 10m"
spec:
  ingressClassName: nginx
  rules:
  - host: static.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp02
            port:
              number: 80

金丝雀灰度(90% 走稳定版,10% 走新版本,权重按需调):

bash 复制代码
root@master30:~# vim canary-ingress.yaml
apiVersion: networking.k8s.io/v1                          # Ingress API版本
kind: Ingress                                             # 资源类型Ingress
metadata:
  name: canary-ingress                                    # Ingress名称:灰度Ingress(金丝雀)
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"            # 开启灰度标记,告诉ingress-nginx这是灰度规则
    nginx.ingress.kubernetes.io/canary-weight: "10"       # 分配10%流量到这个Ingress
spec:
  ingressClassName: nginx                                 # 使用名称为nginx的IngressClass
  rules:
  - host: www.laoma.cloud                                 # 域名必须和主Ingress完全相同!
    http:
      paths:
      - path: /                                           # 匹配根路径
        pathType: Prefix                                  # 前缀匹配
        backend:
          service:
            name: webapp02                                # 灰度流量转发到新版本服务 webapp02
            port:
              number: 80                                  # 服务端口
bash 复制代码
# 配套主 Ingress(必须一起存在才生效!)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: main-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01   # 老版本服务,承接剩余90%流量
            port:
              number: 80

自定义后端错误页(同时可用于接管 502/503,避免暴露默认报错页):

bash 复制代码
root@master30:~# vim errors-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: errors-ingress
  annotations:
    # 自定义错误页面:当后端返回这些状态码时,启用自定义错误页
    nginx.ingress.kubernetes.io/custom-http-errors: "404,500,502,503"
    # 错误页服务
    nginx.ingress.kubernetes.io/default-service: "error-svc"
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            port:
              number: 80

HTTPS 透传(后端 Service 自身就是 HTTPS,Ingress 不解密、直接转发加密流量):

bash 复制代码
root@master30:~# vim passthrough-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: passthrough-ingress
  annotations:
    # 关键:透传 TLS,Ingress 不做解密,证书由后端自己持有
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    # 指定 ingress-nginx 和后端 service 通信时使用 HTTPS 协议
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            # 后端端口要是 443,且 Secret 不需要在这里挂载
            port:
              number: 443

ssl-passthrough 与普通 tls: 互斥:透传模式下 Ingress 看不到明文,也无法做基于路径的七层改写,命中条件只能靠 SNI。启用透传还需控制器开启 --enable-ssl-passthrough 参数。

4.8 生产高频注解速查

注解(nginx.ingress.kubernetes.io/ 前缀) 作用
force-ssl-redirect: "true" 80 端口强制跳转 HTTPS
rewrite-target: /$1 路径重写、裁剪路由前缀
use-regex: "true" 开启正则路径匹配
whitelist-source-range IP 白名单,多个网段用逗号分隔
limit-rps / limit-connections 单 IP 限速、防 CC
enable-cors / cors-allow-origin 前后端跨域
proxy-read-timeout 解决长接口超时断开
x-forwarded-for / proxy-real-ip-cidr 后端获取真实客户端 IP
proxy-cache / proxy-cache-valid 静态资源缓存与有效期
canary / canary-weight 金丝雀权重分流
ssl-passthrough / backend-protocol HTTPS 透传,Ingress 不解密
custom-http-errors 自定义错误页接管

4.9 收尾清理

bash 复制代码
root@master30:~# kubectl delete ns ingress

五、容器网络:从单机 veth pair 到跨主机 Overlay

5.1 Docker / Containerd 的单机网络

Docker 的网络接口默认都是虚拟接口,优势是转发效率极高:Linux 在内核中做数据复制完成虚拟接口间的转发,发送接口缓存中的包被直接复制到接收接口缓存,无需经过外部物理网络设备交换。

Docker 服务启动时默认创建一个 docker0 Linux 网桥,并在本地主机与容器内各创建一个虚拟接口,成对连通------这就是 veth pair 。docker0 自带 IP 与掩码,让主机与容器能通过网桥互通。

Containerd 与 Docker 原理一致:创建容器时在主机与容器内各建一个虚拟接口并连通,只是没有 docker0 这套守护进程绑定。

外部主机要访问容器,需要通过端口映射实现。

5.2 跨主机通信:Underlay 与 Overlay 的取舍

跨主机网络意味着把不同主机上的容器用同一个虚拟网络连起来,两种基本模型:

  • Underlay :直接使用底层物理网络转发 Pod 流量,无隧道封装,性能优于 Overlay;
  • Overlay :在宿主机网络之上构建虚拟隧道(典型是 VXLAN),把数据包封装到 UDP 里传输。开销比 Underlay 大,但 Overlay 支持更多二层网段,能更好利用已有网络,并避免物理交换机 MAC 表耗尽。

5.3 四大 CNI 方案横向对比

特性 Flannel Calico Macvlan Open vSwitch 直接路由
方案特性 通过 flannel0 虚拟设备管理 docker0 基于 BGP 的纯三层网络 基于内核 macvlan 技术 基于隧道的虚拟路由器 基于内核 vRouter
对底层网络要求 三层互通 三层互通 二层互通 三层互通 二层互通
配置难度 简单,基于 etcd 简单,基于 etcd 简单,直接用宿主网络,需仔细规划 IP 复杂,需手工配各节点 bridge 简单,需规划每个 Node 的 IP
网络性能 host-gw 优于 VXLAN BGP 模式最优,IPIP 略低 损耗可忽略 损耗较小 损耗较小
连通性限制 无 不支持 BGP 的网络环境无法使用 macvlan 容器无法与宿主机网络通信 无 无法大二层互通的环境不可用

拆到每种方案的核心机制:

Flannel :host-gw 走 Underlay,节点上写静态路由,目标 Pod 网段下一跳指向目标宿主机 IP,无任何封装、性能高,但要求底层能转发 Pod 网段路由(公有云大多不支持);VXLAN 走 Overlay,跨节点报文封装 UDP VXLAN 头,只要求宿主机 IP 连通,公有云通用,但有封包解包开销。Flannel 只负责连通,不提供 NetworkPolicy。

Calico :BGP 模式用 BGP 在节点间交换 Pod 路由,原生 IP 转发、性能最优,前提是网络允许发布 BGP 路由;底层不支持 BGP 时启用 IPIP/VXLAN 隧道。Calico 最大亮点是原生支持 NetworkPolicy。

Weave:默认 Overlay,节点间建 UDP 隧道封装 Pod 流量并自动发现节点。特色是内置 DNS、支持 NetworkPolicy、可混跑加密隧道;短板是隧道持续占 CPU,大规模性能弱于 Flannel VXLAN 与 Calico。

Macvlan:属于 Underlay。在宿主机物理网卡上虚拟多张独立网卡,每个 Pod 有独立 MAC,直接接入物理局域网,无隧道封装,延迟极低。限制有三:宿主机网卡不能开桥接;同一父网卡下的 macvlan 子接口之间无法直接互通(同宿主机 Pod 互通缺陷);IP 规划复杂易冲突。

场景选型可以记成一句话:

CNI 网络模型 是否封装 NetworkPolicy 适用场景
Flannel host-gw / VXLAN VXLAN 封装,host-gw 不封装 不支持 简单优先、无需 Pod 访问控制的中小集群
Calico BGP / IPIP / VXLAN IPIP/VXLAN 封装,BGP 不封装 原生支持 需要网络策略,自建机房或公有云通用
Weave Net UDP 隧道 Overlay UDP 封装 支持 测试与小规模集群,部署简单无需 etcd
Macvlan Underlay 二层直连 不封装 取决于配套 CNI Pod 需占用局域网独立 IP、低延迟业务

5.4 CNM 与 CNI:两套抽象模型

容器网络方案多(flannel、calico、kube-ovn、weave),容器平台也多(Kubernetes、OpenShift、rkt),因此需要一个抽象接口层,把"网络配置方案"与"容器平台方案"解耦。

CNM(Container Network Model)由 Docker 提出,在 libnetwork 项目中采用,按该模型开发的 driver 能与 docker daemon 协同工作。它把网络抽象成三类组件:

  • Sandbox:容器的网络栈,含 interface、路由表和 DNS 设置,标准实现是 Linux Network Namespace;一个 Sandbox 可包含来自不同 Network 的 Endpoint;
  • Endpoint:把 Sandbox 接入 Network,典型实现是 veth pair;一个 Endpoint 只能属于一个网络、一个 Sandbox;
  • Network:一组 Endpoint 的集合,同 Network 的 Endpoint 可直接通信,实现可以是 Linux Bridge、VLAN 等。

以 docker bridge driver 为例:默认网络 bridge 与自定义网络 my_net2 分别由 docker0、br-xxxx 两个 Linux Bridge 实现;每个容器有一个由 Network Namespace 实现的 Sandbox;每个容器一个由 veth pair 实现的 Endpoint,一端挂 Bridge,另一端是容器内的 eth0。

CNI (Container Network Interface)由 Google 与 CoreOS 联合制定,是 Kubernetes 采用的规范:定义容器与网络插件之间的通信标准。CNI 有两个概念------容器 (独立的 Linux 网络命名空间)与网络(拥有各自唯一 IP 的互联实体集合)。插件分两类:CNI Plugin 负责配置容器网络,IPAM Plugin 负责分配 IP,IPAM 作为 CNI 插件的一部分协同工作。

特点 CNM CNI
标准规范 Libnetwork cni
最小单元 容器 Pod
对守护进程的依赖 依赖 dockerd 不依赖任何守护进程
跨主机通信 依赖外部 KV 数据库 使用自身 KV 数据库
灵活程度 被 Docker 绑定 插件可随意替换

六、Calico 深入:IPIP 与 VXLAN,三种模式怎么选

6.1 两种隧道模式的封装差异

IPIP 是三层隧道:把内层 IP 包完整封装进外层 IP 包,虚拟网卡为 tunl0,封装结构是「外层 IP 头(节点 IP)+ 内层 IP 头(Pod IP)+ 应用数据」,只封装 IP,不携带二层帧。

VXLAN 是二层隧道:走 UDP,默认端口 4789,虚拟网卡为 vxlan.calico,封装结构是「外层 IP + UDP 头 + VXLAN 头(VNI)+ 完整二层帧」,保留完整二层,支持广播、组播。

项目 IPIP VXLAN
隧道类型 三层 IP 隧道 二层 UDP 隧道
传输载体 IP 协议号 4 UDP 4789
是否带二层 MAC 不带 完整二层帧
包头开销 较小 更大,MTU 需更小
云厂商限制 很多公有云封禁协议号 4,IPIP 直接不通 UDP 端口一般放行,兼容性更好
广播/组播 不支持 支持
Calico 配置 ipipMode: Always、vxlanMode: Never ipipMode: Never、vxlanMode: Always
故障排查 抓包简单,只有内外两层 IP 包结构更复杂
适用场景 私有机房、底层放行 IPIP 公有云、底层不放行 IPIP

选型判断:

  • 选 IPIP:私有物理机房且不拦截协议号 4;只需单播 Pod 通信;追求更小包头开销。缺点是公有云(阿里云/AWS 等)默认拦 IPIP,直接不通。
  • 选 VXLAN:公有云环境;需要二层广播能力;底层不支持透传协议号 4。缺点是头部更大、MTU 要调小、性能略低于 IPIP。

6.2 三种工作模式:Always / Never / CrossSubnet

ipipMode 与 vxlanMode 取值完全对称:

  1. Always(IPIP 默认) :所有跨节点 Pod 流量封装隧道包,路由下一跳指向 tunl0;节点只需三层可达,底层无需感知 Pod 网段;
  2. Never(BGP 原生 / Underlay):不封装,依靠 BGP 在节点间同步 Pod 网段路由,路由下一跳是节点物理网卡;要求节点间二层互通;
  3. CrossSubnet(混合):同子网节点之间不封装(走 BGP 原生),跨子网节点之间自动启用隧道封装。

必须记住的一点:无论是否开启隧道,Calico 都会运行 BGP 进程同步 Pod 网段;隧道开关只决定数据包是否封装。

模式选择:

  • Never:无封装、性能优,抓包能直接看到 Pod 原生 IP;
  • Always:节点分布在不同子网、无法二层互通;公有云部署;不想依赖底层支持 BGP;
  • CrossSubnet:企业机房节点跨多个子网,同网段直路由、跨网段自动走隧道------最省事也最常用。

6.3 查看当前工作模式

IPPool 是 Calico 控制隧道模式的核心资源。

bash 复制代码
# 方法 1:直接看 IPPool 资源
root@master30:~# kubectl get ippool -o yaml | egrep 'ipip|vxlan'

# 方法 2:看宿主机路由表的下一跳设备
root@master30:~# ip route | grep '/26 via'

判读规则(关键):路由条目末尾是 dev tunl0 → IPIP 隧道模式,数据包走 IPIP 封装;是 dev ens32(物理网卡)→ BGP 原生路由模式。

bash 复制代码
# IPIP 模式下的典型输出
10.224.19.0/26 via 10.1.8.31 dev tunl0 proto bird onlink
10.224.113.128/26 via 10.1.8.32 dev tunl0 proto bird onlink

6.4 修改工作模式(含备份与回滚)

风险提示:改 IPPool 会立刻改变集群跨节点 Pod 转发行为,生产环境必须在业务低峰操作并提前备份。

bash 复制代码
# 步骤 1:备份
root@master30:~# kubectl get ippool default-ipv4-ippool -o yaml > ippool-backup.yaml

# 步骤 2:编辑,修改 ipipMode / vxlanMode 字段
root@master30:~# kubectl edit ippool default-ipv4-ippool
bash 复制代码
root@master30:~# vim ippool-never.yaml
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: default-ipv4-ippool
spec:
  cidr: 10.224.0.0/16
  # 切换为 BGP 原生模式
  ipipMode: Never
  vxlanMode: Never
  natOutgoing: true
  nodeSelector: all()

root@master30:~# calicoctl apply -f ippool-never.yaml

配置下发后等 30--60 秒自动生效;长时间不生效可重启 calico-node:

bash 复制代码
root@master30:~# kubectl rollout restart daemonset calico-node -n kube-system
bash 复制代码
# 步骤 3:验证路由表已切到物理网卡
root@master30:~# ip route | grep '/26'
bash 复制代码
# 预期:下一跳设备从 tunl0 变成物理网卡
10.224.19.0/26 via 10.1.8.31 dev ens32 proto bird
10.224.113.128/26 via 10.1.8.32 dev ens32 proto bird

七、NetworkPolicy:把 Pod 变成"默认拒绝 + 白名单"

7.1 默认连通性与隔离模型

默认情况下集群网络是完全放通的:集群外部能访问集群内应用,集群内应用能访问外部主机,各个 namespace 之间没有任何隔离策略。要在 IP 与端口层面做控制,就用 NetworkPolicy。

Pod 有两种隔离方向,且默认都是"非隔离":

  • 出口 Egress :默认所有外向连接允许。一旦有 NetworkPolicy 选中该 Pod 且 policyTypes 含 Egress,该 Pod 的出口即被隔离,唯一允许的是各策略 egress 列表放行的连接;
  • 入口 Ingress :默认所有入站连接允许。一旦被选中且含 Ingress,唯一允许的入站是来自该 Pod 所在节点的连接 以及各策略 ingress 列表放行的连接。

三条必须记住的语义:

  1. 策略是相加的,不会冲突。适用于某方向的策略取并集,评估顺序不影响结果;
  2. 连接要通,源出口与目标入口必须同时放行,任一方不允许即失败;
  3. 模型是纯白名单,只能"添加允许",没有显式拒绝语句。
bash 复制代码
# 查看策略生效范围(POD-SELECTOR 是绑定对象,最容易设错)
root@master30:~# kubectl get netpol -n web

7.2 规约字段逐个拆解

bash 复制代码
root@master30:~# vim netpol-full-example.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  # 策略作用对象:选中带 role=db 标签的 Pod;空 {} 表示本 ns 全部 Pod
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress   # 管别人进来到这些 Pod
  - Egress    # 管这些 Pod 出去
  ingress:
  - from:
    # 来源一:按 IP 段,except 从大段里挖掉黑名单
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    # 来源二:按命名空间标签
    - namespaceSelector:
        matchLabels:
          project: myproject
    # 来源三:按同命名空间 Pod 标签
    - podSelector:
        matchLabels:
          role: frontend
    # 且只允许目标端口 TCP 6379
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

字段含义:

  • podSelector:策略作用在哪些 Pod 上(不是限制谁能访问,别搞反);
  • policyTypes:未指定时默认始终含 Ingress,有 egress 规则时补 Egress;
  • ingress:白名单规则列表,每条规则要同时匹配 from 和 ports 才放行;
  • egress:白名单规则列表,每条规则要同时匹配 to 和 port。

7.3 四种选择器(含最容易混淆的一条)

  1. podSelector:仅在本 NetworkPolicy 所在命名空间内选 Pod;
  2. namespaceSelector:选命名空间,其下所有 Pod 作为来源/目的地;
  3. namespaceSelector + podSelector :选特定命名空间中的特定 Pod。注意写在同一个数组元素里是**"与",分写成两个数组元素是"或"**:
bash 复制代码
root@master30:~# vim selector-and-vs-or.yaml
# 写法 A(与):来自 user=alice 命名空间中 role=client 的 Pod
ingress:
- from:
  - namespaceSelector:
      matchLabels:
        user: alice
    podSelector:
      matchLabels:
        role: client
bash 复制代码
root@master30:~# vim selector-or-example.yaml
# 写法 B(或):来自任意命名空间的 user=alice Pod,或者本命名空间 role=client 的 Pod
ingress:
- from:
  - namespaceSelector:
      matchLabels:
        user: alice
  - podSelector:
      matchLabels:
        role: client
  1. ipBlock :按 CIDR 选集群外部 IP,支持 except 挖掉子段。这里的 except 必须是 cidr 的子集,写外面的段不会报错但也不生效。

重要提醒:集群的入站出站机制常会重写源/目标 IP(kube-proxy、LoadBalancer 等),且 NetworkPolicy 处理发生在重写之前还是之后不确定。入站时被作用的"源 IP"可能是 LoadBalancer 地址或 Pod 所在节点 IP,而不是客户端真实 IP。

7.4 实验环境:三个 Pod、两个命名空间

bash 复制代码
root@master30:~# kubectl create ns web
root@master30:~# kubectl create ns laoma
root@master30:~# kubectl config set-context --current --namespace web

# web 命名空间:web1 / web2 / test
root@master30:~# kubectl run web1 --image=hub.laoma.cloud/library/nginx --image-pull-policy=IfNotPresent
root@master30:~# kubectl exec -it web1 -- bash -c 'echo Hello web1 > /usr/share/nginx/html/index.html'

root@master30:~# kubectl run web2 --image=hub.laoma.cloud/library/nginx --image-pull-policy=IfNotPresent
root@master30:~# kubectl exec -it web2 -- bash -c 'echo Hello web2 > /usr/share/nginx/html/index.html'

root@master30:~# kubectl expose pod web1 --port=80 --target-port=80 --type=NodePort
root@master30:~# kubectl expose pod web2 --port=80 --target-port=80 --type=NodePort

root@master30:~# kubectl run test --image=hub.laoma.cloud/library/nginx

# laoma 命名空间:同名测试 Pod
root@master30:~# kubectl run test -n laoma --image=hub.laoma.cloud/library/nginx

打策略前的基线(全部应通):

bash 复制代码
# 同命名空间互访
root@master30:~# kubectl exec test -- curl -s web1
root@master30:~# kubectl exec test -- curl -s web2

# 跨命名空间访问
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web
root@master30:~# kubectl exec -n laoma test -- curl -s web2.web

# 集群内访问 ClusterIP
curl 10.103.36.143

# 集群外访问 NodePort
curl http://10.1.8.30:30790

7.5 按 Pod 标签限定

示例 1:只允许同命名空间 run=test 的 Pod 访问 run=web1 的 80 端口。

bash 复制代码
root@master30:~# vim netpol-pod.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector:
    matchLabels:
      run: web1          # 选中目标Pod:标签run=web1,是被保护的对象
  policyTypes:
  - Ingress              # 只管控入站流量(别人访问web1)
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test      # 允许来源:标签run=test的Pod
    ports:
    - protocol: TCP
      port: 80           # 只允许访问TCP 80端口

root@master30:~# kubectl apply -f netpol-pod.yaml
root@master30:~# kubectl get netpol

验证结果与判读:

bash 复制代码
# test 可以访问 web1
root@master30:~# kubectl exec test -- curl -s web1

# 其他 ns 的 pod 一律超时(exit code 28)
root@master30:~# kubectl exec test -n laoma -- curl -s --connect-timeout 3 web1.web

# 改掉 test 的标签后,同 ns 也访问不了了
root@master30:~# kubectl label pod test run=web2 --overwrite
root@master30:~# kubectl get pods --show-labels
root@master30:~# kubectl exec test -- curl -s --connect-timeout 3 web1

这一步是理解 NetworkPolicy 的关键:标签一改,白名单立即失效,证明策略绑定的是标签而不是 Pod 名字。超时而不是拒绝,是因为策略默认丢弃。

示例 2:允许同命名空间所有 Pod 访问 run=web1 的 80 端口。

bash 复制代码
root@master30:~# vim netpol-ns-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    # 空 podSelector 表示本命名空间全部 Pod
    - podSelector: {}
    ports:
    - protocol: TCP
      port: 80

root@master30:~# kubectl apply -f netpol-ns-all.yaml
root@master30:~# kubectl exec test -- curl -s web1
root@master30:~# kubectl exec web2 -- curl -s web1

示例 3:允许 run=test 访问本命名空间所有 Pod 的 80 端口 ------注意 podSelector: {} 挪到了外层,作用对象变成全部 Pod。

bash 复制代码
root@master30:~# vim netpol-all-target.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 80

7.6 按命名空间标签限定

示例 1:只允许带 project=myproject 标签的命名空间访问 web1。

bash 复制代码
root@master30:~# vim netpol-ns.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web        # 策略本身属于 web 命名空间
spec:
  podSelector:
    matchLabels:
      run: web1         # 在 web 命名空间里,选中标签 run=web1 的Pod(被保护对象)
  policyTypes:
  - Ingress             # 管控入站流量
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          project: myproject  # 来源条件:命名空间带有标签 project:myproject
    ports:
    - protocol: TCP
      port: 80                 # 允许访问TCP 80端口

root@master30:~# kubectl apply -f netpol-ns.yaml

验证逻辑:打策略后 web 与 laoma 两个 ns 都访问不了;给 laoma 打上标签后立即可通。

bash 复制代码
# 两个 ns 都失败
root@master30:~# kubectl exec test -- curl -s web1
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web

# 给 ns 打标签后恢复
root@master30:~# kubectl label namespaces laoma project=myproject
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web

示例 2:允许所有命名空间访问 web1 的 80 端口。

bash 复制代码
root@master30:~# vim netpol-ns-any.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 80

root@master30:~# kubectl apply -f netpol-ns-any.yaml
root@master30:~# kubectl exec test -- curl -s web1
root@master30:~# kubectl exec -n laoma test -- curl -s web1.web

7.7 不限端口、端口范围与多条件

不写 ports = 放行全部端口,此时连 ICMP 也能通,但 Service 层仍然只暴露 80。

bash 复制代码
root@master30:~# vim netpol-all-ports.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
  # 注意:这里故意不写 ports

root@master30:~# kubectl apply -f netpol-all-ports.yaml
root@master30:~# kubectl get pods -o wide
bash 复制代码
# Pod IP 可 ping 通
root@master30:~# kubectl run --rm -it ubuntu -l run=test --image ubuntu -- bash
# 容器内执行
apt update && apt install -y iputils-ping
ping -c1 10.224.193.67
# 但 ping Service 不通,因为 Service 只开放 80 端口
ping web1 -c1
exit

端口范围用 endPort(要求 CNI 支持,Calico 支持):

bash 复制代码
cat <<'EOF' > netpol-port-range.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 32000
      endPort: 32768
EOF

多条件:满足任意一个 from 即放行(或关系)。

bash 复制代码
cat <<'EOF' > netpol-multi-from.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 10.1.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 80
EOF

7.8 五套默认策略模板(复制即用)

bash 复制代码
# 1) 默认允许所有入站
cat <<'EOF' > allow-all-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - {}
EOF

# 2) 默认拒绝所有入站(不影响出站隔离行为)
cat <<'EOF' > default-deny-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress
EOF

# 3) 默认允许所有出站
cat <<'EOF' > allow-all-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - {}
EOF

# 4) 默认拒绝所有出站(不影响入站隔离行为)
cat <<'EOF' > default-deny-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress
EOF

# 5) 双向全拒(零信任基线)
cat <<'EOF' > default-deny-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
EOF

生产建议:先用第 5 套做兜底全拒,再按业务逐条加放行规则,而不是反过来"先全通再补拒绝"------NetworkPolicy 根本没有拒绝语句。

7.9 NetworkPolicy 做不到的事

  • 特定于节点的策略;
  • 基于名字(DNS 域名)的策略;
  • 真正意义上的"全局默认策略"(策略本身是命名空间级的,podSelector: {} 只覆盖本 ns);
  • 显式拒绝能力------模型默认拒绝,唯一能力是添加允许;
  • 禁止本地回环或指向宿主机的流量:Pod 无法阻塞 localhost 访问,也无法禁止来自所在节点的访问请求。

7.10 清理

bash 复制代码
root@master30:~# kubectl delete ns web laoma

八、高频踩坑清单(实测总结)

现象 真正原因 处理办法
NetworkPolicy 写了却完全不拦 CNI 是 Flannel,原生不支持 NetworkPolicy 换成 Calico / Cilium,kubectl get pods -n kube-system 先确认
策略 YAML 正确,但目标 Pod 没被约束 podSelector 标签与 Pod 实际标签不一致 kubectl get pod --show-labels 逐字核对,run:test 与 app:test 不是一回事
容器内 curl 127.0.0.1 通,外部访问超时 服务只监听 127.0.0.1,或策略把源 IP 拦了 ss -tlnp 确认监听 0.0.0.0:80;再核对策略源段
访问 NodePort 被策略误拦 kube-proxy 做 SNAT,Pod 看到的源 IP 变成节点 IP,原客户端 IP 丢失 直接 curl PodIP 做 ipBlock 实验;或给 Service 加 externalTrafficPolicy: Local 保留源 IP
ipBlock 放行了客户端网段还是不通 访问路径经过 ClusterIP/NodePort,源 IP 已被重写 同上,实验一律用 PodIP 直连
Ingress 改完不生效 漏写 ingressClassName,或控制器未抓到变更 kubectl describe ingress 看 Ingress Class 与 Events 里的 Sync
正则路径不匹配 忘开 use-regex: "true",或 pathType 写成 Prefix 正则必须配 ImplementationSpecific
路径裁剪后接口 404 后端返回绝对路径重定向,前缀丢了 后端配 base path,或补 proxy-redirect 类注解
kubectl label ns web project=- 报 invalid label value 删除标签写法是 key-,不能带等号 用 kubectl label ns web project-,或用 patch 置 null
标签删掉又自动回来 有策略/控制器或准入 Webhook 在回写 kubectl patch ns web -p '{"metadata":{"labels":{"project":null}}}' 验证,再查自动化控制器

九、总结

三块内容其实是同一条链路上的三个层次:

  • Ingress 解决"外部请求进来后,按域名和路径分给谁"------七层入口;
  • Calico 解决"Pod 的包跨节点怎么走"------三层/二层数据面,Underlay 拼性能、Overlay 拼兼容;
  • NetworkPolicy 解决"谁能访问谁"------在 Calico 数据面上执行的白名单规则。

排障时按这个顺序走:先确认流量从哪进(Ingress / NodePort / PodIP)、再看源 IP 有没有被 SNAT 改写、最后才怀疑策略本身。绝大多数"策略不生效"都不是 YAML 写错,而是观察到的源 IP 和你以为的不一样。

相关推荐
2601_958736991 小时前
教师课件能不能做虚拟产品?一个被低估的方向
网络
承渊政道2 小时前
新增一台服务器时还要重新装Nginx?Ansible只改hosts目标列表,同一份Playbook继续执行
运维·nginx·ansible·内网穿透·cpolar
KIDULT°2 小时前
K8s 网络进阶实战:Ingress-Nginx、Calico 与 NetworkPolicy 实验 + 排坑记录
网络·nginx·云原生·kubernetes
倔强的石头1062 小时前
【Linux指南】进程通信系列(四):命名管道 FIFO —— 让陌生进程按名字见面
linux·服务器·网络
updayday8542 小时前
电脑换了硬盘、加了内存,资产台账却没更新 - Ping64 硬件资产管理
大数据·网络·数据库·安全·智能路由器
fengkai45452 小时前
7、Kubernetes的Ingress&Network&Scheduler
运维·kubernetes·k8s·ingress·network·scheduler
yyuuuzz14 小时前
跨境站点日志时区混乱的排查记录
运维·服务器·网络·云计算·aws·1024程序员节
GlobalInfo15 小时前
全球及中国风电叶片检测机器人行业研究报告(2026版):市场现状、行业前景与占有率分析
运维·网络·人工智能·机器人
运维行者_16 小时前
网络监控工具怎么选?从设备监控到智能运维的能力清单
运维·服务器·开发语言·网络·人工智能·支持向量机·php