Docker生产环境架构讨论

讲师:方云麟,就职于网宿科技,负责docker集群的架构设计以及项目推进。兴趣:docker、传统虚拟化、golang

大家好,我是云麟。我今天和大家交流docker以及kubernetes。我们先讲下docker。

docker的特性是什么?

这个网上很多。我就不复制粘贴了。

我总结下我觉得比较好的几个特性

  1. 一键部署,部署原子化:我们可以狭隘的把 docker 理解成是一个应用打包解决方案:把运维环境、代码进行打包,在部署的时候,只需要载入该镜像,则服务被运行起来。这个过程是原子化的,所以很大程度避免了部署过程中人为或者不可预料的意外。同时也大大加快了部署过程,基本上可以在1分钟内部署好一个业务,因此也具有很大的弹性扩展优势。

  2. 隔离和性能:docker专注于隔离,并且镜像是只读的。于是提高了业务的安全性——复用主机的各业务之间,一旦被黑,影响的因素会少很多。并且 docker 仅仅做了隔离,并不虚拟硬件,所以和虚拟机比,没有虚拟化带来的性能消耗。

网上曾经有文章爆说docker有安全问题,官方registry镜像仓库,1/3镜像有安全漏洞。

我觉得这是有道理的。但是,哪个原始系统没漏洞?所以这时候问题就在于我们操作了。

我在正式环境中,都是要把系统更新到最新的。修复若干bug。

  1. 复用宿主机内核:docker 并不是虚拟机,仅仅是进程等资源的隔离,因此并没有独立的内核,所有进程都是复用宿主机内核。

这三点,是我认为docker比较显著的特性。

大家可以把docker的业务部署,狭隘的理解成是苹果的 app store。我要运行什么业务,一键下载运行,不需要我们去make config 等等。

也就是说,我认为docker更亲和于面向业务。

而不是面向部署细节的纠结(当然,需要镜像打包人员去纠结。)

有哪些公司已经把 docker 应用到生产环境呢?

我来出卖下一个队友。

福州的17173。80后还有70后,调皮的同学应该都多少有接触,游戏攻略网站。

此外,京东。

特别要指出 docker 分享了 618 大促的案例:http://www.infoq.com/cn/news/2015/06/jd-618-docker/ 很鼓舞人心。

京东用docker的弹性伸缩的支持性,抗住了618大促。

这里有么有jd的同学,来辟谣下?

当前,国内已经有把docker做成产品的服务商,很多细节已经不用我们烦恼了(如果你不介意把业务放在别人家里)

从这个角度,是可以省掉中小公司很多技术开支的。

希云 – https://csphere.cn

这是三家docker服务商,都提供了docker运行平台,还有镜像。

这里不一定有jd的人,但是一定有这三家的人,有木有?

我建议,有兴趣的可以去这三个网站看看,都可以免费试用的。

这是从0到1的比较好的了解方式,我是这么认为的。

希云有几个不错的 docker 入门视频,大家可以看看:https://csphere.cn/training

感谢一下希云的同学。

不只一个人问我,docker要直接运行在虚拟机里好呢,还是运行在物理机上好。

我是这么想的

docker 专注于环境隔离,并非一个完全的虚拟机产品。

docker 不具有内存状态迁移特性,即使是所谓迁移,也仅仅是在另一台机器运行同一个镜像而已。如果在 docker 外部包一层虚拟机,则可以提供内存热迁移特性。

但是如果和虚拟机结合,则丧失了 docker 相对于虚拟机的性能优势。

这就是业务和性能的一个平衡博弈。

重要的事情将三遍:docker不是虚拟化。docker不是虚拟化。docker不是虚拟化。

docker的存储。

大家脑补一下苹果的app。系统为每个app分配了单独的目录/存储空间。

当我们删除这个app,这个空间内的数据也可能给删掉的。

所以docker什么数据使用默认存储,什么数据需要外挂永久存储,这是比较重要的一个点。

docker镜像默认使用

AUFS文件系统。这个大家可以百度下。是一个分层文件系统。

docker 如果使用 AUFS 文件系统(默认),是有虚拟的分区大小概念的,通常是 8G 或者 20G,这个也不满足需求。因此有一个原则:不要在容器内保存永久数据。永久数据需要挂载到容器外。

其实类似AUFS理念的,还有很多看似不相干的技术。比如Linux逻辑卷LVM的snapshot快照。

硬件存储器Equallogic的快照,等等。

实现不同,理念类似。

有一个点容一个忽略。镜像使用自己的文件存储空间。而从每个镜像运行的实例(容器),也要单独分配一部分空间的。

新版本的docker,在运行镜像的时候,可能会报一些警告,这是因为默认情况,容器container分配的空间是作为file形式存在的。多了一层文件系统。

新版本推荐使用device-mappler方式,跳过文件系统层,性能更高。

这么看,docker似乎还是很美好的。

但是,这里面坑还是很多,用着用着就遇到了。

比如

docker 在正式环境中通常是以集群的方式使用的,因此会在各机器上运行,所以为了保证数据的一致性和可访问性,共享存储是不可避免的。

推荐使用分布式文件系统。比较常用的,且适用于 docker 的分布式有 GlusterFS、MooseFS。其他分布式文件系统我尚未测试。

其实,共享文件系统有多种方式。

比如传统共享文件系统,红帽的Global Filesystem(GFS),Oracle的OCFS等。

但是这些文件系统的本质都是加锁访问,节点越多,性能越差。并且也有节点限制。

而是用分布式文件系统,虽然也有锁概念,但是毕竟数据分散了,锁也不一定是锁整个元数据,所以还是会好一点。

并且分布式文件系统对多节点的支持性会更好。这也是趋势了。

docker的镜像

docker 官方的镜像仓库叫做 docker hub,网址是:https://hub.docker.com,上面提供了大量的基础镜像以及业务镜像。

如果对docker比较没概念的同学,可以上去看看。

基础镜像诸如各操作系统镜像,CentOS、Fedora、Debian、Ubuntu等。业务镜像诸如 Tomcat、ThinkPHP、LNMP 等。

我强调一下,我们是用这些镜像的环境,也就是动态链接库、工具等,总之就是文件。

类似一个chroot环境。

docker内部程序还是运行在物理机内核上的。

docker hub 还提供了私有仓库。但是我相信大多数人还是不放心的,所以可以很快捷的搭建自有仓库,把镜像推送到自己的存储上

我要插一个事情。不止一个人问过我:我们业务需要用到定制内核,怎么办?

回答是:不用担心,你只要确保宿主系统的内核是定制内核就行了。

我们继续讲镜像。

刚才谈到说私有仓库。

私有仓库已经被打包成 image,只要运行 registry 镜像即可。镜像提供三种验证方式:

  1. 无验证

  2. 账号密码验证

  3. 密钥对验证

部署细节请参考:https://docs.docker.com/registry/deploying/

docker的文档都很细,我很爱,推荐大家珍惜!

我觉得有时候买市面一些docker入门书,不如静下心把官网文档先瞄一遍。

群里出书的同学别砸我。。。。

最新的Registry 是v2协议,之前可能用v1比较多。v2比v1多提供了很多接口,比如

就是镜像仓库协议。

  • 直接删除镜像

  • 查询镜像

查询tag

具体API请参考:https://docs.docker.com/registry/spec/api/

比较尴尬的是,registry只有restful接口。不过v1版本协议,市面上有很多UI程序。可以配合。

但是v2版本我没细看,我用过的UI管理界面不支持v2协议。。

此外v2提供了原生的验证机制,如果是v1,验证要在前面加一个Nginx进行验证。

当然,你也可以用Nginx + v2 registry。

Nginx/Apache 的 basic 验证,大家可以网上百度/google下。

利用的是这个。

通常我们要对docker官方提供的镜像定制,才能符合我们自家业务。

所以就涉及到一个镜像修改和打包的问题。

编写打包规则 > 打包成镜像存在本地 > 推送到镜像服务器

大概是这么一个过程。

当然我们希望越自动化越好。

docker image 的打包通常会和 SVN/GIT/Jenkins 结合在一起,以实现自动化打包甚至推送。

github 和 docker的官方镜像库 docker hub 就很有基情。

github 和 docker hub 推出了自动打包功能,一旦打包文件上传到 github,可自动打包成 docker image,并推送到 docker hub。

docker官方镜像库的镜像基本都是这么做出来的。

这样我们就可以把自己打包的空间和CPU剩下来了。

对于私有的代码库也可以实现自动打包,SVN/GIT均有钩子功能。

自行搜 svn hooks ,git hooks 即可。

docker的网络解决方案

现在业务是离不开网络的。客户总是要通过网络访问到我们。

那么客户怎么访问到我们服务器内部的容器呢?

有几种方案。

  1. docker run -p 直接暴露端口 —— 存在端口冲突风险

  2. pipework 桥接,给容器分配独立可用 IP

  3. 反向代理(如Nginx/HaProxy)转发

那么问题又来了(如果你只是看看,没有相关业务,你一定不会想到,但是其实都会遇到)

如何及时更新Nginx/HaProxy的配置信息,以适应动态增加的业务呢?

通用的解决方案有 confd 方案。 confd 可以监听 etcd (也许可以支持zk?) 的数据变化,一旦有变化,根据之前定义好的 confd template ,生成新的配置,然后测试,重新加载服务。

etcd是一个目录状数据库,和ZooKeeper属于一个领域的产品

Nginx 还有另一个解决方案 —— dyups。这是一个 nginx 扩展,通过RESTful协议,动态修改 upstream(backend) 信息。

我们可以在启动业务的时候,出发脚本,比如调用curl等,通知到信息中心。

这个是第一部分。docker。

docker管理工具

docker其实是蛮尴尬的。

我狭隘的认为,有一个理念,叫做docker生态环境。

docker本身只专注于隔离技术,所以没有去添加很多管理或者其他功能。

而一个用在正式环境的产品,这样是远远不够的。

所以docker是有很多周边产品的。

我们需要和乐高玩具一样,自行组装。

这也是使用docker的一个门槛(如果你只是玩玩,倒是不用烦恼这些)

举个例子。shipyard这个项目,给docker提供UI界面,和初级的批量管理功能。

还有cAdvisor为docker提供性能监控

我们接下去要讲的是,如果我有成千上万台服务器(我司真的有!)

我要怎么管理这些docker?

要意识到一点,docker有一个优势在于容器的打包隔离,所以一定会涉及到机器的复用的。

一台服务器会运行很多docker 容器(app)

假设我有10000台服务器,上面混杂了N个业务,这种排列组合,管理起来要死的!

所以,就产生了docker集群管理工具。

现在比较经常上镜的,有侧重资源调度的MesOS(它不是针对docker,但是兼容docker接入)

侧重容器容灾和业务管理的kubernetes

还有比较轻量级的swarm(swarm不仅本身可以作为docker管理,也可以作为一个包装,嵌入其他docker管理产品)

swarm的API接口和docker是兼容的。

不过我们今天讲的是kubernetes,因为swarm是给我个人嫌弃的(s粉莫丢我)

我们先约定一下

kubernetes,由于首位字母之间有8个字母,所以也简称k8s

和i18n同理。

k8s 最简部署方法可看我博客 http://www.fangyunlin.com/?p=54

大家排队看,阿里云小带宽。

为什么选择kubernetes呢

k8s 具有如下特性:

  • 自带容灾,高可用性方案

  • 自带负载均衡方案

  • 负载均衡机制,原生前后端结构,内嵌负载均衡器。

  • 基于业务为管理单位,符合生产环境,减少管理成本。

研发团队是google,技术值得信赖,代码更新频率很快。

这些特性,给大伙儿做二次研发或者应用,省掉很多工作量呀!

不过,我也得说一下k8s的不便之处。。

网络解决方案比较繁琐,难以直接暴露业务给集群外部用户。

我们讲一下kubernetes狭隘逻辑层面组成:

  • Frontend – Service

Backend – Pod & Replication Controller

这个叫做Service的,是前端。

Pod和RC是后端。

也就是说,我们把容器跑在k8s上,默认会给每个业务做前后端分离。

这样的好处是灵活呀。

我可以不停迁移后端,而客户访问的是前端,基本感觉不到的。

或者说是后端做副本增加,对前端来讲,是感觉不到的。

实际业务跑在后端,也就是Pods和Replication Controller

Replication Controller 继承自 Pod,和 Service 配合具有自动迁移特性。

Service 提供一个IP,用户访问该IP,k8s会把请求按照策略分发到后端某一个 Pod 上。Service 和 Pod/Replication Controller 通过标签进行关联,也就是说更改标签,可以灵活的组合 Service 和 Pod/Replication Controller,达到业务升级以及灰度升级的目的。

比如我跑了10个不同业务,Service是几个?10个哟。10个Service,10 个IP

mik

容我盗图一张。

我们接下去说kubernetes运维层面组成

运维层面,由控制角色 master 、 以及计算节点node组成。

master有三个服务:

kube-apiserver – 负责管理员控制命令的接收和分发

kube-controller-manager – 负责把对客户端的操控指令下发到各节点

kube-scheduler – 资源和筛选,但是只支持业务创建时候进行调度。也就是说如果业务已经分配到某个节点,这时候压力忽然增高,并不会出现自动迁移操作。

这三个服务是可拆分的,可以运行在不同的机器上,以确保更好的稳定性,避免单点故障牵连。

node角色有两个服务,是不方便拆分的:

kubelet – 负责 k8s node 管理层面的一切逻辑

kube-proxy – 负责 k8s 网络层面的逻辑

我们详细讲一下master的细节

master的一切数据都保存在 etcd 服务器中,并且 master 挂掉并不影响 Service 和 Replication Controller 的访问。因此 master 的高可用方案,最笨的话,配置一套作为冷备,出问题顶上去就行。

tips:etcd是一个类似ZooKeeper的服务,用来存放、共享数据,适合做服务发现等。

如果要实现热备,要注意 controller-manager 和 scheduler 在内存中保存有集群节点信息,所以无法同时多实例提供服务,需要规避这个问题。

然后我们说下 node的细节,也就是计算节点的细节。刚才是管理节点。

kubelet 负责 k8s 层面的一切逻辑

重点要说 kube-proxy:

默认情况下,k8s 没有对 docker 网络做任何改造,所以无法实现跨主机的容器之间的访问。kube-proxy只是一个反向代理,提供宿主机到容器内部的网络通信,并不提供跨宿主机的网络通信功能。

  1. 默认情况下,如果要访问容器端口,必须有和 docker run -p 一样的机制,这个在 k8s 中是 Service 的 NodePort。NodePort会在运行了业务的宿主机上暴露指定端口,映射到容器内部。

NodePort机制我之前觉得很二笔,但是在实际应用中,却后来发现还是能解决燃眉之急的。尤其是一个公司某些业务不可避免的复杂的时候。

比如客户要主动发起到服务器的连接的时候。。

  1. 跨宿主机的容器通讯方案,比较主流的有 flannel 和 OpenVSwitch。这两个工具可以打通各主机的 kube-proxy,也就是说需要和 kube-proxy 配合。

这是kube-proxy的3个点。

我再次啰嗦:docker和k8s默认都没提供跨物理主机的容器之间的通讯方案。

这是个问题。

比如,物理机A,内容器1,要访问物理机B内的容器2,就是这么一个场景。

没关系,docker 生态环境就有这么一个环节,把这个功能补上。

k8s 常用的网络解决方案有 flannel 和 OpenVSwitch。这两个工具可以打通各主机的 kube-proxy,也就是说需要和 kube-proxy 配合。因此,如果需要访问 k8s 业务的机器,都需要装这两个组件。此外,flannel 依赖于 docker,因此不管这台机器是否要运行 docker 容器,都必须安装 docker 以让 flannel 正常工作。

flannel 和OVS,自行google或者

AOL官网即可。

有些同学可能不知道,AOL的搜索引擎就是google的,不需要翻墙哈。

刚才说到,flannel 依赖于 docker,因此不管这台机器是否要运行 docker 容器,都必须安装 docker 以让 flannel 正常工作。

flannel

因为外界客户不可能为了访问 k8s 环境的业务,去装 kube-proxy 等组件,所以通常会在 k8s 环境中加一台反向代理,打通外部网络和内部网络环节。所以这台机器就必须装 kube-proxy 和 flannel 或者 OpenVSwitch 了。

通常有几种方案

用户 – Nginx/HaProxy – k8s Service – k8s RC(Replication Controller)

用户 – Nginx/HaProxy – k8s RC

我上面说的这两行消息,是很重要很重要的~~~~~在实际应用中,不管你是直接用docker,还是用k8s+docker,基本都会遇到这个问题。解决方案就是如此。

我们再提一下 OpenVSwitch和flannel 这两种网络解决方案的性能比较

在默认配置下,flannel的性能会比OVS低很多,调优之后性能接近,但是OVS还是略好一点。

我同事之前从huawei过来,告诉我说flannel不行,我没看到数据,不信。

然后这两天他在我司环境测试了下,确实如此。我信了。

talk is cheap, show me the 数据。

哈哈。

不过我还是倾向于使用flannel,因为部署比较方便。OVS部署需要手动创建一个网桥,这对于我司几万台线上服务器来说,是很头疼的。毕竟如果配置出错,网络会断掉。flannel则不用。

Okay,下面是只有在实际业务中才会遇到的需求:

日志解决方案

多实例的情况下收集日志会遇到一些问题,比如:

在多实例+共享存储的情况下,如何避免多实例读写同一个文件?

如何判断日志来自什么业务和主机?

所以我选择了专门的日志收集工具来解决这问题。比较常用的日志收集工具有:

logstash

fluent

这两种log agent都可以给日志添加标签,表明来自什么hostname,也可以自定义标签,表明来自什么业务。比如我定义了一个Replication Controller,名为 test-controller,则每个容器的hostname会自动是 test-controller-一个随机字符串,这样。于是我们可以通过hostname来区别业务和主机。

docker当前提供了日志收集工具 fluent 的驱动,但是我看好像只支持收集 stdout 的信息。所以我选择了如下方案

Log Server:ElasticSearch

Log Agent:fluent

每一个Pod可以分解成若干Container

业务container

日志container(fluent)

配置文件container

一个Pod是可以由多个container组成的,这个别忽视掉。

这个思想,不只是k8s的设计思想。我推测docker生态环境就是希望这样的。

即使我们不用k8s

现在docker社区很推的一个docker编排工具-docker compose 就是用来做容器整合的

docker run –link –volume –volumes-from三个参数,也是很明显的告诉大家:我们容器来组合吧。

我负责计算,你负责提供代码,他提供DB,如此。

再次强调,docker有一种思想:我每个容器的功能尽量单一,最小。通过多个容器之间共享存储和网络接口,达到协同工作的目的。

我们说回日志解决方案。

不过我这个方案有一个问题:我实际测试,一个fluent大概要占用50M-60M的内存,如果一台机器启动太多个Pods,内存的消耗还是有的。

改进的办法是一台主机使用一个log agent,监控所有Pods的日志。但是这个比较难实现,因为每台机器可能运行的的Pods组合都是不一样的。(我目前还没搞定)

接下去我们说下业务升级方案。

k8s提供了若干升级方案

直接升级 —— 先启动新的Replication Controller,然后把Server从老的RC指向新的RC

滚动升级 —— 按照指定的时间间隔,依次升级RC的image

其实我已经很欣赏了,k8s连这些都考虑到了。

但是,客户总是更贱的。比如我司就觉得这还不够好。

所以还有一种思路,可以提供第三种升级方案

此外结合前端反向代理,比如Nginx或者HaProxy,还可以做到小规模测试升级。比如为新版本业务启动新的Service和RC,然后根据权重,把90%的用户指向老的Service & RC,剩下的10来测试新的Service & RC。

那么让我们来总结下哦

kubernetes总体还是不错的,提供了方便的高可用方案和管理方案。

但是由于kubernetes的网络是多层的: 外部分发器Nginx/HaProxy -> Service -> PodIP -> ContainerIP 层层封装 ,所以在一些涉及端口暴露的复杂应用上还是比较不方便,这也是我近期要烦恼的事情。

目前有折衷的解决办法,用NodePort模式把端口暴露到物理机的公网IP,但是我也希望能有更方便的做法。

如果大家有想法,请和我讨论哦!

我的分享到此就结束啦。谢大要不要组织下提问环节?

Q&A

最近同时在看k8s和mesos,不知如何取舍。有啥判别依据吗?

实话告诉你,我现在没有对MesOS做详细调研,所以不能做太多评论。但是从我初步了解,mesos侧重点应该是在于资源调度管理。而kubernetes侧重点更多在于 docker的管理。

并且如果kubernetes的学习成本(比如文档丰富度),我认为比mesos好一点。不过在公司,这个不能构成不用mesos的理由。

  1. docker目前在生产环境使用的话,风险怎么评估?

A2:

docker使用在生产环境,需要评估和测试的点有几个:

1 操作系统选型

2 系统内核选型

3 docker版本选型

这三个是不稳定性的很大因素

而对于docker本身对业务的稳定性,我在上一家公司运行了半年的生产业务,当时用的是Ubuntu 14.04 + docker 1.6.0 没有遇到任何一次docker直接导致的不稳定因素。。

  1. docker的local镜像,你有什么好的推荐吗?

A3:

docker 国内镜像,我没有好的推荐。如果直接使用docker服务提供商,希云 灵雀云 时速云都提供了镜像仓库。 阿里云也是有提供的。而对于自己搭建,直接用docker 镜像“registry:2”就可以了。一键运行。

  1. 你目前测试下来,单台服务器最多可以起多少个docker实例?

A4:我在上一家公司,用比较破的服务器,8 core CPU,8G内存,跑了10多个container,没问题。

而在于现在公司,由于业务还没上线,也还没到压测阶段,所以没法告诉你极限。。。

补充A4:上一家公司基本都是PHP

5、网宿作为CDN公司,节点数是国内CDN公司最多的,在集群的运维和应用部署上遇到什么样的问题,相比于其他公司是不是有更适合使用docker的场景。

6、除了docker外,有没有考虑过其他的方案

sorry,本次分享不扯上公司哈。docker我认为是一种趋势,不管是否有其他方案,既然我领了这个任务,我就希望在这个方向上尽量做好。

其实有一些方案,或许用传统虚拟化更合适。比如OpenStack 二开。虚拟化有docker无法做到的好处,尤其在迁移和运维技术成本上。

而docker也有传统虚拟化无法比的东西,比如打包概念,快速部署,原子化,以及性能损失更小。

7、选择docker后,愿景是什么,比如:实现更自动化的运维?

A7:我自然希望做运维自动化,这也是我的工作。

8 单个 docker container 有memory cpu 资源限制吗? 比如有个上限 然后内部应用达到上限会发生什么?

A8:docker可以绑定和限制CPU的核心数量,但是目前我没看到限制CPU load的办法。

当前可以做到限制mem最大使用量。

上线之后我没实际测试,但是我不负责的推测,应该会发生OOM killer类似现象。

  1. mysql运行在k8s,稳定吗?推荐把数据库的这些放在docker之上跑嘛?

A9:mysql 的问题网上讨论很多。有一篇测试是在多实例的情况下,mysql运行在docker里,mysql能更充分的利用CPU,因为docker可以对cpu进行绑定,排除多个mysql实例本身调度的干扰。

但是我目前不会这么考虑,我会选择把mysql放在docker之外,让docker去调用就好。

因为我需要优先考虑mysql的数据安全、内存状态、以及读写性能。

  1. 有不适合放在docker里的业务么?

A10:我认为需要客户主动连到Server某个端口的业务,以及需要大量进行磁盘数据存储的业务,可能会比较麻烦。因为我要考虑说我这些数据如何持久化(默认情况,容器删除,数据会丢的)。

如果说业务结构单一,会好一点。如果我这个业务需要和多个兄弟业务联动,放到docker,考虑的因素会更多。。

  1. 说到 文件尽量以挂载的方式 不要拷贝到cotainer里面 那 不同的机器文件的路径可能不一样 不好实现一个container就处处运行。比如机器A data的路径是:/user/a/data 机器B data的路径是:/user/b/data

A11:如果你是这样,那么就要从运维层面多考虑了。我是不是应该把共享存储的路径统一了。否者你这个情景不管用不用docker,都是不利于批量管理的。

原文来自微信公众号:GO中国。