代码级操作指南 | 如何在Docker Swarm中运行服务

最新版本Docker Engine v1.12中,包含了多项与Docker Swarm紧密相关的功能变更。在今天的文章中,数人云将和大家探讨如何利用Docker的Swarm Mode进行服务部署。

在Ubuntu 16.04之上激活Swarm Mode

在向Docker Engine Swarm部署一项服务之前,我们首先需要设置一套Swarm集群。由于本文旨在展示1.12版本中的各项新增功能,因此我们应当安装Docker Engine的最新版本。

以下操作指南适用于Ubuntu 16.04环境下的Docker Engine安装。对于其它版本及平台,大家可以查看Docker的官方安装指南。

设置Docker Apt库

在安装过程中,我们需要使用Ubuntu提供的标准安装方法,即Apt软件包管理器。由于我们将安装Docker Engine的最新版本,因此需要配置Apt以从Docker官方Apt库处获取docker-engine软件包,而非直接使用系统预配置库。

添加Docker公钥

配置Apt以使用新库的第一步,需要使用apt-key命令将该库的公钥添加至Apt缓存当中。

# apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

以上apt-key命令会向p80.pool.sks-keyservers.net密钥服务器请求该特定密钥(58118E89F3A912897C070ADBF76221572C52609D)。该公钥将被用于验证下载自此新库的全部软件包。

指定Docker的库位置

现在Docker公钥已经导入完成,我们可以配置Apt以使用Docker项目的库服务器。要实现这一目标,我们需要在/etc/apt/sources.list.d/目录内添加以下条目。

# echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" >> /etc/apt/sources.list.d/docker.list

在刷新Apt软件包缓存时,Apt会对sources.list.d/目录内的全部文件进行搜索,旨在查找新的软件包库。以上命令会创建(若尚不存在)一个名为docker.list的新文件,并将对应条目添加至apt.dockerproject.org库当中。

更新Apt的软件包缓存

要更新Apt的软件包缓存,我们可以运行apt-get命令并添加update选项。 # apt-get update 如此一来,Apt会通过重新读取全部配置文件以填充各库列表,其中包括我们刚刚添加的配置文件。另外,其还将查询这些库以缓存一份可用软件包清单。

安装linux-image-extra的先决条件

在安装Docker Engine之前,我们还需要安装另一软件包。linux-image-extra软件包是一套特定内核软件包,负责帮助Ubuntu系统支持aufs存储驱动器。此驱动程序由Docker用于向容器内安装各分卷。

要安装此软件包,我们需要再次使用apt-get命令,但这一次配合install选项。

# apt-get install linux-image-extra-$(uname -r)

在此apt-get命令当中,$(uname -r)部分或者命令将返回当前运行中内核的实际版本。任何对此系统内核的更新都应包含对应linux-image-extra软件包版本的安装。如果此软件包未进行更新,则Docker可能会在安装分卷时出现某些问题。

安装Docker Engine

Apt配置完成且linux-image-extra安装完毕之后,我们现在可以着手安装Docker Engine。要完成这项任务,我们需要再次使用apt-get命令配合install选项,从而安装docker-engine软件包。

apt-get install docker-engine

在这里,我们应当已经拥有Docker Engine v1.12.0或者更新版本。要验证是否具备正确版本,可以执行docker命令配合version选项。

# docker versionClient: Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Jul 28 22:11:10 2016
OS/Arch: linux/amd64
Server:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Jul 28 22:11:10 2016
OS/Arch: linux/amd64

在这里,我们可以看到服务器与客户端版本皆为1.12.0。下面开始创建Swarm。

创建 Docker Swarm

在本篇教程中,我们将跨越多台设备执行此项任务。为了清晰起见,这里我们在命令示例中包含主机名称。

我们首先建立一套双节点Swarm集群,其中的两个节点皆应利用上述操作完成Docker Engine的安装。

在创建该Swarm集群时,我们需要指定一个节点管理器。在本示例中,我们将使用名称为swarm-01的主机作为节点管理器。要让swarm-01成为节点管理器,我们需要首先在swarm-01之上执行一条命令以创建Swarm集群。这里我们使用docker命令配合swarm init选项。

root@swarm-01:~# docker swarm init --advertise-addr 10.0.0.1 Swarm initialized: current node (awwiap1z5vtxponawdqndl0e7) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-51pzs5ax8dmp3h0ic72m9wq9vtagevp1ncrgik115qwo058ie6-3fokbd3onl2i8r7dowtlwh7kb \
10.0.0.1:237 7To add a manager to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-51pzs5ax8dmp3h0ic72m9wq9vtagevp1ncrgik115qwo058ie6-bwex7fd4u5aov4naa5trcxs34 \
10.0.0.1:2377

利用以上命令外加swarm init,我们还利用10.0.0.1值指定了--advertise-addr标记。Swarm节点管理器将利用该IP声明该Swarm集群服务。尽管该地址属于专有IP,但为了让各节点顺利加入此Swarm,各节点都需要能够通过此IP在端口2377上接入此节点管理。

在运行docker swarm init命令之后,我们会看到swarm-01已经被分配了一个节点名称(awwiap1z5vtxponawdqndl0e7)并成为此Swarm集群中的管理器。其输出结果分为两条命令:其一用于向该Swarm内添加一个工作节点,其二为向该Swarm内添加另一节点管理器。

Docker Swarm Mode能够支持多套节点管理器,不过其仍会选择其中之一作为主节点管理器,并由其负责Swarm集群内部的编排工作。

向Swarm集群内添加一个工作节点

在Swarm集群创建完成后,现在我们可以利用由Swarm创建给出的输出结果,通过docker命令添加新的工作节点。

root@swarm-02:~# docker swarm join \ 》 --token SWMTKN-1-51pzs5ax8dmp3h0ic72m9wq9vtagevp1ncrgik115qwo058ie6-3fokbd3onl2i8r7dowtlwh7kb \ 》 10.0.0.1:2377 This node joined a swarm as a worker.

在本示例中,我们将swarm-02添加至Swarm当中作为工作节点。工作节点属于Swarm集群当中的一个成员,其角色在于运行各类任务; 而其中的任务则表现为容器形式。在另一方面,节点管理器则负责管理任务(容器)编排并维护Swarm集群本身。

除了节点管理职责之外,节点管理器本身亦作为工作节点存在,意味着其同样能够为Swarm集群分担部分任务。

查看当前Swarm节点

在上述命令执行完毕之后,现在我们已经拥有了一套基本的双节点Swarm集群。下面我们执行docker命令与node ls选项以验证集群的当前状态。

root@swarm-01:~# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
13evr7hmiujjanbnu3n92dphk swarm-02.example.com Ready Active
awwiap1z5vtxponawdqndl0e7 * swarm-01.example.com Ready Active Leader

从此命令的输出结果中,我们可以看到swarm-01与swarm-02都处于Ready与Active状态。如此一来,现在我们即可进一步向此套Swarm集群部署服务了。

创建一项服务

在Docker Swarm Mode当中,服务属于一套长期运行的Docker容器,其可被部署在任意工作节点之上。另外,该服务亦允许任意Swarm集群内的远程系统或者容器进行接入与使用。

在本示例当中,我们将部署一项Redis服务。

部署一项复制服务

复制服务属于一项Docker Swarm服务,其中包含特定数量的运行副本。这些副本由特定Docker容器的多个实例所构成。在本示例中,每套副本都将表现为一个独特的Redis实例。

要创建新服务,我们使用docker命令并配合service create选项。以下命令将创建一项名为redis的服务,其拥有2套副本并在整套集群内通过6379端口进行发布。

root@swarm-01:~# docker service create --name redis --replicas 2 --publish 6379:6379 redis er238pvukeqdev10nfmh9q1kr

除了指定service create选项之外,我们还需要使用--name标记以命名redis服务,并由--replicas标记指定该服务应运行在2套不同节点之上。我们可以在两个节点中执行docker命令配合service ls选项以实现这一效果。

root@swarm-01:~# docker service ls ID NAME REPLICAS IMAGE COMMAND
er238pvukeqd redis 2/2 redis

在输出结果中,我们可以看到2套副本中的2套正处于运行当中。如果我们希望了解与这些任务相关的更多细节信息,则可运行docker命令配合service ps选项。

root@swarm-01:~# docker service ps redis ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
5lr10nbpy91csmc91cew5cul1 redis.1 redis swarm-02.example.com Running Running 40 minutes ago
1t77jsgo1qajxxdekbenl4pgk redis.2 redis swarm-01.example.com Running Running 40 minutes ago

其中service ps选项将显示此特定服务的全部任务(容器)。在本示例中,我们可以看到redis服务拥有一项任务(容器),且同时运行在两个Swarm节点之上。

接入此 Redis服务

由于我们已经证实此服务开始运行,因此接下来可利用redis-cli客户端立足于远程系统接入该项服务。 vagrant@vagrant:~$ redis-cli -h swarm-01.example.com -p 6379
swarm-01.example.com:6379>
通过以上连接,我们能够成功接入该redis服务。这意味着我们的服务已经启动并处于可用状态。

DockerSwarm如何公布服务

在创建此redis服务时,我们使用了--publish标记并配合docker service create命令。此标记用于告知Docker公布6379端口作为该redis服务的可用端口。

而当Docker为某项服务发布端口时,其会在Swarm集群内的全部节点之上监听该端口。当有流量经过该端口,则对应流量会被路由至负责运行该服务的对应容器。尽管此概念在全部节点皆运行单一服务容器时能够以标准化方式实现,但在面对多套副本时则会发生一些变化。

要了解其工作原理,让我们向Swarm集群中添加第三个工作节点。

将第三个工作节点添加进来

要添加另一工作节点,我们只需要重复文章开头处的安装与设置步骤即可。这里直接跳过重复部分,而后再次运行docker命令以检查集群中的当前运行状态。

root@swarm-01:~# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
13evr7hmiujjanbnu3n92dphk swarm-02.example.com Ready Active
awwiap1z5vtxponawdqndl0e7 * swarm-01.example.com Ready Active Leader
e4ymm89082ooms0gs3iyn8vtl swarm-03.example.com Ready Active

现在可以看到我们的集群由三个节点构成: swarm-01
swarm-02
swarm-03
当我们利用两套副本创建服务时,其会在swarm-01与swarm-02之上创建任务(容器)。下面看看添加另一工作节点会对此产生何种影响。

root@swarm-01:~# docker service ps redis ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
5lr10nbpy91csmc91cew5cul1 redis.1 redis swarm-02.example.com Running Running 55 minutes ago
1t77jsgo1qajxxdekbenl4pgk redis.2 redis swarm-01.example.com Running Running 55 minutes ago

利用复制服务,Docker Swarm能够确保每套指定副本皆拥有一个任务(容器)处于运行当中。在创建redis服务时,我们指定其中应存在2套副本。这意味着即使添加第三个节点,Docker仍然不会在该节点上建立新的任务。

这样一来,我们就面对着一种有趣的状态:我们的服务运行在3个Swarm节点中的2个之上。在非Swarm环境下,这意味着redis服务将无法接入第三个Swarm节点。然而由于Swarm Mode的存在,这种接入关系仍可正常实现。

在未运行任务的工作节点上接入服务

之前我们已经探讨了Docker如何公布一个服务端口,当时提到该端口会被公布至Swarm内的全部节点。而真正值得关注的是,当我们接入某个并未运行服务相关容器(任务)的工作节点时,会发生怎样的情况。

下面我们看看经由redis公开端口接入swarm-03,会有怎样的结果。

vagrant@vagrant:~$ redis-cli -h swarm-03.example.com -p 6379 swarm-03.example.com:6379>

有趣的是,我们的连接尝试仍然能够成功。之所以成功,是因为尽管swarm-03并没有运行任何redis容器,但Docker内部会将我们的redis服务流量重新路由至运行有redis容器的工作节点。

Docker将此称为入口负载均衡。其工作原理在于,全部工作节点都会监听指向公开服务端口的连接。当外部系统调用某项服务时,接收节点会接受流量并利用Docker提供的内部DNS服务对其进行负载均衡。

所以即使我们将Swarm集群扩展至包含100个工作节点,服务的最终用户仍然能够轻松接入任意工作节点。他们的请求随后会被重新定向至两套运行有redis服务任务(容器)的Docker主机之一。

这种重新路由与负载均衡机制对于最终用户是完全透明的,且全程发生于Swarm集群内部。

实现全局服务

现在我们设置的redis服务已经运行有2套副本,意味着其运行在3个节点中的2个之上。

如果希望我们的redis服务在每个工作节点上皆拥有实例,我们可以将服务的必要副本数量由2修改为3。这意味着只要添加或者除去工作节点,我们就需要对副本数量做出调整。

当然,我们也可以将服务转化为全局服务从而自动完成上述调整。Docker Swarm Mode中的全局服务用于创建能够自动在各个工作节点上拥有运行任务的服务。这项机制适用于Redis等需要在内部由其它服务使用的常规服务。

要完成这一调整,我们需要将自己的redis服务重新创建为一项全局服务。

root@swarm-01:~# docker service create --name redis --mode global --publish 6379:6379 redis 5o8m338zmsped0cmqe0guh2to

以上命令同样使用docker service create命令进行复制服务的创建,不过区别在于--mode标记之后添加了global值。

服务创建完成之后,可以看到Docker如何一次执行docker命令与service ps选项将该服务的各项任务进行分发。

root@swarm-01:~# docker service ps redis ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
27s6q5yvmyjvty8jvp5k067ul redis redis swarm-03.example.com Running Running 26 seconds ago
2xohhkqvlw7969qj6j0ca70xx \_ redis redis swarm-02.example.com Running Running 38 seconds ago
22wrdkun5f5t9lku6sbprqi1k \_ redis redis swarm-01.example.com Running Running 38 seconds ago

现在该服务已经被创建为全局服务,接下来Swarm集群内的各个节点皆将运行与之相关的任务(容器)。

总结

在本篇文章中,我们不仅完成了Docker Engine的安装,同时亦设置了一套Swarm集群,部署了复制服务而后创建了全局服务。

在将Docker Swarm Mode服务与Kubernetes服务进行比较时,我们发现Swarm Mode服务的设置与创建更为简便易行。如果大家需要使用Kubernetes的“服务”功能但又用不到其它机制,那么Docker Swarm Mode可能是更为理想的便捷选项。

(文章转自:CODESHIP,作者 Ben Cane)