从Docker 1.12开始,docker公司展露了一统容器技术生态的野心。docker engine集成swarmkit, 使得你可以不用安装额外的软件包,就可以使用简单的命令创建和管理docker swarm集群。docker engine同时集成了swarm集群的安全特性和KV存储, 你现在也不需要额外去部署etcd或者consul等应用。

下面,我们就介绍docker-compose模式的使用,然后推广到swarm模式。


1. 准备工作

1.1 在所有主机上安装docker-ce

安装docker-ce

1.2 在工作节点上安装docker-compose

安装docker-compose


2. 通过docker-compose部署应用容器

  • 部署docker-compose.yml
# cd /path/to/docker-compose.yml

// 在含有docker-compose.yml的目录下执行
# docker-compose up       

// 我们使用-d参数,在后台启动应用
# docker-compose up -d

# docker-compose ps
  • 查看应用服务的日志
# cd /path/to/docker-compose.yml

# docker-compose logs  

// 滚动输出应用日志, 每个容器输出最新10行。
// 注意:日志滚动输出时,按Ctrl+S暂停, Ctrl+Q继续输出, Ctrl+C退出
# docker-compose logs --tail 10 --follow  

//停止应用
# docker-compose stop 
  • scale up扩展应用容器到一定数目
// 查看我们work实例容器只有一个
# docker-compose ps

// 扩展work实例容器到2个
# docker-compose scale up --scale worker=2


3. 扩展应用服务的瓶颈

  • 瓶颈

当扩展worker实例容器的个数为10,我们发现性能并没有提升上去,原因是所有的应用容器都跑在一台主机上,多个work实例容器会竞争主机资源,所以即使增加work服务的实例个数依然不能改善性能,另外也可能存在其他因素导致的瓶颈。

  • 瓶颈问题解决

针对第一个work主机节点,我们可以建立docker swarm集群,使得worker实例容器可以分布在不同主机上运行;针对后面其他节点瓶颈问题,我们可以建立多个相关节点来消除瓶颈。

注意:docker-compose不能在swarm集群中部署服务,只能在当前节点上部署。否则,会报以下错误。

WARNING: The Docker Engine you’re using is running in swarm mode.
Compose does not use swarm mode to deploy services to multiple nodes in a
swarm. All containers will be scheduled on the current node.
To deploy your application across the swarm, use docker stack deploy.


4. 创建Swarm集群

Docker Engine 1.12及后续版本集成了SwarmKit编排服务,即Swarm Mode。它将服务对象引入到docker中,提供swarm集群管理的原生支持并实现scaling、rolling update、service discovery、load balance、routing mesh等特性。

4.1 基本概念

  • Node

节点(Node)是swarm集群中的一个Docker Engine实例。其中管理节点(Manager)负责swarm集群管理并向工作节点分配任务;工作节点(Worker)接受并执行来自管理节点的Task。简单可理解为一个Node就是一台Docker宿主机,管理节点为领导,工作节点为小兵。需要注意的是领导也会干小兵的活儿(真是个好领导啊),而且小兵也可以根据需求随时提拔成领导(真是个好团队啊)。

  • Service

服务(Service)是对运行在worker主机节点上一组任务的定义,它是整个swarm的核心,一个Service由多个任务组成。

  • Task

任务(Task)包含Docker容器和容器中运行的命令或应用,它是swarm中被调度的最小单元。简单可理解为一个Task就是一个容器。

4.2 基本命令

Swarm Mode下主要使用三组命令行工具,创建和管理一个Swarm集群。

  • docker swarm相关命令

开启Swarm模式,加入Swarm集群,配置集群参数。

  • docker node相关命令

查询集群节点信息,提升/移除一个管理节点,管理Swarm节点主机。

  • docker service相关命令

创建和管理service。

4.3 初始化Swarm集群

在node1上,初始化Swarm集群

# docker swarm init --advertise-addr 192.168.0.2

备注:你只需要在一个node上初始化swarm集群, 其他node加入这个集群就行了, 所以以上命令只需要在node1上运行。--advertise-addr参数, 后面跟你swarm集群的通讯地址, 这里是node1的IP地址,如果实在node1上执行,则可以省略--advertise-addr参数。

在需要加入集群的manager/work节点,我们复制粘贴运行以下命令,即可加入到Swarm集群中。

// 将node1以manager角色加入Swarm集群
# docker swarm join \
--token SWMTKN-1-4d8z30svnj6wi8y64ttzirpildro5vego1qrrldepj8auwxa6l-3e8p1ojarz8rdq1rx0ves1a9o \
192.168.0.2:2377     

// 检查node1的docker swarm mode信息
# docker info     

// 查看Swarm集群node列表 
# docker node ls 

// 将其他节点加入到Swarm集群
# docker swarm join \
--token SWMTKN-1-4d8z30svnj6wi8y64ttzirpildro5vego1qrrldepj8auwxa6l-4l4b6o7q1wnjyiwsubkpffkkn \
192.168.0.2:2377 

如果你不记得初始化后提示加入Swarm集群的命令和密钥也没关系,可以使用以下方式查看以worker角色和manager角色加入的命令

# docker swarm join-token worker

# docker swarm join-token manager

为了避免单点故障,实现Swarm集群的高可用,我们可以多建立几个manager节点。此时通过# docker node ls可以看到,node1是Leader节点, node02和node03是Reachable的,我们也可以在node02和node03上面管理整个Swarm集群。

// 提升node02和node03节点为manager节点
# docker node promote node02 node03  

从Swarm集群中删除worker节点时,我们需要先把目标worker节点停机,然后在manager节点上执行

# docker node ls

# docker node rm -f node_name

然后在worker节点上执行命令,脱离Swarm集群

# docker swarm leave


5. 在Swarm集群中部署应用容器

5.1 部署、扩展应用服务

  • 创建(create)Service

有了Swarm集群,我们如何把应用服务跑在Swarm集群上呢?很简单, 把我们原来使用的docker run命令替换成docker service create就行了,命令后面的格式和选项全都一样。

// 在Swarm集群上, 启动一个alpine镜像来执行ping 8.8.8.8命令(如果出错,swarm集群会不断帮你重试启动容器,直到成功为止)
# docker service create --name ping-google alpine ping 8.8.8.8  

// 查看服务到底跑在哪个节点服务器上
# docker service ps <Service ID or Name>
  • 扩展(scale)Service

前面ping-google服务只有一个容器,现在我们想scale服务到10个副本

# docker service up --scale ping-google=10    

# docker service ls

ID            NAME         REPLICAS  IMAGE   COMMAND
9cyy6xrk2n0w  ping-google  1/10      alpine  ping 8.8.8.8

这里REPLICAS显示的1/10表示这个Service一共有10个副本,现在成功运行了一个,集群正在启动其他副本。

  • 查看service进程
# dockerr service ps ping-goole

等所有的副本都启动成功了, 你会看到Swarm集群会自动编排10个副本到5台worker节点上,而且每个节点会启动2个容器。

5.2 为Service开放一个服务端口

与单机使用docker一样, 你可以把容器的端口暴露到主机网络中,从而使容器服务可以被外部访问。Swarm集群的Service端口暴露有如下特性:

a. 公共的端口会暴露在每一个Swarm集群中的节点服务器上

b. 请求进入公共端口后,会负载均衡到所有的Sevice实例上

发布端口的跟单机环境一样, 就是把docker run -p替换成docker service create -p就行。

// 创建一个elasticsearch服务, 发布9200端口

# docker service create --name search --publish 9200:9200 --replicas 7 elasticsearch   

// 监控service创建过程
# watch docker service ps search 

注意DESIRED STATE列, 一个Service副本的创建过程会经历以下几个状态:

a. accepted,任务已经被分配到某一个节点执行
b. preparing,准备资源, 现在来说一般是从网络拉取image
c. running,副本运行成功
d. shutdown,报错,被停止

当一个任务被终止(stoped or killed), 任务不能被重启, 但是一个替代的任务会被创建。

5.3 测试服务端口

# curl localhost:9200
{
  "name" : "Rachel Grey",
  "cluster_name" : "elasticsearch",
  "version" : {
    "number" : "2.3.4",
    "build_hash" : "e455fd0c13dceca8dbbdbb1665d068ae55dabe3f",
    "build_timestamp" : "2016-06-30T11:24:31Z",
    "build_snapshot" : false,
    "lucene_version" : "5.5.0"
  },
  "tagline" : "You Know, for Search"
}

反复执行这个命令, 你会看到name会改变, 请求会被分发到不同的service副本。

5.4 删除服务

使用# docker service rm <Service_ID or Name>命令,删除服务。

//删除所有servcie

# docker service ls -q | xargs docker service rm  

5.5 Swarm集群的网络

为了让应用跑在Swram集群上,我们要解决容器间的网络通信问题。单节点场景中,我们所有的容器都跑在一台docker主机上, 所以容器之间的网络是内部互通的。那我们该如何保证不同主机上的容器网络互通呢?不用担心,Swarm集群已经帮我们解决了这个问题了,那就是overlay network。

在docker 1.12以前, Swarm集群需要一个额外的key-value存储(consul, etcd etc)来同步网络配置, 保证所有容器在同一个网段中。在docker 1.12中已经内置了这个存储并且集成了overlay networks的支持。

  • 创建一个overlay network
// 创建一个名为test的overlay network
# docker network create --driver overlay test 

// 查看docker network列表
# docker network ls  

在网络列表中,你可以看到dockercoins网络的SCOPE是swarm, 表示该网络是在整个Swarm集群生效的, 其他一些网络是local, 表示本机网络。你只需要在manager节点创建overlay network即可, Swarm集群会自动配置到其他的节点(前提是各worker节点都提权成为manager ,即# docker node ls中MANAGER STATUS为Reachable)。当overlay network在manager节点创建完毕后再查看其他节点的网络状态,可以看到各节点的test网络都已经创建了。

# ssh node03 docker network ls

注:前提是各节点之间需要配置ssh免密钥登陆。

  • 在集群网络上运行容器

直接使用--network <network_name>参数, 在指定网络上创建Service。

# docker service create --network test --name redis redis

// 创建hasher服务
# docker service create --network test --name hasher localhost:5000/dockercoins_hasher:v0.1 

// 删除webui服务
# docker service rm webui 

# docker service create --network test --name webui -p 8000:80 localhost:5000/dockercoins_webui:v0.1

好了, 现在你可以用浏览器访问http://192.168.0.2:8000/index.html,就能看到我们熟悉的webui了。事实上, 你可以通过访问swarm集群中的所有节点(192.168.0.2 - 192.168.0.6)的8000端口来访问webui。

  • 扩展(Scaling)应用

两种方式:

a. 通过docker service up --scale worker=10

b. 通过修改服务的属性来实现

// 查询service属性
# docker service inspect rng   

“Mode": {
              "Replicated": {
                   "Replicas": 1
                    }

// 更新rng服务属性
# docker service update rng --replicas 5  

//查看服务
# docker service ls  
  • 找出应用服务的瓶颈
// 启动一个临时容器, debug使用alpine镜像, 连接到test网络中
# docker service create --network test --name debug --mode global alpine sleep 1000000000

a. --mode global,global模式的service, 就是在swarm集群的每个节点创建容器副本, 如果你想让一个service分布在集群的每个节点上,可以使用这个模式。

b. sleep 1000000000,因为懒, 想保持这个容器, 方便我们debug.

随便进入某个节点中的debug容器,安装性能测试工具:curl、ab和drill

# apk add --update curl apache2-utils drill

5.6 负载均衡模式

  • 检查rng服务
# drill rng
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 16923
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; rng. IN A
;; ANSWER SECTION:
rng. 600 IN A 10.0.0.6

可以看到rng服务的IP地址是10.0.0.6,我们一共有5个rng服务, 为啥只有一个IP地址呢?其实这个IP地址是Swarm集群分配给所有rng应用服务的VIP(Virtual IP,负载均衡)。

Swarm集群的service负载均衡有两种方式:

a. VIP模式,每个Service会得到一个virtual IP作为服务请求的入口。基于virtual IP进行负载均衡。

b. DNSRR模式,Service利用DNS解析来进行负载均衡, 这种模式在旧的Docker Engine下有 ,不推荐。

  • 查看Service的Endpoint模式
# docker service inspect rng

...
            "EndpointSpec": {
                "Mode": "vip"
            }
        },
        "Endpoint": {
            "Spec": {
                "Mode": "vip"
            },
            "VirtualIPs": [
                {
                    "NetworkID": "396fwpd5ja6azh93dshalmxro",
                    "Addr": "10.0.0.6/24"
                }
            ]
        },
...
  • 指定一个Service的Endpoint模式
# docker service create --endpoint-mode [vip|dnssrr] <service name>
  • 修改一个Service的模式
# docker service update --endpoint-mode [vip|dnssrr] <service name>


6. 压力测试

6.1 rng服务压力测试

介绍完负载均衡模式, 下面使用ab对我们的rng服务进行简单的压力测试。测试之前, 我们要关掉所有的worker服务, 避免worker服务影响测试结果。

# docker service scale worker=0
worker scaled to 0

# docker service ls
ID            NAME      REPLICAS  IMAGE                                   COMMAND
...
d7g0estex65u  worker    0/0       localhost:5000/dockercoins_worker:v0.1

回到我们的debug容器中

a. 模拟一个客户端,发送50个请求给rng服务

# ab -c 1 -n 50 http://rng/10

This is ApacheBench, Version 2.3 <$Revision: 1748469 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking rng (be patient).....done
Server Software:        Werkzeug/0.11.10
Server Hostname:        rng
Server Port:            80
Document Path:          /10
Document Length:        10 bytes
Concurrency Level:      1
Time taken for tests:   5.386 seconds
Complete requests:      50
Failed requests:        0
Total transferred:      8250 bytes
HTML transferred:       500 bytes
Requests per second:    9.28 [#/sec] (mean)
Time per request:       107.716 [ms] (mean)
Time per request:       107.716 [ms] (mean, across all concurrent requests)
Transfer rate:          1.50 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    2   0.6      1       3
Processing:   103  106   1.5    106     110
Waiting:      102  105   1.2    105     109
Total:        104  107   1.7    107     112
WARNING: The median and mean for the initial connection time are not within a normal deviation
        These results are probably not that reliable.
Percentage of the requests served within a certain time (ms)
  50%    107
  66%    108
  75%    108
  80%    108
  90%    110
  95%    110
  98%    112
  99%    112
 100%    112 (longest request)

b. 模拟50个并发客户端, 发送50个请求

# ab -c 50 -n 50 http://rng/10

This is ApacheBench, Version 2.3 <$Revision: 1748469 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking rng (be patient).....done
Server Software:        Werkzeug/0.11.10
Server Hostname:        rng
Server Port:            80
Document Path:          /10
Document Length:        10 bytes
Concurrency Level:      50
Time taken for tests:   1.105 seconds
Complete requests:      50
Failed requests:        0
Total transferred:      8250 bytes
HTML transferred:       500 bytes
Requests per second:    45.23 [#/sec] (mean)
Time per request:       1105.436 [ms] (mean)
Time per request:       22.109 [ms] (mean, across all concurrent requests)
Transfer rate:          7.29 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        7    9   1.3      9      12
Processing:   103  590 313.4    627    1087
Waiting:      103  589 313.3    626    1087
Total:        115  599 312.2    637    1095
Percentage of the requests served within a certain time (ms)
  50%    637
  66%    764
  75%    869
  80%    946
  90%   1050
  95%   1092
  98%   1095
  99%   1095
 100%   1095 (longest request)

在单个客户端时,rng的响应时间平均107.716ms, 多并发情况下增加到大约1000ms+。

6.2 hasher服务压力测试

hasher的服务测试稍微复杂点, 因为hasher服务需要POST一个随机的bytes数据,所以我们需要先通过curl制作一个bytes数据文件:

# curl http://rng/10 > /tmp/random

a. 模拟单客户端,发送50个请求给hasher服务

# ab -c 1 -n 50 -T application/octet-stream -p /tmp/random http://hasher/

This is ApacheBench, Version 2.3 <$Revision: 1748469 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking hasher (be patient).....done
Server Software:        thin
Server Hostname:        hasher
Server Port:            80
Document Path:          /
Document Length:        64 bytes
Concurrency Level:      1
Time taken for tests:   5.323 seconds
Complete requests:      50
Failed requests:        0
Total transferred:      10450 bytes
Total body sent:        7250
HTML transferred:       3200 bytes
Requests per second:    9.39 [#/sec] (mean)
Time per request:       106.454 [ms] (mean)
Time per request:       106.454 [ms] (mean, across all concurrent requests)
Transfer rate:          1.92 [Kbytes/sec] received
                        1.33 kb/s sent
                        3.25 kb/s total
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    1   0.4      1       3
Processing:   103  105   0.8    105     107
Waiting:      103  104   0.8    104     107
Total:        104  106   1.0    106     109
Percentage of the requests served within a certain time (ms)
  50%    106
  66%    106
  75%    106
  80%    107
  90%    108
  95%    108
  98%    109
  99%    109
 100%    109 (longest request)

b. 模拟50个并发客户端, 发送50个请求

# ab -c 50 -n 50 -T application/octet-stream -p /tmp/random http://hasher/
This is ApacheBench, Version 2.3 <$Revision: 1748469 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking hasher (be patient).....done
Server Software:        thin
Server Hostname:        hasher
Server Port:            80
Document Path:          /
Document Length:        64 bytes
Concurrency Level:      50
Time taken for tests:   0.345 seconds
Complete requests:      50
Failed requests:        0
Total transferred:      10450 bytes
Total body sent:        7250
HTML transferred:       3200 bytes
Requests per second:    144.95 [#/sec] (mean)
Time per request:       344.937 [ms] (mean)
Time per request:       6.899 [ms] (mean, across all concurrent requests)
Transfer rate:          29.59 [Kbytes/sec] received
                        20.53 kb/s sent
                        50.11 kb/s total
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        5   10   4.8      8      17
Processing:   131  214  71.5    238     323
Waiting:      126  207  72.2    231     322
Total:        147  224  67.2    246     328
Percentage of the requests served within a certain time (ms)
  50%    246
  66%    249
  75%    252
  80%    314
  90%    324
  95%    328
  98%    328
  99%    328
 100%    328 (longest request)

从结果可以看出, 单客户端hasher平均响应时间106.454ms, 50并发平均响应时344.937ms,hasher服务并发响应时间也慢, 不过比rng的1000+ms好太多。

7. 参考文章

https://blog.csdn.net/weiguang1017/article/details/52386478

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐