专栏名称: 分布式实验室
最专业的Docker文章,最权威的Docker新闻。关注容器生态圈的发展。
目录
相关文章推荐
51好读  ›  专栏  ›  分布式实验室

去哪儿网基于Mesos和Docker构建私有云服务的实践

分布式实验室  · 公众号  · 后端  · 2017-09-12 07:46

正文

本文深入介绍了去哪儿网利用Mesos和Docker构建私有云服务的全过程,分享了从无状态应用向有状态应用逐步过度的经验与心得。


平台概览

2014年下半年左右,去哪儿完成了有关构建私有云服务的技术调研,并最终拍定了Docker/Mesos这一方案。下图1展示了去哪儿数据平台的整体架构:


图1:去哪儿数据平台的整体架构


该平台目前已实现了如下多项功能:


  • 每天处理约340亿/25TB的数据;

  • 90%的数据在100ms内完成处理;

  • 最长3h/24h的数据回放;

  • 私有的Elasticsearch Cloud;

  • 自动化监控与报警。


为什么选择Docker/Mesos


目前为止,这个数据平台可以说是公司整个流数据的主要出入口,包括私有的Elasticsearch Cloud和监控报警之类的数据。那么为什么选择Docker/Mesos?


选择Docker有两大原因。第一个是打包:对于运维来讲,业务打完包之后,每天面对的是用脚本分发到机器上时所出现的各种问题。业务包是一个比较上层的话题,这里不做深入的讨论,这里讲的“打包”指软件的Runtime层。如果用Docker的打包机制,把最容易出现问题的Runtime包装成镜像并放在registry里,需要的时候拿出来,那么整个平台最多只执行一个远程脚本就可以了,这是团队最看好的一个特性。第二个是运维:Docker取消了依赖限制,只要构建一个虚拟环境或一个Runtime的镜像,就可以直接拉取到服务器上并启动相应的程序。此外Docker在清理上也较为简单,不需要考虑环境卸载不干净等问题。


以常见的计算框架来说,它们本质上仍然属于运行在其上的Job的Runtime。综合上述情况,团队选择针对Runtime去打包。


选择Mesos是因为它足够简单和稳定,而且拥有较成熟的调度框架。Mesos的简单体现在,与Kubernetes相比其所有功能都处于劣势,甚至会发现它本身都是不支持服务的,用户需要进行二次开发来满足实际要求,包括网络层。不过,这也恰好是它的强项。Mesos本身提供了很多SDN接口,或者是有模块加载机制,可以做自定义修改,平台定制功能比较强。所以用Mesos的方案,需要考虑团队是否可以Hold住整个开发过程。


从框架层面来看,Marathon可以支撑一部分长期运行的服务,Chronos则侧重于定时任务/批处理。


以下图2是Mesos的一个简单结构图:


图2:Mesos结构


数据平台的最终目标架构如下图3所示:


图3:平台目标


组件容器化与部署


组件的容器化分为JVM容器化和Mesos容器化。JVM容器化需要注意以下几方面:


潜在创建文件的配置都要注意


  • java.io.tmpdir

  • -XX:HeapDumpPath

  • -Xloggc


-Xloggc会记录GC的信息到制定的文件中。现在很少有直接用XLoggc配置的了(已经用MXBean方式替代了)。如果有比较老的程序是通过-Xloggc打印GC日志的话,那么要额外挂载volume到容器内。


时区与编码


  • –env TZ=Asia/Shanghai

  • –volume /etc/localtime:/etc/localtime:ro

  • –env JAVA_TOOL_OPTIONS=”-Dfile.encoding=UTF-8 -Duser.timezone=PRC


时区是另一个注意点。上面所列的三种不同的方法都可以达到目的,其中第一/三个可以写在Dockerfile里,也可以在docker run时通过–env传入。第二种只在docker run时通过volume方式挂载。另外,第三种额外设置了字符集编码,推荐使用此方式。


主动设置heap


  • 防止ergonomics乱算内存


这是Docker内部实现的问题。即使给Docker设置内存,容器内通过free命令看到的内存和宿主机的内存是一样的。而JVM为了使用方便,会默认设置一个人机功能会根据当前机器的内存计算一个堆大小,如果我们不主动设置JVM堆内存的话,很有可能计算出一个超过 Memory Cgroup限制的内存,启动就宕掉,所以需要注意在启动时就把内存设置好。


CMS收集器要调整并行度


  • -XX:ParallelGCThreads=cpus

  • -XX:ConcGCThreads=cpus/2


CMS是常见的收集器,它设置并行度的时候是取机器的核数来计算的。如果给容器分配2个CPU,JVM仍然按照宿主机的核数初始化这些线程数量,GC的回收效率会降低。想规避这个问题有两点,第一点是挂载假的Proc文件系统,比如Lxcfs。第二种是使用类似Hyper的基于Hypervisor的容器。


Mesos容器化要求关注两类参数:配置参数和run参数。


  • 需要关注的配置参数 


    • MESOS_systemd_enable_support

    • MESOS_docker_mesos_image

    • MESOS_docker_socket

    • GLOG_max_log_size

    • GLOG_stop_logging_if_full_disk


Mesos是配置参数最多的。在物理机上,Mesos默认使用系统的Systemd管理任务,如果把Mesos通过Docker run的方式启动起来,用户就要关systemd_Enable_support,防止Mesos Slave拉取容器运行时数据造成混乱。


第二个是Docker_Mesos_Image,这个配置告诉Mesos Slave,当前是运行在容器内的。在物理机环境下,Mesos Slave进程宕掉重启,、就会根据executor进程/容器的名字做recovery动作。但是在容器内,宕机后executor全部回收了,重启容器,Slave认为是一个新环境,跳过覆盖动作并自动下发任务,所以任务有可能会发重。


Docker_Socket会告诉Mesos,Docker指定的远端地址或本地文件,是默认挂到Mesos容器里的。用户如果直接执行文件,会导致文件错误,消息调取失败。这个时候推荐一个简单的办法:把当前物理机的目录挂到容器中并单独命名,相当于在容器内直接访问整个物理机的路径,再重新指定它的地址,这样每次一有变动Mesos就能够发现,做自己的指令。


后面两个是Mesos Logging配置,调整生成logging文件的一些行为。


  • 需要关注的run参数 


    • –pid=host

    • –privileged

    • –net=host (optional)

    • root user


启动Slave容器的时候最好不加Pid Namespace,因为容器内Pid=1的进程一般都是你的应用程序,易导致子进程都无法回收,或者采用tini一类的进程启动应用达到相同的目的。–privileged和root user主要是针对Mesos的持久化卷功能,否则无法mount到容器内,–net=host是出于网络效率的考虑,毕竟源生的bridge模式效率比较低。


图4:去哪儿数据平台部署流程图


上图4就是去哪儿数据平台部署的流程图。


基于Marathon的Streaming调度


拿Spark on Mesos记录子,即使是基于Spark的Marathon调度,也需要用户开发一个Frameworks。上生产需要很多代码,团队之前代码加到将近一千,用来专门解决Spark运行在Master中的问题,但是其中一个软件经常跑到Master,对每一个框架写重复性代码,而且内部逻辑很难复用,所以团队考虑把上层的东西全都跑在一个统一框架里,例如后面的运维和扩容,都针对这一个框架做就可以了。团队最终选择了Marathon,把Spark作为Marathon的一个任务发下去,让Spark在Marathon里做分发。


除去提供维标准化和自动化外,基于Spark的Marathon还可以解决Mesos-Dispatcher的一些问题:


  • 配置不能正确同步;这一块更新频率特别慢,默认速度也很慢,所以需要自己来维护一个版本。第一个配置不能正确同步,需要设置一些参数信息、Spark内核核数及内损之类,这里它只会选择性地抽取部分配置发下去。


  • 基于attributes的过滤功能缺失;对于现在的环境,所设置的Attributes过滤功能明显缺失,不管机器是否专用或有没有特殊配置,上来就发,很容易占满ES的机器。


  • 按role/principal接入Mesos;针对不同的业务线做资源配比时,无法对应不同的角色去接入Mesos。


  • 不能re-registery;框架本身不能重注册,如果框架跑到一半挂掉了,重启之后之前的任务就直接忽略不管,需要手工Kill掉这个框架。


  • 不能动态扩容executor。最后是不能扩容、动态调整,临时改动的话只能重发任务。


整个过程比较简单,如下图5所示:


图5:替代Spark Mesos Dispatcher


不过还是有一些问题存在:


Checkpoint & Block


  • 动态预留 & 持久化卷

  • setJars

  • 清理无效的卷


关于Checkpoint&Block,通过动态预留的功能可以把这个任务直接“钉死”在这台机器上,如果它挂的话可以直接在原机器上重启,并挂载volume继续工作。如果不用它预留的话,可能调度到其他机器上,找不到数据Block,造成数据的丢失或者重复处理。


持久化卷是Mesos提供的功能,需要考虑它的数据永存,Mesos提供了一种方案:把本地磁盘升级成一个目录,把这个转移到Docker里。每次写数据到本地时,能直接通过持久化卷来维护,免去手工维护的成本。但它目前有一个问题,如果任务已被回收,它持久化卷的数据是不会自己删掉的,需要写一个脚本定时轮巡并对应删掉。


临时文件


  • java.io.tmpdir=/mnt/mesos/sandbox

  • spark.local.dir=/mnt/mesos/sandbox


如果使用持久化卷,需要修改这两个配置,把这一些临时文件写进去,比如shuffle文件等。如果配置持久化卷的话,用户也可以写持久化卷的路径。


Coarse-Grained


Spark有两种资源调度模式:细粒度和粗粒度。目前已经不太推荐细粒度了,考虑到细粒度会尽可能的把所有资源占满,容易导致Mesos资源被耗尽,所以这个时候更倾向选择粗粒度模式。


图6:Storm on Marathon


上图6展示了基于Storm的Marathon调度,Flink也是如此。结合线上的运维和debug,需要注意以下几方面:


源生Web Console


  • 随机端口

  • OpenResty配合泛域名

  • 默认源生Web Console,前端配置转发,直接访问固定域名


Filebeat + Kafka + ELK


  • 多版本追溯

  • 日常排错

  • 异常监控


大部分WebUI上看到的都是目前内部的数据处理情况,可以通过ELK查询信息。如果任务曾经运行在不同版本的Spark上,可以把多版本的日志都追踪起来,包括日常、问题监控等,直接拿来使用。


Metrics


第三个需要注意的就是指标。比如Spark ,需要配合Metrics把数据源打出来就行。


ELK on Mesos


目前平台已有近50个集群,约100TB+业务数据量,高峰期1.2k QPS以及约110个节点,Elasticsearch需求逐步增多。


图7:ELK on Mesos


上图7是ELK on Mesos结构图,也是团队的无奈之选。因为Mesos还暂时不支持multi-role framework功能,所以选择了这种折中的方式来做。在一个Marathon里,根据业务线设置好Quota后,用业务线重新发一个新的Marathon接入进去。对于多租户来讲,可以利用Kubernetes做后续的资源管控和资源申请。


部署ES以后,有一个关于服务发现的问题,可以去注册一个callback,Marathon会返回信息,解析出master/slave进程所在的机器和端口,配合修改Haproxy做一层转发,相当于把后端整个TCP的连接都做一个通路。ES跟Spark不完全相同,Spark传输本身流量就比较大,而ES启动时需要主动联系Master地址,再通过Master获取相应集群,后面再做P2P,流量比较低,也不是一个长链接。


监控与运维


这部分包括了Streaming监控指标与报警、容器监控指标与报警两方面。


Streaming监控指标与报警


  • Streaming监控含拓扑监控和业务监控两部分。


  • Streaming拓扑监控


    • 业务监控

    • Kafka Topic Lag

    • 处理延迟mean90/upper90

    • Spark scheduler delay/process delay

    • Search Count/Message Count

    • Reject/Exception

    • JVM


拓扑监控包括数据源和整个拓扑流程,需要用户自己去整理和构建,更新的时候就能够知道这个东西依赖谁、是否依赖线上服务,如果中途停的话会造成机器故障。业务监控的话,第一个就是Topic Lag,Topic Lag每一个波动都是不一样的,用这种方式监控会频繁报警,90%的中位数都是落在80—100毫秒范围内,就可以监控到整个范围。


容器监控指标与报警


容器监控上关注以下三方面:


  • Google cAdvisor足够有效 


    • mount rootfs可能导致容器删除失败 #771

    • –docker_only

    • –docker_env_metadata_whitelist


  • Statsd + Watcher


    • 基于Graphite的千万级指标监控平台


  • Nagios


容器这一块比较简单,利用Docker并配合Mesos,再把Marathon的ID抓取出来就可以了。我们这边在实践的过程发现一个问题,因为Statsd Watcher容易出现问题,你直接用Docker的时候它会报一些错误出来,这个问题就是Statsd Watcher把路径给挂了的原因。目前我们平台就曾遇到过一次,社区里面也有人曝,不过复现率比较低。用的时候如果发现这个问题把Statsd Watcher直接停掉就好。指标的话,每台机器上放一个statsd再发一个后台的Worker,报警平台也是这个。


其实针对Docker监控的话,还是存在着一些问题:


  • 基础监控压力 


    • 数据膨胀

    • 垃圾指标增多

    • 大量的通配符导致数据库压力较高


  • 单个任务的容器生命周期


    • 发布

    • 扩容

    • 异常退出


首先主要是监控系统压力比较大。原来监控虚拟机时都是针对每一个虚拟机的,只要虚拟机不删的话是长期汇报,指标名固定,但在容器中这个东西一直在变,它在这套体系下用指标并在本地之外建一个目录存文件,所以在这种存储机制下去存容器的指标不合适。主要问题是数据膨胀比较厉害,可能一个容器会起名,起名多次之后,在Graphite那边对应了有十多个指标,像这种都是预生成的监控文件。比如说定义每一秒钟一个数据点,要保存一年,这个时候它就会根据每年有多少秒生成一个RRD文件放那儿。这部分指标如果按照现有标准的话,可能容器的生命周期仅有几天时间,不适用这种机制。测试相同的指标量,公司存储的方式相对来说比Graphite好一点。因为Graphite是基于文件系统来做的,第一个优化指标名,目录要转存到数据库里做一些索引加速和查询,但是因为容器这边相对通配符比较多,不能直接得知具体对应的ID,只能通配符查询做聚合。因为长期的通配符在字符串的索引上还是易于使用的,所以现在算是折中的做法,把一些常用的查询结果、目录放到里边。


另一个是容器的生命周期。可以做一些审计或者变更的版本,在Mesos层面基于Marathon去监控,发现这些状态后打上标记:当前是哪一个容器或者哪一个TASK出了问题,对应扩容和记录下来。还有Docker自己的问题,这样后面做整个记录时会有一份相对比较完整的TASK-ID。


原文链接:http://geek.csdn.net/news/detail/235946


烧脑式Kubernetes实战训练营


本次培训理论结合实践,主要包括:Kubernetes架构和资源调度原理、Kubernetes DNS与服务发现、基于Kubernetes和Jenkins的持续部署方案 、Kubernetes网络部署实践、监控、日志、Kubernetes与云原生应用、在CentOS中部署Kubernetes集群、Kubernetes中的容器设计模式、开发Kubernetes原生应用步骤介绍等,点击识别下方二维码加微信好友了解具体培训内容




点击阅读原文链接即可报名。