准备工作

  • 安装1.13或者更高版本的docker。
  • 如第三部分准备工作所述那样获取docker compose。
  • 安装Docker Compose。Mac和window的Docker桌面版已经安装好,可以直接使用。在Linux上直接安装即可。在没有Hyper-v的window10系统中,请使用Docker Toolbox。
  • 已阅读第一部分中的概念。
  • 通过第二部分学会了如何创建一个容器。
  • 确保已经向仓库中提交了firendlyhello镜像,我们在本文中将使用这个已分享的公共镜像。
  • 确保你的镜像已经部署成了一个容器。执行命令docker run -p 4000:80 username/repo:tag,使用你的用户名(username)、仓库名(repo)和标签名(tag)对应替换即可。然后使用http://localhost:4000进行访问
  • 一份第三部分中的docker-compose.yml副本。

引言

通过第三部分的学习,你应该知道如何将我们在第二部分中编写的应用如何以服务的形式运行,并且拓展5个实例进程。

在第四部分中,我们将会把应用部署到集群中,运行在多台机器上。我们把多台机器相互组合成的docker集群称为swarm,可以部署多容器、多机器的应用。

理解swarm集群

Swarm就是一组运行docker的服务器组建而成的集群。创建后,你可以像之前那样使用docker指令,不过是需要在swarm管理节点上执行。集群中的节点可以是物理机也可以是虚拟机,他们加入集群后,被称之为节点。

Swarm有多种容器执行策略,比如“最空节点(emptiest node)”——该节点的资源利用率最低。或者“全局(global)”——确保每个节点之运行一个指定容器。你可以在docker-compose.yml指定使用什么策略。

Swarm管理节点是唯一能执行docker指令,并且有权确认其他节点作为工作节点的节点。工作节点仅提供计算资源和能力,无权告诉其他节点能做什么和不能做什么。

到目前为止,你都是在单机上运行docker。其实docker也可以集群使用,这就是为什么要使用swarm。从现在开始,在swarm管理节点上执行的命令将在集群中运行,而不是仅在当前设备上运行。

配置swarm

一个swarm集群是有多个节点组成,可以是物理机也可以是虚拟机。其基本概念很简单:运行docker swarm init开启swarm模式并使其成为管理节点,运行docker swarm join将其他主机添加到该swarm集群中并成为工作节点。使用后面的指令,我们将了解到swarm在各种情况下的效果。我们当前使用虚拟机快速创建一个由两台设备组成的双机swarm集群。

创建一个集群

虚拟机在mac, Linux, window7 和window8平台上

你需要一个可以创建虚拟机的虚拟机管理程序,所以请在你的设备上安装VirtualBox。

注意:如果你使用的是像已经安装Hyper-v的window这样的系统,你不需要再安装VirtualBox。你可以直接使用Hyper-v即可,后面将介绍Hyper-V中的相关指令。如果你使用的是docker toolbox, 那么VirtualBox已经作为其一部分安装过了。

现在使用docker-machine在VirtualBox上创建两台虚拟机:

docker-machine create --driver virtualbox myvm1
docker-machine create --driver virtualbox myvm2

虚拟机在Windows 10/Hyper-V平台上

首先,创建虚拟机共享的虚拟交换机,以便虚拟机能够互通。

  1. 运行Hyper-V管理软件
  2. 单击右边菜单中的Virtual Switch Manager
  3. 点击创建External类型的虚拟交换机
  4. 为虚拟交换命名为“myswitch”,并勾选共享本地主机网络适配器的勾选框。

现在使用节点管理工具docker-machine创建两个虚拟机:

docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm1
docker-machine create -d hyperv --hyperv-virtual-switch "myswitch" myvm2

列举虚拟机并获取其IP地址

你创建了两个虚拟机,名字分别为:myvm1myvm2

使用下面命令列举虚拟机并获取其IP地址。

docker-machine ls

下面是该命令的一个输出示例:

$ docker-machine ls
NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v17.06.2-ce
myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v17.06.2-ce

初始化Swarm和添加节点

第一台设备将作为管理节点,用于执行命令和授权其他设备成为工作节点,第二台设备作为工作节点。

你可以使用docker-machine ssh向虚拟机发送指令,使用docker swarm init指定让myvm1成为管理节点的指令如下所示:

$ docker-machine ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>"
Swarm initialized: current node <node ID> is now a manager.

向swarm中添加节点可以使用下面的命令:

  docker swarm join \
  --token <token> \
  <myvm ip>:<port>

要向swarm中添加管理节点,需要执行docker swarm join-token manager,然后按照指示操作。

2377端口和2376端口

未设置docker swarm initdocker swarm join的端口时,docker一般默认使用2377端口作为管理端口。

docker-machine ls返回的机器IP地址包括端口2376,它是Docker守护程序端口。请勿使用此端口,否则您可能会遇到错误。

如果ssh指令错误,可以尝试一下--native-ssh选项

由于某些原因无法向swarm管理节点发送指令时,docker-machine还可以使用本地系统的ssh服务。只需要在使用ssh指令时添加--native-ssh选项即可:

docker-machine --native-ssh ssh myvm1 ...

正如你看到的那样,你需要在新节点中执行docker swarm join命令之前在某一个节点中执行docker swarm init指令。复制这个指令,通过docker-machine ssh命令发送到myvm2中,使其成为一个工作节点:

$ docker-machine ssh myvm2 "docker swarm join \
--token <token> \
<ip>:2377"

This node joined a swarm as a worker.

恭喜!你已经创建了第一个swarm集群!

在管理节点上运行docker node ls查看swarm中的所有节点:

$ docker-machine ssh myvm1 "docker node ls"
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
brtu9urxwfd5j0zrmkubhpkbd     myvm2               Ready               Active
rihwohkh3ph38fhillhhb84sk *   myvm1               Ready               Active              Leader

离开swarm

如果你想重新开始,可以使用docker swarm leave移除每一个节点。

在swarm集群中部署应用

最难的部分已经完成了,现在我们只需要安装第三部分中的那样再次进行部署应用即可。只需要注意只能在swarm管理端执行docker命令,工作节点仅提供计算资源。

配置一个Swarm管理端的docker-machineshell

到目前为止,我们都是在使用docker-machine ssh来包装docker命令。其实还有另一种方法,使用docker-machine env <machine>来与一个docker主机取得通信。这个方法在接下来更加使用,因为它允许你访问本地主机的docker-compose.yml文件,而不需要将其复制到其他地方。

键入docker-machine evn myvm1,然后进行复制粘贴并运行,使用下面最后提供的语句将配置发送给到swarm管理节点。

这个命令在Mac、Linux和window中所有不同,如下面的示例中所示:

在Linux和mac中

运行docker-machine env myvm1myvm1通信并进行相关配置。

$ docker-machine env myvm1
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/sam/.docker/machine/machines/myvm1"
export DOCKER_MACHINE_NAME="myvm1"
# Run this command to configure your shell:
# eval $(docker-machine env myvm1)

执行下面的命令将当前配置发送到myvm1节点中去:

eval $(docker-machine env myvm1)

执行docker-machine ls来确定myvm1处于激活状态,你应该得到类似下面的结果:

$ docker-machine ls
NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
myvm1   *        virtualbox   Running   tcp://192.168.99.100:2376           v17.06.2-ce
myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v17.06.2-ce

在window中

运行docker-machine env myvm1myvm1通信并进行相关配置。

PS C:\Users\sam\sandbox\get-started> docker-machine env myvm1
$Env:DOCKER_TLS_VERIFY = "1"
$Env:DOCKER_HOST = "tcp://192.168.203.207:2376"
$Env:DOCKER_CERT_PATH = "C:\Users\sam\.docker\machine\machines\myvm1"
$Env:DOCKER_MACHINE_NAME = "myvm1"
$Env:COMPOSE_CONVERT_WINDOWS_PATHS = "true"
# Run this command to configure your shell:
# & "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression

执行下面的命令将当前配置发送到myvm1节点中去:

& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env myvm1 | Invoke-Expression

执行docker-machine ls来确定myvm1处于激活状态,你应该得到类似下面的结果:

PS C:PATH> docker-machine ls
NAME    ACTIVE   DRIVER   STATE     URL                          SWARM   DOCKER        ERRORS
myvm1   *        hyperv   Running   tcp://192.168.203.207:2376           v17.06.2-ce
myvm2   -        hyperv   Running   tcp://192.168.200.181:2376           v17.06.2-ce

在swarm管理端部署应用

现在有了拥有swarm管理权限的myvm1节点,你可以使用第三部分中学习到的docker stack deploy指令和你本地的docker-complse.yml文件副本进行部署应用。这个指令可能需要一些时间才能完成,应用也需要一些时间之后才能访问。可以在swarm管理使用docker service ps <service_name>来确定所有服务都已经完成重部署。

你通过docker-machine连接到了myvm1中,你任然可以访问本地主机的文件。确保你当前与第三部分创建的docker-compose.yml文件在相同的目录中。

只需要像前面那样再myvm1中运行下面的指令进行部署应用。

docker stack deploy -c docker-compose.yml getstartedlab

然后这个应用部署再swarm集群中了。

注意:如果你的镜像存储在一个私有的注册附中,你需要先使用 docker login <your-registry>指令进行登陆,然后再上面的指令中添加--with-registry-auth选项。例如:

docker login registry.example.com

docker stack deploy --with-registry-auth -c docker-compose.yml getstartedlab

这会使用加密的WAL日志将登录令牌从本地客户端传递到部署服务的swarm节点。有了这些信息,节点就可以登录注册服务并拉取镜像。

Now you can use the same docker commands you used in part 3. Only this time notice that the services (and associated containers) have been distributed between both myvm1 and myvm2.

$ docker stack ps getstartedlab

ID            NAME                  IMAGE                   NODE   DESIRED STATE
jq2g3qp8nzwx  getstartedlab_web.1   gordon/get-started:part2  myvm1  Running
88wgshobzoxl  getstartedlab_web.2   gordon/get-started:part2  myvm2  Running
vbb1qbkb0o2z  getstartedlab_web.3   gordon/get-started:part2  myvm2  Running
ghii74p9budx  getstartedlab_web.4   gordon/get-started:part2  myvm1  Running
0prmarhavs87  getstartedlab_web.5   gordon/get-started:part2  myvm2  Running

使用docker-machine envdocker-machine ssh连接虚拟机:

要让shell中可以连接像myvm2这样的不同的虚拟机,只需要重新运行相同(连接myvm2)或不同(连接其他虚拟机,需更换相应节点名)docker-machine env myvm2即可。如果没有运行shell或者打开新的shell终端,需要重新执行命令。可以使用docker-machine ls命令列出节点,并查看其状态与ip地址,可以随意选择一个连入(如果有)。更多信息请查看docker machine的入门主题。

另外,可以使用docker-machine ssh <machine> "<command>"指令直接将命令写入到指定节点中去,不过该命令无法直接访问本地主机的文件。

在Mac和Linux中,可以使用docker-machine scp <file> <machine>:~命令进行跨域复制文件,但是在window中需要使用Git Bash类Linux终端才行。

本教程演示了docker-machine sshdocker-machine env,因为它们可以通过docker-machine CLI在所有平台上使用。

访问集群

你可以通过myvm1myvm2中任意一个的IP来访问你的应用。

您创建的网络在它们之间共享并进行负载平衡。 运行docker-machine ls获取虚拟机的IP地址,并在浏览器上访问其中任何一个,点击刷新(或使用curl指令)。

Hello World in browser

5个容器ID随机循环出现证明了他们进行了负载均衡。

两个IP均能正常访问是因为在每个节点的入口都运行着相同的网络路由机制。这确保了swarm集群中的任意的点对应端口指向的均是相同服务,无论容器实际运行在哪个节点上。下图为三个节点的8080端口均指向一个名为my-web的服务的网络路由示意图:

routing mesh diagram

有连接异常?

有一点要注意:在使用swarm集群网络入口路由时,需要在节点激活swarm模式之前打开以下端口:

  • 端口 7946 TCP/UDP 用于容器的发现与注册。
  • 端口 4789 UDP 用于绑定集群入口路由。

应用的迭代和拓展

现在你可以完成在第二部分和第三部中所学的一切内容。

通过改变docker-compose.yml文件来拓展你的应用。

通过编辑相关代码来改变应用行为,然后重新构建、上传新的镜像。(通过最近收获的知识来完成构建、发布镜像等操作)

不管什么情况,只需要重新运行docker stack deploy就可以完成这些改变。

你可以使用在myvm2上执行的docker swarm join指令将任何机器加入到swarm集群中,物理机、虚拟机均可,并将对应的容量添加到集群中。

清除和重启

堆栈和swarm

可以通过docker stack rm指令移除堆栈。例如:

docker stack rm getstartedlab

Swarm是保留还是移除?

在某些时候你不想保留Swarm,你可以通过在worker上执行ocker-machine ssh myvm2 "docker swarm leave" 和在manager上执行docker-machine ssh myvm1 "docker swarm leave --force"进行移除集群操作。但你在第五部分的时候需要这个集群,所以还先保留着。

释放docker-machineshell环境

你可以通过下面指令从docker-machine主机环境中切换出来:
Mac或者Linux命令:

eval $(docker-machine env -u)

window命令:

& "C:\Program Files\Docker\Docker\Resources\bin\docker-machine.exe" env -u | Invoke-Expression

docker命令行工具(比如window和mac的docker桌面端)中,这个指令用于从docker-machine创建的虚拟机中解放出来,然后shell终端还可以做其他的事情。可以在docker主机主题中了解更多相关知识。

重启docker机器

如果你关闭了本地主机,docker机器也会停止运行。可以运行docker-machine ls指令查看机器状态。

$ docker-machine ls
NAME    ACTIVE   DRIVER       STATE     URL   SWARM   DOCKER    ERRORS
myvm1   -        virtualbox   Stopped                 Unknown
myvm2   -        virtualbox   Stopped                 Unknown

重启一台已停止的机器:

docker-machine start <machine-name>

例如:

$ docker-machine start myvm1
Starting "myvm1"...
(myvm1) Check network to re-create if needed...
(myvm1) Waiting for an IP...
Machine "myvm1" was started.
Waiting for SSH to be available...
Detecting the provisioner...
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.

$ docker-machine start myvm2
Starting "myvm2"...
(myvm2) Check network to re-create if needed...
(myvm2) Waiting for an IP...
Machine "myvm2" was started.
Waiting for SSH to be available...
Detecting the provisioner...
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.

——————————————————————————
行路不知花开处,蓦然回首芷兰香。