阿里集团八年容器化演进之路

PouchContainer 如今服务于阿里巴巴集团和蚂蚁金服集团的绝大部分 BU, 包括交易&中间件,B2B/CBU/ICBU,搜索广告数据库,还有收购或入股的一些公司,好比优酷高德、UC等。其中体量最大的是交易和电商平台,在 2017 年双 11 的时候咱们支撑了破纪录的峰值,背后的应用都是跑在 PouchContainer 里面,总体容器实例已经到了百万级规模。使用了 PouchContainer 的应用涵盖了各类各样的场景。这些场景从运行模式来看,有标准的在线 App,还有像购物车、广告、测试环境等比较特殊的场景。不一样的场景对 PouchContainer 有不一样的使用方式和需求。从编程语言看,实际运行着 JAVA、C/C++,Nodejs,GoLang 等语言编写的应用。从技术栈的角度看,包含了电商、DB、流计算、大数据、专有云等场景,每一个场景对于容器各方面要求,所用到的特性都不太同样,PouchContainer 针对每一个场景的需求都在产品上都作了支持。linux

PouchContainer 容器技术在阿里的演进过程伴随着阿里技术架构自己的演进。阿里内部技术架构经历了一个从集中式单体应用到分布式微服务化的演进。git

淘宝最开始是一个巨石型的应用,一个应用里包含了商品、用户、下单等等全部交易链路的功能。随着功能愈来愈完善,维护起来也愈来愈困难。为了提升研发效率,从 2008 年开始咱们逐渐把这个应用拆分红了多个分布式应用,商品的,交易的,用户的,前台的,后端的;经过 HSF 远程调用框架,TDDL 分布式数据层和 Notify 分布式消息中间件串联起来。其中每一个服务都有多个实例,均可以独立研发演进,并能够进一步继续拆分。因而就逐渐造成了一个庞大的分布式服务集群。从巨石型应用到多个单一功能的轻量级服务型应用,总的应用实例数变多了,每一个实例须要的系统资源变少了。因而从最初的每一个实例直接使用物理机天然过渡到使用 xen,kvm 等虚拟化技术。VM 使用了一段时间以后,发现总体物理机的利用率仍是很低。当时一个 24 核的物理机只能虚出 4 台 4 核的 VM,除了当时虚拟化自己的开销不小外,每一个应用实例在 VM 里仍然用不完分到的资源。因而就想能不能不用虚拟机,用更轻量的基于进程级别的资源切分使用方式。github

这个时候阿里内部的运维体系已经比较庞大了,从应用的构建部署到分发,到一些运行期的监控告警等管控系统,都依赖于一个应用实例跑在一个独立机器里的假定。这个假定已经不经意间贯穿到了研发运维的各个环节里面,包括系统的设计,运维习惯等都严重依赖这个假定。咱们不可能从新搭建集群,把存量的业务停掉再到新的集群里面用新的运维模式去跑起来,这个业务和运维上都是无法接受的,不可能电商交易的研发停几个月,系统停几天来搞这个事情。因此咱们首先要作到兼容,新的资源使用方式必须兼容原先的假定。咱们通过仔细分析了这个假定的内涵,发现每一个应用实例概括下来无非有以下 4 点要求:算法

● 有独立IP数据库

● 可以ssh登录编程

● 有独立的,隔离的文件系统后端

● 资源隔离,而且使用量和可见性隔离安全

首先是有独立 IP,可以 SSH 登陆。其次有独立的文件系统,应用程序跑起来,但愿程序看到的整个文件系统都是给他专用的,由于现有的代码和配置中必然有不少路径的硬编码,须要知足这个潜在要求。还有无论经过工具仍是代码,他只能看到分配给他本身的资源。好比 4 个 CPU,8G 的内存,他可以根据这些资源的用量作一些监控,作一些对本身资源使用量的采集和告警。这四个特色总结下来就是新的资源使用方式要作到和物理机或者 VM 的使用体验一致。可以作到这样的话原先跑在 VM 里的应用就能够很平滑的迁移过来,现有的应用系统和运维系统不须要作很大的改动。服务器

咱们为了能达到这四点,最开始是多隆大神手工 Hack 系统调用,glibc 基础库等,实现了一些资源上的隔离。像有独立的 IP 可登陆 ,就用虚拟网卡,在每一个容器里面起一个 sshd 进程;资源的隔离和可见性上,就用 Cgroup 和 Namespace 等内核特性;后来发现开源的 LXC 项目也在作一样的事情,而且比手工 Hack 更通用化,更优雅一些。因而咱们集成 LXC,而且在内核上加了定制的资源可见性隔离的 patch,让用户的实例只能看到分配给他的 CPU和内存,另外还增长了基于目录的磁盘空间隔离的 patch,这样就造成了咱们第一代的容器产品。这个产品当时代号是 T4,寓意是第四代淘宝技术,淘宝 4.0;在 2011 年的时候 T4 容器技术灰度上线。T4 相比 VM,彻底没有虚拟化 Hypervisor 层的开销,资源切分和分配上更加灵活,能够支持不一样程度的资源超卖。这样就很好的支持了业务爆发增加的需求,控制了物理机按业务增加比例膨胀的势头。另外由于 T4 彻底兼容了以前研发和运维对物理机和 VM 的使用习惯,绝大多数应用都可以作到透明的切换,应用无感知。由于有这些特性,在接下来的短短几年时间里,T4 逐步接管了交易和电商主体的在线应用。网络

到 2015 年的时候 Docker 技术火起来了。咱们写程序的都知道有个著名的公式,程序=数据结构+算法。从程序交付使用变成一个软件产品的角度来看,咱们能够套用这个公式:

● 软件= 文件(集)+ 进程(组);

从静态来看,软件从构建分发到部署,最终形式是一个有依赖层次的文件集。从动态来看,这些文件集,包括二进制和配置,由操做系统加载到内存后执行,就是一个有交互关系的进程组。咱们以前的 T4 容器在进程(组),或者说运行时上作的事情和 Docker 基本相似,好比说都使用了 Cgroup、Namespace、linux bridge 等技术。还有些是 T4 特有的,好比基于目录的磁盘空间的隔离,资源可见性隔离,对老版本内核的兼容等。咱们从最先物理机演化到 VM,再到如今的容器,内核的升级周期比较漫长,迭代很慢,15年的时候存量的机器上所有都是 2.6.32 内核,T4是兼容 2.6.32 内核的。 可是另外一方面在文件(集)的处理上 Docker 作得更好,更加系统化。 T4 只作了很薄的一层镜像,给相同的业务域作了一个基础的运行和配置环境,这个镜像没有深刻到每个特定的应用。 而 Docker 是将每一个应用的整个依赖栈打包到了镜像中。所以在 2015 年咱们引入了 Docker 的镜像机制来完善本身的容器。

clipboard.png

在将 Docker 镜像整合进来以后,原来基于 T4 的研发运维体系受到了很大的冲击。 首先交付方式变了,以前是 build 一个应用的代码包,把代码包交给咱们的部署发布系统,后者建立一个空的容器,根据这个业务所在的很薄的模板把一个空的容器跑起来,再到容器里面安装依赖的一些 IPM 包,设置一些配置,按每一个应用定好的一个列表一个一个的安装好,而后把应用包解压启动起来。这个应用依赖的软件和配置列表咱们内部叫作应用的基线。引入镜像以后,在将 Docker 镜像整合进来以后,原有的交付方式发生了变化。以前是 build 一个应用的代码包,把代码包交给咱们的部署发布系统,后者建立一个空的容器,根据这个业务对应的很薄的一个模板,把一个空的容器跑起来,再到容器里面安装依赖的一些 RPM 包,设置一些配置,按每一个应用定好的一个清单一个一个的安装好,而后把应用包解压到主目录启动起来。这个应用依赖的软件和配置清单咱们内部叫作应用的基线。引入镜像以后,咱们应用的代码包和依赖的全部的这些三方软件、二方软件都会打成一个镜像。以前经过基线维护应用依赖环境,如今都放到每一个应用本身的 Dockerfile 中了,整个研发构建和分发运维的过程大大简化了。

作了这个事情以后,研发和运维之间的职责和边界就发生了变化。以前研发只须要关注功能,性能,稳定性,可扩展性,可测试性等等。引入了镜像以后,由于要本身去写 Dockerfile,要了解这个技术依赖和运行的环境倒底是什么,应用才能跑起来,原来这些都是相应运维人员负责的。研发人员本身梳理维护起来后,就会知道这些依赖是否合理,是否能够优化等等。研发还须要额外关注应用的可运维性和运维成本,关注本身的应用是有状态的仍是无状态的,有状态的运维成本就比较高。这个职责的转换,能够更好的让研发具有全栈的能力,思考问题涵盖运维领域后,对如何设计更好的系统会带来更深入的理解。因此引入 Docker 以后对研发也提出了新的要求。咱们总结新的时期,新的运维模式下对研发能力要求的几个要素,总结起来就是几个原则:

clipboard.png

为了更好的把本身的系统建设好,咱们要倡导研发从第一天创建系统的时候,就要考量最终的可运维性,好比参数是否可配置,是否能够随时重启。机器天天都有硬件故障产生,这些硬故障不可能天天都人工处理,必需要尽量自动化处理,自动化处理时,虽然有些故障只影响了一部分实例,另外一部分是好的,可是也可能须要一块儿处理,好比须要物理机上的业务所有迁移走来维修物理机的时候。因此无论当时容器里的业务是好的仍是很差的,都要知足随时可重启,可迁移的要求。原来是部分交付,如今要考虑你到底运行环境是什么样的,什么样的运行环境才能跑起来,尽可能作标准化的操做。好比说启动,Dockerfile 里面写好启动的路径,不要再搞一些特殊的处理,若是有任何特殊的处理都无法作统一的调度和运维。统一的业务迁移,机器腾挪也无法作。咱们的目标其实就是从一开始的比较粗放的运维,到不断的开发自动化的工具和系统,造成一个体系,经过前期人工运维的过程把一些固定的故障处理的流程模式化,最后提取出来一些能够自动处理故障,自动恢复的机制。咱们的最终目标是无人职守。全部这些加起来其实就是咱们引入镜像化以后,而且要朝着无人值守的方向演进时,对研发和运维的新的要求。

为了更好的把本身的系统建设好,咱们要倡导研发从第一天创建系统的时候,就要考量最终的可运维性,好比参数是否可配置,是否能够随时重启。机器天天都有硬件故障产生,这些硬故障不可能天天都人工处理,必需要尽量自动化处理,自动化处理时,虽然有些故障只影响了一部分实例,另外一部分是好的,可是也可能须要一块儿处理,物理机上的业务所有迁移走来修物理机。因此无论当时容器里的业务是好的仍是很差的,都要接受随时可重启,可迁移。原先是部分交付,如今要考虑你到底运行环境是什么样的,什么样的运行环境才能跑起来,尽可能作标准化的操做。好比说启动,Dockerfile 里面写好启动的路径,不要再搞一些特殊的处理,若是有任何特殊的处理都无法作统一的调度和运维。统一的业务迁移,机器腾挪也无法作。咱们最后的目标其实就是从一开始的比较粗放的运维到不少人都能介入,到最后的自动化不断的开发自动化的工具,造成一个体系,经过前期人工运维的过程把一些固定的故障处理的流程模式化,最后提取出来一些能够自动处理故障自动恢复的机制,最后咱们的目标是无人职守。全部这些加起来其实就是咱们引入镜像化以后,而且要朝着无人值守的方向演进时,对研发和运维的新的要求。

clipboard.png

上面是 PouchContainer 容器的 Roadmap, 2011 年的时候 T4上线 ,到 2015 年 3 月的T4 覆盖了交易的大部分应用。这个时候开始引入了 Docker 镜像机制,这里面作了不少兼容性的工做。好比说原来 T4 轻量化的模板转化成对应的基础镜像,里面兼容了不少以前运维的习惯和运维的工具,如帐号推送,安全策略,系统检测。咱们在 2016 年初上线了第一个镜像化应用,到 5 月份的时候集团决定主站所有应用容器化。在作镜像以前阿里是有一两百人的团队作每一个应用的部署,运维,稳定性控制,后来这个团队都没有了,所有转成了 DevOps,转向开发工具和运维平台,经过代码的方式,工具的方式解决运维的问题。以前专职作运维的同窗最大的负担就是线上环境的变动,研发提交变动申请给运维同窗,运维同窗作线上操做,研发不知道代码运行环境具体依赖了哪些基础软件。作了镜像化的事情后,研发本身负责编写 Dockerfile,运维就把环境变动的事情经过 Dockerfile 的机制移交给了研发。运维和研发之间的边界就很是清楚了,这个边界就是由 Dockerfile 来定义的。研发负责把他代码依赖的环境在 Dockerfile 定义好,运维保证其构建分发时没有问题。咱们在 2016 年双11的时候完成了交易核心应用的镜像化 PouchContainer 化改造。在 2017 年双11的时候交易所有应用完成了镜像化改造。而后咱们在 2017 年 11 月 19 日的时候宣布了 PouchContainer 的正式开源。

咱们的内部 PouchContainer 通过大规模的运行,支持了各类各样的业务场景,各类不一样的技术栈,不一样的运行形态,积累了很是多的经验。这些经验以前跟阿里内部的环境耦合性比较大。好比说咱们的网络模型,咱们实际上是嵌入到了阿里内部的网络管控平台,包括IP分配在内部都有独立的系统去完成。好比何时启用 IP,何时下发路由等等,这些是有一个统一的 SDN 网络管理系统来管理的。还有相似的内部存储系统,还有运维的一些指令推送系统。内部系统耦合性比较大,无法直接开源。因此咱们最后选择的策略是先在外部孵化一个从零开始全新的项目,把内部的特性一点点搬上去。这个过程当中咱们内部的版本也会作重构,把内部的依赖作一些插件化解耦合的方式,这样最后全新的项目在外部能够跑得很好;在内部用一些耦合内部环境的插件也能够跑起来,最终的目标是内外用一套开源版本。

那么咱们的 PouchContainer 容器相对于其余容器有什么差别呢?主要体如今隔离性、镜像分发优化、富容器模式、规模化应用和内核兼容性几个方面。传统的容器隔离维度就是 namespace、cgroup;在资源可见性方面,咱们前几年是经过在内核上打 patch,在容器内看内存和 CPU 利用率等数据时,把统计数值和当前容器的 Cgroup 和 Namespace 关联起来,使容器能使用的资源和已使用的资源都是容器本身的。18年的时候咱们引入了社区的lxcfs,这样就不须要对特定内核 patch 的依赖了。磁盘空间的限制也是在低版本内核上加了补丁,支持了基于文件目录的磁盘空间隔离,可以把每一个容器的 rootfs 限制住。在 4.9 以上的内核上,咱们是用 overlay2 文件系统来完成一样功能的。咱们也在作基于 hypervisor 的容器方案,提高容器的隔离性和安全性,咱们在 PouchContainer 里面集成了 RunV,用于一些多租户的场景。

clipboard.png

阿里内部的离在线混部之因此能推动,在同一个机器上既能跑在线的业务又能跑离线的一些任务,互相之间不会出现太大的干扰,其核心的技术就是 PouchContaienr 容器能够根据优先级,把不一样业务的资源使用隔离开来,保证在线业务优先使用资源。这个资源包括不少的维度,好比 CPU、内存,CPU cache、磁盘、网络等等。

clipboard.png

这是 PouchContainer 的镜像分发设计。咱们内部有不少比较核心的应用,体量比较大,实例会分布在上万台物理机上。发布新版本的时候上万台机器同时拉镜像,任何中心的镜像仓库都扛不住。所以咱们设计了一套镜像分发的二级架构,在每一个地域建一个 mirror,在同一个地域内拉镜像的时候用 P2P 分发技术---咱们内部的产品名叫蜻蜓,已经开源;须要拉镜像的服务器之间能够分散互相拉文件片断,这样就直接化解了中心镜像仓库的服务压力和网络压力。后面其实还有更好的解决镜像分发的思路,咱们正在尝试镜像的远程化,经过存储计算分离技术,用远程盘的方式挂载镜像,直接跳过或者说异步化了镜像分发这一步,目前正在内部环境灰度运行中。

clipboard.png

这是 PouchContainer 内部版本的体系结构。在最底层的宿主机层面,咱们会作一些管理和运维,目的是为了确保容器运行依赖的基础环境是健康的,包括宿主机的一些镜像清理,包括安全控制、权限管理等。OS 的低版本内核咱们是适配到最低 2.6.32 内核,包括容器里面的进程管理也作了不少的适配。资源隔离前面讲过了,网络模型咱们内部其实主体用的是 Bridge,可是其余各类各样的场景也都支持。咱们开发了不少插件,PouchContainer 开源后,咱们才将这些插件逐步作了标准化,兼容适配了社区的 CNI 标准。最上层是一个富容器模式的支持,每一个容器里面会启动一些跟内部的运维工具,运维系统息息相关的一些组件,包括一些发布模式的优化。能够看到咱们内部体系结构是比较复杂的,尤为依赖内部的其余系统比较多,在外部直接跑是跑不起来的,所以也无法直接开源。

clipboard.png

因此咱们开源版本是从新开始搭建的,这样会比较清爽一些。咱们引入了contained,支持不一样的 runtime 实现,包括咱们本身包装 lxc 开发的 RunLXC 运行时,能够用来支持老版本 2.6.32 内核。开源版 PouchContainer 兼容全部 Docker 的接口,也支持 CRI 协议,这样也就同时支持了比较主流的两种集群管理系统。网络方面咱们内部基于 libnetwork 作了加强,包括不一样场景暴露出来的一些问题,一些稳定性,规模化的时候各类细节的一些优化。存储方面咱们支持了多盘,内存盘,远程盘等各类不一样形式的存储。PouchContainer 能够无缝集成到上层编排工具中,包括 Kubelet 和 Swarm 等。咱们内部的 Sigma 调度系统,不一样的版本Docker 协议和CRI协议都会使用。

这是 PouchContainer 的开源地址:https://github.com/alibaba/pouch

如何贡献:

https://github.com/alibaba/po...

最近 PouchContainer 开源版本 GA 已经发布,PouchContainer 可以在如此短的时间内 GA,离不开容器社区的支持,在超过 2300 个 commit 的背后,有 80 多位社区开发者的踊跃贡献,其中不乏国内一线互联网公司、容器明星创业公司贡献者的参与。

PouchContainer 开源版本发布 GA 以前,此开源容器引擎技术已在阿里巴巴数据中心获得大规模的验证;GA 以后,相信其一系列的突出特性一样能够服务于行业,做为一种开箱即用的系统软件技术,帮助行业服务在推动云原生架构转型上占得先机。

本文做者:林轩

阅读原文

本文来自云栖社区合做伙伴“阿里系统软件技术”,如需转载请联系原做者。

相关文章
相关标签/搜索