查看原文
其他

干货 | 携程混合云之kubernetes@AWS揭秘

陈丹双 携程技术中心 2019-05-01

作者简介

 

陈丹双,携程高级工程师,毕业于复旦大学,积累多家公有云对接经验,持续探索混合云,关注kubernetes、docker等容器技术。


一、引言


随着携程国际化战略的实施和业务海外的部署,混合云已经达到了一定的规模。为了支持容器,我们在AWS上部署了kubernetes。主要是弹性发布的考量。


应用交付效率上,初始化一台虚拟机在分钟级别,涉及操作庞杂,除了启动虚拟机,还需要进行应用环境的安装,而拉起一台容器的时间可以控制在秒级。


运维管理效率上,虚拟机的维护比容器要复杂得多,特别是在目前有着多个公有云合作厂商的情况下,工作量和复杂程度爆增。以镜像为例,自定义的镜像无法在不同公有云厂商之间进行统一,比如AWS的C5等系列要求我们安装ENA(Elastic Network Adapter)增强联网的驱动。目前我们在几家公有云上的自定义镜像是也是保持着异构的状态。


所以,引入了混合云的容器发布。借助着业务在AWS的应用部署,有了Kubernetes@AWS的主题。

 

二、为什么在AWS上自己搭建kubernetes


首先是网络上的重要考量,目前适配携程的技术体系需要满足两个网络适配的要求。


IP直连:存在应用互访是通过IP进行访问的情况,而且携程的应用并非全是跑在kubernetes集群中,所以为了保证全局应用的可用性,就需要容器的IP是kubernetes集群外可见的IP。


IP固定:假设应用在重新发布时IP发生变化,那必须有良好的机制通知各个相关的系统。但是目前存在有运维等系统的架构是基于IP固定这一前提条件。要完善这种架构不是一时之力,所以我们在这期项目的折中方案是适应当前的技术架构,保证容器应用在发布新版本、宿主机故障重新拉起容器时,IP保持不变。


这两个需求都不是kubernetes可以原生支持的,kubernetes提供了Service、Ingress等服务暴露的方法,但是不能完全满足我们上述的网络需求,社区现成的flannel、calico等容器网络方案不能采用,需要进行定制化。


同时,我们也针对kubernetes做了调度等层面的自定义开发。虽然AWS也发布了kubernetes产品——EKS,是一套打通公有云的方案,但也意味着失去了自定义开发及控制平面运维管控的能力。所以,我们也没有采用公有云的原生kubernetes产品。


出于对定制化能力和控制平面管控的需求,也为了与私有云IDC的技术栈保持一致,尽量减少运维成本,进行过一系列的调研之后,我们最终选择自己在云上搭建kubernetes集群。

 

三、如何在AWS上自己搭建kubernetes


主要从网络、镜像、日志监控这几个组件讲讲我们的方案选型及设计。

 

3.1 网络


容器网络的方案我们是基于VPC(Virtual Private Cloud)和弹性网络接口(Elastic Network Interface,ENI)进行实现的。


VPC是一个用户在公有云上自建的隔离的虚拟网络,可以自定义地址空间段。我们以一个地域(Region)为例,一个VPC在其中一个可用区内的架构大致如下:



每个可用区内有一个公有子网和一个私有子网。


公有子网:想对internet直接暴露的服务一般部署在公有子网,给实例绑定一个弹性IP(公有IP),Internet即可通过这个公网IP进行访问。公有子网里的缺省路由是Internet网关,由Internet网关实现VPC与Internet之间的通信。其中公有子网里有个NAT网关,有对应的私有IP和公有IP。NAT网关的作用是允许私有子网中的实例连接到Internet或其他AWS服务的能力,但能避免Internet直接连接这些实例。


私有子网:一般用于供内部调用的服务,如数据库、应用后端等。私有子网里的实例如果要访问Internet上的服务,与私有IDC里通过代理实现的方式不一样,会通过描述私有子网的缺省路由为公有子网里的NAT网关,实现与Internet的通信。


图上子网里实例的私有IP地址,其实是和弹性网络接口(Elastic Network Interface,ENI)相关的。它是VPC中的一个逻辑网络组件,特性如下:


1个弹性网络接口具有:

  • 1个Mac地址

  • 1个主要私有IP地址

  • 多个辅助私有IP地址

  • 1个或多个安全组,需要注意的是:ENI上的IP地址的安全组是跟随着对应网卡的安全组而决定出入控制的。


其中每个私有IP地址都可以绑定一个弹性IP。


一个实例可以有多个弹性网络接口:

  • 1个主要弹性网络接口,不可插拔,如ifconfig中eth0

  • 多个辅助网络接口,可动态插拔,如ifconfig中eth1~n



实例可以容纳的最大弹性网络接口数和每个接口的最大IP地址个数是有限制的,一般来说,与实例类型相关,总体来说与配置成正比。


利用弹性网络接口,我们衍生出了两种容器网络的子方案:

  • 单网卡多IP

  • 单网卡单IP

 

单网卡多IP容器网络方案



该方案的网络要点在于:一个弹性网络接口绑定多个IP,其中第一个IP(主要私有IP)作为宿主机对外通信的IP,后面的IP(辅助私有IP)是容器的IP。容器和宿主机之间通过veth pair进行连接,同时通过路由表进行容器出入流量的控制。

 

单网卡单IP容器网络方案



虽然一个弹性网络接口可以绑定多个IP,但是也提高了复杂性。该单网卡单IP子方案中,把一个弹性网络接口作为一个调度的单元,每个弹性网络接口只使用一个IP,其中第一个弹性网络接口(eth0)留给宿主机继续使用,在实例上添加新的辅助弹性网络接口(eth1-N),并把弹性网络接口移入容器的网络命名空间,直接给容器使用。对比如下:


网络子方案

优势

劣势

单网卡多IP

可部署的Pod个数较多,资源利用率高

调度层要选择可容纳新IP的网卡

IP的出入访问由网卡的安全组决定,调度还需选择对应安全组的网卡

单网卡单IP

独占网卡设备,便于隔离

无新增的虚拟化层,性能是网卡的性能

只利用了网卡上的第一个IP,可容纳的Pod个数较少,资源利用率相对较少

 

我们最终选择了单网卡单IP的子方案,原因在于,比较简单,同时,承载的主要是Java应用,对应的配置一般不会太低,不会有很明显的资源浪费。

 

可以看到,如上的网络方案,由于容器的IP其实是VPC范畴地址空间段的IP地址,所以可以达到kubernetes集群外IP可见的需求,还有IP固定的需求,是这么实现的。



有一个全局的IP地址管理模块,简称GIPAM,它存储了pod name(我们使用的是Statefulset)与IP的一对一关系,并负责与公有云进行API的交互。Node上的cni插件,在设置网络前,先去GIPAM上查询一下IP的信息。应用重新发布,故障迁移,重新拉起一个容器时,不管是不是在同个node,由于cni插件会以GIPAM作为IP的数据源,这样就保证了IP的固定和漂移。


举个创建容器网络的图例:



3.2 镜像


我们在私有云的每个IDC都部署了一套Harbor,组成了Harbor集群联邦,并且跨越测试环境和生产环境。并使用了DNS劫持的技术,推送docker镜像时,是往各自IDC的Harbor进行推送,后台自动异步地同步到其他IDC。保证应用在部署时优先从同IDC的Harbor进行镜像的拉取,如果失败,再跨IDC拉取镜像,减少应用拉起的时间。


在AWS上我们选择了自己搭建Harbor,后端的存储采用S3。由于目前我们在公有云IDC上的应用相对较少,暂时未加入联邦,未开启自动同步,在发布流程上保证在应用在部署时预先推送镜像到AWS IDC的Harbor。同样也使用了DNS劫持技术,保证使用同样的镜像域名。


 

3.3 日志监控


网络解决了,镜像可以拉取了,可以正式拉起一台容器了。那监控和日志是如何解决的呢?



在公有云我们部署了一套与私有云一致的日志监控系统,hickwall、grafana等,采用了Prometheus+Telegraf+InfluxDB+Grafana等与私有云一致的技术栈。在云上采集,云上处理,只送回必要的告警数据,并对接了NOC体系,进行7*24的监控。

 

四、同步私有云数据到公有云及疑难杂症


4.1 同步私有云数据到公有云


涉及到IDC之间大量数据的同步,我们是采用公网传输的方式,毕竟专线带宽有限而且昂贵。有关AWS数据的流量费用,可以总结为:


如果服务部署在私有子网,与Internet的通信会经过NAT网关。经过NAT网关的流量不管出入,一律收取数据处理费用。


如果服务部署在公有子网,通过公网IP与Internet通信。而公网IP的收费模式是只收出流量,进流量不收费。


所以我们通用的把数据从私有IDC同步到公有云的部署架构是:把数据收集服务部署在公有子网,实例带有公有IP,私有云IDC的推送服务通过Internet进行数据的推送。



同样,如果公有云上的服务需要从外部供应商拉取的数据量比较大,我们也是采用服务部署在公有子网的模式。如此节省了经过NAT网关的数据处理费用。


4.2 AWS ELB负载均衡不支持回环路由


我们通过AWS ELB(Elastic Load Balancing)把kubernetes api server组成一个高可用的集群,但是上线后,发现在master节点上使用kubectl命令经常会超时,集群中也有比较多的访问api-server的i/o timeout日志。


这个问题在于通过实例id进行注册的ELB不支持回环路由。即通过实例id注册到ELB的目标时,客户端的源IP地址会保留,ELB在路由后发现源地址和目的地址相同,会丢弃这些包,连接仅在请求路由到不同的实例时才会成功。Kubernetes在部署架构中,通常采用混合部署的模式,在同一台主机上同时启用kube-scheduler、kube-api等组件。


该问题可以通过改用通过IP地址注册目标到ELB负载均衡进行解决。为了支持这种应用混布场景,AWS推出了通过IP地址进行注册的ELB负载均衡,即可避免不支持回环路由而导致连接超时的问题。



4.3 kubelet进程max-pods设置


由于我们的网络方案受限于实例上弹性网络接口的最大个数,要注意在kubernetes node上对kubelet进程的max-pods参数进行设置以限制Pod的最大数量。

 

五、总结


本文首先介绍了在携程混合云平台上支持容器发布的背景,并引出了自搭kubernetes的初衷,要适配的网络需求,再详细从网络、镜像、日志监控等组件介绍了携程在AWS自己搭建kubernetes的方案选型及设计,接着给出了从私有云IDC同步数据到公有云的通用部署架构,并从几个示例介绍了探索过程中碰到过的注意事项。


我们在混合云上的广大探索还在持续中,欢迎继续关注互相交流学习。


【推荐阅读】




    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存