容器的SDN
很多人都说2017将是容器年,大卫也这么认为。但在很长一点时间里,容器与虚拟化都是相互依存,相互补充的问题。
之前笔者发表过一篇文章,放开眼界,看看开源界的新动向。里面清晰描述了未来两年内,整个开源界将会被主流目标客户采用的几项技术:
High-Performance Message Infrastructure、Interoperable Storage Encryption
Private IaaS、Container Networking、Open-Source Virtualization Platforms。可以看到,容器的SDN是很重要的一块。
在虚拟化时代,计算虚拟化(KVM、ESXi)、存储虚拟化(Gluster/Ceph/Gluster/ScaleIO/vSAN等)、网络虚拟化(ODL/NSX等)是云计算三大基础。在这里面,网络虚拟化是架构最复杂、最难理解的。同样,容器的SDN也是相对比较复杂。在以往与客户的交流以及在给学员的培训中,大家提的问题SDN方面很多。为了把容器SDN的问题彻底讲清楚,今天本topic专门花大篇幅进程阐述。
好了,废话少说,翠花,倒酒!
容器的企业级解决方案
关于什么是容器这些问题,本文不再赘述,想了解的同学请参照本公众号如下文章:大卫看Docker---第一篇。
基于Docker,各个厂商有自己的企业级容器解决方案。红帽的做法是:以Docker和K8S为基础,构建Openshift企业级解决方案。Openshift的开源版是Origin。而本文讨论的容器SDN,是基于红帽企业级Openshift。
Openshift的SDN
openshft的虚拟网络整体上是基于Openvswitch实现的。在开源界,虚拟网络默认使用Linux Bridge,后来随着Open vSwitch的兴起,目前RHEL既可以使用Linux Bridge,也可以使用OVS。
OVS可以简单理解成运行在虚拟化平台(例如 KVM,Xen)上的虚拟交换机。在虚拟化平台上,OVS 可以为动态变化的端点提供 2 层交换功能,很好的控制虚拟网络中的访问策略、网络隔离、流量监控等等。OVS 遵循 Apache2.0 许可证, 能同时支持多种标准的管理接口和协议。OVS 也提供了对 OpenFlow协议的支持,用户可以使用任何支持 OpenFlow 协议的控制器对 OVS 进行远程管理控制。
Openshift对网络端口的要求
Openshift的节点有Master节点、Node节点。
Master是OpenShift集群的管理节点,它包含管理组件,如API Server,controller manager server, 和etcd。Master节点通过Node节点上的服务管理Node节点,管理Node节点的健康状态。在生产环境中,通常至少部署3个master节点。
Node节点提供容器的运行环境。每个Node节点都被Master管理。Node节点可以是物理机,也可以是虚拟机(当然需要安装RHEL),甚至可以是云环境。生产中,Node节点通常多余一个。
而在Master和node之间的几种内部通讯组合中,需要开放的端口有:
Communication | Port | Description |
---|---|---|
Node to Node | 4789(UDP) | Required for SDN communication between pods on separate hosts. |
Node to Master | 53 or 8053 | Provides DNS services within the environment (not DNS for external access). New installations will use 8053 by default so that dnsmasq may be configured. |
4789(UDP) | Required between nodes for SDN communication between pods on separate hosts | |
8443 | Provides access to the API | |
Master to Node | 10250 | Endpoint for master communication with nodes |
4789(UDP) | Required between nodes for SDN communication between pods on separate hosts | |
Master to Master | 4789(UDP) | Required between nodes for SDN communication between pods on separate hosts |
53 or 8053 | Provides internal DNS services. New installations will use 8053 by default so that dnsmasq may be configured. | |
2379 | Used for standalone etcd (clustered) to accept changes in state. | |
2380 | etcd requires this port be open between masters for leader election and peering connections when using standalone etcd (clustered). | |
4001 | Used for embedded etcd (non-clustered) to accept changes in state. |
外部通讯需要开放的网络端口:
Type | Port | Description |
---|---|---|
External - Master | 8443 | CLI and IDE plug-ins communicate via REST to this port |
Web console runs on this port | ||
External - Node(or nodes) hosting Default Router(HAProxy) container | 80, 443 | Ports opened and bound to Default Routercontainer |
Proxy communication from external world to pods (containers) internally. |
Openshift-sdn的外提供访问的几种方式
openshift中service对外提供访问服务的四种方式,分别为:Cluster IP, NodePort, LoadBalancer、External IP。
Cluster IP是默认的的方式。在这种方式下,service将会暴露给cluster ip,cluster ip只能被内部访问。如果service想被外部访问,要想被外部访问,就要用router。利用openshift router的负载均衡机制,外部请求被直接转发到pod IP。cluster IP是集群内部svc之间通讯的时候的寻址方案。
NodePort
NodePort的方式,指的是将应用暴露到Node节点的一个静态端口。
Node Port其实在openshift每个node上开放同一个端口给特定的服务,访问的时候需要使用任意一个node+port就可以(默认端口范围是:30000-32767)。
LoadBalancer
LoadBalancer的方式指的是,将应用通过云提供商的负载均衡器暴露出去。
External IP
external ip是指集群外部的任意ip,绑定到node上。当外部访问service时,可以通过绑定到node的这个外部地址,实现对service访问。
service类型 | 应用场景 |
---|---|
Cluster IP | 默认的http的的访问请求,使用此方式。通过router转发。 |
NodePort | 客户端使用tcp访问service,使用本方式(默认端口:30000-32767)。 |
LoadBalancer | Openshif构建在到公有云上,使用本方式 |
External IP | 针对master/node IP不可达的情况。如果dns无法指向集群内部的ip,这时可以用一个集群外部的共有ip,绑定到某个openshift的node,然后配置dns,以实现客户端对service的访问。这种方式http和tcp访问方式都支持。 |
可能很多童鞋看了上面的描述还觉得有点绕,那么我们这么讲,大家一定能看明白:
我们搭建一套openshift环境,例如使用5三台服务器,一个master,两个node。
情况1:
如果master和node的FQDN都可以被外网访问。这种情况下,如果我们发布的server是通过http/https这种浏览器方式被客户端访问的,那么使用Cluster IP方式;
情况2:
如果一个应用,它不是http类的应用,并且也需要被外网访问,那么使用NodePort的方式;
情况3:
如果我们觉得把openshift搭在本地太麻烦,决定把openshift在搭建在aws或者azure上,发布的应用类型使用LoadBalancer的方式;
情况4:
我们搭建的这套openshift,如果master和node的ip都是私有网络IP,如172网段(这种情况并不少见),master的node的FQDN都无法被dns解析。这时候可以把一个可以访问到的公网IP,绑定到某个node节点,然后设置dns。这样发布的service就可以被外网访问了(被访问的方式是:External IP:port)。
需要注意的是,上面提到的router,就是为cluster ip这种方式的service提供服务的(实现被外部访问)。NodePort, LoadBalancer、External IP。这三种方式,与router压根儿没关系。
由于篇幅有限,接下来本文着重介绍Cluster IP这种默认方式。其它三种方式,将在后续文章中介绍。
从Pod谈起
在openshift中,最小的计算资源单位是pod。一个pod包含一个或者多个容器。一个Pod有一个IP地址,默认是10网段的。需要注意的是,一个pod中的多个容器,没有独立的IP,他们是共享pod的IP地址。
除此之外,每个service都有自己的IP,它默认的网段是172.30。一个service包含一个或者多个pod。
Pod IP:10.x.x.x网段
Service IP:172.30.x.x网段
Service IP只负责service之间(一个或者多个node上的多个pod)之间的通讯。我们想象一个场景,有个三层的应用,Nginx,tomcat,mysql,存在于三个pod上,属于三个service,有独立的三个server ip。他们之间的通讯,是通过service IP实现的。而不必绕去外网。除此之外,当一个pod重启以后,它10网段的IP地址会发生变化。但service ip不会发生变化,通过service IP,依然可以找到这个pod。
如果Nginx需要对外提供服务,那么就需要Routing layer的协助。Routing Layer也是一个pod,里面运行HAproxy。每个node节点上有一个Routing pod。HAproxy会获取到Pod的IP地址,并将应用对外提供的域名和80端口与Pod IP之对应起来。当对应关系建立起来以后,互联网请求访问域名时,请求就会直接链接到Pod IP上(如果一个应用有多个Pod,HAproxy胡会自动实现负载均衡),而不必再经过Service IP层。
Service IP与pod IP的对应关系是也可以清晰查到的:
如下图,service ip后端对应的两个pod的IP,分别是10.1.0.17和10.1.0.22。
而service ip与pod之间的关系,是存在openshift的etcd数据库中的。etcd存在与master节点上。
etcd是什么?
A highly-available key value store for shared configuration and service discovery.
接下来,我们看看routing layer(router)和pod之间的关系
Routing layer作用是提供对外网服务,他是用的是HA Proxy。把外部的请求,路由到内部。routing layer的服务由一个pod提供。routing定期通过service ip获取到pod的IP(因为pod ip可能会经常发生变化),并刷新对外提供服务的FQDN与pod ip之间的关系。routing将对外服务的域名,指向到pod ip,而非service ip。
目前,openshift routing能够提供如下网络服务:
http/https(with SNI)
websockets TLS(with SNI)
我们在实验环境进行查看:首先进入到routing对应的pod,查看HAproxy的配置文件(haproxy.conf),在配置文件中,最后部分是backend的对应地址,也就是pod的地址:
查看配置文件中前面的部分,可以查看/var/lib/haproxy/conf/os_http_be.map。该文件记录了Routing对外提供的域名。
查看该配置文件,可以看到对外暴露的域名:
其中,bookstore-template.cloudapps6.example.com就是应用对外的域名。通过它,用户就可以访问这个域名,然后请求按照负载均衡的方式会,被转发到两个Pod上,它们的IP是:10.1.0.17、10.1.0.22。
一个案例说清网络访问场景(cluster ip的方式) 举个例子,我们从笔记本客户端,要访问myadd.mydomain.org:80。在浏览器输入这个地址以后:
第一步DNS将会解析这个域名,将它解析成运行routing layer pod的node IP。
第二步:客户端对80端口的访问请求,将会到达routing layer。
第三步:routing查看FQDN对应的enpoints(Pod IP)
第四步:routing将请求指向到pod IP:10.1.0.2:8080.