标签 Google 下的文章

Docker 1.12 swarm模式下遇到的各种问题

前段时间,由于工作上的原因,与Docker的联系发生了几个月的中断^_^,从10月份开始,工作中又与Docker建立了广泛密切的联系。不过这次,Docker却给我泼了一盆冷水:(。事情的经过请允许多慢慢道来。

经过几年的开发,Docker已经成为轻量级容器领域不二的事实标准,应用范围以及社区都在快速发展和壮大。今年的年中,Docker发布了其里程碑的版本Docker 1.12,该版本最大的变动就在于其引擎自带了swarmkit ,一款Docker开发的容器集群管理工具,可以让用户无需安装第三方公司提供的工具或Docker公司提供的引擎之外的工具,就能搭建并管理好一个容器集群,并兼有负载均衡、服务发现和服务编排管理等功能。这对于容器生态圈内的企业,尤其是那些做容器集群管理和服务编排平台的公司来说,不亚于当年微软在Windows操作系统中集成Internet Explorer。对此,网上和社区对Docker口诛笔伐之声不绝于耳,认为Docker在亲手打击社区,葬送大好前程。关于商业上的是是非非,我们这里暂且不提。不可否认的是,对于容器的普通用户而言,Docker引擎内置集群管理功能带来的更多是便利。

9月末启动的一款新产品的开发中,决定使用容器技术,需要用到容器的集群管理以及服务伸缩、服务发现、负载均衡等特性。鉴于团队的能力和开发时间约束,初期我们确定直接利用Docker 1.12版本提供的这些内置特性,而不是利用第三方,诸如k8sRancher这样的第三方容器集群管理工具或是手工利用各种开源组件“拼凑”出一套满足需求的集群管理系统,如利用consul做服务注册和发现等。于是Docker 1.12的集群模式之旅就开始了。

一、环境准备

这次我们直接使用的是阿里的公有云虚拟主机服务,这里使用两台aliyun ECS:

manager: 10.46.181.146/21(内网)
worker: 10.47.136.60/22 (内网)

系统版本为:

Ubuntu 14.04.4:
Linux iZ25cn4xxnvZ 3.13.0-86-generic #130-Ubuntu SMP Mon Apr 18 18:27:15 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

Docker版本:

# docker version
Client:
 Version:      1.12.1
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   23cf638
 Built:        Thu Aug 18 05:22:43 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.12.1
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   23cf638
 Built:        Thu Aug 18 05:22:43 2016
 OS/Arch:      linux/amd64

Ubuntu上Docker的安装日益方便了,我个人习惯于采用daocloud推荐的方式,在这里可以看到。当然你也可以参考Docker官方的doc

如果你的Ubuntu上已经安装了old版的Docker,也可以在docker的github上下载相应平台的二进制包,覆盖本地版本即可(注意1.10.0版本前后的Docker组件有所不同)。

二、Swarm集群搭建

Docker 1.12内置swarm mode,即docker原生支持的docker容器集群管理模式,只要是执行了docker swarm init或docker swarm join到一个swarm cluster中,执行了这些命令的host上的docker engine daemon就进入了swarm mode。

swarm mode中,Docker进行了诸多抽象概念(这些概念与k8s、rancher中的概念大同小异,也不知是谁参考了谁^_^):

- node: 部署了docker engine的host实例,既可以是物理主机,也可以是虚拟主机。
- service: 由一系列运行于集群容器上的tasks组成的。
- task: 在具体某个docker container中执行的具体命令。
- manager: 负责维护docker cluster的docker engine,通常有多个manager在集群中,manager之间通过raft协议进行状态同步,当然manager角色engine所在host也参与负载调度。
- worker: 参与容器集群负载调度,仅用于承载tasks。

swarm mode下,一个Docker原生集群至少要有一个manager,因此第一步我们就要初始化一个swarm cluster:

# docker swarm init --advertise-addr 10.46.181.146
Swarm initialized: current node (c7vo4qtb2m41796b4ji46n9uw) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-1iwaui223jy6ggcsulpfh1bufn0l4oq97zifbg8l5na914vyz5-2mg011xh7vso9hu7x542uizpt \
    10.46.181.146:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

通过一行swarm init命令,我们就创建了一个swarm集群。同时,Docker daemon给出了清晰提示,如果要向swarm集群添加worker node,执行上述提示中的语句。如果其他node要以manager身份加入集群,则需要执行:docker swarm join-token manager以获得下一个“通关密语”^_^。

# docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-1iwaui223jy6ggcsulpfh1bufn0l4oq97zifbg8l5na914vyz5-8wh5gp043i1cqz4at76wvx29m \
    10.46.181.146:2377

对比两个“通关密语”,我们发现仅是token串的后半部分有所不同(2mg011xh7vso9hu7x542uizpt vs. 8wh5gp043i1cqz4at76wvx29m)。

在未添加新node之前,我们可以通过docker node ls查看当前集群内的node状态:

# docker node ls
ID                           HOSTNAME      STATUS  AVAILABILITY  MANAGER STATUS
c7vo4qtb2m41796b4ji46n9uw *  iZ25mjza4msZ  Ready   Active        Leader

可以看出当前swarm仅有一个node,且该node是manager,状态是manager中的leader。

我们现在将另外一个node以worker身份加入到该swarm:

# docker swarm join \
     --token SWMTKN-1-1iwaui223jy6ggcsulpfh1bufn0l4oq97zifbg8l5na914vyz5-2mg011xh7vso9hu7x542uizpt \
     10.46.181.146:2377
This node joined a swarm as a worker.

在manager上查看node情况:

# docker node ls
ID                           HOSTNAME      STATUS  AVAILABILITY  MANAGER STATUS
8asff8ta70j91myh734os6ihg    iZ25cn4xxnvZ  Ready   Active
c7vo4qtb2m41796b4ji46n9uw *  iZ25mjza4msZ  Ready   Active        Leader

Swarm集群中已经有了两个active node:一个manager和一个worker。这样我们的集群环境初建ok。

三、Service启动

Docker 1.12版本宣称提供服务的Scaling、health check、滚动升级等功能,并提供了内置的dns、vip机制,实现service的服务发现和负载均衡能力。接下来,我们来测试一下docker的“服务能力”:

我们先来创建一个用户承载服务的自定义内部overlay网络:

root@iZ25mjza4msZ:~# docker network create -d overlay mynet1
avjvpxkfg6u8xt0qd5xynoc28
root@iZ25mjza4msZ:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
dba1faa24c0d        bridge              bridge              local
a2807d0ec7ed        docker_gwbridge     bridge              local
2b6eb8b95c00        host                host                local
55v43pasf7p9        ingress             overlay             swarm
avjvpxkfg6u8        mynet1              overlay             swarm
6f2d47678226        none                null                local

我们看到在network list中,我们的overlay网络mynet1出现在列表中。这时,在worker node上你还看不到mynet1的存在,因为按照目前docker的机制,只有将归属于mynet1的task调度到worker node上时,mynet1的信息才会同步到worker node上。

接下来就是在mynet1上启动service的时候了,我们先来测试一下:

在manager节点上,用docker service命令启动服务mytest:

# docker service create --replicas 2 --name mytest --network mynet1 alpine:3.3 ping baidu.com
0401ri7rm1bdwfbvhgyuwroqn

似乎启动成功了,我们来查看一下服务状态:

root@iZ25mjza4msZ:~# docker service ps mytest
ID                         NAME          IMAGE       NODE          DESIRED STATE  CURRENT STATE                     ERROR
73hyxfhafguivtrbi8dyosufh  mytest.1      alpine:3.3  iZ25mjza4msZ  Ready          Preparing 1 seconds ago
c5konzyaeq4myzswthm8ax77w   \_ mytest.1  alpine:3.3  iZ25mjza4msZ  Shutdown       Failed 1 seconds ago              "starting container failed: co…"
6umn2qlj34okagb4mldpl6yga   \_ mytest.1  alpine:3.3  iZ25mjza4msZ  Shutdown       Failed 6 seconds ago              "starting container failed: co…"
5y7c1uoi73272uxjp2uscynwi   \_ mytest.1  alpine:3.3  iZ25mjza4msZ  Shutdown       Failed 11 seconds ago             "starting container failed: co…"
4belae8b8mhd054ibhpzbx63q   \_ mytest.1  alpine:3.3  iZ25mjza4msZ  Shutdown       Failed 16 seconds ago             "starting container failed: co…"

似乎服务并没有起来,service ps的结果告诉我:出错了!

但从ps的输出来看,ERROR那行的日志太过简略:“starting container failed: co…” ,无法从这里面分析出失败原因,通过docker logs查看失败容器的日志(实际上日志是空的)以及通过syslog查看docker engine的日志都没有特殊的发现。调查了许久,无意中尝试手动重启一下失败的Service task:

# docker start 4709dbb40a7b
Error response from daemon: could not add veth pair inside the network sandbox: could not find an appropriate master "ov-000101-46gc3" for "vethf72fc59"
Error: failed to start containers: 4709dbb40a7b

从这个Daemon返回的Response Error来看似乎与overlay vxlan的网络驱动有关。又经过搜索引擎的确认,大致确定可能是因为host的kernel version太low导致的,当前kernel是3.13.0-86-generic,记得之前在docker 1.9.1时玩vxlan overlay我是将kernel version升级到3.19以上了。于是决定升级kernel version

升级到15.04 ubuntu版本的内核:

命令:

    apt-get install linux-generic-lts-vivid

升级后:

# uname -a
Linux iZ25cn4xxnvZ 3.19.0-70-generic #78~14.04.1-Ubuntu SMP Fri Sep 23 17:39:18 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

reboot虚拟机后,重新启动mytest service,这回服务正常启动了。看来升级内核版本这味药是对了症了。

这里issue第一个吐槽:Docker强依赖linux kernel提供的诸多feature,但docker似乎在kernel版本依赖这块并未给出十分明确的对应关系,导致使用者莫名其妙的不断遇坑填坑,浪费了好多时间。

顺便这里把service的基本管理方式也一并提一下:

scale mytest服务的task数量从2到4:

docker service scale mytest=4

删除mytest服务:

docker service rm mytest

服务删除执行后,需要一些时间让docker engine stop and remove container instance。

四、vip机制测试

Docker 1.12通过集群内置的DNS服务实现服务发现,通过vip实现自动负载均衡。单独使用DNS RR机制也可以实现负载均衡,但这种由client端配合实现的机制,无法避免因dns update latency导致的服务短暂不可用的情况。vip机制才是相对理想的方式。

所谓Vip机制,就是docker swarm为每一个启动的service分配一个vip,并在DNS中将service name解析为该vip,发往该vip的请求将被自动分发到service下面的诸多active task上(down掉的task将被自动从vip均衡列表中删除)。

我们用nginx作为backend service来测试这个vip机制,首先在集群内启动mynginx service,内置2个task,一般来说,docker swarm会在manager和worker node上各启动一个container来承载一个task:

# docker service create --replicas 2 --name mynginx --network mynet1 --mount type=bind,source=/root/dockertest/staticcontents,dst=/usr/share/nginx/html,ro=true  nginx:1.10.1
3n7dlr8km9v2xd66bf0mumh1h

一切如预期,swarm在manager和worker上各自启动了一个nginx container:

# docker service ps mynginx
ID                         NAME       IMAGE         NODE          DESIRED STATE  CURRENT STATE               ERROR
bcyffgo1q3i5x0qia26fs703o  mynginx.1  nginx:1.10.1  iZ25mjza4msZ  Running        Running about a minute ago
arkol2l7gpvq42f0qytqf0u85  mynginx.2  nginx:1.10.1  iZ25cn4xxnvZ  Running        Running about a minute ago

接下来,我们尝试在mynet1中启动一个client container,并在client container中使用ping、curl对mynginx service进行vip机制的验证测试。client container的image是基于ubuntu:14.04 commit的本地image,只是在官方image中添加了curl, dig, traceroute等网络探索工具,读者朋友可自行完成。

我们在manager node上尝试启动client container:

# docker run -it --network mynet1 ubuntu:14.04 /bin/bash
docker: Error response from daemon: swarm-scoped network (mynet1) is not compatible with `docker create` or `docker run`. This network can only be used by a docker service.
See 'docker run --help'.

可以看到:直接通过docker run的方式在mynet1网络里启动container的方法失败了,docker提示:docker run与swarm范围的网络不兼容。看来我们还得用docker service create的方式来做。

# docker service create --replicas 1 --name myclient --network mynet1 test/client tail -f /var/log/bootstrap.log
0eippvade7j5e0zdyr5nkkzyo

# docker ps
CONTAINER ID        IMAGE                                                 COMMAND                  CREATED             STATUS              PORTS                    NAMES
4da6700cdf4d        test/client:latest   "tail -f /var/log/boo"   33 seconds ago      Up 32 seconds                                myclient.1.3cew8x46i5b28e2q3kd1zz3mq

我们使用exec命令attach到client container中:

root@iZ25mjza4msZ:~# docker exec -it 4da6700cdf4d /bin/bash
root@4da6700cdf4d:/#

在client container中,我们可以通过dig命令查看mynginx service的vip:

root@4da6700cdf4d:/# dig mynginx

; <<>> DiG 9.9.5-3ubuntu0.9-Ubuntu <<>> mynginx
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34806
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;mynginx.            IN    A

;; ANSWER SECTION:
mynginx.        600    IN    A    10.0.0.2

;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Tue Oct 11 08:58:58 UTC 2016
;; MSG SIZE  rcvd: 48

可以看到为mynginx service分配的vip是10.0.0.2。

接下来就是见证奇迹的时候了,我们尝试通过curl访问mynginx这个service,预期结果是:请求被轮询转发到不同的nginx container中,返回结果输出不同内容。实际情况如何呢?

root@4da6700cdf4d:/# curl mynginx
^C
root@4da6700cdf4d:/# curl mynginx
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> 主标题 | 副标题< /title>
</head>
<body>
<p>hello world, i am manager</p>
</body>
</html>
root@4da6700cdf4d:/# curl mynginx
curl: (7) Failed to connect to mynginx port 80: Connection timed out

第一次执行curl mynginx,curl就hang住了。ctrl+c后,再次执行curl mynginx,顺利返回manager节点上的nginx container的response结果:”hello world, i am manager“。

第三次执行curl mynginx,又hang住了,一段时间后显示timed out,这也从侧面说明了,swarm下的docker engine的确按照rr规则将request均衡转发到不同nginx container,但实际看来,从manager node上的client container到worker node上的nginx container的网络似乎不通。我们来验证一下这两个container间的网络是否ok。

我们在两个node上分别用docker inspect获得client container和nginx container的ip地址:

    manager node:
        client container: 10.0.0.6
        nginx container: 10.0.0.4
    worker node:
        nginx container: 10.0.0.3

理论上,位于同一overlay网络中的三个container之间应该是互通的。但实际上通过docker exec -it container_id /bin/bash进入每个docker container内部进行互ping来看,manager node上的两个container可以互相ping通,但无法ping通 worker node上的nginx container,同样,位于worker node上的nginx container也无法ping通位于manager node上的任何container。

通过docker swarm leave将worker节点从swarm cluster中摘出,docker swarm会在manager上再启动一个nginx container,这时如果再再client container测试vip机制,那么测试是ok的。

也就是说我遇到的问题是跨node的swarm network不好用,导致vip机制无法按预期执行。

后续我又试过双swarm manager等方式,vip机制在跨node时均不可用。在docker github的issue中,很多人遇到了同样的问题,涉及的环境也是多种多样(不同内核版本、不同linux发行版,不同公有云提供商或本地虚拟机管理软件),似乎这个问题是随机出现的。 按照docker developer的提示检查了swarm必要端口的开放情况、防火墙、swarm init的传递参数,都是无误的。也尝试过重建swarm,在init和join时全部显式带上–listen-addr和–advertise-addr选项,问题依旧没能解决。

最后,又将docker版本从1.12.1升级到最新发布的docker 1.12.2rc3版本,重建集群,问题依旧没有解决。

自此确定,docker 1.12的vip机制尚不稳定,并且没有临时解决方案能绕过这一问题。

五、Routing mesh机制测试

内部网络的vip机制的测试失败,让我在测试Docker 1.12的另外一个机制:Routing mesh之前心里蒙上了一丝阴影,一个念头油然而生:Routing mesh可能也不好用。

对于外部网络和内部网络的边界,docker 1.12提供了ingress(入口) overlay网络应对,通过routing mesh机制,保证外部的请求可以被任意集群node转发到启动了相应服务container的node中,并保证高
可用。如果有多个container,还可以实现负载均衡的转发。

与vip不同,Routing mesh在启动服务前强调暴露一个node port的概念。既然叫node port,说明这个暴露的port是docker engine listen的,并由docker engine将发到port上的流量转到相应启动了service container的节点上去(如果本node也启动了service task,那么也会负载分担留给自己node上的service task container去处理)。

我们先清除上面的service,还是利用nginx来作为网络入口服务:

# docker service create --replicas 2 --name mynginx --network mynet1 --mount type=bind,source=/root/dockertest/staticcontents,dst=/usr/share/nginx/html,ro=true --publish 8091:80/tcp nginx:1.10.1
cns4gcsrs50n2hbi2o4gpa1tp

看看node上的8091端口状态:

root@iZ25mjza4msZ:~# lsof -i tcp:8091
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
dockerd 13909 root   37u  IPv6 121343      0t0  TCP *:8091 (LISTEN)

dockerd负责监听该端口。

接下来,我们在manager node上通过curl来访问10.46.181.146:8091。

# curl 10.46.181.146:8091
^C
root@iZ25mjza4msZ:~# curl 10.46.181.146:8091
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title> 主标题 | 副标题< /title>
</head>
<body>
<p>hello world, i am master</p>
</body>
</html>
root@iZ25mjza4msZ:~# curl 10.46.181.146:8091

在vip测试中的一幕又出现了,docker swarm似乎再将请求负载分担到两个node上,当分担到worker node上时,curl又hang住了。routing mesh机制失效。

理论上再向swarm cluster添加一个worker node,该node上并未启动nginx service,当访问这个新node的8091端口时,流量也会被转到manager node或之前的那个worker node,但实际情况是,跨node流量互转失效,和vip机制测试似乎是一个问题。

六、小结

Docker 1.12的routing mesh和vip均因swarm network的问题而不可用,这一点出乎我的预料。

翻看Docker在github上的issues,发现类似问题从Docker 1.12发布起就出现很多,近期也有不少:

https://github.com/docker/docker/issues/27237

https://github.com/docker/docker/issues/27218

https://github.com/docker/docker/issues/25266

https://github.com/docker/docker/issues/26946

*https://github.com/docker/docker/issues/27016

这里除了27016的issue发起者在issue最后似乎顿悟到了什么(也没了下文):Good news. I believe I discovered the root cause of our issue. Remember above I noted our Swarm spanned across L3 networks? I appears there is some network policy that is blocking VxLAN traffic (4789/udp) across the two L3 networks. I redeployed our same configuration to a single L3 network and can reliably access the published port on all worker nodes (based on a few minutes of testing)。其余的几个issue均未有solution。

不知道我在阿里云的两个node之间是否有阻隔vxlan traffic的什么policy,不过使用nc探测4789 udp端口均是可用的:

nc -vuz 10.47.136.60 4789

无论是配置原因还是代码bug导致的随机问题,Docker日益庞大的身躯和背后日益复杂的网络机制,让开发者(包括docker自己的开发人员)查找问题的难度都变得越来越高。Docker代码的整体质量似乎也呈现出一定下滑的不良趋势。

针对上述问题,尚未找到很好的解决方案。如果哪位读者能发现其中玄机,请不吝赐教。

GopherChina2016后记

4月17日晚22:51,伴随着D7次动车缓缓驶入沈阳北站,拖着疲惫的身体和些许兴奋的我,结束了两天的GopherChina 2016之旅。

一、GopherChina大会

GopherChina大会是中国大陆地区Golang语言推广第一品牌。2015年在上海成功了举办了第一届大会;2016年,大会发起人astaxie为充分照顾帝都(及周边)Gophers们的情绪^_^,将GopherChina 2016搬到了北京举行。

这是我第一次参加GopherChina大会,也是由于“第一次”,心里有种莫名的小兴奋。

第一天会议,8:30来到亚洲大酒店。虽然酒店外面人员密度稀疏,但主会场入口处却是接踵摩肩,人山人海:注册、领“Gopher战斗服”、收集卡片印章,场面好不热闹,不过主会场内部倒是一片井然有序之气象。会场内主屏幕上循环播放着这次大会几大赞助商的宣传视频:七牛DaocloudGrabtaxi等。作为Gopher,首先应该感谢这些金主,没有他们的”金元”,谢大也难为无米之炊不是。

img{512x368}

二、Topic主观短评

大会的日程很紧张,Topic较多,能全神贯注的聆听每个Topic基本很难。开始还好,后来只能重点听听自己感兴趣的了,第二天的时光尤甚。相信坚持听完两天的topic的Gopher们都或多或少有疲惫之感。下面就自己的感受,用短短一两句话,主观短评一下各个Topic:

第一天

陈辉的“Go 人工智能”:
话题挺“唬人”^_^,实质则是陈总个人的opensource project show,从“悟空”到“弥勒佛”一应俱全。并且鉴于陈总的Facebook、Google和Alibaba的从业经历,他的开源项目应该值得学习一番。

刘奇的“Go在分布式数据库中的应用”:
刘总依旧幽默风趣,这次除了带来了TiDB外,还带来了砸场子的用Rust实现的TiKv,为晚上在技术Party上撕逼打下了伏笔^_^。

李炳毅的“Go在百度BFE的应用”:
“车轮大战、车轮大战、车轮大战”,重要的事情说三遍!不过这仅是go在baidu特定场景应用下的tradeoff。个人倒是不建议关掉默认GC。

毛剑的“Go在数据存储上面的应用”:
基于FaceBook的Haystack paper,为B站造的一个轮子,细致入微。其中的设计考量值得同样在做分布式文件系统的朋友们借鉴和参考。

Marcel van Lohuizen的”I18n and L10n for Go using x/text”:
Marcel也是今年GopherCon2016的speaker,这次来到GopherChina讲解x/text也是让我们先睹为快了。Marcel 对x/text进行了详尽的分类讲解,以及给出当前状态、todo 以及 plan。内容结构很有外国speaker共同具备的那些特点。

米嘉 的”Go build web”:
对Go web dev进行了庖丁解牛,Go味儿十足。现场的很多web dev都反映很有赶脚。

邓洪超 的“Go在分布式系统的性能调试和优化”:
来自CoreOS的邓洪超很萌,演讲很有激情。但也许是外语说惯了,中文反倒不那么利落了。不过整体效果依旧不错。

沈晟的”Golang在移动客户端开发中的应用”:
心动网络(前verycd)的沈总讲解了心动网络将gomobile 用于游戏客户端client library的例子。记不得沈总是否说过心动网络已经在正式产品中使用gomobile了,不过无论这样,这种“敢为天下先”的气魄还是值得赞颂的^_^。

技术Party

晚上大约80多人聚集在二楼会议厅举行GopherChina技术Party,Party上,PingCAP的刘奇引发Rust vs. Golang的重度pk。由于高铁晚点而迟到的七牛CEO许式伟也再次站出来成为golang的捍卫者。pk从语言特性延伸到社区文化,“民主集中制”的精英文化主导的Golang社区与纯粹美式民主的Rust社区到底孰好孰坏,大家也是众说纷纭,见仁见智。外国友人“马尾辫”(Marcel)和大胡子(Dave Cheney)也参与了论战,不过他们自然是站在Golang一方。之后大家在Docker话题上又燃战火,人们就Docker究竟能给企业和开发者带来何种好处进行了深入PK。

第二天

Dave Cheney的”Writing High Performance Go”:
Dave Cheney不愧为Go语言的知名布道师,这个topic“编程哲学”与实践并存,干货满满,估计事后消化也需要很长时间。值得一提的是本次大会只有Dave的slide是采用Go team常用的.slide格式文件制作的,赶脚非常go native。

吴小伟的“Go在阿里云CDN系统的应用”:
围绕Go在阿里CDN的应用,看得出Go用的还是蛮多的。印象深刻的一个观点:老板决定语言!

许式伟的“谈谈服务治理”:
大家似乎都想知道国内第一家采用golang技术栈实现的七牛,内部到底是如何使用go的,但许总就是不能让我们如愿哈。

孙宏亮的”Go在分布式docker里面的应用”:
赞助商Daocloud的技术和产品展示,可以看到Daocloud内部的一些架构设计和实现,值得参考。

高步双的“Go在小米商城运维平台的应用与实践”:
由于困了,听这个speak时很迷糊,无感。

赵畅的“Golang项目的测试,持续集成以及部署策略”:
我也是第一次听说Grab这家公司。不过赵畅这个speak我很喜欢,把公司技术栈的变迁讲的很生动,关于golang的实践和一些数据正是我们需要的。

孙建良的“Go在网易广域网上传加速系统中的应用”:
不知为何,slide的首页标题居然是:Go&网易云对象存储服务。原以为标题发生了切换,但没过几页,又回到了“广域网上传加速系统”,这两者似乎也没啥联系啊。也许是我没听完提前离场赶火车的缘故吧。

三、会后

谢大组织的这次GopherChina2016非常成功,表现为几点:

  • 参会者众多,会场爆满,还有不辞辛苦,站着聆听的gopher。
  • 多数Speaker表现优异,达到了Gopher传道的目的。
  • 技术Party气氛热烈,论战持久,让Gopher收获满满。
  • 硬件以及组织到位,会场井然有序。
  • 这次Gopher战斗服非常棒,材质很好。
  • 会场的水、水果、奖品、party前自助餐也很给力。

这里对谢大也表示大大的感谢!

个人也有一些小建议:

  • 多些场上互动,尤其是下午场,易困倦。如果此次能将daocloud的抽奖环节挪到主会场,全员参与,想必更能活跃气氛,为大家提神^_^。
  • 从GopherChina大会品牌角度出发,如果能统一讲师slide模板会更好,如果都能使用go team那种native的.slide文件格式就更Go味道十足了。
  • 希望类似GopherCon大会那样,增加open keynote(语言历史,当前,未来plan)和close keynote(社区文化推广)两个环节。

另外我觉得应该对讲师slide内容做一些审核,考虑像gopherchina这样的围绕一门编程语言的conference,到底什么话题才是最佳的呢?当前借着Go之名,实则讲解某一行业领域系统架构的内容似乎多了一些。针对语言本身、语言标准库、语言工具和语言最佳实践的内容略少了一些。

如果要谈语言应用,那个人认为至少如下几个方面应该提及:

  • 使用什么go版本
  • 版本切换时的差异(内存、cpu、GC延迟、吞吐)和坑
  • 用Go开发了哪些服务?为何?为何其他服务不用Go开发,理由。
  • 遇到问题/坑,如何解决
  • 组织内Go的最佳实践

各位讲师的slide后续还得慢慢消化,另外感谢极客学院展台工作人员的拍照服务^_^:

img{512x368}

Rancher使用入门

上个月末,Rancher Labs在其官方博客上宣布了 Rancher 1.0正式版本发布。 这是继Apache MesosGoogle Kubernetes以及Docker 原生 Swarm 之后,又一个可用于Production环境中的容器管理和服务编排工具,而Rancher恰似这个领域的最后一张拼图(请原谅我的孤陋寡闻,如 果有其他 厂商在做这方面产品,请在评论中留言告诉我)。从Rancher Labs的官方about中我们可以看到:Rancher Labs致力于为DevOps team打造一个最好的容器管理平台,让容器的部署和管理变得更加Easy。

本文将带大家与Rancher来个亲密接触,直观的体会一下Rancher的入门级使用方法。

注意:由于Rancher还在active development中,本文仅适用于刚刚发布的v1.0.0版本,包括:

rancher/server:v1.0.0
rancher/agent:v0.11.0
rancher/agent-instance:v0.8.1
rancher-compose-v0.7.3

后续版本演进可能会导致本文中某些操作不再适用或某些UI元素发生变化。

零、实验环境

这里继续使用之前文章中的两个Ubuntu 14.04主机环境(kernel版本 >= 3.16.7),Docker 1.9.1+。

其中:

rancher server:
    10.10.126.101

rancher agents:
    10.10.126.101
    10.10.105.71
    10.10.105.72

一、搭建单节点Rancher Server

Rancher的各种容器管理理念均架构在由Rancher server和rancher agent构建的Infrastructure之上。Rancher server是Rancher的核心,其地位就类似于k8s、Docker swarm或mesos中的master,提供核心容器管理服务以及API服务。作为正式版发布的Rancher v1.0.0支持HA(high available)的多节点rancher server集群,不过Install起来也的确复杂些,依赖的第三方组件也较多,什么MySQLRedisZooKeeper等统统都要额外部署。由于是入门,这里就偷个赖儿,我们就搭建一个单节点的Rancher Server。

Rancher的一个设计理念是所有组件都Containerized(容器化),更有甚者Rancher Labs的另外一个产品RancherOS(地位类似于CoreOS,一款专门为运行容器而设计的Linux发行版)中所有系统服务都是 Dockerized的,这里的Rancher Server也不例外,极大的方便了我们的Install。

下面我们就在126.101 host上安装一个Rancher server。

首先,我们将rancher/server image pull到local,这个image size很大,需要耐心等待一段时间,即便是使用国内容器云厂商提供的加速器:

$ docker pull rancher/server
... ...

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
rancher/server      latest              26bce58807d1        22 hours ago        775.9 MB

接下来,启动rancher server:

$ docker run -d --restart=always -p 8080:8080 rancher/server
d8ce1654ff9f1d056d7cdc9216cf19173d85037bf23be44f802d627eabc8e607

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                              NAMES
d8ce1654ff9f        rancher/server      "/usr/bin/s6-svscan /"   12 seconds ago      Up 8 seconds        3306/tcp, 0.0.0.0:8080->8080/tcp   agitated_ardinghelli

映射的8080端口既服务于Rancher UI,也是Rancher API的服务端口。用浏览器打开http://10.10.126.101:8080,如果你看到如下页面,则说明你的Rancher Server搭建成功了:

img{512x368}

Rancher image size之所以大,是因为其内部安装和运行了诸多服务程序,我们来hack一下:

$ docker exec d8ce1654ff9f ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0    188     4 ?        Ss   03:50   0:00 /usr/bin/s6-svscan /service
root         5  0.0  0.0    188     4 ?        S    03:50   0:00 s6-supervise cattle
root         6  0.0  0.0    188     4 ?        S    03:50   0:00 s6-supervise mysql
root         7  6.5 18.1 3808308 710284 ?      Ssl  03:50   1:05 java -Xms128m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/lib/cattle/logs -Dlogback.bootstrap.level=WARN -cp /usr/share/cattle/9283c067b6f96f5ff1e38fb0ddfd8649:/usr/share/cattle/9283c067b6f96f5ff1e38fb0ddfd8649/etc/cattle io.cattle.platform.launcher.Main
mysql       28  0.4  2.3 2135756 92164 ?       Ssl  03:50   0:04 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --log-error=/var/log/mysql/error.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306
root       170  0.1  0.2 264632 11552 ?        Sl   03:52   0:01 websocket-proxy
root       179  0.0  0.2 274668  8632 ?        Sl   03:52   0:00 rancher-catalog-service -catalogUrl library=https://github.com/rancher/rancher-catalog.git,community=https://github.com/rancher/community-catalog.git -refreshInterval 300
root       180  0.0  0.3 254044 12652 ?        Sl   03:52   0:00 rancher-compose-executor
root       181  0.5  0.4 1579572 16692 ?       Sl   03:52   0:05 go-machine-service
root       610  0.0  0.0  14988  2576 ?        S    04:06   0:00 git -C ./DATA/library pull -r origin master
root       611  0.0  0.0   4448  1696 ?        S    04:06   0:00 /bin/sh /usr/lib/git-core/git-pull -r origin master
root       640  0.0  0.0  15024  3020 ?        S    04:06   0:00 git fetch --update-head-ok origin master
root       641  3.0  0.1 161180  6028 ?        S    04:06   0:00 git-remote-https origin https://github.com/rancher/rancher-catalog.git
root       643  0.0  0.0  15572  2120 ?        Rs   04:07   0:00 ps aux

可以看出里面有mysql、cattle、go-machine-service、rancher-compose-executor以及 websocket-proxy等。通过PID我们可以看出/usr/bin/s6-svscan是容器的第一个启动进程,/service这个 路径作为其命令行参数,估计这是一个类似于supervisord的进程控制程 序,由其 负责启动和管理Rancher server的两个重要服务:MySQL和cattle。注:单节点rancher server的数据都保存在其内部的MySQL中,而多节点rancher server则采用一个外部的MySQL存储数据。

二、设置Account

第一次启动Rancher后,Rancher的UI是没有访问控制的,所有人都可以访问这个地址并控制一切。

切换到API菜单,可以看到当前默认Environment(后续会详细说这个概念)的API访问endpoint是: http://10.10.126.101:8080/v1/projects/1a5

我们可以用curl来访问一下这个url:

$ curl http://10.10.126.101:8080/v1/projects/1a5
{"id":"1a5","type":"project","links":{"self":"http://10.10.126.101:8080/v1/projects/1a5","agents":"http://10.10.126.101:8080/v1/projects/1a5/agents","auditLogs":"http://10.10.126.101:8080/v1/projects/1a5/auditlogs","certificates":"http://10.10.126.101:8080/v1/projects/1a5/certificates",
... ...
"swarm":false,"transitioning":"no","transitioningMessage":null,"transitioningProgress":null,"uuid":"adminProject"}

返回超过一屏的信息,这同时也说明Rancher Server在正常工作。

在正式感受Rancher功能前,我们来给Rancher添加一个Account,相信这也是所有要在生产环境使用Rancher的朋友必须要做 的事情。

在Rancher UI中,也许你已经注意到了,在第一行菜单栏中,“ADMIN”菜单项右侧有一个红色的“!”,这也是在提醒你Rancher当前未设防。我们点击 “ADMIN”,选择出现的二级菜单中的”ACCOUNTS”菜单项,我们将看到如下页面:

img{512x368}

添加权限控制,需要在【”ADMIN” -> “ACCESS CONTROL”】中。Rancher支持四种权限控制方案,分别是:Active Directory、GitHub、Local Auth和OpenLDAP。我们使用最简单的Local Auth,即设置一个用户名和密码,然后点击“Enable Local Auth”按钮即可。然后我们再回到”ACCOUNTS”页面:

img{512x368}

可以看到我们已经建立了一个新的Admin权限的账号:tonybai。当前的登录账号也换成了tonybai。

这时如果你再用API访问当前默认环境的EndPoint的话,结果就会变成下面这样:

 curl http://10.10.126.101:8080/v1/projects/1a5
{"id":"b052db07-d58e-45bf-872e-06ced8bcc4e1","type":"error","links":{},"actions":{},"status":401,"code":"Unauthorized","message":"Unauthorized","detail":null}

提示错误:Unauthorized

这时如果还想用API访问,就需要为该环境添加一个API Key了。在”API”页面下,点击 “Add Environment API Key”按钮,在弹出的窗口中输入key的name:tonybai-default-env-key,点击”Create”创建:

img{512x368}

Rancher会随机生成一对access key和secret key,即user和password,使用它们即可通过API访问该环境,注意:secret key只显示这么一次,你需要手工将其记录下来,否则一旦关闭这个窗口,就无法再找到secret key的内容了,只能再重新生成一对。

$curl -u 5569108BE7489DEE47A5:76Yw5v63ag8SdKYQDYgVok7Co6HRncU7bUCEShXh http://10.10.126.101:8080/v1/projects/1a5
{"id":"1a5","type":"project","links":{"self":"http://10.10.126.101:8080/v1/projects/1a5","agents":"http://10.10.126.101:8080/v1/projects/1a5/agents","auditLogs":"http://10.10.126.101:8080/v1/projects/1a5/auditlogs","certificates":"http://10.10.126.101:8080/v1/projects/1a5/certificates",
... ...
"swarm":false,"transitioning":"no","transitioningMessage":null,"transitioningProgress":null,"uuid":"adminProject"}

三、Environment

前面说过,Rancher中有个概念是Environment。在Rancher UI的右上角,我们可以看到”Default Enviromnet”字样,点击向下箭头,打开下拉菜单,选择:“Manage Enviromnets”,可以看到当前的Enviroments列表:

img{512x368}

在这个页面,我们可以看到Rancher对Enviroments的诠释:

Rancher supports grouping resources into multiple environments. Each one gets its own set of services and infrastructure resources, and is owned by one or more GitHub users, teams or organizations.

For example, you might create separate "dev", "test", and "production" environments to keep things isolated from each other, and give "dev" access to your entire organization but restrict the "production" environment to a smaller team.

大致意思就是一个Environment就是一个resource group,每个Environment都有自己的服务和基础设施资源,并且通过Access Control来赋予每个Account访问该Environments的权限。Rancher Labs的一个目标就是为DevOps Team打造一个Easy的容器管理工具,因此在解释Environment术语时,还特地以DevOps Workflow来解释,比如建立dev、test、production environment,保证Environments间的隔离。下面的这幅图可能会更直观的展现出Environment在Rancher中的“角 色”:

img{512x368}

Rancher Server建立后,会建立一个”Default” Environment,我们可以Edit一下这个Environment的信息,可以修改它的Name、Container Orchestration引擎(cattle、k8s和swarm,默认cattle)以及Access Control,我们看到tonybai的用户是这个Environment的Owner,当然我们也可以修改tonybai这个用户的Role,比如 member、readonly或restricted。这里我们将Default的名字改为”dev”。

我们再添加一个Environment “test”,引擎用cattle:

img{512x368}

我们看到dev environment后面有一个”对号”,说明dev environment是当前active environment,所有操作均针对该environment,你当然可以通过点击每个environment列表后面的切换图标来切换active environment。

到目前为止,虽然Rancher Server建立ok了,environment这个逻辑实体也建立了,但dev environment仍处于“无米下炊”的状态。因为除了Rancher自身外,该Environment下没有任何Resources(主机、存储 等)可供使用(比如创建Containers)。

我们来为dev environment添加两个主机资源:10.10.126.101和10.10.105.72。在”INFRASTRUCTURE”-> HOSTS中点击”Add Host”按钮添加主机资源。Rancher支持多种主机资源,包括Custom(本地自定义)、Amazon EC2Azure 以及 DigitalOcean 等。

我们以本地Host资源(选择Custom)为例,在添加Host页面中,我们输入第一个Host的IP,Rancher UI会生成下面这段命令行:

sudo docker run -e CATTLE_AGENT_IP='10.10.126.101'  -d --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v0.11.0 http://10.10.126.101:8080/v1/scripts/B0C997705263867F519F:1460440800000:1Rd9TyJIS2Fnae5lcjsvnIRDJE

我们需要手动在10.10.126.101这个Host上执行上述命令行:

$ sudo docker run -e CATTLE_AGENT_IP='10.10.126.101'  -d --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v0.11.0 http://10.10.126.101:8080/v1/scripts/B0C997705263867F519F:1460440800000:1Rd9TyJIS2Fnae5lcjsvnIRDJE
2d05764d42c52b1449021766a5c0e104098605cd7d53b632571c46f1e84f2a4b

$ docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS              PORTS                              NAMES
2d05764d42c5        rancher/agent:v0.11.0   "/run.sh http://10.10"   27 seconds ago      Up 22 seconds                                          big_bhabha
d8ce1654ff9f        rancher/server          "/usr/bin/s6-svscan /"   4 days ago          Up 4 days           0.0.0.0:8080->8080/tcp, 3306/tcp   agitated_ardinghelli

等待一会儿,我们刷新一下”INFRASTRUCTURE”-> HOSTS页面,我们会看到10.10.126.101这个Host被加入到dev environment的Infrastructure中了:

img{512x368}

按照同样的步骤,我们再将10.10.105.72加入到Infrastructure中:

$ sudo docker run -e CATTLE_AGENT_IP='10.10.105.72'  -d --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v0.11.0 http://10.10.126.101:8080/v1/scripts/B0C997705263867F519F:1460440800000:1Rd9TyJIS2Fnae5lcjsvnIRDJE
e1f335c665853348810aef8736c67f610ae7f4c93e4b6265361b95a354af434a

$docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS                  PORTS               NAMES
2e212fda35d3        rancher/agent:v0.11.0   "/run.sh inspect-host"   23 seconds ago      Up Less than a second                       trusting_noyce
e1f335c66585        rancher/agent:v0.11.0   "/run.sh http://10.10"   39 seconds ago      Up 23 seconds                               clever_bohr

我们注意到:上面的命令启动了两个Container,image虽然都是rancher/agent:v0.11.0,但执行的命令行参数略有 不同(其中一个Container为临时Container,一段时间后会自动退出)。片刻,我们就在Hosts下看到了两个Host资源了。

我们点击Rancher UI右上角的下拉箭头,将当前Environment从dev切换到test,我们发现test Environment下的Hosts又为空了(不过此处似乎有个bug,在我的Mac Chrome浏览器中,等的时间足够久后,似乎test environment把dev enviroment的Host资源显示出来了,很怪异)。可以看出Infra是Environment相关的。我们在test环境下增加一个 10.10.105.71 host:

$ sudo docker run -e CATTLE_AGENT_IP='10.10.105.71'  -d --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v0.11.0 http://10.10.126.101:8080/v1/scripts/A63B9C5F8066E29377C3:1460448000000:UbPcmDXOqoI6mls6e75Qp17QR0
4a5f9e13615e562636cd515763e293449607a8b2d827d2599f80f9ad8f16aa2d

$ docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED              STATUS                  PORTS                    NAMES
d101095c7709        rancher/agent:v0.11.0   "/run.sh run"            6 seconds ago        Up Less than a second                            rancher-agent
4a5f9e13615e        rancher/agent:v0.11.0   "/run.sh http://10.10"   About a minute ago   Up About a minute                                evil_khorana

到这里,test Environment下也有了一个Host了,从Rancher UI页面可以看到。

四、Stack

Rancher UI的左上角APPLICATIONS下面有一个“STACKS”的二级菜单项。Rancher官方docs对Stack的解释是:”A Rancher stack mirrors the same concept as a docker-compose project. It represents a group of services that make up a typical application or workload.”。同时Rancher UI上关于Service的解释如下:“A service is simply a group of containers created from the same Docker image but extends Docker’s “link” concept to leverage Rancher’s lightweight distributed DNS service for service discovery”。从这两段描述中,我们大致可以推出如下关系:

A Stack <=> An Application <=> A group of services(由类docker-compose的工具rancher-compose管理)

下面这幅图直观描述了user account, environment与stacks之间的关系:

img{512x368}

我们在dev environment下添加一个Service。Rancher UI “APPLICATIONS” -> “STACKS”下面支持两种添加Service的方式,一种是手工添加,一种是从Catalog添加。Catalog类似于一个Rancher App Market,里面有Rancher预定义好的service template。我们这次采用手工添加的方式,便于控制。我们基于nginx:1.8-alpine创建单一实例的service: nginx-alpine-service,端口映射:10086->80。其他采用默认配置。添加Service时,并没有位置让你为Stack 起名,但添加一个Service后,我们会看到当前Stack是Default Stack,你可以修改Stack name,这里改为nginx-app-stack。启动后,我们看到第一个nginx-alpine-service的Container运行在 105.72上。

img{512x368}

点击stack名字,可以查看stack的详细信息:

img{512x368}

点击”nginx-alpine-service”,进入到service属性页面,我们将nginx-alpine-service的 Scale +1。Rancher会自动在Resource host上根据默认调度策略,运行一个新的基于nginx image的Container。我们可以看到这个新Container运行在126.101上,这样dev Environmnet中的两个Host上就各自运行了一个nginx-alpine-service的Container:

img{512x368}

nginx-alpine-service的两个容器分别为:

 Running    Default_nginx-alpine-app_1  10.42.96.91 10.10.105.72  nginx:1.8-alpine
 Running    nginx-app-stack_nginx-alpine-service_1  10.42.164.174   10.10.126.101 nginx:1.8-alpine

Rancher内置“Internal DNS Services”,同一Stack下的Container可以通过Container name相互ping通。Rancher以Environment为界限,每个Environment下的Container name都是全局唯一的。

在10.10.105.72上,我们执行如下命令来ping 10.10.126.101上的容器:nginx-app-stack_nginx-alpine-service_1:

$ docker exec r-Default_nginx-alpine-app_1  ping -c 3 nginx-app-stack_nginx-alpine-service_1
PING nginx-app-stack_nginx-alpine-service_1 (10.42.164.174): 56 data bytes
64 bytes from 10.42.164.174: seq=0 ttl=62 time=0.729 ms
64 bytes from 10.42.164.174: seq=1 ttl=62 time=0.754 ms
64 bytes from 10.42.164.174: seq=2 ttl=62 time=0.657 ms

--- nginx-app-stack_nginx-alpine-service_1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.657/0.713/0.754 ms

在10.10.126.101上,我们执行如下命令来ping 10.10.105.72上的容器:Default_nginx-alpine-app_1:

$ docker exec r-nginx-app-stack_nginx-alpine-service_1 ping -c 3 Default_nginx-alpine-app_1
PING Default_nginx-alpine-app_1 (10.42.96.91): 56 data bytes
64 bytes from 10.42.96.91: seq=0 ttl=62 time=0.640 ms
64 bytes from 10.42.96.91: seq=1 ttl=62 time=0.814 ms
64 bytes from 10.42.96.91: seq=2 ttl=62 time=0.902 ms

--- Default_nginx-alpine-app_1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.640/0.785/0.902 ms

我们按照上述方法为nginx-app-stack再添加一个Service: redis-alpine-service,该service基于redis:alpine image,该service的Container被运行在105.72上了:

$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                          NAMES
7246dce88ea6        redis:alpine                    "/entrypoint.sh redis"   3 minutes ago       Up 3 minutes        6379/tcp                                       r-nginx-app-stack_redis-service_1

我们来测试一下同一stack下,不同Service的互ping:

我们在redis-alpine-service的Container中来ping nginx-alpine-service,地址直接使用”nginx-alpine-service”这个service name即可:

$ docker exec r-nginx-app-stack_redis-service_1 ping -c 3 nginx-alpine-service
PING nginx-alpine-service (10.42.164.174): 56 data bytes
64 bytes from 10.42.164.174: seq=0 ttl=62 time=0.660 ms
64 bytes from 10.42.164.174: seq=1 ttl=62 time=0.634 ms
64 bytes from 10.42.164.174: seq=2 ttl=62 time=0.599 ms

--- nginx-alpine-service ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.599/0.631/0.660 ms

可以看到Rancher的Internal DNS Service将”nginx-alpine-service”这个service name解析为nginx-alpine-service的两个Container中的一个:10.42.164.174。

我们再添加一个Stack:memcached-app-stack,来看一下跨Stack的容器连通方法。ping之前我们需要为该Stack添加一个基于memcached:latest image的Service: memcached-service

10.10.105.72

$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                          NAMES
184e8e8f448e        memcached:latest                "/entrypoint.sh memca"   24 seconds ago      Up 16 seconds       11211/tcp                                      r-memcached-app-stack_memcached-service_1

Rancher官方docs中明确说明:不同Stack间service互ping,需要采用“ service_name.stack_name”的地址格式,我们在memcached-app-stack中的“r-memcached-app-stack_memcached-service_1”容器里去ping nginx-app-stack中的nginx-alpine-service服务:

$ docker exec r-memcached-app-stack_memcached-service_1  ping -c 3 nginx-alpine-service.nginx-app-stack
PING nginx-alpine-service.nginx-app-stack (10.42.164.174): 56 data bytes
64 bytes from 10.42.164.174: icmp_seq=0 ttl=62 time=0.710 ms
92 bytes from 10.42.84.96: Redirect Host
64 bytes from 10.42.164.174: icmp_seq=1 ttl=62 time=2.543 ms
--- nginx-alpine-service.nginx-app-stack ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.710/1.627/2.543/0.917 ms

ping nginx-app-stack中的redis-alpine-service服务:

$ docker exec r-memcached-app-stack_memcached-service_1  ping -c 3 redis-alpine-service.nginx-app-stack
PING redis-alpine-service.nginx-app-stack (10.42.220.43): 56 data bytes
64 bytes from 10.42.220.43: icmp_seq=0 ttl=64 time=0.161 ms
64 bytes from 10.42.220.43: icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from 10.42.220.43: icmp_seq=2 ttl=64 time=0.051 ms
--- redis-alpine-service.nginx-app-stack ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.050/0.087/0.161/0.052 ms

我们通过cat /etc/resolv.conf可以查看到Rancher内部DNS的地址:

$docker exec r-memcached-app-stack_memcached-service_1  cat /etc/resolv.conf
search memcached-app-stack.rancher.internal memcached-service.memcached-app-stack.rancher.internal rancher.internal
nameserver 169.254.169.250

五、Rancher Compose CLI

Rancher除了提供UI工具外,还提供了一个名为rancher-compose的CLI工具,用于在一个stack的范围内管理各个services。rancher-compose的灵感来源于docker-compose,兼容docker-compose的配置文件格式,并有自己的扩展。此外与docker-compose不同的是rancher-compose支持跨多主机管理。

在Rancher UI的右下角有一个Rancher-compose的下载链接,支持Linux,Windows和Mac。rancher-compose当前版本是0.7.3,下载后将其路径放到PATH环境变量里,验证一下运行是否ok:

$ rancher-compose -v
rancher-compose version v0.7.3

要管理某个stack下的Service,我们至少需要提供一个docker-compose.yml文件,这里针对memcached-app-stack下的memcached-service这个服务做一些操作,我们提供一个docker-compose.yml:

memcached-service:
  log_driver: ''
  tty: true
  log_opt: {}
  image: memcached:latest
  stdin_open: true

利用dev环境的api key和secret,rancher-compose可以实现与rancher的交互:

$ rancher-compose --url http://10.10.126.101:8080  --access-key 5569108BE7489DEE47A5 --secret-key 76Yw5v63ag8SdKYQDYgVok7Co6HRncU7bUCEShXh -p memcached-app-stack up
INFO[0000] Project [memcached-app-stack]: Starting project
INFO[0000] [0/1] [memcached-service]: Starting
INFO[0000] [1/1] [memcached-service]: Started
INFO[0000] Project [memcached-app-stack]: Project started

由于memcached-service已经存在并启动了相应Container,因此上面的命令实际上没有做任何改动。如果想看rancher-compose的执行细节,可以在rancher-compose后面加上–verbose命令行option,可以看到如下结果:

$ rancher-compose --verbose --url http://10.10.126.101:8080  --access-key 5569108BE7489DEE47A5 --secret-key 76Yw5v63ag8SdKYQDYgVok7Co6HRncU7bUCEShXh -p memcached-app-stack up
DEBU[0000] Environment Context from file : map[]
DEBU[0000] Opening compose file: docker-compose.yml
DEBU[0000] [0/0] [memcached-service]: Adding
DEBU[0000] Opening rancher-compose file: /home1/tonybai/rancher-compose.yml
DEBU[0000] Looking for stack memcached-app-stack
DEBU[0000] Found stack: memcached-app-stack(1e3)
DEBU[0000] Launching action for memcached-service
DEBU[0000] Project [memcached-app-stack]: Creating project
DEBU[0000] Finding service memcached-service
DEBU[0000] [0/1] [memcached-service]: Creating
DEBU[0000] Found service memcached-service
DEBU[0000] [0/1] [memcached-service]: Created
DEBU[0000] Project [memcached-app-stack]: Project created
INFO[0000] Project [memcached-app-stack]: Starting project
DEBU[0000] Launching action for memcached-service
DEBU[0000] Finding service memcached-service
INFO[0000] [0/1] [memcached-service]: Starting
DEBU[0000] Found service memcached-service
DEBU[0000] Finding service memcached-service
INFO[0000] [1/1] [memcached-service]: Started
INFO[0000] Project [memcached-app-stack]: Project started
DEBU[0000] Found service memcached-service
DEBU[0000] Finding service memcached-service
DEBU[0000] Found service memcached-service

我们再通过rancher-compose将memcached-service扩展到两个Container:

$ rancher-compose --url http://10.10.126.101:8080  --access-key 5569108BE7489DEE47A5 --secret-key 76Yw5v63ag8SdKYQDYgVok7Co6HRncU7bUCEShXh -p memcached-app-stack scale memcached-service=2
INFO[0000] Setting scale memcached-service=2...

几秒后,Rancher UI上memcached-service的Container数量就会从1变为2。在105.72上我们也可以看到两个memcached service container:

$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                          NAMES
43c1443fec9f        memcached:latest                "/entrypoint.sh memca"   8 minutes ago       Up 7 minutes        11211/tcp                                      r-memcached-app-stack_memcached-service_2
184e8e8f448e        memcached:latest                "/entrypoint.sh memca"   14 hours ago        Up 13 hours         11211/tcp                                      r-memcached-app-stack_memcached-service_1

六、Service upgrade

Rancher支持stack中Service的upgrade管理。Rancher提供了两种Service Upgrade方法:In-service upgrade和Rolling upgrade(滚动升级)。rancher-compose同时支持两种升级方法,Rancher UI中针对stack下的service也有upgrade菜单选项,但UI提供的升级方式等同于in-service upgrade。

根据官方docs的说明,In-Service upgrade的默认upgrade步骤大致是:

1、停掉existing service的containers;
2、等待interval时间;
3、启动new version service的containers;
4、confirm upgrade or rollback。

而Rolling upgrade的升级步骤则是:

1、启动new service ;
2、将old service的scale降为0。

下面我们就每种method分别举一个例子说明一下(均采用rancher-compose工具)。

1、In-Service Upgrade

我们来将dev Environment下nginx-app-stack的nginx-alpine-service从nginx:1.8-alpine升级到nginx:1.9-alpine。为此我们需要给rancher-compose提供一份升级后的service的docker-compose.yml文件:

//docker-compose-nginx-service-upgrade.yml

nginx-alpine-service:
  ports:
  - 10086:80/tcp
  log_driver: ''
  labels:
    io.rancher.container.start_once: 'true'
  tty: true
  log_opt: {}
  image: nginx:1.9-alpine
  stdin_open: true

可以看到我们仅是将nginx-alpine-service的image从1.8-alpine改为1.9-alpine了。接下来我们就来升级nginx-alpine-service:

$ rancher-compose -f ./docker-compose-nginx-service-upgrade.yml --url http://10.10.126.101:8080  --access-key 5569108BE7489DEE47A5 --secret-key 76Yw5v63ag8SdKYQDYgVok7Co6HRncU7bUCEShXh -p nginx-app-stack up --upgrade nginx-alpine-service
INFO[0000] Project [nginx-app-stack]: Starting project
INFO[0000] [0/1] [nginx-alpine-service]: Starting
INFO[0000] Updating nginx-alpine-service
INFO[0001] Upgrading nginx-alpine-service
INFO[0056] [1/1] [nginx-alpine-service]: Started
INFO[0056] Project [nginx-app-stack]: Project started

我们通过Rancher UI可以看到upgrade执行在界面上体现出来的变化:

img{512x368}

Upgrade后,nginx-alpine-service的详细信息如下:

img{512x368}

我们来Confirm一下:

$ rancher-compose -f ./docker-compose-nginx-service-upgrade.yml  --url http://10.10.126.101:8080  --access-key 5569108BE7489DEE47A5 --secret-key 76Yw5v63ag8SYQDYgVok7Co6HRncU7bUCEShXh -p nginx-app-stack up --upgrade --confirm-upgrade
INFO[0000] Project [nginx-app-stack]: Starting project
INFO[0000] [0/1] [nginx-alpine-service]: Starting
INFO[0001] [1/1] [nginx-alpine-service]: Started
INFO[0001] Project [nginx-app-stack]: Project started
ERRO[0002] Failed to get logs for Default_nginx-alpine-app_1: Failed to find action: logs
ERRO[0002] Failed to get logs for nginx-app-stack_nginx-alpine-service_1: Failed to find action: logs

Confirm后,Rancher UI上的upgrade标记不见了,两个没有running的old版本 container也被cleanup了。confirm时出现两个ERRO,不知何原因,但问题不大,没有影响到confirm结果。

2、Rolling Upgrade

与In-service upgrade服务中断不同,Rolling Upgrade会先启动new Service,然后再逐渐将old service的scale减少到0。这种情况下,如果其他服务配合到位,该服务是不会中断的。

我们以nginx-app-stack中的redis-alpine-service为例,将其从redis:alpine版本升级到3.0.7-alpine。

$docker images
redis                                  3.0.7-alpine        633ba621a23f        6 weeks ago         15.95 MB
redis                                  alpine              633ba621a23f        6 weeks ago         15.95 MB
... ...

我们同样要为这次Roll upgrade准备一份docker-compose.yml文件:

//docker-compose-redis-service-upgrade.yml

redis-alpine-service:
redis-alpine-service-v1:
  log_driver: ''
  tty: true
  log_opt: {}
  image: redis:3.0.7-alpine
  stdin_open: true

执行Rolling upgrade命令:

$rancher-compose -f ./docker-compose-redis-service-upgrade.yml --url http://10.10.126.101:8080  --access-key 5569108BE7489DEE47A5 --secret-key 76Yw5v63ag8SdKYQDYgVok7Co6HRncU7bUCEShXh -p nginx-app-stack upgrade  redis-alpine-service redis-alpine-service-v1
INFO[0000] Creating service redis-alpine-service-v1
INFO[0005] Upgrading redis-alpine-service to redis-alpine-service-v1, scale=2

Rancher UI上出现如下状态变化:

img{512x368}

最终redis-alpine-service-v1启动,redis-alpine-service停止,但Rancher UI并未将其Remove,你可以手动删除,或者在上面命令中加入–cleanup自动删除old service。

七、参考资料

关于Rancher,网上可用的资料并不多,这里主要是参考了官方文档:

http://rancher.com/announcing-rancher-1-0-ga/

http://docs.rancher.com/rancher/quick-start-guide/

不过Rancher的Doc文字太多,少图,尤其是在Rancher UI介绍这块,基本无图,还待改善。

另外国内的云舒网络与 Rancher Labs是深度的合作伙伴,云舒公司博客上的内容也值得大家认真参考。

八、小结

相比于Mesos、Kubernetes和Swarm这三位欧巴,Rancher还最为年轻(至少从发布时间上来看是这样的),也刚刚起步。而这个领域的激烈的竞争也才刚刚开始。 谁能笑道最后,还待观察。




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:


如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:


以太币:


如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多