K8s运维-高级网络策略介绍

1

什么是NetworkPolicy?


如果你希望在 IP 地址或端口层面(OSI 第 3 层或第 4 层)控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。NetworkPolicy 是一种以应用为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络“实体” (我们这里使用实体以避免过度使用诸如“端点”和“服务”这类常用术语, 这些术语在 Kubernetes 中有特定含义)通信。

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

  1. 其他被允许的 Pods(例外:Pod 无法阻塞对自身的访问)

  1. 被允许的名字空间

  1. IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)

在定义基于 Pod 或名字空间的 NetworkPolicy 时,你会使用 选择算符 来设定哪些流量 可以进入或离开与该算符匹配的 Pod。

同时,当基于 IP 的 NetworkPolicy 被创建时,我们基于 IP 组块(CIDR 范围) 来定义策略。

前置条件

网络策略通过网络插件 来实现。要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的。

Pod 隔离的两种类型

Pod 有两种隔离: 出口的隔离和入口的隔离。它们涉及到可以建立哪些连接。这里的“隔离”不是绝对的,而是意味着“有一些限制”。另外的,“非隔离方向”意味着在所述方向上没有限制。这两种隔离(或不隔离)是独立声明的, 并且都与从一个 Pod 到另一个 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 的入口策略都需要允许连接。如果任何一方不允许连接,建立连接将会失败。

2

NetworkPolicy配置详解


参阅 NetworkPolicy 来了解资源的完整定义。

下面是一个 NetworkPolicy 的示例:

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: test-network-policynamespace: defaultspec:podSelector: matchLabels:    role: dbpolicyTypes:-Ingress-Egressingress:-from: - ipBlock:      cidr: 172.17.0.0/16      except:      - 172.17.1.0/24 - namespaceSelector:      matchLabels:        project: myproject - podSelector:      matchLabels:        role: frontend ports: - protocol: TCP    port: 6379egress:-to: - ipBlock:      cidr: 10.0.0.0/24 ports: - protocol: TCP    port: 5978

  • 说明: 除非选择支持网络策略的网络解决方案,否则将上述示例发送到API服务器没有任何效果。

必需字段:与所有其他的 Kubernetes 配置一样,NetworkPolicy 需要 apiVersion、 kind 和 metadata 字段。关于配置文件操作的一般信息,请参考使用 ConfigMap 配置容器, 和对象管理。

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

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

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

ingress: 每个 NetworkPolicy 可包含一个 ingress 规则的白名单列表。每个规则都允许同时匹配 from 和 ports 部分的流量。示例策略中包含一条 简单的规则:它匹配某个特定端口,来自三个来源中的一个,第一个通过 ipBlock 指定,第二个通过 namespaceSelector 指定,第三个通过 podSelector 指定。

egress: 每个 NetworkPolicy 可包含一个 egress 规则的白名单列表。每个规则都允许匹配 to 和 port 部分的流量。该示例策略包含一条规则, 该规则将指定端口上的流量匹配到 10.0.0.0/24 中的任何目的地。

所以,该网络策略示例:

  1. 隔离 "default" 名字空间下 "role=db" 的 Pod (如果它们不是已经被隔离的话)。

  1. (Ingress 规则)允许以下 Pod 连接到 "default" 名字空间下的带有 "role=db" 标签的所有 Pod 的 6379 TCP 端口:

  • "default" 名字空间下带有 "role=frontend" 标签的所有 Pod

  • 带有 "project=myproject" 标签的所有名字空间中的 Pod

  • IP 地址范围为 172.17.0.0–172.17.0.255 和 172.17.2.0–172.17.255.255 (即,除了 172.17.1.0/24 之外的所有 172.17.0.0/16)

  1. (Egress 规则)允许从带有 "role=db" 标签的名字空间下的任何 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口的连接。

3

NetworkPolicy注意事项


在配置网络策略时,有很多细节需要注意,比如上述的示例中,一段关于ingress的from配置:

 - from:    - ipBlock:        cidr: 172.17.0.0/16        except:        - 172.17.1.0/24    - namespaceSelector:        matchLabels:          project: myproject    - podSelector:        matchLabels:          role: frontend#namespaceSelector和podSelector是或的关系,表示两个条件满足一个就可以

需要注意的是在ipBlock、namespaceSelector和podSelector前面都有一个“-”,如果前面没有这个横杠将是另外一个完全不同的概念。可以看一下下面的示例:

- from:    - ipBlock:        cidr: 172.17.0.0/16        except:        - 172.17.1.0/24    - namespaceSelector:        matchLabels:          project: myproject      podSelector:        matchLabels:          role: frontend#namespaceSelector和podSelector是并且的关系,表示两个条件都满足

此时的namespaceSelector有“-”,podSelector没有“-”,那此时的配置,代表的含义是允许具有user=alice标签的Namespace下,并且具有role=client标签的所有Pod访问,namespaceSelector和podSelector是且的关系。那我们继续看一下示例:

ingress:  - from:    - namespaceSelector:        matchLabels:          user: alice    - podSelector:        matchLabels:          role: client

此时的namespaceSelector和podSelector都有“-”,配置的含义是允许具有user=alice标签的Namespace下的所有Pod和当前Namespace下具有role=client标签的Pod访问,namespaceSelector和podSelector是或的关系。

除了上述的差别外,在配置ipBlock时,可能也会出现差异性。因为我们在接收或者发送流量时,很有可能伴随着数据包中源IP和目标IP的重写,也就是SNAT和DNAT,此时会造成流量的目标IP和源IP与配置的ipBlock出现了差异性,造成网络策略不生效,所以在配置IPBlock时,需要确认网络交换中是否存在源目地址转换,并且IPBlock最好不要配置Pod的IP,因为Pod发生重建时,它的IP地址一般就会发生变更,所以IPBlock一般用于配置集群的外部IP。

4

NetworkPolicy示例1:隔离中间件服务


  1. 有一个项目,它有自己数据库MySQL和缓存Redis中间件,我们只希望这个项目的应用能够访问该中间件

  1. 假如有一个项目需要通过Ingress进行对外发布,我们想要除了Ingress外,其他任何Namespace下的Pod都不能访问该项目。

假设有一个项目叫nw-demo,里面部署了三个微服务,分别是MySQL、Redis和Nginx。现需要对MySQL、Redis、Nginx进行隔离,分别实现如下效果:

  • MySQL、Redis只能被该Namespace下的Pod访问;

  • Nginx可以被Ingress-nginx命名空间下的Pod和该Namespace下的Pod访问;

首先创建该项目所用的Namespace(如果已经存在,或者用其他Namespace测试,也可以不创建):

[root@k8s-master01 ~]# kubectl create ns nw-demonamespace/nw-demo created

创建MySQL服务,MySQL以容器启动时,必须配置root的密码,或者设置密码为空,所以需要设置一个MYSQL_ROOT_PASSWORD的变量:

[root@k8s-master01 ~]# kubectl create deploy mysql --image=registry.cn-beijing.aliyuncs.com/mysql:5.7.23 -n nw-demodeployment.apps/mysql created# 设置密码[root@k8s-master01 ~]# kubectl  set env deploy/mysql  MYSQL_ROOT_PASSWORD=mysql -n nw-demodeployment.apps/mysql env updated

创建Redis服务:

[root@k8s-master01 ~]# kubectl create deploy redis --image=registry.cn-beijing.aliyuncs.com/redis:5.0.9-alpine3.11 -n nw-demodeployment.apps/redis created

确认容器是否启动:

[root@k8s-master01 ~]# kubectl  get po -n nw-demo -owideNAME                     READY   STATUS    RESTARTS   AGE    IP               NODE                         NOMINATEDNODE   READINESSGATESmysql-64478b7cf9-qrczt   1/1     Running   0          2m9s   192.170.21.240   k8s-node03.example.local     <none>           <none>redis-5f69645dc-vvnnj    1/1     Running   0          41s    192.162.55.68    k8s-master01.example.local   <none>           <none>

在没有配置任何网络策略时,测试下网络的连通性,可以在任意Kubernetes节点上执行telnet命令:

[root@k8s-master01 ~]# telnet 192.170.21.240 3306Trying 192.170.21.240...Connected to 192.170.21.240.Escape character is'^]'.J/}REGq yfCo@Gjumysql_native_passwordConnection closed by foreign host.[root@k8s-master01 ~]# telnet 192.162.55.68 6379Trying 192.162.55.68...Connected to 192.162.55.68.Escape character is'^]'.quit+OKConnection closed by foreign host.

注意: 本文章进行测试的IP和Pod名字可能与实际测试的不一致,需要与实际情况为准

可以看到此时的网络都是可以通信的。接下来可以根据Pod的标签进行网络隔离,首先查看一下Pod的标签:

[root@k8s-master01 ~]# kubectl  get po -n nw-demo --show-labelsNAME                     READY   STATUS    RESTARTS   AGE     LABELSmysql-64478b7cf9-qrczt   1/1     Running   0          5m15s   app=mysql,pod-template-hash=64478b7cf9redis-5f69645dc-vvnnj    1/1     Running   0          3m47s   app=redis,pod-template-hash=5f69645dc

然后根据标签配置网络策略,本示例的配置将MySQL和Redis进行了拆分,配置了两个网络策略,当然也可以给两个Pod配置一个相同的标签,这样就可以使用同一个网络策略进行限制。但是在生产环境中,并不推荐使用同一个网络策略,因为有时候需要更细粒度的策略,同一个网络策略可能会有局限性,也会导致配置失败,所以本示例采用分开的网络策略进行配置:

[root@k8s-master01~]# vim mysql-redis-nw.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: mysql-npnamespace: nw-demospec:podSelector: matchLabels:    app: mysqlpolicyTypes: -Ingressingress: -from:  - namespaceSelector:      matchLabels:        access-nw-mysql-redis: "true" ports:  - protocol: TCP    port: 3306---apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: redis-npnamespace: nw-demospec:podSelector: matchLabels:    app: redis policyTypes: -Ingressingress: -from:  - namespaceSelector:      matchLabels:        access-nw-mysql-redis: "true" ports:  - protocol: TCP    port: 6379

该yaml含有两个NetworkPolicy,其中mysql-np是对具有app=mysql标签的Pod进行管理,redis-np是对具有app=redis标签的Pod进行管理。但是需要注意的是该网络策略的ingress from是以namespaceSelector的标签进行匹配的,并非podSelector,或者是两者的结合。因为在生产环境中,同一个Namespace下可能会有很多不同类型、不同标签的Pod,并且它们可能并不具有一个相同的标签,所以如果通过podSelector进行选择,可能会比较麻烦,因为Pod一旦创建,对其标签的修改是很不方便的(apps/v1一旦创建就不可修改)。而使用namespaceSelector另一个好处是,可以很方便的对某个Namespace下的Pod进行管控,直接给指定Namespace添加标签即可,当然,如果需要更细粒度的管控,也可以结合podSelector使用。

接下来创建该NetworkPolicy:

[root@k8s-master01 ~]# kubectl  create -f mysql-redis-nw.yaml -n nw-demonetworkpolicy.networking.k8s.io/mysql-np creatednetworkpolicy.networking.k8s.io/redis-np created[root@k8s-master01 ~]# kubectl  get networkpolicy -n nw-demoNAME       POD-SELECTOR   AGEmysql-np   app=mysql      18sredis-np   app=redis      18s

创建后宿主机和任何Pod都已不能访问该Namespace下的MySQL和Redis:

[root@k8s-master01 ~]# telnet 192.170.21.240 3306Trying 192.170.21.240...^C[root@k8s-master01 ~]# telnet 192.162.55.68 6379Trying 192.162.55.68...^C

在nw-demo命名空间下创建一个用于测试连通性的工具,然后进行测试,也是不能访问该服务的:

[root@k8s-master01 ~]# kubectl  run -ti debug-tools --image=registry.cn-beijing.aliyuncs.com/debug-tools:latest -n nw-demoIf you don't see a command prompt, try pressing enter.(09:43 debug-tools:/) (09:43 debug-tools:/) curl 192.170.21.240:3306 --connect-timeout 3curl: (28) Connection timed out after 3001 milliseconds(28 09:44 debug-tools:/) exitexitSession ended, resume using 'kubectl attach debug-tools -c debug-tools -i -t' command when the pod is running

由于之前的from配置的是namespaceSelector,所以如果想要某一个Namespace下的Pod能够访问,直接给该Namespace添加一个NetworkPolicy中配置的标签即可,比如允许nw-demo命名空间下的所以Pod访问该NetworkPolicy隔离的服务:

[root@k8s-master01 ~]# kubectl label ns nw-demo access-nw-mysql-redis=truenamespace/nw-demo labeled

使用nw-demo命名空间下的debug-tools再次测试:

[root@k8s-master01 ~]# kubectlexec-tidebug-tools-nnw-demo--curl 192.170.21.240:33065.7.23      ,)ar\jÿÿ󿿕~V7gKo"Zmysql_native_passwordotpacketsoutoforder

此时nw-demo下的Pod已经可以访问MySQL和Redis,可以对其他Namespace下的Pod进行测试,比如在default命名空间进行测试:

[root@k8s-master01 ~]# kubectl  run -ti debug-tools --image=registry.cn-beijing.aliyuncs.com/debug-tools:latest -n default If you don't see a command prompt, try pressing enter.(09:46 debug-tools:/) (09:46 debug-tools:/) curl 192.170.21.240:3306 --connect-timeout 3curl: (28) Connection timed out after 3001 milliseconds(28 09:47 debug-tools:/) exitexitSession ended, resume using 'kubectl attach debug-tools -c debug-tools -i -t' command when the pod is running

可以看到此时default命名空间下的Pod并不能访问nw-demo的服务,如果想要MySQL和Redis对default命名空间开放,只需要添加一个access-nw-mysql-redis=true的标签即可。

相对于传统架构,对中间件的访问限制,在Kubernetes中实现同样的效果,可能配置更加方便且易于管理。

5

NetworkPolicy示例2:服务发布限制


一般情况下,一个项目的服务发布,会把域名的根路径指向前端应用,接口路径指向对应的网关或者微服务。假设现在创建一个Nginx服务充当前端页面,配置网络策略只让Ingress Controller访问该应用:

# 创建应用[root@k8s-master01 ~]# kubectl  create deploy nginx --image=registry.cn-beijing.aliyuncs.com/nginx:latest -n nw-demodeployment.apps/nginx created# 暴露服务[root@k8s-master01 ~]# kubectl expose deploy nginx -n nw-demo --port=80service/nginx exposed# 查看创建的服务[root@k8s-master01 ~]# kubectl  get svc,po -n nw-demo -l app=nginxNAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGEservice/nginx   ClusterIP   10.102.194.2   <none>        80/TCP    31sNAME                         READY   STATUS    RESTARTS   AGEpod/nginx-65bf9b498b-smwhg   1/1     Running   0          61s

在没有任何网络策略的情况下,该服务可以被任何Pod访问:

[root@k8s-master01 ~]# kubectl  exec debug-tools -- curl -Is nginx.nw-demoHTTP/1.1 200 OKServer: nginx/1.19.6Date: Sun, 10 Apr 2022 10:13:15 GMTContent-Type: text/htmlContent-Length: 612Last-Modified: Tue, 15 Dec 2020 13:59:38 GMTConnection: keep-aliveETag: "5fd8c14a-264"Accept-Ranges: bytes[root@k8s-master01 ~]# kubectl  -n nw-demo exec debug-tools -- curl -Is nginx.nw-demoHTTP/1.1 200 OKServer: nginx/1.19.6Date: Sun, 10 Apr 2022 10:13:34 GMTContent-Type: text/htmlContent-Length: 612Last-Modified: Tue, 15 Dec 2020 13:59:38 GMTConnection: keep-aliveETag: "5fd8c14a-264"Accept-Ranges: bytes

配置网络策略只让Ingress Controller访问该服务:

[root@k8s-master01~]# vim nginx-nw.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: nginx-npnamespace: nw-demospec:podSelector: matchLabels:    app: nginxpolicyTypes: -Ingressingress: -from:  - namespaceSelector:      matchLabels:        app.kubernetes.io/name: ingress-nginx    podSelector:      matchLabels:        "app.kubernetes.io/name": ingress-nginx  - podSelector: {} ports:  - protocol: TCP    port: 80

注意: 该条策略对具有app=nginx标签的Pod生效,只让具有app.kubernetes.io/name=ingress-nginx标签的Namespace下的具有app.kubernetes.io/name=ingress-nginx标签的Pod访问,同时还有一个允许当前Namespace下的Pod访问的策略- podSelector: {} 。需要注意的是,读者的集群中已经安装了相关Ingress,并且需要根据实际的标签进行更改,如果读者集群目前并未安装Ingress,可以完成Ingress的学习后,再来测试该实验。

创建该NetworkPolicy,并测试连通性:

[root@k8s-master01 ~]# kubectl  create -f nginx-nw.yaml networkpolicy.networking.k8s.io/nginx-np created# 没有被允许的命名空间无法访问[root@k8s-master01 ~]# kubectl  exec debug-tools -- curl --connect-timeout 2 -Is nginx.nw-democommand terminated withexit code 28# 允许范围内的Pod可以访问[root@k8s-master01 ~]# kubectl  exec debug-tools -n nw-demo -- curl --connect-timeout 2 -Is nginx.nw-demoHTTP/1.1200 OKServer: nginx/1.19.6Date: Sun, 10 Apr 202210:16:54 GMTContent-Type: text/htmlContent-Length: 612Last-Modified: Tue, 15Dec202013:59:38 GMTConnection: keep-aliveETag: "5fd8c14a-264"Accept-Ranges: bytes[root@k8s-master01 ~]# kubectl get ns -l app.kubernetes.io/name=ingress-nginxNAME            STATUS   AGEingress-nginx   Active   9d[root@k8s-master01 ~]# kubectl get pod -n ingress-nginx -l app.kubernetes.io/name=ingress-nginxNAME                                        READY   STATUS    RESTARTS   AGEingress-nginx-controller-79cd59dd94-qfq5n   1/1     Running   0          4m14s[root@k8s-master01 ~]# kubectl label node k8s-master02.example.local ingress=true[root@k8s-master01 ~]# kubectl get pod -n ingress-nginx -o wideNAME                                       READY   STATUS    RESTARTS   AGE    IP             NODE                         NOMINATED NODE   READINESS GATESingress-nginx-controller-7f9fbd446-58bw2   1/1     Running   0          102s   172.31.3.112   k8s-node02.example.local     <none>           <none>ingress-nginx-controller-7f9fbd446-hmhng   1/1     Running   0          45s    172.31.3.102   k8s-master02.example.local   <none>           <none>ingress-nginx-controller-7f9fbd446-ps5hf   1/1     Running   0          45s    172.31.3.113   k8s-node03.example.local     <none>           <none>#在node上测试有问题,标签打到master上测试正常[root@k8s-master01 ~]# kubectl exec ingress-nginx-controller-7f9fbd446-hmhng -n ingress-nginx -- curl -Is nginx.nw-demoHTTP/1.1200 OKServer: nginx/1.19.6Date: Sun, 10 Apr 202211:27:24 GMTContent-Type: text/htmlContent-Length: 612Last-Modified: Tue, 15Dec202013:59:38 GMTConnection: keep-aliveETag: "5fd8c14a-264"Accept-Ranges: bytes

可以看到Ingress Controller和该Namespace下的Pod可以访问,其他Namespace不可以访问。此时可以创建一个Ingress,然后用域名测试:

[root@k8s-master01 ~]# kubectl  create ingress nginx  --rule="testnp.com/*=nginx:80" -n nw-demoingress.networking.k8s.io/nginx created[root@k8s-master01 ~]# curl -H "Host:testnp.com" 172.31.3.102<head><title>404 Not Found</title></head><body><center><h1>404 Not Found</h1></center><hr><center>nginx</center></body></html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凤舞飘伶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值