对于蘑菇街而言,每年的11.11已经成为一年中最大的考验,考验的是系统稳定性,容灾能力,紧急故障处理,运维等各个方面的能力。蘑菇街的私有云平台,从无到有,已经经过了近一年的发展,生产环境上经历了3次大促,稳定性方面得到了初步验证。本文我将从架构、技术选型、应用等角度来谈谈蘑菇街的私有云平台。
另,ArchSummit全球架构师峰会北京站将于2015年12月18日~19日在北京国际会议中心召开,大会设置了《揭秘双十一背后的技术较量》专题来深入解读双十一背后的技术故事,欢迎关注。
蘑菇街的私有云平台(以下简称蘑菇街私有云)是蘑菇街面向内部上层业务提供的基础性平台。通过基础设施的服务化和平台化,可以使上层业务能够更加专注在业务自身,而不是关心底层运行环境的差异性。它通过基于Docker的CaaS层和KVM的IaaS层来为上层提供IaaS/PaaS层的云服务,以提高物理资源的利用率,以及业务部署和交付的效率,并促进应用架构的拆分和微服务化。
在架构选型的时候,我们觉得Docker的轻量化,秒级启动,标准化的打包/部署/运行的方案,镜像的快速分发,基于镜像的灰度发布等特性,都十分适合我们的应用场景。而Docker自身的集群管理能力在当时条件下还很不成熟,因此我们没有选择刚出现的Swarm,而是用了业界最成熟的OpenStack,这样能同时管理Docker和KVM虚拟机。相对来说,Docker适合于无状态,分布式的业务,KVM适合对安全性,隔离性要求更高的业务。
对于上层业务来说,它不需要关心是运行在容器中,还是KVM虚拟机里。今后的思路是应用的微服务化,把上层的业务进行拆分,变成一个个微服务,从而对接PaaS基于容器的部署和灰度发布。
技术架构
在介绍双十一的准备工作之前,我先简单介绍一下蘑菇街私有云的技术架构。
我们采用的是OpenStack+novadocker+Docker的架构模式,novadocker是StackForge上一个开源项目,它做为nova的一个插件,通过调用Docker的RESTful接口来控制容器的启停等动作。每个Docker就是所谓的“胖容器”,它会有独立的IP地址,通过supervisord来管理容器内的子进程,常见的如SSHD、监控agent等进程。
我们在IaaS的基础上自研了PaaS层的编排调度等组件,实现了应用的弹性伸缩、灰度升级,支持一定的调度策略。我们通过Docker和Jenkins实现了持续集成(CI)。Git中的项目如果发生了git push等动作,便会触发Jenkins Job进行自动构建,如果构建成功便会生成Docker Image并push到镜像仓库。基于CI生成的Docker Image,可以通过PaaS的API或界面,进行开发测试环境的实例更新,并最终进行生产环境的实例更新,从而实现持续集成和持续交付。
网络方面,我们没有采用Docker默认提供的NAT网络模式,NAT会造成一定的性能损失。通过OpenStack,我们支持Linux bridge和openvswitch,不需要启动iptables,Docker的性能接近物理机的95%。
准备工作 稳定性
迎战双11,最重要的当然是确保稳定性。通过近一年的产品化和实际使用,我们积累了丰富的提高稳定性的经验。
对于那些已遇到过的问题,需要及时采用各种方式进行解决或者规避。
比如说,CentOS6.5对network namespace支持不好,在Docker容器内创建Linux bridge会导致内核crash,upstream在2.6.32-504中修复了这个bug,因此线上集群的内核版本,必须升级至2.6.32-504或以上。
又比如,CentOS6.5自带的device mapper存在dm-thin discard导致内核可能随机crash,这个问题我们早在四月份的时候已经发现并解决了,解决的办法是关闭discard support,在docker配置中添加“--storage-opt dm.mountopt=nodiscard --storage-opt dm.blkdiscard=false”,并且严格禁止磁盘超配,因为磁盘超配可能会导致整个device mapper无法分配磁盘空间,而把整个文件系统变成只读,从而引起严重问题。
监控
我们在双11前重点加强的是针对容器的监控。
在此之前,我们已经自研了一套container tools。主要功能有两个:一是能够以容器为粒度计算load值,可以根据load值进行容器粒度的qps限流。二是替换了原有的top、free、iostat、uptime等命令,确保运维在容器内使用常用命令时看到的是容器的值,而不是整个物理机的值。双十一之后我们还会把lxcfs移植到我们的平台上来。
在宿主机上,我们增加了多维度的阈值监控和报警,包括对关键进程的存活性监控/语义监控,内核日志的监控,实时pid数量的监控,网络连接跟踪数的监控,容器oom的监控报警等等。
实时pid数量监控
为什么要监控实时的pid数量呢?因为目前的Linux内核对pid的隔离性支持是不完善的。还没有任何Linux发行版能做到针对pid按照容器粒度进行pid_max限制。
曾经发生过一个真实的案例是:某个用户写的程序有bug,创建的线程没有及时回收,容器中产生了大量的线程,最后在宿主机上都无法执行命令或者ssh登陆,报的错是"bash: fork: Cannot allocate memory",但是此时通过free命令看到空闲的内存却是足够的。
为什么会这样呢?根本原因是内核中的pid_max(/proc/sys/kernel/pid_max)是全局共享的。当一个容器中的pid数目达到上限32768,会导致宿主机和其他容器无法创建新的进程。最新的4.3-rc1才支持对每个容器进行pid_max的限制。
内存使用监控
值得一提的是,我们发现cgroup提供的内存使用值是不准确的,比真实使用的内存值要低。因为内核memcg无法回收slab cache,也不对dirty cache量进行限制,所以很难估算容器真实的内存使用情况。曾经发生过统计的内存使用率一到70-80%,就发生OOM的情况。为此,我们调整了容器内存的计算算法,根据经验值,将cache的40%算做rss,调整后的内存计算比之前精确了不少。
日志乱序
还有一个问题是跑Docker的宿主机内核日志中经常会产生字符乱序,这样会导致日志监控无法取到正确的关键字进行报警。
经过分析发现,这个跟我们在宿主机和Docker容器中都跑了rsyslogd有关。由于内核中只有一个log_buf缓冲区,所有printk打印的日志先放到这个缓冲区中,docker host以及container上的rsyslogd都会通过syslog从kernel的log_buf缓冲区中取日志,导致日志混乱。通过修改container里的rsyslog配置,只让宿主机去读kernel日志,就能解决这个问题。
隔离开关
平时我们的容器是严格隔离的,我们做的隔离包括CPU、内存和磁盘IO,网络IO等。但双十一的业务量可能是平时的十几倍或几十倍。我们为双十一做了不少开关,在压力大的情况下,我们可以为个别容器进行动态的CPU,内存等扩容或缩容,调整甚至放开磁盘iops限速和网络的TC限速。
健康监测
我们还开发了定期的健康监测,定期的扫描线上可能存在的潜在风险,真正做到提前发现问题,解决问题。
灾备和紧急故障处理
除了稳定性,灾备能力也是必须的,我们做了大量的灾备预案和技术准备。比如我们研究了不启动Docker Daemon的情况下,离线恢复Docker中数据的方法。具体来说,是用dmsetup create命令创建一个临时的dm设备,映射到Docker实例所用的dm设备号,通过mount这个临时设备,就可以恢复出原来的数据。
我们还支持Docker容器的冷迁移。通过管理平台的界面可以一键实现跨物理机的迁移。
与已有运维系统的对接
Docker集群必须能与现有的运维系统无缝对接,才能快速响应,真正做到秒级的弹性扩容/缩容。我们有统一的容器管理平台,实现对多个Docker集群的管理,从下发指令到完成容器的创建可以在7秒内完成。
性能优化
我们从系统层面也对docker做了大量的优化,比如针对磁盘IO的性能瓶颈,我们调优了像vm.dirty_expire_centisecs,vm.dirty_writeback_centisecs, vm.extra_free_kbytes这样的内核参数。还引入了Facebook的开源软件flashcache,将SSD作为cache,显著的提高docker容器的IO性能。
我们还通过合并镜像层次来优化docker pull镜像的时间。在docker pull时,每一层校验的耗时很长,通过减小层数,不仅大小变小,docker pull时间也大幅缩短。
镜像 | 文件层数 | 文件大小 | docker pull时间 |
原镜像 | 13 | 1.051 GB | 2m13 |
新镜像 | 1 | 674.4 MB | 0m26 |
总结
总的来说,双11是对蘑菇街私有云的一次年终大考,对此我们已有了充分的准备。随着Docker集群部署的规模越来越大,我们还有很多技术难题有待解决,包括容器本身的隔离性问题,集群的弹性调度问题等等。同时我们也很关注Docker相关的开源软件Kubernetes、Mesos、Hyper、criu、runC的发展,未来将引入容器的热迁移,Docker Daemon的热升级等特性。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
新闻热点
疑难解答
图片精选