当前技术世界的发展形势就是让开发人员从繁琐的应用配置和管理中解放出来,使用容器镜像来处理复杂的程序运行依赖库的需求,保证代码运行环境的一致性。既然这样的好处是如此清晰,但为什么企业中的基础设施环境没有往容器集群切换呢?关键问题还是风险,新技术意味着未经检验的技术和实践经验的缺乏,这就会带来很多不可预知的风险。
当企业的运维团队去维护一个弹性的容器集群时,传统的软件部署方式需要向容器迁移,这个过程中需要有风险预判和规避之道。而Docker和Rancher正是提供了解决这些风险问题的解决方案,比如基于Rancher的Catalog功能就能快速的完成一些基础软件的部署(比如ElasticSearch)。在风险管理方面,我们可以看看基于Docker和Rancher来运行弹性集群的五个关键点:
运行Rancher高可用模式 (本文将介绍)
Rancher中使用负载均衡服务
Rancher中的服务监控和监控检查
开发者自定义针对Rancher的安装部署
讨论利用Convoy对数据的管理
我原本希望展现一下用一台老式笔记本部署Rancher Server,然后用docker-machine加入几个树莓派节点部署Rancher Agent,这样来构建一个Rancher集群。但是这样会有些问题,就是大部分Docker镜像都是基于Intel CPU构建的,这会和树莓派的ARM CPU很不兼容。所以我还是老老实实的用AWS的虚机来构建Rancher集群吧。
初始安装,我们暂时先部署一台Rancher Server和一台Rancher Agent,然后再部署一个简单的多实例应用。
上面这张图展现了我的整个集群的设置,我选择AWS是因为我个人比较熟悉,当然你完全可以选择你擅长的云提供商。
我们可以尝试创建一个Wordpress,顺带来检测一下Rancher是否正确部署了。
这样我们的应用就运行起来了,试想,如果Rancher Server所在服务器出现故障,或者有网络中断问题发生,会对应用产生什么影响,Wordpress还能继续接受请求么?
为了确认我们的疑问,我将会按照下面的步骤执行,然后记录一下其中的运行结果:
阻断Rancher Server和Rancher Agent间的网络
停止Rancher Server的容器
瞧一瞧Rancher Server的容器里面到底有什么
最终我们要解决这些问题,那就要看看Rancher HA来怎样解决这些风险。
阻断Rancher Server和Rancher Agent间的网络
进入AWS,然后看我各种犀利的操作:
阻断Rancher Server和Rancher Agent间的访问
记录一下发生了什么
干掉几个WordPress的实例
恢复原先的网络
观察结果
首先,网络阻断后没过多久Rancher Host就出现了reconnecting状态
此时我依然可以正常访问Wordpress的地址,服务没有中断,IPSec隧道网络还存储,Wordpress实例还是可以正常访问数据库。
现在我们要停掉一个Wordpress实例,看看会发生什么?因为我们已经无法从Rancher UI上管理这些容器了,所以还是到对应的主机上执行docker命令吧。
很遗憾Wordpress容器没有重新启动,这有点麻烦了。我们还是把Rancher Server弄回来吧,看看它能不能感知到其中一个Wordpress容器已经被停掉了。
在各种犀利的操作和等待之后,Rancher Server与Agent重新连接了,Wordpress容器就被重新启动了。完美~
所以我们可以看,Rancher Server还是能够处理间歇性的网络问题,可以实现Rancher Server和Agent的故障重连。但是如果要让网络中断对Rancher的影响进一步减小,那么部署多个Rancher Server就比较重要了。
我们还是回过来,按照先前计划看看Rancher Server 容器停掉后会发生什么?我们会因此失去对这些Rancher Host的管理能力么?看看如何!
停掉Rancher Server
这次我需要登入到Rancher Server的host上手动停止Rancher Server,其实Rancher Server一般是设置 –restart=always,所以自己有一定的恢复能力。但我们可以假设比如磁盘写满了后,Rancher Server真的起不来了。
观察结果
执行docker stop rancher-server
之后,Wordpress还是能够正常工作,但是Rancher UI是不能访问了,这可不行,得赶紧把Rancher Server弄回来。
再执行docker start rancher-server
,Rancher Server启动后又一切恢复正常,这很酷啊,这是什么魔法?赶紧着,麻溜地分析起来!
揭开Rancher Server的神秘面纱
我们来一次对Rancher Server的简要探究之旅,看一看Rancher Server的Dockerfile。
# Dockerfile contents
FROM ...
...
...
CMD ["/usr/bin/s6-svscan", "/service"]
我们可以看到使用了s6-svscan,它可以运行指定的目录结构内的程序,目录内的主要文件就是Run、Down、Finish。下面这张图能看到会运行两个服务cattle(Rancher的核心调度器)和mysql。
其实在容器内部我们也可以看到起了哪些服务。
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /usr/bin/s6-svscan /service
7 ? S 0:00 s6-supervise cattle
8 ? S 0:00 s6-supervise mysql
9 ? Ssl 0:57 java -Xms128m -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/lib/cattle/logs -Dlogback.bootstrap.level=WARN -cp /usr/share/cattle/1792f92ccdd6495127a28e16a685da7
135 ? Sl 0:01 websocket-proxy
141 ? Sl 0:00 rancher-catalog-service -catalogUrl library=https://github.com/rancher/rancher-catalog.git,community=https://github.com/rancher/community-catalog.git -refreshInterval 300
142 ? Sl 0:00 rancher-compose-executor
143 ? Sl 0:00 go-machine-service
1517 ? Ss 0:00 bash
1537 ? R+ 0:00 ps x
我们可以看到Rancher的大脑,一个名叫Cattle的Java应用,它需要一个MySQL来存储对应的数据。这确实很方便,但是我们发现这样会有单点故障,所有的数据存储在一个单点的mysql中。如果对mysql中的数据来一些不礼貌的操作,会发生什么呢?
破坏MySQL存储数据
进入容器中执行一些MySQL命令,我们一起来干点坏事:
docker exec -it rancher-server bash
$ > mysql
mysql> use cattle;
mysql> SET FOREIGN_KEY_CHECKS = 0;
mysql> truncate service;
mysql> truncate network;
结果是可想而知的,Rancher不记得任何事了,你删掉一个Wordpress容器,它也不会恢复。
而且我也删除了network表的数据,Wordpress也不知道如何找到它的MySQL服务了。
显然,若要在生产环境运行Rancher,我们需要一个方式来保护Rancher Server的数据,既然如此那我们就讲一下Rancher HA吧。
Rancher HA安装过程
首先我们要确保数据的安全,我选择了AWS的RDS服务,RDS可以提供一个信赖的MySQL服务,数据安全是可以保证的。当然你也可以使用其他的,我只是对RDS更熟悉一些。
下面我们继续Rancher HA的安装过程:
按照我们之前的约定,我是创建了RDS的MySQL实例,然后把它当做外部数据库和Rancher Server连接。
一旦我们使用了外部数据库模式,UI上将会打开两个选项来设置HA。
怎么办!选择困难症啊!没关系,我来解释一下每个选项的含义。
Cluster size,注意这里怎么都是奇数?因为在后端,Rancher会设置一个ZooKeeper Quorum保证锁同步,ZooKeeper推荐奇数集群,因为偶数节点数量不能提供额外的容错性。我们这里就选择3个Host吧,这是一个可用和易用的平衡。
Host registration URL,这里是填写一个Rancher Cluster入口的FQDN,推荐使用外部负载均衡服务或者DNS服务(round robin策略)。比如图中我使用的是支持SRV记录的DNS服务,通过DNS来做三个Rancher Server的负载均衡:
SSL Certificates是列表中的最后一个选项,如果你有域名的SSL证书可以配置在这里,否则Rancher会自动生成一个自签名证书。
所有都填完后,就会给你提供一个rancher-ha.sh的脚本来下载。
有了脚本就可以到每个Rancher Server节点上执行了,执行前还需要注意目前只能支持docker v1.10.3。安装指定版本的Docker Engine,可以参考下面的方式:
#!/bin/bash
apt-get install -y -q apt-transport-https ca-certificates
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" > /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y -q docker-engine=1.10.3-0~trusty
# run the command below to show all available versions
# apt-cache showpkg docker-engine
Docker安装之后,就要确认一下端口开放了,需要开放哪些端口可以看这里,不过如果是第一次安装,我建议你把端口全部开放吧,免得坑太深被活埋。
一切准备妥当之后,可以执行脚本,执行后能看到这样的输出:
...
ed5d8e75b7be: Pull complete
ed5d8e75b7be: Pull complete
7ebc9fcbf163: Pull complete
7ebc9fcbf163: Pull complete
ffe47ea37862: Pull complete
ffe47ea37862: Pull complete
b320962f9dbe: Pull complete
b320962f9dbe: Pull complete
Digest: sha256:aff7c52e52a80188729c860736332ef8c00d028a88ee0eac24c85015cb0e26a7
Status: Downloaded newer image for rancher/server:latest
Started container rancher-ha c41f0fb7c356a242c7fbdd61d196095c358e7ca84b19a66ea33416ef77d98511
Run the below to see the logs
docker logs -f rancher-ha
执行过程中会下载一些额外的镜像,毕竟要支持HA特性么。另外Host的内存建议至少4G,执行完毕后通过docker ps可以看看都启动了什么:
常见的问题和解决方案
正常来说,一般我们会在日志中能看到各个成员加入到Rancher HA Cluster中:
time="2016-07-22T04:13:22Z" level=info msg="Cluster changed, index=0, members=[172.30.0.209, 172.30.0.111, ]" component=service
...
time="2016-07-22T04:13:34Z" level=info msg="Cluster changed, index=3, members=[172.30.0.209, 172.30.0.111, 172.30.0.69]" component=service
但有时候会有意外,比如会看到一些error信息:
time="2016-07-23T14:37:02Z" level=info msg="Waiting for server to be available" component=cert
time="2016-07-23T14:37:02Z" level=info msg="Can not launch agent right now: Server not available at http://172.17.0.1:18080/ping:" component=service
这个问题产生的背后原因有很多,我阅读了一些Github上的issue和各种论坛的帖子,帮大家整理了一些产生此问题的根本原因。
网络设置问题
有时候容器自动获取了节点的错误的IP,这时候你需要强制指定正确的IP。
ZooKeeper没有正常启动
Zookeeper的容器是分布在每个节点上的,如果节点之间的问题导致Zookeeper容器不能通信,就会导致这个问题,如果要确认这个问题,可以参考这样的日志输出。
目录/var/lib/rancher/state下有残留文件
如果多次运行Rancher-ha.sh这个脚本,那么你需要运行前清理一下这个目录下残留文件。
多次尝试后HA状态被破坏
删库重试,重启大法好。
机器资源不足
内存至少需要4GB,此外mysql的连接数也要足够,可以按照每个HA节点需要50个连接数来计算。如果你看到下面的错误,那么此问题确信无疑。
time="2016-07-25T11:01:02Z" level=fatal msg="Failed to create manager" err="Error 1040: Too many connections"
rancher/server版本不匹配
rancher-ha.sh执行的时候默认是下载rancher/server:latest镜像,如果你没有host上的镜像不一致会导致难以想象的问题,最好执行的时候指定版本号。比如:
./rancher-ha.sh rancher/server:
docker0返回了错误的IP
这个错误具体就是在HA的安装过程中会去检查agent健康状态,此时它获取了一个错误的docker0 IP地址,因为我先前已经将其设置成了172.17.42.1。
curl localhost:18080/ping
> pong
curl http://172.17.0.1:18080/ping
> curl: (7) Failed to connect to 172.17.0.1 port 18080: Connection refused
我的解决办法就是重装了我的AWS虚机,导致获取docker0 IP错误的原因,我感觉可能是我在虚机里多次安装了Docker。重装之后,一切就正常了,我也看到了期待的日志信息。
HA部署完毕后
我们终于看到了梦寐以求的正确日志输出,和美好的UI展现:
time="2016-07-24T20:00:11Z" level=info msg="[0/10] [zookeeper]: Starting "
time="2016-07-24T20:00:12Z" level=info msg="[1/10] [zookeeper]: Started "
time="2016-07-24T20:00:12Z" level=info msg="[1/10] [tunnel]: Starting "
time="2016-07-24T20:00:13Z" level=info msg="[2/10] [tunnel]: Started "
time="2016-07-24T20:00:13Z" level=info msg="[2/10] [redis]: Starting "
time="2016-07-24T20:00:14Z" level=info msg="[3/10] [redis]: Started "
time="2016-07-24T20:00:14Z" level=info msg="[3/10] [cattle]: Starting "
time="2016-07-24T20:00:15Z" level=info msg="[4/10] [cattle]: Started "
time="2016-07-24T20:00:15Z" level=info msg="[4/10] [go-machine-service]: Starting "
time="2016-07-24T20:00:15Z" level=info msg="[4/10] [websocket-proxy]: Starting "
time="2016-07-24T20:00:15Z" level=info msg="[4/10] [rancher-compose-executor]: Starting "
time="2016-07-24T20:00:15Z" level=info msg="[4/10] [websocket-proxy-ssl]: Starting "
time="2016-07-24T20:00:16Z" level=info msg="[5/10] [websocket-proxy]: Started "
time="2016-07-24T20:00:16Z" level=info msg="[5/10] [load-balancer]: Starting "
time="2016-07-24T20:00:16Z" level=info msg="[6/10] [rancher-compose-executor]: Started "
time="2016-07-24T20:00:16Z" level=info msg="[7/10] [go-machine-service]: Started "
time="2016-07-24T20:00:16Z" level=info msg="[8/10] [websocket-proxy-ssl]: Started "
time="2016-07-24T20:00:16Z" level=info msg="[8/10] [load-balancer-swarm]: Starting "
time="2016-07-24T20:00:17Z" level=info msg="[9/10] [load-balancer-swarm]: Started "
time="2016-07-24T20:00:18Z" level=info msg="[10/10] [load-balancer]: Started "
time="2016-07-24T20:00:18Z" level=info msg="Done launching management stack" component=service
time="2016-07-24T20:00:18Z" level=info msg="You can access the site at https://" component=service
如果使用了自签名证书且前端希望通过HTTPS来访问,那么你需要把你的证书添加到你的受信任证书中。然后再等待数据库资源约束调整完毕后,三个节点的服务就完全运行起来了。
结论
这比原来想象的要多了很多。这就是为什么分布式系统一直是PHD的研究领域。解决所有的故障点后,我认为Rancher HA已经开始向顶尖分布式系统出发啦。
我最终会将Rancher HA的脚本配置成Ansible的一个简单任务,敬请关注!
附录
对于任何分布式系统来说,基本的套路就是统一管理其中产生的状态和变化,这意味着多个服务需要一个进程来协调更新。
Rancher的处理方式就是把状态都放倒数据库中,需要同步状态时把状态信息放入到event中,发送event就可以向其他组件同步状态。所以当正在处理一个事件时,它有一个锁,事件未处理完前数据库中的状态是不能被修改的。
在单台服务器设置中,所有的协调都发生在主机上的内存中。一旦你涉及多服务器HA配置,像zookeeper和redis是必要的。
报名11.6成都Docker技术沙龙