干货巨献:Openshift3.9的网络管理大全.加长篇---Openshift3.9学习系列第二篇
作者:互联网
一、OVS的三种模式
OpenShift的OVS网络组件有三种模式:ovs-subnet、ovs-multitenant、ovs-networkpolicy。
ovs-subnet是默认的OVS插件模式,提供扁平的pod网络。每个pod都可以与其他pod和服务进行通信。
ovs-multitenant为pod和服务提供项目级隔离,每个项目都会收到唯一的虚拟网络ID(VNID),用于标识分配给项目的pod的流量;一个项目中的Pod无法向不同项目中的pod和服务发送数据包或从其接收数据包(除非用命令行将两个项目join一起)
ovs-networkpolicy提供pod级别隔离。它完全由NetworkPolicy对象控制。这样,网络控制的规则,可以由集群管理员下放到项目管理员。项目管理员可以创建网络策略,例如配置项目的入口规则保护服务免受***。
二、OVS网络隔离的实现
在OCP中,如果配置了ovs-multitenant或者ovs-networkpolicy可以实现网络隔离。那么,网络隔离是如何实现的呢?
在ovs-multitenant模式下,每个项目将会被分配一个VNID。
每个项目都会收到唯一的虚拟网络ID(VNID),用于标识分配给项目的pod的流量
一个项目中的Pod无法向不同项目中的pod和服务发送数据包或从其接收数据包
也就是说,不同VNID的pod之间是无法互相通讯的。
但有一种例外,VNID为0的项目,能够默认被所有项目的pod访问,原因何在?
VNID为0的项目是default项目。
default项目的pod是router和registry,它本身就需要其他项目的pod的访问。
在ovs-networkpolicy模式下,每个项目将会被分配一个VNID。
ovs-networkpolicy提供pod级别的隔离。它的隔离策略的特点:
完全由NetworkPolicy对象控制
项目管理员可以创建网络策略,而不仅仅是集群管理员
networkpolicy只对入口流量有效
我们分析三种流量的网络隔离策略:
第一种:通一个node上不同项目之间pod的通讯
针对于VNID非0的项目:
1.网络包从一个VNID非0的pod发出;
2.OVS桥br0标记带有项目分配的VNID的数据包,如果
子网和VNID匹配
所需的端口和源IP匹配(仅对ovs-networkpolicy)
3.OVS桥允许将数据包传递到目标pod
第二种:不同node上不同项目之间pod的通讯
一个网络包通过一个VXLAN tunnel传过来
Tunnel ID被当做 VNID,当
Tunnel ID与目标pod VNID匹配;
并且
端口对源地址开放(仅限ovs-networkpolicy)
OVS桥允许数据包传递到本地pod
第三种:从本node发出网络包
网络包被标记VNID
网络包被送到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中;
每个pod都有自己的虚拟以太网接口(veth0)和唯一的IP地址(一般是10网段的,可以进行网段的设置)。
Docker使用Linux bridge lbr0来连接所有容器
创建pod后,OpenShift将veth接口连接到br0 OVS桥上的开放端口,并创建网络流入pod的规则
OVS上的每个端口,都标记有pod所在项目唯一的VxLAN ID,它控制不同OpenShift项目之间的访问
几乎所有数据包传送决策都是使用br0 OVS桥中的OpenFlow规则执行的
设备 | 描述 |
br0 | pod链接到ovs的接口 |
lbr0 | 用于Docker的Linux桥接设备。 它会被分配群集pod子网网关地址(10.1.x.1 / 24)。 |
tun0 | OVS内部端口(br0上的端口2)。它也被分配群集子网网关地址,并用于外部网络访问。 OpenShift SDN配置netfilter和路由规则,以支持通过NAT从集群子网到外部网络的访问。 |
vxlan0 | OVS 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的通讯,分为两大种情况:
1.pod之间的通讯
2.pod与外部网络的通讯
针对第一种情况,pod直接的通讯又分为两种情况:
1.1 两个pod在一个OCP Node上:
例如Pod1要和pod2进行通讯(在ovs的br0中转发是依赖于Openflow的流表规则):
通讯流量是这样走的:pod1的eth0→veth1→br0→veth2→pod2的eth0
1.2 两个pod在不同的OCP Node上:
Pod使用VxLAN与对端node上的pod进行通信
在对端node收到数据包时,使用对端的VxLAN识别internal IP
我们举个例子:
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通讯的流量总图如下:
总结:
一个OCP Node上的pod通讯,流量只会到br0,不会到节点的物理网卡;
不同OCP Node上的pod通讯,流量会穿过VXLAN和OCP Node的物理网卡,最终到另外一个OCP Node的物理网卡和vxlan,流量不会走到交换机的uplink。
一个pod与外网进行通讯,流量领过br0的时候,不会再经过vxlan,而是通过iptables进行地址转换,转换为OCP的Node IP,对外进行通讯。
关于iptables的表,可以通过iptables -L进行查看:
#iptables -L
名词解释:
br0: the OVS bridge device that pod containers will be attached to. OpenShift SDN also configures a set of non-subnet-specific flow rules on this bridge.br0会被分配Pod IP的网关。br0也是OVS的网桥,pod会与这个网桥链接在一起。CNI - openshift-sdn负责分配IP,具体而言,是在每个OCP节点的/opt/cni/bin下二进制的程序负责:
tun0: an OVS internal port (port 2 on br0). This gets assigned the cluster subnet gateway address, and is used for external network access. OpenShift SDN configures netfilter and routing rules to enable access from the cluster subnet to the external network via NAT. tun0是OVS的内部端口,是整个OCP集群的默认网关,pod对外网的访问流量,从tun0出去。
vxlan:该端口用于OCP Node之间pod通讯的流量,东西向流量。
七、OCP的几种对外网络提供方式
Openshift/Docker中,我们会遇到(听到)的几种网络端口模式有:
Hostport
Nodeport
Hostnetwork
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