第0章 前言

自从dockercon 2016发布docker1.12版本以来, 经历了几个RC版本, docker1.12终于迎来了第一个稳定版.
docker1.12展示了docker大统一平台的野心, 集成了swarmkit, 使你可以不用安装额外的软件包, 使用简单的命令启动创建docker swarm集群.
集成了swarm集群的安全特性, 集成了K-V存储, 你现在不需要额外部署etcd或者consul.

本文来自与Pycon2016的一个workshop笔记总结.
Deploying and scaling applications with Docker, Swarm, and a tiny bit of Python magic.
Docker最新版1.12为基础. 介绍了docker编排使用的最新情况.希望大家能有所所收获. 推荐直接观看视频讲解.
主要分以下及部分:

  • 第一章 环境准备, 主要准备实验环境, 安装docker, 使用docker-compose运行我们的示例应用docker币.
  • 第二章 创建swarm集群, 使用SwarmKit配置docker swarm集群. 创建swarm service.
  • 第三章 在swarm上运行app, 部署本地registry, overlay network介绍
  • 第四章 swarm集群运维.

第一章 环境准备

本workshop实验环境一共需要5台虚拟机, 去创建一个docker swarm 集群.
VM Hostname 和 IP地址如下:

1
2
3
4
5
"node01" => "192.168.33.101",
"node02" => "192.168.33.102",
"node03" => "192.168.33.103",
"node04" => "192.168.33.104",
"node05" => "192.168.33.105"

1.1 使用Vagrant创建实验环境

什么是vagrant?
简单说vagrant是一个命令行的虚拟机操作,编排工具. 支持各种虚拟化引擎.可以帮助我们快速搭建实验环境.
具体细节请看Vagrant官方网站

1.1.1 安装 Vagrant

先下载安装 Oracle VM Virtualbox,
再下载安装 Vagrant.

1.1.2 创建实验环境

打开window命令行工具:

新建一个目录:

1
2
3
4
5
6
7
8
9
C:\Users\XXX\vagrantVM>mkdir docker-workshop

C:\Users\XXX\vagrantVM>cd docker-workshop
# 初始化一个vagrant 配置文件
C:\Users\xiangxu\vagrantVM\docker-workshop>vagrant init
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

你会发现目录下面多了一个 Vagrantfile文件. 编辑这个文件替换成如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
## files: Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

hosts = {
"node01" => "192.168.33.101",
"node02" => "192.168.33.102",
"node03" => "192.168.33.103",
"node04" => "192.168.33.104",
"node05" => "192.168.33.105"
}

Vagrant.configure("2") do |config|
hosts.each do |name, ip|
config.vm.define name do |machine|
machine.vm.box = "bento/centos-7.2"
machine.vm.box_check_update = false
machine.ssh.insert_key = false
machine.vm.hostname = name
machine.vm.network :private_network, ip: ip
machine.vm.synced_folder ".", "/vagrant", disabled: true
machine.vm.provider "virtualbox" do |v|
v.name = name
v.customize ["modifyvm", :id, "--memory", 512]
end
end
end
end

Note 你可以根据你环境的总内存情况调整每台VM的内存. 修改v.customize ["modifyvm", :id, "--memory", 512].默认为每台VM 512MB.

使用vagrant up命令,启动实验环境:

Note 第一次启动vagrant会从网络下载VM镜像, 所以速度较慢. 请耐心等待:)

1
2
3
4
5
6
7
C:\Users\xiangxu\vagrantVM\docker-workshop>vagrant up
Bringing machine 'node01' up with 'virtualbox' provider...
Bringing machine 'node02' up with 'virtualbox' provider...
Bringing machine 'node03' up with 'virtualbox' provider...
Bringing machine 'node04' up with 'virtualbox' provider...
Bringing machine 'node05' up with 'virtualbox' provider...
....

验证VM启动状态

1
2
3
4
5
6
7
8
C:\Users\xiangxu\vagrantVM\docker-workshop>vagrant status
Current machine states:

node01 running (virtualbox)
node02 running (virtualbox)
node03 running (virtualbox)
node04 running (virtualbox)
node05 running (virtualbox)

ssh登录实验环境VM, 两种方法:

  • 方法一:
    使用vagrant ssh <nodename>命令直接在window终端登录VM:

    1
    2
    C:\Users\xiangxu\vagrantVM\docker-workshop> vagrant ssh node01
    [vagrant@node01 ~]$
  • 方法二:
    查看ssh配置 使用ssh客户端登录, 使用vagrant ssh-config <nodename>查看每台VM的登录配置.
    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    C:\Users\xiangxu\vagrantVM\docker-workshop> vagrant ssh-config node01
    Host node01
    HostName 127.0.0.1
    User vagrant
    Port 2222
    UserKnownHostsFile /dev/null
    StrictHostKeyChecking no
    PasswordAuthentication no
    IdentityFile C:/Users/xiangxu/.vagrant.d/insecure_private_key
    IdentitiesOnly yes
    LogLevel FATAL

如此, 你可以使用任意ssh客户端, 如xshell使用 vagrant用户, 密钥C:/Users/xxx/.vagrant.d/insecure_private_key 登录node01. ip 可以使用192.168.33.101.也可以使用配置文件的 127.0.0.12222端口.

1.1.3 其他vagrant命令介绍

vagrant一些常用命令如下:

1
2
3
4
5
6
vagrant halt # 关闭VM
vagrant up # 启动VM
vagrant destroy #删除VM
vagrant status # VM 状态
# vagrant 帮助
vagrant -h

1.2 系统环境准备

准备系统环境, 配置host列表, 安装ntp, 配置所有节点密钥登录.
以下命令请在所有节点执行, 以node01 为例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#切换至root用户
[vagrant@node01 ~]$ sudo su -
#设置root用户密码
[root@node01 ~]# echo rootpassword | passwd --stdin root
#更新系统
[root@node01 ~]# yum update -y
# 安装ntp时间同步
[root@node01 ~]# yum install ntp -y
# 启动ntp服务, 配置开机启动
[root@node01 ~]# systemctl start ntpd
[root@node01 ~]# systemctl enable ntpd
# 配置hosts文件
[root@node01 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
192.168.33.101 node01
192.168.33.102 node02
192.168.33.103 node03
192.168.33.104 node04
192.168.33.105 node05

配置所以节密钥互信, 在node01可以免密码登录各节点,
只在node01上执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 生成sshkey
[root@node01 ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
cd:84:03:a2:06:df:e4:ca:d1:0d:73:80:48:cd:3f:28 root@node01
The key's randomart image is:
+--[ RSA 2048]----+
|ooo.*.o |
|.o.O * . . |
| = * . o . |
| E + o = |
| + . S o |
| |
| |
| |
| |
+-----------------+
# 发布sshkey到各个节点
[root@node01 ~]# ssh-copy-id node01
The authenticity of host 'node01 (192.168.33.101)' can't be established.
ECDSA key fingerprint is 3b:6f:e4:72:5e:72:f3:76:b9:d0:7e:0e:dd:83:9f:5a.
Are you sure you want to continue connecting (yes/no)? yes
...
root@node01's password: #输入root用户密码

Number of key(s) added: 1

Now try logging into the machine, with: "ssh 'node01'"
and check to make sure that only the key(s) you wanted were added.
[root@node01 ~]# ssh-copy-id node02
...
[root@node01 ~]# ssh-copy-id node03
...
[root@node01 ~]# ssh-copy-id node04
...
[root@node01 ~]# ssh-copy-id node05

测试密钥登录

1
2
3
4
5
6
[root@node01 ~]# for N in $(seq 1 5); do ssh node0$N hostname; done;
node01
node02
node03
node04
node05

1.3 安装docker 1.12

所有节点上安装docker 1.12:
以下命令请在所有节点上执行.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## 添加docker repo文件
[root@node01 ~]# tee /etc/yum.repos.d/docker.repo <<-'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF
## 安装 docker package
[root@node01 ~]# yum install docker-engine -y
## 启动docker
[root@node01 ~]# systemctl start docker
[root@node01 ~]# systemctl enable docker
## 检查docker版本
[root@node01 ~]# docker -v
Docker version 1.12.0, build 8eab29e

node01上安装docker-compose:

1
2
3
4
[root@node01 ~]# curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
[root@node01 ~]# chmod +x /usr/local/bin/docker-compose
[root@node01 ~]# docker-compose -v
docker-compose version 1.8.0, build f3628c7

1.4 运行docker币应用

1.4.1 下载docker币应用配置

你可以从github库中,得到所有关于这个workshop的文档和配置.
https://github.com/jpetazzo/orchestration-workshop

node01上下载这个github库.

1
2
3
4
5
6
7
8
9
10
11
#安装git
[root@node01 ~]# yum install git -y
# 克隆git 库
[root@node01 ~]# git clone https://github.com/jpetazzo/orchestration-workshop.git
# 进入dockercoins应用目录
[root@node01 ~]# cd orchestration-workshop/dockercoins
# 可以看到 dockercoins应用的`docker-compose`文件
[root@node01 dockercoins]# ls
docker-compose.yml docker-compose.yml-portmap hasher webui
docker-compose.yml-ambassador docker-compose.yml-scaled-rng ports.yml worker
docker-compose.yml-logging docker-compose.yml-v2 rng

Note 该github库中包含了, workshop的详细文档和配置. 部署脚本, PPT等.有基础的同学可以自己看看.

1.4.2 docker币应用介绍

查看 docker币的docker-compose文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@node01 dockercoins]# cat docker-compose.yml
version: "2"

services:
rng:
build: rng
ports:
- "8001:80"

hasher:
build: hasher
ports:
- "8002:80"

webui:
build: webui
ports:
- "8000:80"
volumes:
- "./webui/files/:/files/"

redis:
image: redis

worker:
build: worker

docker币应用一共有4个服务. 分别对应4个目录rng, hasher, webui, worker:
详情可进入对应目录查看Dockerfile.

  • rng是一个web service 生成随机的bytes数据.
  • hasher是一个计算POST 数据hash值的web service.
  • worker作为工作节点 后台调用rng 生成随机bytes数据, 然后将数据post到hasher服务计算hash值, 如果hashe值开头为0.则产生一个docker币.并将docker币保存到redis数据库中.
  • webui一个web接口来监控整个系统.读取redis数据库中的docker币数量

1.4.3 运行docker币应用

你可以直接使用如下命令运行docker币应用,docker-compose会自动pull镜像和build镜像 然后启动应用:

1
[root@node01 dockercoins]# docker-compose up

由于某些你懂的原因, 可能导致你的网络连接docker hub的时候不稳定, 导致docker pull镜像失败.
你也可以先把需要的镜像 pull下来.

Note 如果pull镜像的时候报网络连接错误, 可以反复执行多试几遍.

1
2
3
4
5
6
7
8
9
10
11
[root@node01 dockercoins]# docker pull python:alpine
[root@node01 dockercoins]# docker pull ruby:alpine
[root@node01 dockercoins]# docker pull redis
[root@node01 dockercoins]# docker pull node:4-slim
# 检查所需的images
[root@node01 dockercoins]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
python alpine 0298711f3095 12 days ago 73.04 MB
node 4-slim 6585f7b67838 4 weeks ago 207.9 MB
ruby alpine c872d09a2f2e 5 weeks ago 126 MB
redis latest 4465e4bcad80 6 weeks ago 185.7 MB

手工Build docker币应用镜像.

1
2
3
4
5
6
7
8
9
10
11
[root@node01 dockercoins]# docker-compose build
...
# 根据每个应用目录下的Dockerfile build应用镜像, 需要一会儿....
# build完成后, 你可以使用 docker image命令查看镜像
[root@node01 dockercoins]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
dockercoins_webui latest c2d09bb0c442 About a minute ago 212.2 MB
dockercoins_rng latest ff1d886f2fa3 2 minutes ago 83.53 MB
dockercoins_hasher latest 9d361dbaf589 2 minutes ago 310.6 MB
dockercoins_worker latest bef5d2dc4bd0 8 minutes ago 80.5 MB
...

启动 docker币应用:

1
[root@node01 dockercoins]# docker-compose up

启动以后你可以看见应用日志, 一直滚动…
使用Control+C停止应用. 我们使用-d参数后台启动应用.

1
2
3
4
5
6
[root@node01 dockercoins]# docker-compose up -d
Starting dockercoins_rng_1
Starting dockercoins_worker_1
Starting dockercoins_hasher_1
Starting dockercoins_redis_1
Starting dockercoins_webui_1

查看应用运行情况:

1
2
3
4
5
6
7
8
[root@node01 dockercoins]# docker-compose ps
Name Command State Ports
------------------------------------------------------------------------------------
dockercoins_hasher_1 ruby hasher.rb Up 0.0.0.0:8002->80/tcp
dockercoins_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
dockercoins_rng_1 python rng.py Up 0.0.0.0:8001->80/tcp
dockercoins_webui_1 node webui.js Up 0.0.0.0:8000->80/tcp
dockercoins_worker_1 python worker.py Up

查看应用日志:

1
2
3
[root@node01 dockercoins]# docker-compose logs
# 滚动输出应用日志, 每个容器输出最新10行
[root@node01 dockercoins]# docker-compose logs --tail 10 --follow

TIP 日志滚动输出的时候按Ctrl+S暂停, Ctrl+Q 继续输出, Ctrl+C退出.

访问webui监控程序运行:
从上面docker-compose ps命令, 我们可以看到应用的port映射.

浏览器访问 http://192.168.33.101:8000 可以打开应用的webui.
你会看到下面的页面:
webui page

从webui可以看到我们的docker币程序每秒可以产生约4个docker币.

1.5 Scale up向上扩展应用

OK~ 现在我们的docker币应用已经工作了 哈哈, 每秒产生4个docker币.
可以如果我们想产生更多的docker币怎么办.

前面我们已经介绍了docker币应用的架构, worker服务负责产生docker币.所以我们可以尝试scale upworker服务.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@node01 dockercoins]# docker-compose ps
# 可以看到我们现在只有一个worker服务
# 增加到2个 worker服务, 使用如下命令
[root@node01 dockercoins]# docker-compose scale worker=2
Creating and starting dockercoins_worker_2 ... done
[root@node01 dockercoins]# docker-compose ps
Name Command State Ports
------------------------------------------------------------------------------------
dockercoins_hasher_1 ruby hasher.rb Up 0.0.0.0:8002->80/tcp
dockercoins_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
dockercoins_rng_1 python rng.py Up 0.0.0.0:8001->80/tcp
dockercoins_webui_1 node webui.js Up 0.0.0.0:8000->80/tcp
dockercoins_worker_1 python worker.py Up
dockercoins_worker_2 python worker.py Up

好了, 现在我们有2个worker服务了.
在看webui浏览器界面, 发现现在我们每秒可以产出8个docker币了.
webui 1

WoW~ 这么简单, 让我们再来更多的docker币吧.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 再增加8个worker节点.
[root@node01 dockercoins]# docker-compose scale worker=10
[root@node01 dockercoins]# docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------
dockercoins_hasher_1 ruby hasher.rb Up 0.0.0.0:8002->80/tcp
dockercoins_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
dockercoins_rng_1 python rng.py Up 0.0.0.0:8001->80/tcp
dockercoins_webui_1 node webui.js Up 0.0.0.0:8000->80/tcp
dockercoins_worker_1 python worker.py Up
dockercoins_worker_10 python worker.py Up
dockercoins_worker_2 python worker.py Up
dockercoins_worker_3 python worker.py Up
dockercoins_worker_4 python worker.py Up
dockercoins_worker_5 python worker.py Up
dockercoins_worker_6 python worker.py Up
dockercoins_worker_7 python worker.py Up
dockercoins_worker_8 python worker.py Up
dockercoins_worker_9 python worker.py Up

可以看到我们现在有10个worker节点了, 那理论上来说我们可以每秒产生40个docker币.
看看我们的webui
webui 10
注意, 每秒产生的docker币数量.
OMG~ 为什么现在每秒至产生约10个docker币.

1.6 程序的瓶颈

接上面的, 我们也看到了 2个worker节点的时候每秒可以生8个docker币, 可以 10个worker节点每秒也才产生10个docker币.

为什么呢?

  1. 因为我们现在所有的应用都是跑在一台服务器上node01, 10个worker node造成服务器资源竞争, 所以增加worker节点也不能改善性能.

  2. 我们可以简单测试下rng,hasher节点的负载, 因为我们有10个worker节点, 只有一个rng, hasher节点, 所以瓶颈有可能发生在rnghasher节点

使用httping工具, 测试rng, hasher延迟.

1
2
3
## 安装 httping
[root@node01 dockercoins]# yum install epel-release -y
[root@node01 dockercoins]# yum install httping -y

测试rng延迟, docker-compose ps命令可以看到rng应用映射到本地8001端口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@node01 dockercoins]# httping -c 10 localhost:8001
PING localhost:8001 (/):
connected to 127.0.0.1:8001 (159 bytes), seq=0 time=782.49 ms
connected to 127.0.0.1:8001 (159 bytes), seq=1 time=821.42 ms
connected to 127.0.0.1:8001 (159 bytes), seq=2 time=775.34 ms
connected to 127.0.0.1:8001 (159 bytes), seq=3 time=697.87 ms
connected to 127.0.0.1:8001 (159 bytes), seq=4 time=866.71 ms
connected to 127.0.0.1:8001 (159 bytes), seq=5 time=838.10 ms
connected to 127.0.0.1:8001 (159 bytes), seq=6 time=775.62 ms
connected to 127.0.0.1:8001 (159 bytes), seq=7 time=770.14 ms
connected to 127.0.0.1:8001 (159 bytes), seq=8 time=758.90 ms
connected to 127.0.0.1:8001 (159 bytes), seq=9 time=776.65 ms
--- http://localhost:8001/ ping statistics ---
10 connects, 10 ok, 0.00% failed, time 17872ms
round-trip min/avg/max = 697.9/786.3/866.7 ms

测试hasher延迟, 8002端口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@node01 dockercoins]# httping -c 10 localhost:8002
PING localhost:8002 (/):
connected to 127.0.0.1:8002 (210 bytes), seq=0 time= 3.85 ms
connected to 127.0.0.1:8002 (210 bytes), seq=1 time= 24.33 ms
connected to 127.0.0.1:8002 (210 bytes), seq=2 time= 9.28 ms
connected to 127.0.0.1:8002 (210 bytes), seq=3 time= 4.11 ms
connected to 127.0.0.1:8002 (210 bytes), seq=4 time= 1.94 ms
connected to 127.0.0.1:8002 (210 bytes), seq=5 time= 2.23 ms
connected to 127.0.0.1:8002 (210 bytes), seq=6 time= 1.28 ms
connected to 127.0.0.1:8002 (210 bytes), seq=7 time= 2.47 ms
connected to 127.0.0.1:8002 (210 bytes), seq=8 time= 2.41 ms
connected to 127.0.0.1:8002 (210 bytes), seq=9 time= 2.15 ms
--- http://localhost:8002/ ping statistics ---
10 connects, 10 ok, 0.00% failed, time 10062ms
round-trip min/avg/max = 1.3/5.4/24.3 ms

以上结果可以看出, rng节点延迟较大800ms+ , 所以瓶颈有可能出现在rng节点.

如何解决瓶颈?

  1. 建立docker集群环境, 是worker节点可以分布在不同虚拟机上运行.
  2. scale outrng节点. 建立多个rng节点消除瓶颈.

其实主要的性能问题在第二条, 上面两条差不多意思, 就是要scale out横向扩展我们的应用.
所以下一章, 我们会引入docker swarm集群, 介绍在docker1.12, 下如何快速搭建docker swarm集群.
然后, scale out我们的应用.

下一章开始之前, 清理现在的环境

我们开始下一章之前, 先要停止现在的docker币应用, 使用如下命令.

1
[root@node01 dockercoins]# docker-compose down