DockOne技术分享(二十三):暴走漫画的Docker实践


【编者的话】本次主要分享Docker在暴漫中的应用主要包括:开发环境的Service搭建,代码托管、持续集成、Docker镜像等若干Support服务、部分微服务以及整个数据服务系统。

暴走漫画是一家文化传媒公司。公司除了有若干视频娱乐节目,还有相应的社区网站及移动APP。流量UV是200万/天左右,PV大概是千万级别。为了更加有效地运营以及推荐用户个性化,2015年成立了数据部,负责暴漫的数据分析和数据挖掘相关服务。

暴漫没有自己的服务器,是使用的国内某云服务。暴漫的后端主要是基于Ruby开发。也有基于Go、Python的一些微服务。

Docker在暴漫中的应用主要包括:
  • 开发环境的Service搭建
  • 代码托管,持续集成,Docker镜像,等若干Support服务
  • 部分微服务以及整个数据服务系统


所以今天的内容是一些中小规模以及国内云服务下的Docker实践的相关心得,主要包括在数据服务的架构及Docker化的部署。

1. 简单介绍下开发环境以及Support服务Docker应用

由于开发环境主要是Mac,也有少量Ubuntu和Windows,所以主要采用Vagrant+Docker方式。 将微服务做成镜像,在 Vagrant 中起相应的容器,把端口暴露给 Host(Vagrant),本地跑 Ruby(on Rails)。

Support 服务的话,其他都很简单,只有持续集成介绍下。我们用的GitLab CI。GitLab CI支持将 task 跑在Docker Container 里面,所以我们为不同的项目准备不同的测试环境(镜像)以及外部依赖(eg. MySQL、Redis),然后在对应的Container里面跑测试。

关于部署的话,我们平时的开发在develop分支,一旦向Masters分支合并后,会触发部署的task。 部署的 task 跑在特定的Container里面,这个Container共享了 Host 的docker unix sock文件,可以执行 docker builddocker push等命令。

关于开发环境和Support服务的Docker应用,因为不是今天的重点,并且前面也有很多朋友做过类似的介绍,所以先简单介绍到这里。

2. 微服务和数据服务系统的Docker应用

今年我们做了很多微服务的尝试,例如消息推送,推荐系统,反垃圾系统,数据分析系统,视频抓取等等若干子系统的拆分上线。 虽然过程是痛苦的,但是结果却是令人欣慰的。这些微服务,几乎都是基于Docker的。

2.1 Rails +Docker化的微服务

整体来说,我们是个混合的架构,Rails是正常的跑在云主机中的,微服务跑在Docker中。为了协调好各方,我们对基础服务做了一点小小的调整。

这里不得不说说我做架构的一点心得。好的架构除了能满足业务需求,还要是与特定的团队,特定的资源所配套的。在暴漫,由于技术力量有限,开发排期满,所以我都是尽量采用“非侵入式”的方案,这在后面的数据服务的构建中也有体现。

首先,我们给所有的机器都装上了Docker。 其次,我们搭建了一个 etcd集群,将所有的云主机都纳入了etcd集群。而etcd也是跑Docker里的。

为了方便的跑起来etcd,我们写了个一套bash+Python的脚本(Python 的脚本也是跑在Docker里的),然后所有的机器直接访问本机IP可以访问和操作etcd。

这里插一句,我们没有去折腾如何让Docker跨主机组网,而是直接采用映射到host的方式。一方面国内云主机只能这么干。另一方面,我们之前使用云主机也是单个主机特定用途的。 另外,在生产环境中,我们大量的使用了shell + etcd来启动Docker Container的方式。可以给大家看个 etcd 的启动脚本。这个script放到最初的机器上就可以方便地启动起来etcd 集群。
#!/bin/sh

# pull pycsa docker image
docker pull xxxx/pycsa:latest

# nodes list to init the cluster
ClusterNodes=$(docker run —rm \
-v .:/data \
xxxx/pycsa:latest \
python initial-cluster.py getnodes)

# get host ip
HostIP=$(ifconfig eth0 | awk '/\<inet\>/ { print $2}' | sed 's/addr://g')

# get the etcd node name
EtcdName=$(docker run —rm \
-v .:/data \
xxxx/pycsa:latest \
python initial-cluster.py getname ${HostIP})

# create dir structure
EtcdData=/data/etcd/data
mkdir -p ${EtcdData}

# pull etcd docker image
docker pull quay.io/coreos/etcd:latest

# create etcd container
docker run -d \
-v /usr/share/ca-certificates/:/etc/ssl/certs \
-v ${EtcdData}:/data \
-p 4001:4001 -p 2380:2380 -p 2379:2379 \
—name etcd quay.io/coreos/etcd:latest \
-name ${EtcdName} \
-data-dir /data \
-advertise-client-urls http://${HostIP}:2379,http://${HostIP}:4001 \
-listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 \
-initial-advertise-peer-urls http://${HostIP}:2380 \
-listen-peer-urls http://0.0.0.0:2380 \
-initial-cluster-token bzetcd-cluster \
-initial-cluster ${ClusterNodes} \
-initial-cluster-state new

解释下,initial-cluster.py 是个Python的脚本,跑在一个pycassa的容器里,这个容器有Python环境以及相关的package这样原来的服务几乎不受任何影响,我们可以利用 etcd+Docker+Shell Script来组建新的服务。

2.2 数据服务

我们的数据服务包括数据分析和数据挖掘两大块。数据分析主要是为了给运营提供量化的效果评估以及指导。数据挖掘则包括推荐,反垃圾等。

数据服务的基础是数据流,即:数据收集->数据分发->数据处理<->数据存储。

先给大家看个整体的架构图,由于本人不擅作图,所以直接用手画的,还请见谅。。
13.pic_hd_.jpg

首先数据收集部分,就像之前说的,我尽量采用“非侵入式”的方案,所以,我们的整个数据收集都是基于日志的。我们在每个应用服务器上装了Logstash(跑在Docker中)来收集各个应用服务器的日志,然后打到Kafka(跑在Docker 中)里,给不同的用途使用。

一份COPY 直接由Kafka一端的Logstash存储到Elasticsearch(跑在Docker中)中。 一份COPY 经过Spark(跑在Docker中)Stream做实时处理(包括一些特定日志的提取),然后将处理的结果存储在 Elasticsearch 里 还有一份 COPY 直接存储到 HDFS(由云服务商提供)。

这里有个小问题,比如有些数据本身日志里并没有,比如用户的点击行为。这个时候,我们专门开发了一些 "ping" 接口,这些接口通过 Nginx 直接返回 200,并记录相关日志。

此外还有一部分数据,例如一些比较需要“较严格的完备”的,例如用于推荐系统,反垃圾系统学习的数据,我们存储在 SQL 数据库中 下面我做些稍微详细的介绍。

2.2.1 数据分析

数据分析有两种:实时数据分析和离线数据分析。

实时数据分析从Kafka到Spark stream,处理结果进Elasticsearch,离线分析是定时任务,从 HDFS 到 Spark,处理结果进Elasticsearch。一般来说,离线的结果会逐步包含实时的结果,同时实时的结果领先于离线分析的结果。

这里的分析有些抽象,我来举个例子:

Q:统计某个板块同时在线人数的变化趋势。
A:用户每次访问都有日志,日志里包括访问内容以及用户标识。首先 spark stream 从日志里抽取出特定板块不同用户的访问事件,以秒为单位合并相同用户事件。


这就是分析结果:时间戳:人数。 然后这个结果怎么用?
Elasticsearch有很强大的agg接口。你可以以1秒、10秒、1分等等各种时间间隔单位聚合这段时间内的在线人数,聚合方式用 「平均」或「最大」。

2.2.2 数据挖掘

我们主要做了2个具体的数据挖掘系统:推荐+反垃圾。今天主要讲下架构。

这两个系统基本上步骤是一样的,分为2步:训练(train)和 服务(serve)。

在train阶段,定时起一个spark job,从训练数据集中读取数据,学习出Model,然后将Model存储成文件。

在serve阶段,起一个带 serve 的 Spark job,load 之前学习出来的model 文件进内存,然后接受外部API调用,返回结果。

关于服务的开发这部分因为涉及到太多额外的知识,我就不多说了。 这里讲个难点:Spark的Docker化。

2.2.3 Spark的Docker化

Spark的Docker化分为两个部分:
  • Docker化的Spark集群
  • Docker化的Spark调用


Spark和我们一般用的服务不太一样,它的集群不是提供运算服务的,而是一种资源费配的调度器。

让 Spark 跑 Job,其实是起的一个 Spark 的本地程序,这个本地程序会向cluster(要资源其他机器),cluster 分配资源以后,这个Spark程序就把一些工作放在这些资源当中运行(进程)。

所以Spark的Docker化分为两个部分。

对于Spark调用,也就是启动Spark的本地程序,我们就是在跑程序的镜像中集成Java环境,Spark程序。

对于Spark集群,稍微复杂一些。Spark 支持三种集群:Mesos、Yard还有Spark自己的一个Standalone。

我们搭建的Spark Standalone集群,这还是考虑到我们自身的资源与需求。 由于没找到官方的Spark Docker image,我们自己做了一个,就是Java环境+Spark程序,然后利用script+etcd以不同的姿势(master 或 slave)在不同的云主机上启动Spark容器。

官方推荐要起3个Master,用ZooKeeper做quorum,这个我们最近正在搞,还没上线,就不分享。我们现在线上跑的是 1 master + 7 slave。

Q&A

Q:请问Docker是部署在裸机还是虚拟机,Docker的管理用的什么?部署是人工吗?

A:Docker 是跑在国内某云主机上的,所以应该算虚机。
Q:传统关系数据库怎么Docker化的?

A:我们传统关系数据库并没有Docker化,一方面我们直接用的云主机提供的sql数据库服务,另一方面,也许这部分原有的方案太成熟,短期Docker完全取代可能比较困难。
Q:每台机器上都部署Logstash,那filter部分在哪处理?为什么不用syslog来转发日志到日志服务器?

A:filter部分是通过spark stream来做的,Logstash纯粹收集转发, kafka是一个 MQ 系统,而且实时分析从Kafka到spark stream很方便。
Q:如何做微服务拆分的,经验是什么?

A:这个问题我感觉有点大,我尝试回答下:先做好详细的计划,尽量保证服务的平稳过渡,必要的时候,老系统和新系统同时保留一段时间。
Q:Docker用的时候可以挂载存储?就是想把静态的网站图文数据Docker化,这些静态文件我们存储在单独的分布式存储和阵列中,走的nfs协议和私有api的形式。

A:这个放image里好像不是好主意,要不用一些分布式的存储方案,要不用云存储服务?
Q:“一份copy存到hdfs里”考虑过其他的存储吗?

A:Spark有方便的对hdfs的接口,且云服务商有现成的hdfs服务提供,所以我们就用了。
Q:为什么选择Logstash,而不选择Flume,它们有什么差异,比如对安装端的资源开销投入产出比等?

A:最近在研究用heka,开始用Logstash的话是因团队本身的知识偏好(Ruby 团队)。
Q:我觉得在数据服务部分讲了太多,我更想知道Docker在开发,测试或生产环境怎么使用,利用什么方式管理Docker集群,部署服务?比如那个shell+etcd来启动Docker是怎么回事,为什么不用一些Docker生态相关的开源工具,比如k8s、swarm之类 ?

A:关于为什么没用k8s。我开始也说了,选择技术架构不光要考虑技术本身,还要考虑团队资源,以及现有的限制。我们用的国内的云主机,这样的前提下要在生产环境中用k8s本身就不太可能。另外技术团队人手也很欠缺。
Q:请问Docker容器是如何进行远程部署和自动部署的?

A:我们用了传统的部署工具(Ansible)。
Q:Ansible如何获取Docker主机列表,所有的Docker容器?

A:Ansible 是操作云主机的,就和以前的用法一样。
Q:请问ping nginx获得用户点击是怎么做的,可以详细介绍下吗?

A:将某些统计数据放在ping接口的参数里,这样定制nginx的日志可以记录下来。
Q:反垃圾系统数据库的数据是通过什么方式进入Kafka的,ogg、sqoop吗?

A:样本数据的话是rails程序推进mq的。
Q:搜索引擎选Elasticsearch而不是Solr,可以讲一下这样选择都有哪些考虑吗?

A:Elasticsearch不仅仅是个搜索引擎,更是我们用来做结果聚合的database,而且有很好的水平扩展特性。
===========================
以上内容根据2015年9月29日晚微信群分享内容整理。分享人丁彦,暴走漫画技术总监。负责暴走漫画数据分析、数据挖掘系统的设计与实现。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesx,进群参与,您有想听的话题可以给我们留言。

0 个评论

要回复文章请先登录注册