其他分享
首页 > 其他分享> > 干货巨献:Openshift3.9的网络管理大全.加长篇---Openshift3.9学习系列第二篇

干货巨献:Openshift3.9的网络管理大全.加长篇---Openshift3.9学习系列第二篇

作者:互联网

一、OVS的三种模式


OpenShift的OVS网络组件有三种模式:ovs-subnet、ovs-multitenant、ovs-networkpolicy。



二、OVS网络隔离的实现


在OCP中,如果配置了ovs-multitenant或者ovs-networkpolicy可以实现网络隔离。那么,网络隔离是如何实现的呢?


在ovs-multitenant模式下,每个项目将会被分配一个VNID。


也就是说,不同VNID的pod之间是无法互相通讯的。


但有一种例外,VNID为0的项目,能够默认被所有项目的pod访问,原因何在?


VNID为0的项目是default项目。

图片

default项目的pod是router和registry,它本身就需要其他项目的pod的访问。


在ovs-networkpolicy模式下,每个项目将会被分配一个VNID。

ovs-networkpolicy提供pod级别的隔离。它的隔离策略的特点:



我们分析三种流量的网络隔离策略:


第一种:通一个node上不同项目之间pod的通讯

针对于VNID非0的项目:

1.网络包从一个VNID非0的pod发出;

2.OVS桥br0标记带有项目分配的VNID的数据包,如果

3.OVS桥允许将数据包传递到目标pod



第二种:不同node上不同项目之间pod的通讯

  1.  一个网络包通过一个VXLAN tunnel传过来

  2. Tunnel ID被当做 VNID,当

    Tunnel ID与目标pod VNID匹配;

    并且

    端口对源地址开放(仅限ovs-networkpolicy)

  3. OVS桥允许数据包传递到本地pod



第三种:从本node发出网络包

  1. 网络包被标记VNID

  2. 网络包被送到VXLAN tunnel



三、ovs-networkpolicy策略详解


ovs-networkpolicy的配置,目前在OCP中没有图形化界面,需要cli配置。

ovs-networkpolicy的策略很强大,生产上使用需要进行详细和具体的设计。


默认情况下,本项目所有的pod都不能被其他项目的pod访问。


场景1:设置本项目所有的pod的任意端口都不能被外部pod访问。

图片然后执行:oc create -f ./deny-by-default.yaml -n my_secure_project


场景2:设置本项目所有的pod可以被default项目中的pod访问(路由和镜像仓库)

oc label namespace default name=default

图片


场景3:本项目所有pod之间客户相互访问,但不能被其他项目访问:

图片

图片


场景4:本项目所有pod都能访问项目内pod的80和443端口,其他项目不成:

图片

图片

场景5:本项目所有pod可以访问项目内被标记为红色的pod,其他项目不成:

图片

图片



场景6:所有pod都能访问项目A内的,被标记为红色的pod:

图片

图片


场景7:只允许本项目内,标记为蓝色的pod对标记为红色pod的访问,其他项目不成:

图片

图片


场景8:项目a中的pod,彼此之间可以被访问;项目a中的pod,可以被用户名为alice的项目中的pod访问:


oc label namespace project-a user=bob

oc label namespace project-b user=alice


图片

图片



四、Openshift OVS三种工作模式的设置


OVS的三种工作模式:ovs-subnet、ovs-multitenant、ovs-networkpolicy是如何设置的呢?


在OCP中,OVS模式的设置,在master和node上是分别设置的。


每个Master上的配置文件:

/etc/origin/master/master-config.yaml 

默认是redhat/openshift-ovs-subnet,如果想改成租户隔离,则修改为:redhat/openshift-ovs-multitenant

图片

每个Node上的配置文件:/etc/origin/node/node-config.yaml

图片

默认是redhat/openshift-ovs-subnet,如果想改成租户隔离,则修改为:redhat/openshift-ovs-multitenant。



五、Openshift SDN访问流量详解---从外向内


接下来,我们看一下,应用访问容器里的应用的详细介绍。

举个例子,我们从笔记本客户端,要访问myadd.mydomain.org:80。在浏览器输入这个地址以后:


第一步:DNS将会解析这个域名,将它解析成运行routing layer所在的OCP Node IP。这就需要数据中心的DNS,将应用的FQDN,解析成OCP集群物理服务器的IP地址(如果OCP集群有两个router,那需要给两个router所在的两个物理服务器的IP配置一个VIP,然后将应用的FQDN解析成这个VIP)。


第二步:客户端对80端口的访问请求,将会到达routing layer。


第三步:routing查看FQDN对应的enpoints(Pod IP)。如果pod在routing layer所在的ocp节点,那么直接内部通讯。


第四步:如果pod不在routing layer所在的ocp节点,请求将会通过OCP内部的OVS,转到到对应ocpnode上的pod IP:10.1.0.2:8080。图片


Routing Layer的负载均衡功能

当一个应用对应多个pod,routing layer将会做不同pod之间的负载均衡。负载均衡有三种策略:

roundrobin、leastconn、source。

roundrobin:根据每个pod的权重,平均轮询分配。在不改变routing的默认规则下,每个pod的权重一样,haproxy转发包也是轮着来。


如果我们更改了每个pod的权重,那轮询的时候,也会根据权重来转发请求。如下图:V2和V1是3:2。那么haproxy会给V2发两个包,给V1发一个,周而复始。

图片


leastconn:routing layer转发请求的时候,按照哪个pod的连接数最少,将新的请求发给连接数最少的pod。一般这种方式适合长连接,短链接不建议使用。


source:将源IP地址先进行哈希,然后除以正在运行的pod总权重,然后算出哪个节点接受请求。这确保了只要没有服务器发生故障,相同的客户端IP地址将始终到达同一个pod。 


三种方式,可以通过设置routing layer的环境变量来实现。




六、Openshift SDN访问流量详解---从内向外


在OCP中;

图片


设备描述
br0pod链接到ovs的接口
lbr0用于Docker的Linux桥接设备。  它会被分配群集pod子网网关地址(10.1.x.1 / 24)。
tun0OVS内部端口(br0上的端口2)。它也被分配群集子网网关地址,并用于外部网络访问。  OpenShift SDN配置netfilter和路由规则,以支持通过NAT从集群子网到外部网络的访问。
vxlan0OVS  VXLAN设备(br0上的端口1),用于访问远程节点上的容器。
vlinuxbr and vovsbr两个Linux对等虚拟以太网接口。  将vlinuxbr添加到lbr0,并将vovsbr添加到br0(带有ovs-subnet插件的端口9和带有ovs-multitenant插件的端口3),以便为在OpenShift外部直接使用Docker创建的容器提供连接。



那么,Pod的IP如何被分配的呢?

第一步:Docker使用lbr0网桥,OpenShift SDN已为其分配了网关网关地址(10.1.x.1 / 24),给pod分配IP。


第二步:OpenShift将pod的veth接口对的主机端从lbr0桥移动到br0 OVS桥


第三步:将OpenFlow规则添加到OVS数据库,以便路由寻址到新pod的流量能够访问到正确的ovs端口。


第四步:当使用ovs-multitenant插件时,OpenFlow规则将添加到:

1.使用pod的VNID标记来自pod的流量

2.如果流量的VNID与pod的VNID匹配,或者虽然不匹配,但pod所在的项目是是特权项目(如default项目或openshift-infra(VNID 0)),则允许流量进入pod。


使用ovs-networkpolicy时,还会添加OpenFlow规则:将传入对象的标记与规则匹配。如果进入流量负责规则,则允许通过,否则将会被拒绝。



Pod的通讯,分为两大种情况:


针对第一种情况,pod直接的通讯又分为两种情况:

1.1 两个pod在一个OCP Node上:

例如Pod1要和pod2进行通讯(在ovs的br0中转发是依赖于Openflow的流表规则):


通讯流量是这样走的:pod1的eth0→veth1→br0→veth2→pod2的eth0

图片

1.2 两个pod在不同的OCP Node上:



我们举个例子:

Pod1(OCP NodeA)要与Pod3(OCP NodeB)进行通讯。


通讯流量是这样走的:Pod1的eth0→veth1→br0→vxlan0→ OCP nodeA的eth0网卡→OCP NodeB的eth0网卡→vxlan0→br0→veth3→ Pod3的eth0.

图片



2. pod对外通讯:

PodA的eth0→vethA→br0→tun0→ 通过iptables实现NAT→ OCP nodeA的eth0(physical device) → Internet


所以,综上所述,pod通讯的流量总图如下:

图片

总结:

  1. 一个OCP Node上的pod通讯,流量只会到br0,不会到节点的物理网卡;

  2. 不同OCP Node上的pod通讯,流量会穿过VXLAN和OCP Node的物理网卡,最终到另外一个OCP Node的物理网卡和vxlan,流量不会走到交换机的uplink。

  3. 一个pod与外网进行通讯,流量领过br0的时候,不会再经过vxlan,而是通过iptables进行地址转换,转换为OCP的Node IP,对外进行通讯。

关于iptables的表,可以通过iptables -L进行查看:


#iptables -L

图片


名词解释:



七、OCP的几种对外网络提供方式



Openshift/Docker中,我们会遇到(听到)的几种网络端口模式有:

  1. Hostport

  2. Nodeport

  3. Hostnetwork

  4. router


它们有什么区别,适用于什么场景?我们先看看它们的作用。



1. 什么是hostport?


hostport它指的是:在一个宿主机上运行的一个容器,为了外部能够访问这个容器,将容器的端口与宿主机进行端口映射。而为了避免宿主机上的端口占用,在容器和宿主机做端口映射的时候,通常映射一个比较大的端口号(小端口被系统服务占用)。


我们看一个实验:

在宿主机上启动一个apache的容器,将容器的端口80,映射成宿主机的端口10080.

图片


然后,我们查看这个容器的网络模式,可以看到,该容器使用的是hostport的模式,占用宿主机的端口号是10080.


图片

我们查看容器的IP,地址为:172.17.0.2

图片



接下来,我们验证apache服务。


首先,图形化登录宿主机,访问宿主机的80端口(确保宿主机的httpd服务是停止的),无法访问:

图片

接下里,访问宿主机的10080端口,可以访问apache网页,说明此时访问的服务,是容器中的:

图片

接下来,我们通过外部访问宿主机的域名加10080端口号,可以成功:

图片

接下来,我们再做一个验证,在宿主机上,直接访问容器的IP和80端口,可以通:

图片

截至到目前,我们对hostnetwork应该有一个比较清晰的了解了。它将容器与宿主机的端口做映射,是为了从外部可以访问到容器。而如果容器不需要被外部访问,则不需要做hostport。


2.什么是nodeport?


nodeport与hostport最重要的一个区别是,hostport是针对一个单宿主机的一个容器的;而nodeport是针对于K8S集群而言的。


在Openshift中,我们知道每个pod有一个IP,通常网段是10.开头的;同时OCP中还有service ip。而nodeport指的是:将service ip和端口,映射到OCP集群所有node的node ip和指定的端口号(通常是大端口:30000-32767)。


为什么将service ip和OCP中所有node做映射?

因为service ip在OCP中是跨node的。


我们看一个service的yaml配置文件,这是一个mysql的service:

图片

这个配置的含义是,采用nodeport的方式,将mysql server的IP和node ip映射,serivce的源端口是3306,映射到node的端口是30306(大端口)。


这样配置完毕以后,外部要访问pod,访问的是nodeip:30306。然后访问请求通过iptables的NAT将nodeip和端口转化为:service ip和3306端口,最终请求通过service负载均衡到pod。

图片


nodeport的缺点很明显:宿主机端口浪费和安全隐患,并且数据转发次数较多多。




3. 什么是hostnetwork

在hostnetwork模式下,pod的IP和端口会直接绑定到宿主机的IP和端口。应用访问的时候,访问宿主机的IP和端口号后,这个请求直接转到pod和相同的端口(不经过iptables和SVC)。也就是说,这种情况下,pod的IP就是宿主机的IP,pod暴露哪个端口,宿主机就对外暴露哪个端口。


我们看一下pod的dc:

图片

上面的配置文件中,打开了hostnetwork模式.。pod部署以后,pod的IP直接就是宿主机的IP。


例如,在Openshift中,router就是hostnetwork模式。下图中node.example.com是node,IP是192.168.137.102,pod的IP也是这个:

图片

我们查看pod暴露的端口,有三个:80、443、1936:

图片

再查看pod和node端口关系,port的端口和node的端口也是一致的。



hostnetwork相比于nodeport,好处在于转发路径短。缺点是占用node的实际端口,无法在用一个节点同时运行相同端口的两个pod。同时,hostnetwork无法跨node。



4.什么是router

在Openshift中,有router的概念。router的作用是对外暴露service的FQDN。

那么,router的本质是什么?


router本质上,一个router是以hostnetwork方式运行在一个node上的容器化hproxy,它的pod ip就是所在node的ip,对外暴露的端口就是:80、443、1936。

图片


客户端访问某一个应用,如在浏览器中输入http://productpage-istio-system.apps.example.com,首先外部DNS将这个域名解析成router所在node的IP,即:192.168.137.102。

图片


图片


然后,请求到达router所在的node以后,会查询到对应的route信息,查到route对应的service的名称:httpd。此时,通过查询etcd,获取到service和相关的信息。将请求通过service负载均衡发给后端的pod。


实验展示如下:


查看route名称:

图片

下图显示的是路由信息:

域名productpage-istio-system.apps.example.com转化为svc,名称为productpge,端口是http:

图片

下图为svc的配置,展示svc的端口以及后端pod的端口以及pod的名,productpge

图片


下图显示的是pod的dc,其中显示pod的名称和端口:

图片

图片

也就是说,productpge这个pod,对外暴露的是9080端口给service,名称为productpge,端口为9080。然后,route将service productpge暴露为productpage-istio-system.apps.example.com,并且端口转为80、443、1936,而外部的DNS将productpage-istio-system.apps.example.com可以解析成router所在的node的IP。


所以说,router就是一个以hostnetwork方式运行在node上的容器化haproxy,它占用了node的80、443、1936端口。所以,这也是为什么一个node上只能运行一个router的原因所在。


5.结论

1. 在OCP中,对于http/https类的应用,对往外通过router暴露的FQDN访问即可。


2. 在OCP中,对于非http/https类的应用,如mysql,存在两种情况:


2.1. 不需要对外提供服务,那么前端应用通过内部service ip访问mysql即可,无需对外暴露;

2.2.需要被外部访问,则需要对外暴露端口,存在两种情况:

(1)应用单副本,无需在多个node上运行,优先使用hostnetwork方式

(2)应用多副本,在多个node上运行,使用nodeport的方式。

整体而言,hostnetwork的方式转发路径短,性能比nodeport好。



八、OCP vs K8S: 如何配置一个安全的OCP路由


而K8S有三种被外部访问方式:NodePort,LoadBalancer 和 Ingress。


我们先开看看OCP和K8S在网络访问方面的异同。



1.OCP的Service IP和K8S的Cluster IP


OCP中的service IP,对应的是K8S的 ClusterIP;无论是Service IP和ClusterIP,都无法被外部直接访问。


而Openshift的Nodeport和K8S的Nodeport是十分类似的;

Nodeport在OCP指的是:将service ip和端口,映射到OCP集群所有node的node ip和指定的端口号(通常是大端口:30000-32767)。




OCP:

图片

在Openshift中,我们知道每个pod有一个IP,通常网段是10.开头的;同时OCP中还有service ip。而nodeport指的是:将service ip和端口,映射到OCP集群所有node的node ip和指定的端口号(通常是大端口:30000-32767)。


为什么将service ip和OCP中所有node做映射?

因为service ip在OCP中是跨node的。


我们看一个service的yaml配置文件,这是一个mysql的service:

图片

这个配置的含义是,采用nodeport的方式,将mysql server的IP和node ip映射,serivce的源端口是3306,映射到node的端口是30306(大端口)。


这样配置完毕以后,外部要访问pod,访问的是nodeip:30306。然后访问请求通过iptables的NAT将nodeip和端口转化为:service ip和3306端口,最终请求通过service负载均衡到pod。



K8S:Nodeport在K8S的定义如下,从描述看,与OCP的nodeport相同:

NodePort: Exposes the service on each Node’s IP at a static port (the NodePort). A ClusterIP service, to which the NodePort service will route, is automatically created. You’ll be able to contact the NodePort service, from outside the cluster, by requesting <NodeIP>:<NodePort>.


图片



在K8S中,还有一个概念:kube-proxy。为service增加proxy,是为了service在集群,被通过API方式访问:

图片

$ kubectl proxy --port=8080

然后可以通过这种方式在内部访问服务:

http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/


kube-proxy目前支持三种模式: userspace、iptable、IPVS


k8s的最开始版本使用的userspace模式,所有的客户端请求svc,都需要先经过iptables,然后在经过kube-proxy到pod,性能很差。

图片


所以在后期版本中K8S增加了iptables方式,在iptables方式下,客户端请求svc会直接经过iptabls转发到pod,而不需要再经过kube-proxy,提高了性能。

图片

但是kube-proxy的缺点是仅支持轮询的负载方式,而且一样存在iptables规则过多导致的性能(iptables是自上而下的匹配,一旦规则多会很慢)。所以目前社区在研究IPVS方式。与iptables类似, ipvs也基于 netfilter 。但ipvs使用哈希表作为底层数据结构,并在内核空间工作,因此IPVS重定向交通速度更快,具有更好的性能。

图片

IPVS支持如下负载均衡策略:

rr: round-robinlc: least connection

dh: destination hashing

sh: source hashing

sed: shortest expected delay

nq: never queue



目前,Openshift安装的时候,模式使用Proxy-mode: iptable模式,也可以修改为Proxy-mode: userspace模式,但没这个必要(因为userspace模式已废弃)。

图片

目前Proxy-mode: ipvs由于在K8S 1.10目前是beta版本,因此在OCP中还没有支持。在K8S正式发布IPVS功能后,相信OCP会同步支持。



2、OCP的router和K8S的Ingress


OCP的router和K8S的Ingress是十分类似的,router和Ingress都是对外暴露http/https类域名。



OCP:


outer本质上,一个router是以hostnetwork方式运行在一个node上的容器化hproxy,它的pod ip就是所在node的ip,对外暴露的端口就是:80、443、1936。

图片


客户端访问某一个应用,如在浏览器中输入http://productpage-istio-system.apps.example.com,首先外部DNS将这个域名解析成router所在node的IP,即:192.168.137.102。

图片


图片


然后,请求到达router所在的node以后,会查询到对应的route信息,查到route对应的service的名称:httpd。此时,通过查询etcd,获取到service和相关的信息。将请求通过service负载均衡发给后端的pod。




K8S:

图片

图片

区别是,OCP用的是容器化的haproxy做router;K8S默认用GCE/Nginx做这件事。



查看ingress的配置:

图片


我们看一下router的dc,我们可以看到,router的安全策略更多一些。

图片



3、K8S的Loadbalance模式


LoadBalancer是K8S借助于cloud provider提供的LoadBalancer,实现被外部网络访问。


LoadBalancer: Exposes the service externally using a cloud provider’s load balancer. NodePort and ClusterIP services, to which the external load balancer will route, are automatically created.


Service的Loadbalancer指定以后,Loadbalancer会将各类访问请求转发到service,HTTP,TCP,UDP,Websocket,gRPC 或其它任意种类。Loadbalancer将会被cloud provider分配一个IP地址。


图片

我们来看一个service的yaml文件,其中制定了loadbalancer:

图片



4、Openshift的路由安全

上面内容已经提到,OCP使用router方式对外暴露80/443/1936端口,为web类应用提供对外访问。


那么,路由的安全如何保证呢?


默认情况,我们expose server,会生成一个域名,这个域名的端口是80:

图片

浏览器访问:

图片


OCP提供三种路由安全策略:Edge Termination(边界终止)、Pass-through Termination(直通终止)、Re-encryption Termination(重加密终止)



Edge Termination(边界终止)的模式,是将安全证书加密到路由上。


Pass-through Termination(直通终止)。这种模式,安全加密不设置在路由上,而是设置在pod中通过证书加密。


Re-encryption Termination(重加密终止),这种模式指的是pod和路由上同时加密,但使用不同的证书。


无论使用哪种方式,创建安全路由以后,应用FQDN的80端口将不能被访问。


我们通过实验进行验证。


我们先生成一个key并进行签名:

图片


图片


然后将旧的路由删掉:

图片

创建新的边界路由:

图片

创建好以后,再度通过80端口方式,失败:

图片

通过443访问,出现安全提示:

图片

添加证书后,可以访问:

图片



5、结论


Kubernetes vs Openshift,谁的网络更安全?


实际上,由于OCP是基于K8S,并且红帽写了大量的K8S代码(2017年K8S代码贡献量第一),因此OCP的网络架构和K8S可以说系出同源。




八、生产环境中的网络规划


OCP如何做网络规划这个问题,很多人问过我。我们举个例子来说明。


一个客户有10台物理服务器,前3台做Master,后7台做node节点。存储使用NAS。


网络规划:

网络1:默认的Openshift集群内部使用的网络(不与网络冲突即可)

在这个网络中,有两个网段:Service IP网段和Pod IP网段(通通过编辑

/etc/origin/master/master-config.yaml进行修改)。

ServiceIP默认网段是172.30.0.0/16

图片

Pod IP的默认网段是:10.128.0.0/14

图片

这两个网段,都不需要分配数据中心IP。


网络2:生产环境业务网络:共需要12个IP。

其中:10个物理服务器,每个都需要1个IP。而因为Master节点是三个,需要有高可用,因此需要一个VIP。客户端访问Master的时候(通过浏览器、命令行),访问这个VIP即可。此外,为了保证routing layer的高可以用,在两个物理服务器上配置分别部署两个routing,然后给两台服务服务器配置一个VIP。


网络3:NAS网络。

需要保证10台物理服务器都可以与NAS网络正常通讯,因此需要配置与NAS网络可通讯的IP地址,每个服务器需要一个。


网络4:服务器硬件管理网络。

如果是HP服务器,硬件管理口是ilo,如果是联想的服务器,管理口是IMM。也需要10个IP。


因此,物理服务器配置,建议每个服务器至少配置两个双口网卡。前两个网卡做NIB,配置的是网络2:生产网络IP。后两个双口网卡配置NIB,配置网络3的IP,负责与NAS通讯。服务器一般有单独的物理管理口,不需要PCI网卡提供端口。


标签:node,service,IP,端口,---,巨献,OCP,pod,Openshift3.9
来源: https://blog.51cto.com/u_15127570/2713360