本文根据谭霖老师在〖2017 GdevOps全球敏捷运维峰会北京站〗现场演讲内容整理而成。
(点击此处获取谭霖老师演讲完整PPT)
讲师介绍
谭霖,滴滴出行弹性云架构师、云计算专家,曾任职于英特尔从事云计算方向研究。OpenStack Ironic 项目核心开发者,《OpenStack设计与实现》作者,多次在OpenStack Summit SRECon峰会上分享Topic。目前从事基于Kubernetes的私有云平台的研发,专注提高服务的稳定性和研发、运维效率。
今天我将带来一些关于滴滴弹性云的分享,标题是从物理机到Kubernetes的那些坑与心得。
先简单自我介绍下,我叫谭霖,之前曾在Intel从事于Open Stack相关的工作,也花了很大精力在开源社区这一方面,后来我来到滴滴的弹性云团队,主要从事基于Kubernetes的基础平台建设,可以说是从一个做菜的“厨师”变成了一个“食客”。
主题简介:
1. 整体架构
2. 产品功能
3. 方案细节
4. 心得展望
在我们准备做弹性云时,我们问了自己三个问题:
1. 为什么要做私有云
2. 为什么选容器
3. 为什么选Kubernetes
这个其实是最好回答的。因为滴滴的集群规模太大,有数万台物理机(这样的规模量要求我们做一个私有云平台),结果因为之前没有一个比较好的方案,造成了资源使用率特别低、资源浪费大的问题。
另一个主要原因是滴滴的发展太快,总有新的业务,隔一段时间就听说成立了一个新的部门,而且随着新需求的出现,更新迭代异常频繁,这些都对平台的稳定性提出了特别高的要求。这个问题靠堆人是解决不了的,所以需要有一个好的平台和机制来解决它。
这个问题也比较好回答。KVM / Xen这样的传统虚拟化已经出现多年了,很稳定,但大家也知道其弊端,就是损耗比较大。另外,传统混部也就是滴滴现在在用的方案(即继续保持原有的传统混部),虽然能够部分解决资源使用率低的问题,但因为完全没有隔离,导致程序之间容易互相影响。
而容器是一个相对折中的方案,具有一定的隔离性、损耗更少,这是我们选择它的最主要原因。同时,更重要的是容器的镜像所带来新的玩法,因为镜像的环境是一致的,对运维人员特别友好,因此他们不用再关心那些纷繁复杂的配置文件,也不用频繁地走初始化流程,更不用每次上线更新都提心吊胆,担心会对新的服务造成影响。
现在这个问题就比较容易回答,但一年前是个比较艰难的选择。大家都知道现在比较著名的容器平台有Mesos、Kubernetes 和Swarm,一年前我们在Mesos和Kubernetes之间犹豫过很久,后来主要觉得因为我们要做的是一个基于容器的平台,相比Mesos,Kubernetes更专注容器。
而Swarm当时还很年轻,很多功能不够完善,相较之下Kubernetes更成熟。当然更关键的原因是Kubernetes的社区更有活力,设计架构也更清晰,可拓展性也高。
现在回过头来看一年前的选择,非常很庆幸我们做了正确的选择,因为之前相较于两者,Kubernetes还只是旗鼓相当,而现在则有一统江山的趋势。我们目前还是基于Kubernetes1.6版本做的改造,接下来1.8版本release之后,我们会尽量跟上保持和社区同步。
滴滴弹性云的目标就是基于容器技术,为滴滴提供稳定高效、可伸缩的服务管理平台。先简单看看弹性云的项目历程。
2016年7月,弹性云项目正式启动,然后10月份出了第一版,2017年4月,发布了第二版。在几个月的使用体验中,我们根据用户的反馈,不管是产品形式还是底层网络方案都经过大规模优化。目前第三版也正在开发中,这一版会更侧重用户交互和可视化上。
截止到目前为止,我们总共有300多台宿主机,2000+的容器实例,不算很多,但目前有多条滴滴核心业务线跑在我们的平台上,预见2018年规模会有指数型的上升。
我们的整体目标有四点,主要是为了解决先前提出的三个问题,如下:
实现资源按需分配,提高资源利用率;
实现资源的弹性伸缩,可以快速响应负载变化;
基础环境保持一致,实现服务的快速交付;
保证服务的容错性,实现基础设施免运维。
针对不同的产品线和需求,我们具体落地的产品解决方案主要分为两大块:一个是基于容器构建的轻量级虚拟机,叫做静态容器组;第二个是更纯粹的微服务类容器,我们称之为弹性伸缩组。同时我们还整合了滴滴现有的部署监控和日志收集系统。
这是整体的架构图,中间最大的一块就是我们的K8S集群,它主要分成控制节点和工作节点两部分。上面一块是我们的控制台和管理员入口,左边是滴滴的一些现有的权限认证系统和服务树,右边是我们的网络方案。
所以按照流程我们主要做的操作有用户先通过权限系统认证后,创建服务(也就是K8S的实例),实现容器的扩容缩容和部署更新。之后我们平台就会进行实时监控和日志收集,完成监控数据的上报和镜像拉取。
刚才说的主要是管理员相关的工作流程,现在看看用户流量。首先我们这边实现了每个容器通过IPAM组件,都能获取一个独立的IP,这个IP可以和物理机双向互通,所以允许用户的流量可以直接通过容器IP访问实例。同时我们也提供了一个4层的LB,允许用户可以通过只访问一个VIP,自动把流量打到后面的容器里。
接下来详细讲讲滴滴弹性云具体的产品功能。
正如之前所说,我们主要提供静态容器和动态伸缩两大产品,同时伸缩组方面又细分为有状态和无状态伸缩组。
其实在刚开始时,我们只想提供K8S支持得最好的Deployment,也就是无状态伸缩,这也是最符合容器使用习惯的产品形式。但在实际落地的过程中,我们发现完全不是那么一回事。
首先,不是所有程序都能比较轻易地改造成Cloud-Native的服务,所以针对这样的传统服务,我们还是需要提供一个比较贴近物理机的用户体验的产品,它的问题就是不能弹性伸缩,但好处是可以挂载本地存储,IP是恒定的。
同时为了支持有状态的服务,我们也提高了有状态伸缩组。既支持弹性伸缩又支持挂载网络存储。不过比较出人意料的是,用户选择有状态伸缩组的很大一部分原因并不是因为挂载Ceph,而是因为有状态伸缩组的容器IP/HostName是不变的,这样不管是从监控和日志上,都是更友好的。
而无状态伸缩我们这个当初最想推动的服务,也就只有在一些对弹性伸缩速度有特别高的要求的场景下才会用到。
具体到产品功能的特性上,我们主要有三大块:复杂配置简单化、上线流程标准化和服务管理。
1.复杂配置简单化
了解过K8S的朋友们都清楚,它上手很难,有特别多的配置,有时候反而给用户带来了困扰。所以我们做了减法,简化了很多配置,只留给用户一些接口,避免不必要的困扰。比如我们支持多种接入方式,既可以一键完成从Git代码仓库到上线镜像的转换过程,也支持直接通过自定义镜像。
同时我们也对镜像做了分层(基础镜像→环境镜像→服务镜像),实现RD和SRE每个角色负责不同的镜像制作,这样分工更合理、层次更清晰,做到比较好的权责分明、高效有序。监控日志自动配置关联也是做了减法,希望用户较少关注这一点,从而得到更好的体验。
2.上线流程标准化
相对于减法来说,我们也做了很多的加法,因为我们是一个严肃的运维平台,之所以强调严肃,是因为只要有人参与的地方就容易犯错。一方面我们要尽量减少人工参与的地方,另一方面又要尽量通过各种规范来减少人犯错的机会。
比如变更审批系统,每一次上线变更都需要开发leader的审核;同时也对它进行了改造,使其每次分组小流量灰度;每次发布更新的过程中都有强制的观察时间,如果在观察过程中,比如说你发布了第二组后,发现你的更新过程中代码出了BUG,我们还支持了一键回滚。
3.服务管理
在服务管理方面,我们实现了服务不可用自动重启、一键扩容和负载均衡等,最为重要的就是对用户强制异地多活,必须在我们的两个机房都有实例。
现在介绍我们的方案细节,主要包括网络监控、日志和镜像市场。
1.SDN
我们的网络方案主要是基于Onos+OVS的SDN网络方案,每个容器都有一个区别于机房实体机的Overlay IP,容器与容器之间实现“大二层”互通,与机房网络之间通过交换机打通。物理机可以直接通过容器Overlay IP访问容器,反之亦然。
2.IP
在IP上,静态容器和伸缩容器都可以进行Hostname绑定,通过容器不变的Hostname保证容器在漂移到其它宿主后IP依旧保持不变,当容器发布更新时,它永远保持稳定。对于IP随机分配的情况,我们还提供了IP池,保证IP只会从IP池里出现。
3.负载均衡
我们使用弹性云独立的4层、7层负载均衡器,实现动态的负载均衡及故障自动剔除。
1.架构
当前弹性云容器网络大体上分为三种类型:同宿主间的容器通信、跨主机间的容器通信,以及容器与物理机间的通信。
每一个部署容器的宿主机,都会部署一套Ovs网络组建,其中关键的就是两个Ovs Bridge,一个负责建立隧道与外界通信,叫Ovs-tunnel桥;一个负责整合本机上的容器通信,叫Ovs-int桥。所有的Ovs-tunnel都会与Onos的Controller建立连接,负责查询流表并建立流表和其他宿主机间的隧道。因此三种通信类型就可以解释为:
同宿主间的容器通信:直接通过Ovs-int桥通信,因此也不依赖Ovs-tunnel与外界的隧道。
跨主机间的容器通信:第一次通信时,Ovs-tunnel也会查询Onos,Onos返回目标容器的主机信息,Ovs-tunnel通过主机间的隧道,将容器数据发送到目的容器的宿主机,目的容器宿主机上的Ovs-tunnel则会将数据继续向上一层传递,经Ovs-int传递给容器。
容器与物理机间的通信:容器发送物理机的数据包,会被Ovs-tunnel经隧道发送给Overlay网关,Overlay网关进行Vxlan解包的操作,将数据传递到物理网络。相反地,物理机发送给容器的数据包,经Overlay网关把数据封包成Vxlan数据包,再通过隧道发往容器宿主,再由宿主传递给上一层的容器。
2.网络IP分配
具体到产品上,每个IP分配方案如下:
静态容器组:IP和Hostname恒定;
有状态伸缩组:IP和Hostname恒定,支持IP池;
无状态伸缩组:支持IP池,IP随机分配,灵活度更高。
3.创建过程
这个是容器网络创建的过程。一旦用户有创建容器需求时,控制节点会对整个集群做一个调度,选择一个合适的工作节点,而作为节点最重要核心的Kubelet就负责创建容器,控制节点会把需求发给它。
具体创建步骤如下:
Step1:当Kubelet收到调度到本Node的Pod信息后,会先创建一个无网络配置的Infrastructure的基础容器,此容器用于承载Pod中所有容器的网络Namespace;
Step2:Kubelet Exec启动CNIPlugin,并将第一步中创建的网络Namespace及Pod名称、容器ID等作为标准输入,传给新启动的Plugin进程;
Step3:CNIPlugin根据收到的参数,去IP Controller中申请容器的网络IP;
Step4:IP Controller会判断该容器属于哪一个子网,子网中是否有可用的IP,及是否有绑定的IP地址等,在根据计算出的IP地址及子网信息等,去SDN IPAM端申请虚拟Port(Overlay IP);
Step5:IPAM会将创建好的虚拟Port返回API调用者IP Controller,并同时将信息同步给SDN Onos;
Step6:IP Controller 会将返回的Port保存在自己的存储库中。如果是绑定IP类型的Pod,它还会将该Pod与该虚拟Port(Overlay IP)进行绑定,则下一次该Pod再一次进行申请时,仍会申请到该Port(Overlay IP)。数据保存后,它会把Port信息再返回给CNIPlugin;
Step7:CNIPlugin收到Port信息后,会根据其中的Overlay IP、Gateway、Vlan Tag 等信息创建Veth Pair、配置Veth信息、配置路由、添加Ovs-brdge等操作。至此,本来没有任何网络的基础容器,就有了网络配置(除了与外部通信的Ovs网络外,同时也会添加Lo网络);
Step8:CNIPlugin在成功配置完网络后,会将结果返回给Kubelet;
Step9:Kubelet会使用配置成功的基础容器的网络Namespace继续去创建其它的业务容器,如果配置失败了,会继续从第一步开始,继续创建基础容器,直到网络配置成功。
监控主要有两方面需求:基础监控和业务监控。基础监控使用了Google开源容器监控软件Cadvisor/Cgroup作为监控数据来源,数据上报至Odin,同时可在Odin和弹性云页面上展开监控数据。
基础监控:物理机监控Agent无法采集到容器有效监控数据,Cadvisor、Proc、Cgroup容器基础监控项同物理机有明显差异,但用户的需求没变。
业务监控:业务监控同物理机需求完全一致,复用物理机监控方式。
这张图就是弹性云配置监控和查看监控的示意图,我们在每个物理机上有获取容器的基础监控指标,容器里面也会有获取业务指标。
日志主要三个方面考虑:时效性、持久性,以及日志展示。
日志时效性:在线日志采集延迟2min,满足大多数场景,紧急情况下可以登录到容器内查看日志。
日志持久性:生成的容器直接拉到远端存储一份,选择分布式存储日志在Ceph存储一份。
日志展示:事后追溯有多种丰富的日志展示、分析系统。
这是我们在弹性云平台上创建容器的一个操作日志,也是一个简单的示意图。通常用户都打在容器里,而容器一旦发布更新,漂移之后数据都丢了,所以我们直接把日志打到Ceph上,用户不喜欢用这个的话可以直接打到远端,远端采集系统可以使用不同的系统,比如说ES和HDFS,这样就提供了更加丰富的日志展示和分析系统。
存储主要提供了本地卷和网络卷。
本地卷Host Path提供给静态容器组,我们把宿主机上的目录挂载到容器里作为数据存储卷,优势是稳定和读写性高。
网络卷Ceph的优势是多备份更可靠,支持容器迁移,缺点是网络抖动不可避免,所以得用Ceph的服务做改造,避免网络抖动影响整个服务的进程。
我们选择Overlay FS作为存储,一方面是因为它的性能/稳定性更好,当然它也有不足,比如DeviceMapper可以通过LVM来解决磁盘的容量限制,但Overlay FS单靠本身是解决不了的,需要靠XFS来限制。
但我们发现在某些场景中XFS因为没有开启ftype功能,导致系统读写缓慢,因此我们建议大家如果也选用了Overlay FS+XFS方式,要把ftype设置为1。
镜像市场主要还是分层,目的是让不同的用户可以专注于不同的事情上。如下图所示:
基础环境镜像:弹性云管理员制作的镜像:FROM 官方镜像,增加了服务用到的各种Agent、常用软件、系统配置(添加User、日志切割等)的镜像。包揽最复杂的周边应用的整合,并兼顾将镜像做得更小。
服务环境镜像: SRE 同学制作和维护的镜像:FROM 基础环境镜像,安装了服务运行所需的环境、配置。更加专注服务环境的保障,使镜像满足产品线的使用。
服务镜像:使用 RD 代码中 Dockerfile(一般只需要COPY代码到线上所需目录即可),FROM 服务环境镜像,通过弹性云系统生成的提供服务的镜像。
首先,特别想强调的是“云计算拼的是运维,拼的是可靠性”。产品做得再好、运维人不靠谱,出了纰漏后就没人敢再用你的东西了,说得再好也没用。
其次,“线上无小事,要对线上保持敬畏感”。我之前是做开源社区的,解决方案做得多,但现在做云计算方案时才发现实际与想象的差别特别大,可以说云计算落地更难。
也许大家看了一些网上的分享后都会觉得搭建一套容器环境是比较容易的,可最难的就像我们这样已经现有的体系,如果你要把它落地就需要做各种打通、妥协,甚至对一些功能做一些切割,有时这些不是我们想做的,但为了推进落地不得不去做。
此外,技术挑战是一方面,但用户习惯是更加大的挑战,特别是业务方,他们肯定会说为什么我要迁,现在用得好好的,即便你和他们说做这个是对公司省成本的,可以提高运维的便利性,他们依然会觉得我不缺钱,怕麻烦。再者,他们不是做运维的,感觉不到这些带来的明显好处。另一方面,有些用户觉得我上你的云可以,但你不要改变我的习惯,原来是什么样现在还得保持什么样。但问题是原来是物理机的用法,可现在已经上容器了 ,还想保持原来的方式,这是不可能的。
运维是一个特别苦的职业,线上无小事,一点点小改动,如果没有考虑清楚、没有充分验证过回滚方案,那分分钟要出事,相信大家也深谙此理。
对于弹性云的未来规划,第一是更细粒度的隔离。大家对容器比较了解的话,就会清楚现在Docker更多的是对Memory使用量的隔离,以及对CPU使用量的隔离。但实际过程中我们会发现,比如你给用户一个8G内存,在某种极端配置下8G内存会不断地刷新读写,那你的L3 Cache很快就会被刷爆了,导致大量的Cache Miss,这样的话还是会对程序性能造成很大影响的。
第二是静态容器的弹性伸缩。因为静态容器确实就像虚拟机一样,运维成本特别高,所以一旦物理机挂了,容器也就挂了,很多东西是找不回来的。而且你要扩容也只能按照传统的方法,先申请一个容器再去上面做部署、配置等,这些都会给运维同学带来非常大的困扰。
第三是更智能的扩容算法。目前我们是根据实时的使用率来进行扩容,但其实对于我们来说,很多业务高峰期是有规律的,如果能做到提前预测出高峰期,做适应的扩容,那将会极大地提高资源使用率。
最重要的一点是依托社区、回馈社区。我们打算腾出手来,把一些定制化需求不是那么高的解决方案来回馈到社区。
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721