09.kube-proxy、Ingress和Network Policy

2026-04-28

复习和预习

昨天课堂内容

  1. CronJob
  2. 服务发现
  3. 服务发布
  4. 金丝雀发布

课前复习

默写

今天课堂内容

  1. kube-proxy
  2. Ingress
  3. Network Policy

kube-proxy

kube-proxy 是 K8s 服务于 Pod 网络的核心组件,负责实现 Service(服务)Endpoint(后端 Pod) 的流量转发与负载均衡。

工作模式类型

kube-proxy 工作模式有以下几种:

模式 地位 性能 适用场景 特点
iptables 默认模式 中(服务数 < 1000) 小规模集群、环境稳定 依赖内核 netfilter,规则多时有性能损耗
IPVS 推荐模式 极高(服务数 10w+) 中大规模生产环境 基于内核 IPVS,哈希表查找,性能碾压 iptables
Userspace 老旧/废弃 仅测试、兼容旧版 全用户态转发,性能最差,K8s 1.25+ 已移除

✅ 生产环境必选 IPVS 模式,配合 Calico 网络插件,性能与稳定性最佳。

工作模式切换

查看工作模式

bash 复制代码
[root@master30 ~]# kubectl get cm -n kube-system kube-proxy -o yaml|grep mode
    mode: ""
# mode值为空。

# 查看pod/kube-proxy日志
[root@master30 ~]# kubectl get pods -n kube-system -l k8s-app=kube-proxy -o name
pod/kube-proxy-9xfwh
pod/kube-proxy-sdflw
pod/kube-proxy-vkfmm

[root@master30 ~]# kubectl logs -n kube-system kube-proxy-9xfwh |grep Using
I0415 13:03:28.380643       1 server.go:511] "Using lenient decoding as strict decoding failed" err=<
I0415 13:03:28.380796       1 server_linux.go:69] "Using iptables proxy"
I0415 13:03:28.473406       1 server_linux.go:165] "Using iptables Proxier"

# 输出内容"Using iptables proxy",表明默认使用iptables

修改工作模式

bash 复制代码
# 步骤 1:编辑 kube-proxy 配置 ConfigMap
[root@master30 ~]# kubectl edit configmap -n kube-system kube-proxy
# 找到并修改 `mode` 字段为相应的值例如ipvs。
....
    metricsBindAddress: ""
    mode: "ipvs"
....

# 步骤 2:重启 kube-proxy DaemonSet
[root@master30 ~]# kubectl rollout restart daemonset -n kube-system kube-proxy

# 步骤 3:验证模式切换
[root@master30 ~]# kubectl get pods -n kube-system -l k8s-app=kube-proxy -o name
pod/kube-proxy-72b7j
pod/kube-proxy-csxlh
pod/kube-proxy-x2stv

[root@master30 ~]# kubectl logs -n kube-system kube-proxy-72b7j |grep Using
I0415 13:22:14.052593       1 server_linux.go:233] "Using ipvs Proxier"

# 输出内容"Using ipvs Proxier",表明使用ipvs

IPVS 模式

IPVS 代理模式基于 netfilter 回调函数,类似于 iptables 模式, 但它使用哈希表作为底层数据结构,在内核空间中生效。 这意味着 IPVS 模式下的 kube-proxy 比 iptables 模式下的 kube-proxy 重定向流量的延迟更低,同步代理规则时性能也更好。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。

IPVS 为将流量均衡到后端 Pod 提供了更多选择:

  • rr:轮询
  • lc:最少连接(打开连接数最少)
  • dh:目标地址哈希
  • sh:源地址哈希
  • sed:最短预期延迟
  • nq:最少队列

工作原理

kube-proxy 在宿主机内核中创建 IPVS 虚拟服务器 ,并将后端 Pod 作为 Real Server 注册。IPVS 基于 哈希表 存储转发规则,查找效率为 O(1)。流量到达后,IPVS 根据配置的调度算法直接将流量转发到后端 Pod,绕过了复杂的 iptables 规则链。

通信流程图
访问 Service IP:Port
哈希查找+调度算法
直接路由/隧道
客户端 Pod
宿主机网卡
内核 IPVS 虚拟服务器
选择最优后端 Pod
后端 Pod IP:Port
响应流量直接返回

核心优势

  • 支持多种高级调度算法(轮询 rr、加权轮询 wrr、最少连接 lc 等)。
  • 性能与服务数量无关,支持十万级服务规模。
  • 内置健康检查(与 K8s Endpoint 联动)。

验证原理

前置准备
  1. 已安装 IPVS 依赖(ipvsadmipset)并加载内核模块。
  2. kube-proxy 已切换为 IPVS 模式。
验证步骤
1. 创建测试资源
bash 复制代码
# 创建一个 nginx Deployment
[root@master30 ~]# kubectl create deployment web --image=nginx --replicas=3
[root@master30 ~]# kubectl get pods -o wide | awk '{print $1,$6}'
NAME IP
web-7c56dcdb9b-5r9jm 10.224.113.165
web-7c56dcdb9b-84bhr 10.224.113.164
web-7c56dcdb9b-nswcm 10.224.19.38

# 设置 pod 主页内容为为自己的pod名称
[root@master30 ~]# \
for pod in $( kubectl get pods -o custom-columns=NAME:.metadata.name --no-headers)
do
  kubectl exec $pod -- bash -c "echo $pod > /usr/share/nginx/html/index.html"
done

# 验证主页内容
[root@master30 ~]# curl http://10.224.113.165
web-7c56dcdb9b-5r9jm
[root@master30 ~]# curl http://10.224.113.164
web-7c56dcdb9b-84bhr
[root@master30 ~]# curl http://10.224.19.38
web-7c56dcdb9b-nswcm

# 创建Service
[root@master30 ~]# kubectl expose deployment web --port=80
[root@master30 ~]# kubectl get svc web 
NAME   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
web    ClusterIP   10.103.143.120   <none>        80/TCP    2m38s
2. 查看 IPVS 规则

在任意集群节点执行,这里在master30节点执行,查看 kube-proxy 创建的 IPVS 虚拟服务:

bash 复制代码
# 列出虚拟服务器
[root@master30 ~]# ipvsadm -Lnt 10.103.143.120:80

看到类似如下的记录,其中 10.97.240.1 是 Service IP,80 是 Service Port:

ini 复制代码
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.103.143.120:80 rr
  -> 10.224.19.38:80              Masq    1      0          0         
  -> 10.224.113.164:80            Masq    1      0          0         
  -> 10.224.113.165:80            Masq    1      0          0 

结论:IPVS 已成功为 Service 创建了虚拟服务,并绑定了所有后端 Pod。

3. 验证负载均衡效果

在集群内或外部访问 Service,观察流量是否分发到不同 Pod:

bash 复制代码
# 连续访问 60 次
[root@master30 ~]# for i in {1..60}; do curl -s 10.103.143.120; done| sort | uniq -c
     20 web-7c56dcdb9b-5r9jm
     20 web-7c56dcdb9b-84bhr
     20 web-7c56dcdb9b-nswcm

预期输出:会看到不同的 Pod 名称各自出现20次,证明 IPVS 轮询(rr)算法生效。

提示:不要清理资源,后续iptable模式继续使用。

调度算法选择

  • rr(轮询):简单均衡,节点配置一致时使用

  • wrr(加权轮询):最推荐,适合通用业务、Web、API、网关。

    wrr 权重来源:1. 与 Pod 的 resources.requests.cpu/memory 成正比;2. ipvsadm命令临时设置,只适合临时测试。

  • lc(最少连接):适合长连接、WebSocket、游戏服务

  • sh(源地址哈希):配合 Service sessionAffinity: ClientIP 使用

更改调度算法:

bash 复制代码
[root@master30 ~]# kubectl edit configmap kube-proxy -n kube-system
......
    ipvs:
      excludeCIDRs: null
      minSyncPeriod: 0s
      scheduler: "wrr"
.......
    mode: "ipvs"
.......

# 重启 kube-proxy
[root@master30 ~]# kubectl rollout restart ds kube-proxy -n kube-system

最佳配置

yaml 复制代码
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs

# ========== IPVS 核心调优 ==========
ipvs:
  # 调度算法(生产推荐:rr / wrr / lc 三选一)
  scheduler: "rr"

  # 会话保持时间(秒),0 表示关闭
  tcpTimeout: 900
  tcpFinTimeout: 120
  udpTimeout: 300

  # 排除 127.0.0.1 负载均衡(必须开启)
  excludeCIDRs:
  - "127.0.0.1/32"

  # 严格arp,避免集群访问异常(必须开启)
  strictARP: true

# ========== 性能优化 ==========
# 最大打开文件数(高并发必调)
oomScoreAdj: -999

# Iptables 规则优化
iptables:
  minSyncPeriod: 5s
  syncPeriod: 30s

# 日志级别
logging:
  format: text
  verbosity: 2

iptables 模式

工作原理

kube-proxy 默认工作模式是 iptables 模式。

kube-proxy 监听 Service 和 Endpoint 变化,在宿主机内核中动态生成 iptables 规则链(KUBE-SERVICES、KUBE-SEP-XXX 等)。当流量进入宿主机时,通过 netfilter 框架逐条匹配 iptables 规则,实现 DNAT(目标地址转换)和负载均衡。

通信流程图
访问 Service IP:Port
匹配 Service 规则
DNAT 转换
客户端 Pod
宿主机网卡
内核 iptables 规则链
随机选择后端 Endpoint IP
后端 Pod IP:Port
响应流量原路返回

核心缺点

  • 每增加一个 Service 或 Endpoint,都会新增/修改 iptables 规则。
  • 当服务数量超过 1000 时,规则链膨胀,CPU 占用飙升,延迟显著增加。

验证原理

前置准备
  1. kube-proxy 已切换为 iptables 模式。
验证步骤
1. 创建测试资源

使用ipvs实验环境准备的资源。

2. 查看防火墙规则

步骤1:获取防火墙规则。

在任意集群节点执行,这里在master30节点执行,保存防火墙规则:

bash 复制代码
[root@master30 ~]# iptables-save > iptables.list

步骤2:分析 svc-nginx 规则。

bash 复制代码
[root@master30 ~]# cat iptables.list |grep 10.103.143.120
-A KUBE-SERVICES -d 10.103.143.120/32 -p tcp -m comment --comment "services/web cluster IP" -m tcp --dport 80 -j KUBE-SVC-7D76YWGERGEPC4GC
-A KUBE-SVC-7D76YWGERGEPC4GC ! -s 10.224.0.0/16 -d 10.103.143.120/32 -p tcp -m comment --comment "services/web cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ

分析防火墙规则:

  • 第一条防火墙规则 :在 natKUBE-SERVICES 服务总入口链中,匹配目的 IP 为 10.103.143.120/32、协议 TCP、目的端口 80 的报文,通过注释标识为 services/web 的 ClusterIP 服务,并跳转至该服务专属调度链 KUBE-SVC-7D76YWGERGEPC4GC
  • 第二条防火墙规则 :则在服务专属链 KUBE-SVC-7D76YWGERGEPC4GC 中,匹配源地址非 Pod 网段 10.224.0.0/16、目的 IP 10.103.143.120/32、协议 TCP、目的端口 80 的报文,注释标识为 services/web cluster IP,并跳转至 KUBE-MARK-MASQ 进行 SNAT 标记

步骤3 :进一步追踪链路KUBE-SVC-7D76YWGERGEPC4GC

bash 复制代码
[root@master30 ~]# cat iptables.list | grep KUBE-SVC-7D76YWGERGEPC4GC
:KUBE-SVC-7D76YWGERGEPC4GC - [0:0]
-A KUBE-SERVICES -d 10.103.143.120/32 -p tcp -m comment --comment "services/web cluster IP" -m tcp --dport 80 -j KUBE-SVC-7D76YWGERGEPC4GC
-A KUBE-SVC-7D76YWGERGEPC4GC ! -s 10.224.0.0/16 -d 10.103.143.120/32 -p tcp -m comment --comment "services/web cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SVC-7D76YWGERGEPC4GC -m comment --comment "services/web -> 10.224.113.164:80" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-HYZM2VM7RCC7M2HX
-A KUBE-SVC-7D76YWGERGEPC4GC -m comment --comment "services/web -> 10.224.113.165:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-CHPZVFCOFL4YLCYM
-A KUBE-SVC-7D76YWGERGEPC4GC -m comment --comment "services/web -> 10.224.19.38:80" -j KUBE-SEP-HM7RLUR2RXSQ3D6F

分析防火墙规则:

  • **第一条防火墙规则:**已分析过。
  • **第二条防火墙规则:**已分析过。
  • **第三条防火墙规则:**在服务调度链 KUBE-SVC-7D76YWGERGEPC4GC 中,对所有协议与地址的访问报文,以 1/3 随机概率 将其跳转至后端端点链 KUBE-SEP-HYZM2VM7RCC7M2HX,注释标识该端点对应 services/web 服务后端 10.224.113.164:80。
  • **第四条防火墙规则:**在服务调度链 KUBE-SVC-7D76YWGERGEPC4GC 中,对未被前序规则匹配的报文,以 剩余流量 1/2 随机概率 (总概率 1/3)跳转至后端端点链 KUBE-SEP-CHPZVFCOFL4YLCYM,注释标识对应 services/web 后端 10.224.113.165:80。
  • **第五条防火墙规则:**在服务调度链 KUBE-SVC-7D76YWGERGEPC4GC 中,对未被前序概率规则匹配的报文,无条件跳转 (总概率 1/3)至后端端点链 KUBE-SEP-HM7RLUR2RXSQ3D6F,注释标识对应 services/web 后端 10.224.19.38:80。

步骤4:进一步追踪上面最后三条规则目标链路。

bash 复制代码
[root@master30 ~]# cat iptables.list | grep -e KUBE-SEP-HYZM2VM7RCC7M2HX -e KUBE-SEP-CHPZVFCOFL4YLCYM -e KUBE-SEP-HM7RLUR2RXSQ3D6F | grep DNAT
-A KUBE-SEP-CHPZVFCOFL4YLCYM -p tcp -m comment --comment "services/web" -m tcp -j DNAT --to-destination 10.224.113.165:80
-A KUBE-SEP-HM7RLUR2RXSQ3D6F -p tcp -m comment --comment "services/web" -m tcp -j DNAT --to-destination 10.224.19.38:80
-A KUBE-SEP-HYZM2VM7RCC7M2HX -p tcp -m comment --comment "services/web" -m tcp -j DNAT --to-destination 10.224.113.164:80

分析防火墙规则:

  • 第一条防火墙规则:在后端端点链 KUBE-SEP-HM7RLUR2RXSQ3D6F 中,匹配TCP 协议 报文,注释标识为 services/web 服务,执行DNAT 目标地址转换 ,将报文目的地址修改为 10.224.19.38:80
  • 第二条防火墙规则:在后端端点链 KUBE-SEP-HYZM2VM7RCC7M2HX 中,匹配TCP 协议 报文,注释标识为 services/web 服务,执行DNAT 目标地址转换 ,将报文目的地址修改为 10.224.113.164:80
  • 第三条防火墙规则:在后端端点链 KUBE-SEP-CHPZVFCOFL4YLCYM 中,匹配TCP 协议 报文,注释标识为 services/web 服务,执行DNAT 目标地址转换 ,将报文目的地址修改为 10.224.113.165:80
3. 总结

全链路流程图:

  1. 入口:访问 ClusterIP
  2. 分流 :进入 KUBE-SERVICES
  3. 服务调度 :进入 KUBE-SVC-XXX(SNAT + 负载均衡)
  4. 端点转发 :进入 KUBE-SEP-XXX
  5. 最终到达:后端 Pod

客户端访问

ClusterIP: 10.103.143.120:80
PREROUTING

nat 表
KUBE-SERVICES

服务总入口
KUBE-SVC-7D76YWGERGEPC4GC

services/web 专属调度链
SNAT 标记:非 Pod 网段流量

→ KUBE-MARK-MASQ
概率 1/3 负载均衡
概率 1/3 负载均衡
默认 1/3 负载均衡
KUBE-SEP-HYZM2VM7RCC7M2HX
KUBE-SEP-CHPZVFCOFL4YLCYM
KUBE-SEP-HM7RLUR2RXSQ3D6F
DNAT 转发

→ Pod: 10.224.113.164:80
DNAT 转发

→ Pod: 10.224.113.165:80
DNAT 转发

→ Pod: 10.224.19.38:80

Userspace 模式

kube-proxy 在用户态监听 Service 端口,接收流量后,通过本地代理转发到后端 Pod。全用户态处理,性能极低。

通信流程图
访问 Service IP:Port
客户端 Pod
宿主机 kube-proxy 用户态进程
负载均衡选择后端 Pod
建立连接转发流量
后端 Pod

现状:K8s 1.25+ 版本已彻底移除该模式,不再支持。

环境清理

bash 复制代码
[root@master30 ~]# kubectl delete ns services

Ingress

学习参考:Ingress

环境准备

bash 复制代码
[root@master30 ~]# kubectl create ns ingress
[root@master30 ~]# kubectl config set-context --current --namespace ingress

Ingress 介绍

Ingress 可为 Service 提供外部可访问的 URL、对其流量作负载均衡、 终止 SSL/TLS,以及基于名称的虚拟托管等能力。 Ingress 控制器 负责完成 Ingress 的工作,通常会使用某个负载均衡器、边缘路由器或其他前端来帮助处理流量。

下面是 Ingress 的一个简单示例,可将所有流量都发送到同一 Service:

Ingress 不会随意公开端口或协议, 将 HTTP 和 HTTPS 以外的服务开放到 Internet 时,通常使用 Service.Type=NodePortService.Type=LoadBalancer 类型的 Service。

Ingress 控制器

为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

与作为 kube-controller-manager 可执行文件的一部分运行的其他类型的控制器不同, Ingress 控制器不是随集群自动启动的。 基于此页面,你可选择最适合你的集群的 ingress 控制器实现。

Kubernetes 作为一个项目,目前支持和维护 AWSGCENginx Ingress 控制器。

ingress-nginx 工作流程

本次实验使用 ingress-nginx 控制器。

项目地址:ingress-nginx

ingress-nginx 工作流程:

  1. 先部署 Ingress Controller 实体(相当于前端--Nginx)
  2. 然后再创建 Ingress (k8s 资源--相当于 Nginx 配置)
  3. Ingress Controller 与 kubernetes apiserver交互,动态的获取集群中的ingress规则。解析ingress规则,生成proxy服务的配置,比如nginx配置。
  4. 再写到 ingress proxy 的pod中,如果pod运行的是nginx服务,就生成nginx配置,并放到/etc/nginx/nginx.conf中。
  5. 然后reload服务。

Ingress 本质是7层http/https代理。

ingress-nginx 部署前提

项目地址: kubernetes/ingress-nginx

  1. 本次环境使用负载均衡器处理流量,提前部署好 LoadBalancer,例如 metallb。

  2. 你必须部署一个 Ingress 控制器 才能满足 Ingress 的要求,例如 ingress-nginx

ingress-nginx 版本

Supported Ingress-NGINX version k8s supported version Alpine Version Nginx Version Helm Chart Version
🔄 v1.11.2 1.30, 1.29, 1.28, 1.27, 1.26 3.20.0 1.25.5 4.11.2
🔄 v1.11.1 1.30, 1.29, 1.28, 1.27, 1.26 3.20.0 1.25.5 4.11.1
🔄 v1.11.0 1.30, 1.29, 1.28, 1.27, 1.26 3.20.0 1.25.5 4.11.0
🔄 v1.10.2 1.30, 1.29, 1.28, 1.27, 1.26 3.20.0 1.25.5 4.10.2
🔄 v1.10.1 1.30, 1.29, 1.28, 1.27, 1.26 3.19.1 1.25.3 4.10.1
🔄 v1.10.0 1.29, 1.28, 1.27, 1.26 3.19.1 1.25.3 4.10.0
...

ingress-nginx 部署

部署方法参考:https://kubernetes.github.io/ingress-nginx/deploy/

bash 复制代码
[root@master30 ~]# tar -xf ingress-nginx-controller-v1.11.2.tar.gz

ingress-nginx 部署文件 deploy.yaml文件包涵4个部分:

  1. 创建一个独立的命名空间 ingress-nginx

  2. 创建 ConfigMap

    ConfigMap是存储通用的配置变量的,类似于配置文件,使用户可以将分布式系统中用于不同模块的环境变量统一到一个对象中管理;而它与配置文件的区别在于它是存在集群的"环境"中的,并且支持K8S集群中所有通用的操作调用方式。

    创建pod时,对configmap进行绑定,pod内的应用可以直接引用ConfigMap的配置。相当于configmap为应用/运行环境封装配置。

    pod使用ConfigMap,通常用于:设置环境变量的值、设置命令行参数、创建配置文件。

  3. Ingress的RBAC授权的控制,其创建了Ingress用到的ServiceAccount、ClusterRole、Role、RoleBinding、ClusterRoleBinding

  4. 创建ingress-controller。前面提到过,ingress-controller的作用是将新加入的Ingress进行转化为httpd的配置

bash 复制代码
# 查看资源使用的镜像
[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

# 创建 Ingress
[root@master30 ~]# kubectl apply -f ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml

注意registry.k8s.io 镜像仓库中镜像无法直接下载,需要配置加速。我们也可以从 这里 获取。

查看部署的资源

bash 复制代码
[root@master30 ~]# kubectl get all -n ingress-nginx 
NAME                                           READY   STATUS      RESTARTS   AGE
pod/ingress-nginx-admission-create-xhvp9       0/1     Completed   0          8m14s
pod/ingress-nginx-admission-patch-j47tm        0/1     Completed   2          8m14s
pod/ingress-nginx-controller-596db54d7-cgbdd   1/1     Running     0          8m14s

NAME                                         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller             LoadBalancer   10.107.255.83   10.1.8.40     80:30973/TCP,443:32344/TCP   14s
service/ingress-nginx-controller-admission   ClusterIP      10.106.37.158   <none>        443/TCP                      8m15s

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           8m14s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-596db54d7   1         1         1       8m14s

NAME                                       COMPLETIONS   DURATION   AGE
job.batch/ingress-nginx-admission-create   1/1           2m26s      8m14s
job.batch/ingress-nginx-admission-patch    1/1           2m28s      8m14s

Ingress 使用

环境准备

站点 webapp01
bash 复制代码
# 创建Deployment
[root@master30 ~]# kubectl create deployment webapp01 --image=docker.io/library/httpd --replicas=2

# 查看pod标签
[root@master30 ~]# kubectl get pod -L app 
NAME                        READY   STATUS    RESTARTS   AGE   APP
webapp01-57b8567f7c-kq56c   1/1     Running   0          18s   webapp01
webapp01-57b8567f7c-vzqj7   1/1     Running   0          18s   webapp01

# 准备pod主页内容
[root@master30 ~]# kubectl exec -it webapp01-57b8567f7c-kq56c -- bash
root@web-96d5df5c8-57qdk:/# echo hello webapp01 pod1 > htdocs/index.html
root@web-96d5df5c8-57qdk:/# mkdir htdocs/laoma     
root@web-96d5df5c8-57qdk:/# echo hello webapp01 laoma1 > htdocs/laoma/index.html
root@web-96d5df5c8-57qdk:/# exit

[root@master30 ~]# kubectl exec -it webapp01-57b8567f7c-vzqj7 -- bash
root@web-96d5df5c8-whb2v:/# echo hello webapp01 pod2  > htdocs/index.html
root@web-96d5df5c8-whb2v:/# mkdir htdocs/laoma
root@web-96d5df5c8-whb2v:/# echo hello webapp01 laoma2 > htdocs/laoma/index.html
root@web-96d5df5c8-whb2v:/# exit

[root@master30 ~]# kubectl expose deployment webapp01 --port=80 --target-port=80
站点 webapp02
bash 复制代码
# 创建Deployment
[root@master30 ~]# kubectl create deployment webapp02 --image=docker.io/library/httpd --replicas=2

# 查看pod标签
root@master30:~/ingress# kubectl get pod --show-labels|grep webapp02
webapp02-598c447b5b-4j76n   1/1     Running   0          2m33s   app=webapp02,pod-template-hash=598c447b5b
webapp02-598c447b5b-dddwh   1/1     Running   0          2m33s   app=webapp02,pod-template-hash=598c447b5b

# 准备pod主页内容
[root@master30 ~]# kubectl exec -it webapp02-598c447b5b-4j76n -- bash
root@web-96d5df5c8-57qdk:/# echo hello webapp02 pod1 > htdocs/index.html
root@web-96d5df5c8-57qdk:/# exit

[root@master30 ~]# kubectl exec -it webapp02-598c447b5b-dddwh -- bash
root@web-96d5df5c8-whb2v:/# echo hello webapp02 pod2 > htdocs/index.html
root@web-96d5df5c8-whb2v:/# exit

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

Ingress 规则

Ingress 规则示例

示例1: 没有rule的Ingress规则

指定一个没有rule的defaultBackend的方式,所有发送给该IP的流量都被转发到了defaultBackend 所列的Kubernetes service上。

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service: 
      name: testsvc
      port: 
        number: 80

示例2: 虚拟主机,一个域名对应一个path

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: www
            port: 
              number: 80
  - host: web.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: web
            port: 
              number: 80

示例3: 一个域名对应对多个path

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: www
            port: 
              number: 80
      - path: /laoma
        pathType: Prefix
        backend:
          service: 
            name: laoma
            port: 
              number: 80

示例4: https 透传

如果后端的svc是https流量,系统ingress直接转发https流量给后端service,则需要配置透传 TLS。

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  annotations:
    # 关键:透传 TLS,Ingress 不解密
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: www
            port: 
              number: 443

配置说明

  • 跟Kubernetes的其他配置一样,ingress的配置也需要apiVersionkindmetadata字段。

  • Ingress spec 中包含配置一个loadbalancer或proxy server的所有信息。最重要的是,它包含了一个匹配所有入站请求的规则列表。目前ingress只支持http规则。

  • 每条http规则包含以下信息:

    • 一个host配置项(比如www.laoma.cloud,默认是*)
    • paths列表(比如:/laoma),每个path都关联一个backendservice:port的组合,比如web:80。在loadbalancer将流量转发到backend之前,所有的入站请求都要先匹配host和path。
IngressClass

Ingress 可以由不同的控制器实现,通常使用不同的配置。 每个 Ingress 应当指定一个类,也就是一个对 IngressClass 资源的引用。 IngressClass 资源包含额外的配置,其中包括应当实现该类的控制器名称。

你可以将一个特定的 IngressClass 标记为集群默认 Ingress 类。 将某个 IngressClass 资源的 ingressclass.kubernetes.io/is-default-class 注解设置为 true 将确保新的未指定 ingressClassName 字段的 Ingress 能够被赋予这一默认 IngressClass.

路径匹配

Ingress 中的每个路径都需要有对应的路径类型(Path Type)。未明确设置 pathType 的路径无法通过合法性检查。

当前支持的路径类型有三种:

  • Prefix :基于以 / 分隔的 URL 路径前缀匹配。匹配区分大小写, 并且对路径中各个元素逐个执行匹配操作。 路径元素指的是由 / 分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。
  • Exact:精确匹配 URL 路径,且区分大小写。
  • ImplementationSpecific :对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者作与 PrefixExact 类型相同的处理。

示例

类型 路径 请求路径 匹配与否?
Prefix / (所有路径)
Exact /foo /foo
Exact /foo /bar
Exact /foo /foo/
Exact /foo/ /foo
Prefix /foo /foo, /foo/
Prefix /foo/ /foo, /foo/
Prefix /aaa/bb /aaa/bbb
Prefix /aaa/bbb /aaa/bbb
Prefix /aaa/bbb/ /aaa/bbb 是,忽略尾部斜线
Prefix /aaa/bbb /aaa/bbb/ 是,匹配尾部斜线
Prefix /aaa/bbb /aaa/bbb/ccc 是,匹配子路径
Prefix /aaa/bbb /aaa/bbbxyz 否,字符串前缀不匹配
Prefix /, /aaa /aaa/ccc 是,匹配 /aaa 前缀
Prefix /, /aaa, /aaa/bbb /aaa/bbb 是,匹配 /aaa/bbb 前缀
Prefix /, /aaa, /aaa/bbb /ccc 是,匹配 / 前缀
Prefix /aaa /ccc 否,使用默认后端
混合 /foo (Prefix), /foo (Exact) /foo 是,优选 Exact 类型

多重匹配

在某些情况下,Ingress 中会有多条路径与同一个请求匹配。这时匹配路径最长者优先。 如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。

主机名匹配

主机名可以是精确匹配 (例如 "foo.bar.com")或者使用通配符匹配 (例如 "*.foo.com")。 精确匹配要求 HTTP host 头部字段与 host 字段值完全匹配。 通配符匹配则要求 HTTP host 头部字段与通配符规则中的后缀部分相同。

主机 host 头部 匹配与否?
*.foo.com bar.foo.com 基于相同的后缀匹配
*.foo.com baz.bar.foo.com 不匹配,通配符仅覆盖了一个 DNS 标签
*.foo.com foo.com 不匹配,通配符仅覆盖了一个 DNS 标签

Ingress 规则使用

示例1:不同站点相同路径
bash 复制代码
[root@master30 ~]# vim ingress-1.yaml
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress-1
spec:
  ingressClassName: nginx
  rules:
  - host: webapp01.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: webapp01
            port: 
              number: 80
  - host: webapp02.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: webapp02
            port: 
              number: 80
bash 复制代码
# 创建Ingress规则
[root@master30 ~]# kubectl apply -f ingress-1.yaml
root@master30:~/ingress# kubectl describe ingress myingress-1 
Name:             myingress-1
Labels:           <none>
Namespace:        ingress
Address:          10.1.8.40
Ingress Class:    nginx
Default backend:  <default>
Rules:
  Host                  Path  Backends
  ----                  ----  --------
  webapp01.laoma.cloud  
                        /   webapp01:80 (10.224.113.140:80,10.224.19.14:80)
  webapp02.laoma.cloud  
                        /   webapp02:80 (10.224.113.141:80,10.224.19.15:80)
Annotations:            <none>
Events:
  Type    Reason  Age   From                      Message
  ----    ------  ----  ----                      -------
  Normal  Sync    4s    nginx-ingress-controller  Scheduled for sync

访问验证

bash 复制代码
# 修改www.laoma.cloud解析,确保客户端能解析该名称
# 如果有多个域名,建议使用配置DNS-wild匹配
root@client:~# echo '10.1.8.40 webapp01.laoma.cloud' >> /etc/hosts
root@client:~# echo '10.1.8.40 webapp02.laoma.cloud' >> /etc/hosts

# 注意: 这里我们直接访问域名,没有加端口号
root@master30:~/ingress# curl webapp01.laoma.cloud
hello webapp01 pod2
root@master30:~/ingress# curl webapp01.laoma.cloud
hello webapp01 pod1
root@master30:~/ingress# curl webapp02.laoma.cloud
hello webapp02 pod2
root@master30:~/ingress# curl webapp02.laoma.cloud
hello webapp02 pod1
示例2:相同站点不同路径
bash 复制代码
[root@master30 ~]# vim ingress-2.yaml
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress-2
spec:
  # 这里一定要指定ingressClassName为nginx
  ingressClassName: nginx
  rules:
  - host: www.laoma.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: webapp01
            port: 
              number: 80
      - path: /laoma
        pathType: Prefix
        backend:
          service: 
            name: webapp01
            port: 
              number: 80
bash 复制代码
# 创建Ingress规则
[root@master30 ~]# kubectl apply -f ingress-2.yaml
[root@master30 ~]# kubectl describe ingress myingress-2 
Name:             myingress-2
Labels:           <none>
Namespace:        ingress
Address:          10.1.8.40
Ingress Class:    nginx
Default backend:  <default>
Rules:
  Host             Path  Backends
  ----             ----  --------
  www.laoma.cloud  
                   /        webapp01:80 (10.224.113.140:80,10.224.19.14:80)
                   /laoma   webapp01:80 (10.224.113.140:80,10.224.19.14:80)
Annotations:       <none>
Events:
  Type    Reason  Age              From                      Message
  ----    ------  ----             ----                      -------
  Normal  Sync    2s (x2 over 8s)  nginx-ingress-controller  Scheduled for sync

访问验证

bash 复制代码
# 修改www.laoma.cloud解析,确保客户端能解析该名称
# 如果有多个域名,建议使用配置DNS-wild匹配
root@client:~# echo '10.1.8.40 www.laoma.cloud' >> /etc/hosts

# 注意: 这里我们直接访问域名,没有加端口号
root@client:~# curl www.laoma.cloud
hello webapp01 pod1
root@client:~# curl www.laoma.cloud
hello webapp01 pod2
root@client:~# curl www.laoma.cloud/laoma/
hello webapp01 laoma1
root@client:~# curl www.laoma.cloud/laoma/
hello webapp01 laoma2

环境清理

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

Network Policy

学习参考:网络策略

环境准备

bash 复制代码
[root@master30 ~]# kubectl create ns network
[root@master30 ~]# kubectl config set-context --current --namespace network

单主机网络通信

Docker 单机网络

Docker中的网络接口默认都是虚拟的接口。虚拟接口的最大优势就是转发效率极高。这是因为Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发,即发送接口的发送缓存中的数据包将被直接复制到接收接口的接收缓存中,而无需通过外部物理网络设备进行交换。

Docker 服务默认会创建一个名称为docker0的Linux网桥(其上有一个docker0内部接口),利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做vethpair)。Docker默认指定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
说明:brctl工具由bridge-utils提供。

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

Containerd 单机网络

Containerd 中的网络 与Docker类似,所有网络接口默认都是虚拟接口。

创建容器时,Containerd 会在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通。

跨主机网络通信

跨主机之间通信

以上图片来源于《基于 kubernetes 的容器云平台实战 》。

跨主机通信方法比较多,可以使用:

  1. docker 原生的方案:overlay 和 macvlan。
  2. 第三方提供的方案:flannel 、calico、weave等。

重点关注两点:配置难易程度是否支持网络策略

以下数据来源于《kubernetes 权威指南》。

方案 特性 Flannel Calico macvlan OpenVswitch 直接路由
方案特性 通过虚拟设备flannel0实现对docker0的管理 基于BGP协议的纯三层的网络方案 基于Linux Kernel 的macvlan技术 基于隧道的虚拟路由器技术 基于Linux Kernel 的 vRouter 技术
对底层网络的要求 三层互通 三层互通 二层互通 三层互通 二层互通
配置难易程度 简单-基于etcd 简单-基于etcd 简单-直接使用宿主机网络,需要仔细规划IP地址 复杂-需手工配置个节点的bridge 简单-使用宿主机vRoute功能,需要仔细规划每个Node的IP地址
网络性能 host-gw > VxLAN > UDP BGP 模式性能随时小 IPIP 模式较小 性能随时可忽略 性能随时较小 性能随时较小
网络连通性限制 在不支持BGP的网络环境下无法使用 基于macvlan的容器无法与宿主机网络通信 在无法实现大二层互通的网络环境下无法使用

跨主机网络通信方案比较

我们将从如下几个方面比较,大家可以根据不同场景选择最合适的方案。

  • 网络模型,采用何种网络模型支持 multi-host 网络?
  • Distributed Store,是否需要 etcd 或 consul 这类分布式 key-value 数据库存储网络信息?
  • IPMA,如何管理容器网络的 IP?
  • 连通与隔离,提供怎样的网络连通性?支持容器间哪个级别和哪个类型的隔离?
  • 性能,性能比较。
网络模型

跨主机网络意味着将不同主机上的容器用同一个虚拟网络连接起来。这个虚拟网络的拓扑结构和实现技术就是网络模型。

  • overlay建立主机间 VxLAN 隧道,原始数据包在发送端被封装成 VxLAN 数据包,到达目的后在接收端解包。
  • Macvlan ,网络在二层上通过 VLAN 连接容器,在三层上依赖外部网关连接不同 macvlan。数据包直接发送,不需要封装,属于underlay 网络。
  • Flannel 我们讨论了两种 backend:vxlan 和 host-gw。vxlan 与 Docker overlay 类似,属于 overlay 网络。host-gw 将主机作为网关,依赖三层 IP 转发,不需要像 vxlan 那样对包进行封装,属于 underlay 网络。
  • Weave, 是 VxLAN 实现,属于 overlay 网络。
  • Calico ,与Flannel使用host-gw类似,将主机作为网关,依赖三层 IP 转发,不需要像 vxlan 那样对包进行封装,属于 underlay 网络。
Distributed Store

Docker Overlay、Flannel 和 Calico 都需要 etcd 或 consul。Macvlan 是简单的 local 网络,不需要保存和共享网络信息。Weave 自己负责在主机间交换网络配置信息,也不需要 Distributed Store。

IPAM

Docker Overlay 网络中所有主机共享同一个 subnet,容器启动时会顺序分配 IP,可以通过 --subnet 定制此 IP 空间。

Macvlan 需要用户自己管理 subnet,为容器分配 IP,不同 subnet 通信依赖外部网关。

Flannel 为每个主机自动分配独立的 subnet,用户只需要指定一个大的 IP 池。不同 subnet 之间的路由信息也由 Flannel 自动生成和配置。

Weave 的默认配置下所有容器使用 10.32.0.0/12 subnet,如果此地址空间与现有 IP 冲突,可以通过 --ipalloc-range 分配特定的 subnet。

Calico 从 IP Pool(可定制)中为每个主机分配自己的 subnet。

连通与隔离

同一 Docker Overlay 网络中的容器可以通信,但不同网络之间无法通信,要实现跨网络访问,只有将容器加入多个网络。与外网通信可以通过 docker_gwbridge 网络。

Macvlan 网络的连通或隔离完全取决于二层 VLAN 和三层路由。

不同 Flannel 网络中的容器直接就可以通信,没有提供隔离。与外网通信可以通过 bridge 网络。

Weave 网络默认配置下所有容器在一个大的 subnet 中,可以自由通信,如果要实现隔离,需要为容器指定不同的 subnet 或 IP。与外网通信的方案是将主机加入到 weave 网络,并把主机当作网关。

Calico 默认配置下只允许位于同一网络中的容器之间通信,但通过其强大的 Policy 能够实现几乎任意场景的访问控制。

性能

性能测试是一个非常严谨和复杂的工程,这里我们只尝试从技术方案的原理上比较各方案的性能。

最朴素的判断是:Underlay 网络性能优于 Overlay 网络

Overlay 网络利用隧道技术,将数据包封装到 UDP 中进行传输。因为涉及数据包的封装和解封,存在额外的 CPU 和网络开销。虽然几乎所有 Overlay 网络方案底层都采用 Linux kernel 的 vxlan 模块,这样可以尽量减少开销,但这个开销与 Underlay 网络相比还是存在的。所以 Macvlan、Flannel host-gw、Calico 的性能会优于 Docker overlay、Flannel vxlan 和 Weave。

Overlay 较 Underlay 可以支持更多的二层网段,能更好地利用已有网络,以及有避免物理交换机 MAC 表耗尽等优势,所以在方案选型的时候需要综合考虑。

跨主机网络模型

容器网络的配置是一个复杂的过程,为了应对各式各样的需求:

  • 容器网络的解决方案也多种多样,例如有flannel,calico,kube-ovn,weave等。
  • 容器平台/运行时也是多样的,例如有Kubernetes,Openshift,rkt等。

想要解决这个问题,我们需要一个抽象的接口层,将容器网络配置方案与容器平台方案解耦。

CNM

CNM ( Container Network Model,容器网络模型),由 Docker 公司提出,在 docker 项目下的 libnetwork 项目中被采用。按照该模型开发出的 driver 就能与 docker daemon 协同工作,实现容器网络。

  • docker 原生的 driver 包括 none、bridge、overlay 和 macvlan。
  • 第三方 driver 包括 flannel、weave、calico 等。

容器网络模型对容器网络进行了抽象,由以下三类组件组成:

  • Sandbox 是容器的网络栈,包含容器的 interface、路由表和 DNS 设置。 Linux Network Namespace 是 Sandbox 的标准实现。Sandbox 可以包含来自不同 Network 的 Endpoint。
  • Endpoint 的作用是将 Sandbox 接入 Network。Endpoint 的典型实现是 veth pair,后面我们会举例。一个 Endpoint 只能属于一个网络,也只能属于一个 Sandbox。
  • Network 包含一组 Endpoint,同一 Network 的 Endpoint 可以直接通信。Network 的实现可以是 Linux Bridge、VLAN 等。

如图所示两个容器,一个容器一个 Sandbox,每个 Sandbox 都有一个 Endpoint 连接到 Network 1,第二个 Sandbox 还有一个 Endpoint 将其接入 Network 2.

下面我们以 docker bridge driver 为例讨论 libnetwork CNM 是如何被实现的。

  1. 两个 Network:默认网络 "bridge" 和自定义网络 "my_net2"。实现方式是 Linux Bridge:"docker0" 和 "br-5d863e9f78b6"。
  2. 三个 Enpoint,由 veth pair 实现,一端(vethxxx)挂在 Linux Bridge 上,另一端(eth0)挂在容器内。
  3. 三个 Sandbox,由 Network Namespace 实现,每个容器有自己的 Sanbox。

CNI

CNI ,全称 Container Network Interface,是 Google 和 **CoreOS **联合定制的网络标准,CNI 规范定义了容器和网络插件之间通信规范,实现多容器通信。各个网络厂商通过该接口实现互相通信。

一个容器可以被加入到被不同插件所驱动的多个网络之中。一个网络有自己对应的插件和唯一的名称。

CNI 模型包含2个概念:

  • 容器: 独立的 linux 网络命名空间。

  • 网络: 用于互联实体, 这些实体拥有各自独立且唯一的 IP 地址, 可以是容器, 物理机, 或者其他网络设备。

    通过插件设置网络, 包括 CNI Plugin 和 IPAM ( IP address management) Plugin 两类插件:

    • CNI Plugin 负责配置容器网络。
    • IPAM plugin 负责分配容器的IP 地址。

    IPAM Plugin 作为 CNI plugin 的一部分, 与CNI plugin 一起工作。

CNM和CNI比较

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

网络策略

网络策略介绍

默认情况,集群外部主机可以访问集群内部应用,集群内部应用也可以访问集群外部主机,各个namespace之间没有做任何的隔离策略。

如果你希望在 IP 地址或端口层面控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。 NetworkPolicy 是一种以应用为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络"实体" 通信。 NetworkPolicy 适用于一端或两端与 Pod 的连接,与其他连接无关。

Pod 可以通信的 Pod 是通过如下三个标识符的组合来辩识的:

  1. 其他被允许的 Pods(例外:Pod 无法阻塞对自身的访问)
  2. 被允许的命名空间
  3. IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)

**提示:**网络策略通过网络插件来实现。 要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案,例如 calico。

网络策略规约

Pod 有两种隔离: 出口的隔离入口的隔离

  • 默认情况下,**一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的。**如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 "Egress",则该 Pod 是出口隔离的, 我们称这样的策略适用于该 Pod 的出口。当一个 Pod 的出口被隔离时, 唯一允许的来自 Pod 的连接是适用于出口的 Pod 的某个 NetworkPolicy 的 egress 列表所允许的连接。 这些 egress 列表的效果是相加的。

  • 默认情况下,**一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的。**如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 "Ingress",则该 Pod 被隔离入口, 我们称这种策略适用于该 Pod 的入口。当一个 Pod 的入口被隔离时,唯一允许进入该 Pod 的连接是来自该 Pod 节点的连接和适用于入口的 Pod 的某个 NetworkPolicy 的 ingress 列表所允许的连接。这些 ingress 列表的效果是相加的。

**网络策略是相加的,所以不会产生冲突。**如果策略适用于 Pod 某一特定方向的流量, Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。 因此,评估的顺序不影响策略的结果。

**要允许从源 Pod 到目的 Pod 的连接,则源 Pod 的出口策略和目的 Pod 的入口策略都需要允许连接。**如果任何一方不允许连接,建立连接将会失败。

示例:

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  # 使用标签过滤限制哪些pod
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  # Ingress控制外部访问内部
  - Ingress
  # Egress控制pod访问外部
  - Egress
  ingress:
  # from限制允许哪些主机可以访问pod
  - from:
    # 通过ip限制
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    # 限制namespace中pod
    - namespaceSelector:
        matchLabels:
          project: myproject
    # 限制同一namespace中pod
    - podSelector:
        matchLabels:
          role: frontend
    # 限制pod上哪些端口可以访问
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978
  • 必需字段 :与所有其他的 Kubernetes 配置一样,NetworkPolicy 需要 apiVersionkindmetadata 字段。

  • spec :NetworkPolicy 规约 中包含了在一个命名空间中定义特定网络策略所需的所有信息。

  • spec.podSelector :每个 NetworkPolicy 都包括一个 podSelector, 它对该策略所适用的一组 Pod 进行选择。示例中的策略选择带有 "role=db" 标签的 Pod。 空的 podSelector 选择命名空间下的所有 Pod。

  • spec.policyTypes :每个 NetworkPolicy 都包含一个 policyTypes 列表,其中包含 IngressEgress 或两者兼具。policyTypes 字段表示给定的策略是应用于进入所选 Pod 的入站流量还是来自所选 Pod 的出站流量,或两者兼有。 如果 NetworkPolicy 未指定 policyTypes 则默认情况下始终设置 Ingress; 如果 NetworkPolicy 有任何出口规则的话则设置 Egress

  • spec.ingress :每个 NetworkPolicy 可包含一个 ingress 规则的白名单列表。 每个规则都允许同时匹配 fromports 部分的流量。示例策略中包含一条简单的规则: 它匹配某个特定端口,来自三个来源中的一个:

    • 第一个通过 ipBlock 指定
    • 第二个通过 namespaceSelector 指定
    • 第三个通过 podSelector 指定。
  • spec.egress :每个 NetworkPolicy 可包含一个 egress 规则的白名单列表。 每个规则都允许匹配 toport 部分的流量。该示例策略包含一条规则, 该规则将指定端口上的流量匹配到 10.0.0.0/24 中的任何目的地。

to 和 from 选择器

可以在 ingressfrom 部分或 egressto 部分中指定四种选择器:

  • podSelector:此选择器将在与 NetworkPolicy 相同的命名空间中选择特定的 Pod,应将其允许作为入站流量来源或出站流量目的地。

  • namespaceSelector:此选择器将选择特定的命名空间,应将所有 Pod 用作其入站流量来源或出站流量目的地。

  • namespaceSelector 和 podSelector :指定 namespaceSelectorpodSelectorto/from 条目选择特定命名空间中的特定 Pod。

    示例:

    yaml 复制代码
      ...
      ingress:
      - from:
        - namespaceSelector:
            matchLabels:
              user: alice
        - podSelector:
            matchLabels:
              role: client
      ...

    from 数组中包含两个元素,允许来自本地命名空间中标有 role=client 的 Pod 的连接, 来自任何命名空间中标有 user=alice 的任何 Pod 的连接。

  • ipBlock:此选择器将选择特定的 IP CIDR 范围以用作入站流量来源或出站流量目的地。 这些应该是集群外部 IP,因为 Pod IP 存在时间短暂的且随机产生。

    集群的入站和出站机制通常需要重写数据包的源 IP 或目标 IP。 在发生这种情况时,不确定在 NetworkPolicy 处理之前还是之后发生, 并且对于网络插件、云提供商、Service 实现等的不同组合,其行为可能会有所不同。

    • 对入站流量而言,这意味着在某些情况下,你可以根据实际的原始源 IP 过滤传入的数据包, 而在其他情况下,NetworkPolicy 所作用的 源IP 则可能是 LoadBalancer 或 Pod 的节点等。

    • 对于出站流量而言,这意味着从 Pod 到被重写为集群外部 IP 的 Service IP 的连接可能会或可能不会受到基于 ipBlock 的策略的约束。

实施网络策略

准备实验环境

环境说明:

  • namespace-web 中有3个 pod:web1、web2、test
  • namespace-laoma 中有1个pod:test
  • 如没有特别说明,默认namespace是web
bash 复制代码
[root@master30 ~]# kubectl create ns web
[root@master30 ~]# kubens web

# 创建 web1
[root@master30 ~]# kubectl run web1 --image=docker.io/library/nginx --image-pull-policy=IfNotPresent
[root@master30 ~]# kubectl exec -it web1 -- bash -c 'echo Hello web1 > /usr/share/nginx/html/index.html'

# 创建 web2
[root@master30 ~]# kubectl run web2 --image=docker.io/library/nginx --image-pull-policy=IfNotPresent
[root@master30 ~]# kubectl exec -it web2 -- bash -c 'echo Hello web2 > /usr/share/nginx/html/index.html'

# 创建service web1和web2
[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 get service
NAME   TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
web1   NodePort   10.103.36.143    <none>        80:30790/TCP   11s
web2   NodePort   10.111.250.224   <none>        80:32686/TCP   7s

# 在namespace-laoma中创建测试pod-test,访问web1和web2
[root@master30 ~]# kubectl create ns laoma
[root@master30 ~]# kubectl run test -n laoma --image=docker.io/library/busybox --image-pull-policy=IfNotPresent sleep 3600
[root@master30 ~]# kubectl exec -n laoma test -- wget web1.web
[root@master30 ~]# kubectl exec -n laoma test -- wget web2.web

# 同一namespace中web1和web2互相访问
root@master30:~/network# kubectl run test --image=docker.io/library/busybox --image-pull-policy=IfNotPresent sleep 3600
root@master30:~/network# kubectl exec -it test -- sh
/ # wget web2.web -O web2-index.html
Connecting to web2.web (10.111.250.224:80)
web2-index.html      100% |*********************************************|    11   0:00:00 ETA
/ # cat web2-index.html 
Hello web2
/ # wget web1.web -O web1-index.html
Connecting to web1.web (10.103.36.143:80)
web1-index.html      100% |*********************************************|    11   0:00:00 ETA
/ # cat web1-index.html 
Hello web1
/ # exit

# 集群内访问service web1和web2
[root@master30 ~]# curl 10.103.36.143
Hello web1
[root@master30 ~]# curl 10.111.250.224
Hello web2

# 集群外节点访问web1和web2
root@client:~# curl http://10.1.8.30:30790
Hello web1
root@client:~# curl http://10.1.8.30:32686
Hello web2
根据 pod 标签限定

根据pod标签限定:

  • 限定同一ns中pod之间访问
  • 所有其他ns中pod或者集群外主机都无法访问ns中pod

示例1 :允许同一ns中具有标签run: test的pod,访问具有标签run: web1的pod 80端口。

bash 复制代码
[root@master30 ~]# vim netpol.yaml
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: test
    ports:
    - protocol: TCP
      port: 80
bash 复制代码
# 创建network policy
[root@master30 ~]# kubectl apply -f netpol.yaml
[root@master30 ~]# kubectl get netpol
NAME                POD-SELECTOR   AGE
my-network-policy   run=web1       4s

# 同一namespace中test可以访问web1,web2不可以访问web1
[root@master30 ~]# kubectl run test --image=docker.io/library/nginx --image-pull-policy=IfNotPresent

[root@master30 ~]# kubectl exec test -- wget web1
Connecting to web1 (10.103.36.143:80)
index.html       100% |*******************************|    11   0:00:00 ETA

# 修改现有标签
root@master30:~/network# kubectl label pod test run=web2 --overwrite
root@master30:~/network# kubectl get pods --show-labels 
NAME   READY   STATUS    RESTARTS   AGE    LABELS
test   1/1     Running   0          9m9s   run=web2
web1   1/1     Running   0          13m    run=web1
web2   1/1     Running   0          13m    run=web2

# 只能解析ip,但是流量无法到达目标pod
root@master30:~/network# kubectl exec test -- wget web1.web
Connecting to web1 (10.103.36.143:80)

# 不同 namespace 中pod,即使标签满足也不可以访问
[root@master30 ~]# kubectl exec test -n laoma -- wget web1.web

示例2 :允许同一namespace中所有pod访问具有标签run: web1的pod 80端口。

matchLabels中不使用任何标签,则允许同一ns中所有pod。

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  podSelector:
    matchLabels:
      run: web1
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}
    # 或者将以上一行改为两行
    # - podSelector:
    #     matchLabels:
    ports:
    - protocol: TCP
      port: 80
bash 复制代码
# 应用策略
[root@master30 ~]# kubectl apply -f netpol.yaml

# 同一namespace中所有pod都可以才访问web1
[root@master30 ~]# kubectl exec test -- wget web1
Hello web1
[root@master30 ~]# kubectl exec web2 -- curl web1
Hello web1

# 不同namespace中pod不可以访问web1
[root@master30 ~]# kubectl exec test -n laoma -- wget web1.web
根据 pod 所属 ns 限定

用于限定,来源于其他namespace中pod。

示例1 :允许具有标签project: myproject的namespace中所有pod,访问当前ns中具有标签run: web1的pod 80端口。

bash 复制代码
[root@master30 ~]# vim netpol.yaml
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:
        matchLabels:
          project: myproject
    ports:
    - protocol: TCP
      port: 80
bash 复制代码
# 应用策略
[root@master30 ~]# kubectl apply -f netpol.yaml

# 此时namespace-web中pod和namespace-laoma中pod都无法访问web1
[root@master30 ~]# kubectl exec test -- wget web1
[root@master30 ~]# kubectl exec -n laoma test -- wget web1.web

# 给namespace-laoma添加标签project=myproject,此时namespace-laoma中pod可以访问web1.web
[root@master30 ~]# kubectl label namespaces laoma project=myproject
[root@master30 ~]# kubectl exec -n laoma test -- wget web1.web
Hello web1

示例2 :允许所有namespace中所有pod,访问当前ns中具有标签run: web1的pod 80端口。

bash 复制代码
[root@master30 ~]# vim netpol.yaml
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
bash 复制代码
# 创建network policy
[root@master30 ~]# kubectl apply -f netpol.yaml

# 所有namespace中所有pod都可以访问web1
[root@master30 ~]# kubectl exec test -- wget web1
Hello web1
[root@master30 ~]# kubectl exec -n laoma test -- wget web1.web
根据 pod IP 限定

示例1 :允许网段172.17.0.0/16但不包括子网172.17.1.0/24中主机,访问具有标签run: web1的pod 80端口。

bash 复制代码
[root@master30 ~]# vim netpol.yaml
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:
        # 放行 网段10.1.8.0/24
        cidr: 10.1.8.0/24
        # 不放行网段
        except:
        - 10.1.8.128/26
    ports:
    - protocol: TCP
      port: 80
bash 复制代码
# 应用策略
[root@master30 ~]# kubectl apply -f netpol.yaml

# 访问失败
[root@master30 ~]# kubectl exec test -- wget web1
[root@master30 ~]# kubectl exec -n laoma test -- wget web1.web

root@client:~# curl http://10.1.8.30:30251
root@client:~# curl http://10.1.8.31:30251
# 访问成功失败
root@client:~# curl http://10.1.8.32:30251
Hello web1

# 理论上:10.1.8.0/24网段中主机都可以通过集群任意节点访问web1
# 实际测试:只可以通过pod所在主机的IP访问web1 (10.1.8.32:30790),不可以访问10.1.8.30:30790或10.1.8.31:30790

# 在worker31上也可以通过10.103.36.143访问pod-web1
[root@master30 ~]# kubectl describe pod web1|grep Node:
Node:         worker32.laoma.cloud/10.1.8.32

从实验测试结果来看,根据网段控制来源还不完善。

如果想放行所有主机,阻止部分主机,规则如下:

yaml 复制代码
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.1.8.0/26
不限定端口
bash 复制代码
[root@master30 ~]# vim netpol.yaml
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
bash 复制代码
# 应用策略
[root@master30 ~]# kubectl apply -f netpol.yaml

[root@master30 ~]# kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE                  NOMINATED NODE   READINESS GATES
test   1/1     Running   0          64m   10.224.193.70   worker32.laoma.cloud   <none>           <none>
web1   1/1     Running   0          82m   10.224.193.67   worker32.laoma.cloud   <none>           <none>
web2   1/1     Running   0          81m   10.224.41.132   worker31.laoma.cloud   <none>           <none>

# 此时满足条件的pod可以ping通pod地址
[root@master30 ~]# kubectl run --rm -it ubuntu -l run=test --image ubuntu -- bash
root@ubuntu:/# apt update
root@ubuntu:/# apt install -y iputils-ping
root@ubuntu:/# ping -c1 10.224.193.67
PING 10.224.193.67 (10.224.193.67) 56(84) bytes of data.
64 bytes from 10.224.193.67: icmp_seq=1 ttl=62 time=0.831 ms

--- 10.224.193.67 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.831/0.831/0.831/0.000 ms

root@ubuntu:/# ping web1 -c1
PING web1.web.svc.cluster.local (10.96.144.145) 56(84) bytes of data.

--- web1.web.svc.cluster.local ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

root@ubuntu:/# exit

# 仍然 ping不通 service,因为service只开放80端口
限定端口范围
bash 复制代码
[root@master30 ~]# vim netpol.yaml
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
不限定项目中特定pod

设置podSelector: {},则针对namespace中所有pod。

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: web
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
......
多条件规则

只要有一个满足条件就可以访问pod。

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
bash 复制代码
# 创建network policy
[root@master30 ~]# kubectl apply -f netpol.yaml

# namespace-web中pod-test可以访问web1
[root@master30 ~]# kubectl exec test -- wget web1
Hello web1

默认策略

默认允许所有入站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - {}
默认拒绝所有入站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

此策略可以确保即使容器没有选择其他任何 NetworkPolicy,也仍然可以被隔离。 此策略不会更改默认的出口隔离行为。

默认允许所有出站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - {}
默认拒绝所有出站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。 此策略不会更改默认的入站流量隔离行为。

默认拒绝所有入口和所有出站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被 允许入站或出站流量。

无法完成的工作

  • 特定于节点的策略

  • 基于名字的策略。

  • 实现适用于所有命名空间或 Pods 的默认策略。

  • 显式地拒绝策略的能力。NetworkPolicy 的模型默认采用拒绝操作, 其唯一的能力是添加允许策略。

  • **禁止本地回路或指向宿主的网络流量。**Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求)。

环境清理

bash 复制代码
[root@master30 ~]# kubectl delete ns network web laoma
[root@master30 ~]# kubectl delete ns web
[root@master30 ~]# kubectl delete ns laoma

ctor:

matchLabels:

project: myproject

  • podSelector:

matchLabels:

run: test

ports:

  • protocol: TCP

port: 80

复制代码
```bash
# 创建network policy
[root@master30 ~]# kubectl apply -f netpol.yaml

# namespace-web中pod-test可以访问web1
[root@master30 ~]# kubectl exec test -- wget web1
Hello web1

默认策略

默认允许所有入站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - {}
默认拒绝所有入站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

此策略可以确保即使容器没有选择其他任何 NetworkPolicy,也仍然可以被隔离。 此策略不会更改默认的出口隔离行为。

默认允许所有出站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - {}
默认拒绝所有出站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。 此策略不会更改默认的入站流量隔离行为。

默认拒绝所有入口和所有出站流量
yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被 允许入站或出站流量。

无法完成的工作

  • 特定于节点的策略

  • 基于名字的策略。

  • 实现适用于所有命名空间或 Pods 的默认策略。

  • 显式地拒绝策略的能力。NetworkPolicy 的模型默认采用拒绝操作, 其唯一的能力是添加允许策略。

  • **禁止本地回路或指向宿主的网络流量。**Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求)。

环境清理

bash 复制代码
[root@master30 ~]# kubectl delete ns network web laoma
[root@master30 ~]# kubectl delete ns web
[root@master30 ~]# kubectl delete ns laoma
相关推荐
剑飞的编程思维1 小时前
传统制造业数字化转型|架构评估核心全维度清单
架构
小程故事多_802 小时前
DeepSeek-V4技术报告全解读 从架构到Infra的全栈重构之路
人工智能·重构·架构·智能体
掘根2 小时前
【微服务即时通讯】客户端数据中心
qt·微服务·架构
默 语2 小时前
AI Agent 数据感知层实战:网络端点定位在跨境电商、金融风控、自动化营销中的技术实现
网络·人工智能·自动化
heimeiyingwang2 小时前
【架构实战】观察者模式在分布式系统中的应用
观察者模式·架构·wpf
skilllite作者2 小时前
从“记忆”到“项目 Wiki”:我在 SkillLite 里实现了一套 Markdown-only LLM Wiki 自动维护机制
开发语言·jvm·人工智能·后端·架构·rust
liao__ran2 小时前
Kubernetes攻防 Docker in Docker
docker·容器·kubernetes
littleM2 小时前
深度拆解 HermesAgent(六):研究功能与测试体系
开发语言·人工智能·python·架构·ai编程
车载诊断技术2 小时前
在工作中如何保持奋斗的动力?
网络·架构·汽车·电子电气架构·ecu 诊断 diag