docker-swarm 教程:部署篇

docker-swarm 教程:部署篇

什么是 docker swarm ?

Swarm是Docker公司推出的用来管理docker集群的平台,几乎全部用GO语言来完成的开发的, 它是将一群 Docker宿主机变成一个单一的虚拟主机,Swarm使用标准的Docker API接口作为其前端的访问入口,也就是各种形式的Docker 均可以直接与Swarm通信,甚至Docker本身都可以很容易的与Swarm集成,这大大方便了用户将原本基于单节点的系统移植到Swarm上,同时Swarm内置了对Docker网络插件的支持,用户也很容易的部署跨主机的容器集群服务。

机器准备

我们准备三台机器以及并划分ip:

  • Manager: Manager: 172.16.95.137
  • Node1: 172.16.95.138
  • Node2: 172.16.95.139

1 、manager节点初始化swarm

docker swarm init --advertise-addr 172.16.95.137

输出:

bash 复制代码
docker swarm init --advertise-addr 172.16.95.137                                                 
Swarm initialized: current node (57mvxsdn9qg2tq6aiz17et7ot) is now a manager.                                   
To add a worker to this swarm, run the following command:                                                       
    docker swarm join --token SWMTKN-1-544xekdg657cd57fecr4g43uxk7t5pjkvxkkcbitjnygxc0nyh-0f4o0ujh0vz4nm5gxcjh3y
wi3 172.16.95.137:2377                                                                                       
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. 

2 、运行docker info以查看集群的当前状态:

shell 复制代码
                                                                                       root@master:~# docker info                                                                                   
Client: Docker Engine - Community                                                                            
 Version:    24.0.5                                                                                           
 Context:    default                                                                                        
 Debug Mode: false                                                                                           
 Plugins:                                                                                                     
  buildx: Docker Buildx (Docker Inc.)                                                                         
    Version:  v0.11.2                                                                                         
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx                                                  
  compose: Docker Compose (Docker Inc.)                                                                      
    Version:  v2.20.2                                                                                         
    Path:     /usr/libexec/docker/cli-plugins/docker-compose                                                    
Server:                                                                                                      
 ......                                                                                    
 Live Restore Enabled: false

3 、查看节点状态

bash 复制代码
root@master:~# docker node ls                                                                                 
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION             
57mvxsdn9qg2tq6aiz17et7ot *   master     Ready     Active         Leader           24.0.5 

将节点添加到swarm集群中

使用管理器节点创建群后,我们就可以添加工作节点了。

  1. 打开一个终端,然后ssh进入要运行工作节点的机器。本教程使用名称master
  2. master节点运行docker swarm init输出生成的命令,在其他机器上执行一下命令并创建工作节点:
sh 复制代码
root@node1:~# docker swarm join --token SWMTKN-1-544xekdg657cd57fecr4g43uxk7t5pjkvxkkcbitjnygxc0nyh-0f4o0ujh0vz4nm5gxcjh3ywi3 172.16.95.137:2377
This node joined a swarm as a master.

如果我们忘记了命令, 则可以在 master 节点执行以下命令来查看相关命令:

sh 复制代码
root@master:~# docker swarm join-token worker                                                                 
To add a worker to this swarm, run the following command:                                                       
    docker swarm join --token SWMTKN-1-544xekdg657cd57fecr4g43uxk7t5pjkvxkkcbitjnygxc0nyh-0f4o0ujh0vz4nm5gxcjh3y
wi3 172.16.95.137:2377 

3 、在 node2 节点重复执行 node1 上面的命令

4、在master节点运行的机器,并运行docker node ls命令以查看工作节点:

sh 复制代码
root@master:~# docker node ls                                                                                
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION             
57mvxsdn9qg2tq6aiz17et7ot *   master     Ready     Active         Leader           24.0.5                     
lqv1e6oa2hle33ff4xgxfslz3     node1      Ready     Active                          24.0.5                     
wwb5rc9nypj1e98wwr5qw99ye     node2      Ready     Active                          24.0.5    

MANAGER列标识了集群中的管理器节点。此列node1node2中的空状态标识为worker节点。

将服务部署到swarm中

创建集群后,我们可以将服务部署到集群中。

  1. 打开一个终端,然后ssh进入运行管理器节点的机器。使用名为master的机器。

  2. 运行以下命令:

    bash 复制代码
     docker service create --replicas 1 --name helloworld alpine ping docker.com
    • docker service create命令创建服务。
    • --name标志为helloworld服务命名。
    • --replicas标志指定了1个运行实例的所需状态。
    • 参数alpine ping docker.com将服务定义为执行命令ping docker.com的Alpine Linux容器。
  3. 运行docker service ls以查看正在运行的服务列表:

检查集群上的服务

  1. 我们使用ssh进入运行master节点的机器。
  2. 运行docker service inspect --pretty <SERVICE-ID>,以易于阅读的格式显示有关服务的详细信息。

执行以下命令查看服务的详细信息

docker service inspect --pretty helloworld

如果需要输出 json 格式的内容,执行命令 docker service inspect helloworld

  1. 查看服务具体运行在那个节点,我们可以执行以下命令查看:helloworld 为我们要查看的服务名称

    docker service ps helloworld

从上图中我们可一看到 服务 helloworld 运行在节点 master 的机器中, 而服务 helloword 运行在 node1 的节点中.

helloworld 的服务运行在master的节点上,说明 master 节点也可以像 node 节点一样执行任务.

  1. 服务 helloword 运行在node1的节点上, 我们在 node1 的机器上执行命令 docker ps
sh 复制代码
root@node1:~# docker ps
CONTAINER ID   IMAGE           COMMAND             CREATED          STATUS          PORTS     NAMES
d3bbf47cf010   alpine:latest   "ping docker.com"   14 minutes ago   Up 14 minutes             helloword.1.nw2qt760d0tupaqqyhodtz2ko

可以看到在 node1 的机器上正在运行 helloword 的服务容器。

扩大集群中服务部署的规模

创建好集群我们就可以使用Docker CLI来扩展服务中的容器数量。在服务中运行的容器被称为"任务"。

  1. 我们使用ssh进入master节点的机器。

  2. 运行以下命令以更改在群中运行的服务的所需状态:

    helloworld 服务扩展为 5 个

sh 复制代码
docker service scale helloworld=5 
  1. 执行完成后我们查看服务具体运行在那个节点
sh 复制代码
docker service ps helloworld

可以看到,swarm 创建了 5 个新的任务,并扩展了总共 5 个的 docker 容器实例, 并分布在不同的节点上。

删除集群上运行的服务

如果我们部署的服务不需要了, 那么我们可以从集群中删除对应的服务。

1 、运行docker service rm helloworld以删除helloworld服务。

执行完命令后我们在执行 docker service ls 查看服务,服务已经被我们删除了。

2、我们可以执行命令 docker service inspect helloworld 来查看服务的信息

从图中执行的命令可以看出服务已经彻底被删除了。

3、当删除服务的命令执行完成后,任务容器也需要几秒钟才能清理完成,我们可以在节点上执行 docker ps 查看容器是否存在。

服务的滚动更新

在工作中我们需要经常的将我们的应用程序的新版本更新,以替换原有的老的版本的应用程序。在 Docker 的 swarm 中我们如何实现应用程序的滚动更新呢?

1 、比如我们需要部署新的一个 redis 服务到我们的swarm服务集群中。 并以10秒的更新延迟配置swarm。为了更直观的操作, 我们先部署一个较老版本的redis:6.0-alpine 服务,执行以下命令部署:

sh 复制代码
docker service create --replicas 3 --name redis --update-delay 10s redis:6.0-alpine

我们看到服务已经部署成功了。我们执行 docker service ls 结果显示已经将服务发布在所有的节点上了。

sh 复制代码
root@master:~# docker service ls   
ID             NAME      MODE         REPLICAS   IMAGE              PORTS                                     e2b2aoag8kr0   redis     replicated   3/3        redis:6.0-alpine                                             root@master:~#   

部署命令参数说明:

  • --replicas : 表示我们部署的服务数量是几个,--replicas 3 表示部署 3 个
  • --update-delay : 表示任务更新的时间延迟,我们可以将时间设置为 TsTm,,Th,比如:5m10s 表示延迟 5 分钟 10 秒。
  • --update-parallelism 表示在更新时候的最大任务数量, 默认情况下调度程序一次只更新一个任务。

默认情况下,当单个任务的更新返回RUNNING状态时,调度程序会安排另一个任务进行更新,直到所有任务更新。如果在更新期间的任何时候任务返回FAILED,调度程序将暂停更新。我们可以使用docker service createdocker service update--update-failure-action标志来控制行为。

2、我们查看部署的 redis 服务情况:

docker service inspect --pretty redis

3 、现在我将已经部署好的 redis 服务更新到一个较新的版本 redis:6.0.20-alpine,管理器根据UpdateConfig策略将更新应用于节点:

执行更新命令:docker service update --image redis:6.0.20-alpine redis

bash 复制代码
root@master:~# docker service update --image redis:6.0.20-alpine redis                                       
redis                                                                                                         
overall progress: 3 out of 3 tasks                                                                           
1/3: running   [==================================================>]                                         
2/3: running   [==================================================>]                                         
3/3: running   [==================================================>]                                         
verify: Service converged  

我们在来查看下服务详细信息:

image 已经变成了 redis:6.0.20-alpine , 已经更新成功.

我们在节点查看容器信息:

bash 复制代码
root@node2:~# docker ps                                                                                       
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS      NAMES 
f036280bde79   redis:6.0.20-alpine   "docker-entrypoint.s..."   2 minutes ago   Up 2 minutes   6379/tcp   redis.1.
01wlu8r8z0jxmb4c2s399a6qo 

同样,容器的镜像已经变成了 redis:6.0.20-alpine

调度程序更新说明:

  • 停止第一个任务。
  • 为停止的任务安排更新。
  • 启动更新任务的容器。
  • 如果任务的更新返回RUNNING,请等待指定的延迟期,然后开始下一个任务。
  • 如果在更新期间的任何时候,任务返回FAILED,请暂停更新。

4、 如果我们需要重启服务,可以执行重启服务的命令:

docker service update redis

bash 复制代码
root@master:~# docker service update redis                                                                   
redis                                                                                                         
overall progress: 3 out of 3 tasks                                                                           
1/3: running   [==================================================>]                                         
2/3: running   [==================================================>]                                         
3/3: running   [==================================================>]                                         
verify: Service converged 

5、查看更新的信息:docker service ps redis

停止Swarm集群上的一个节点

目前我们所有的节点都 ACTIVE 的状态运行的,master可以将任务分配给任何节点,所以所有的节点都可以接收到任务。

很多时候我们需要维护应用的时候,您需要将节点设置为DRAIN可用性。DRAIN状态的节点Maser 阻止此类型的节点接收新任务。这也意味着停止在节点上运行的任务,并在具有ACTIVE可用性的节点上启动副本任务。

注意: 将节点设置为DRAIN不会从该节点中删除独立容器,例如使用docker rundocker-compose up或Docker Engine API创建的容器。节点的状态,包括DRAIN,只影响节点调度群服务工作负载的能力。

1 、我们在 master 节点的机器上查看集群中节点的可用性:

bash 复制代码
root@master:~# docker node ls                                                                                 
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION             
57mvxsdn9qg2tq6aiz17et7ot *   master     Ready     Active         Leader           24.0.5                     
lqv1e6oa2hle33ff4xgxfslz3     node1      Ready     Active                          24.0.5                     
wwb5rc9nypj1e98wwr5qw99ye     node2      Ready     Active                          24.0.5   

查看任务分配的信息:

bash 复制代码
root@master:~# docker service ps redis                                                                       
ID             NAME          IMAGE                 NODE      DESIRED STATE   CURRENT STATE             ERROR     PORTS   
01wlu8r8z0jx   redis.1       redis:6.0.20-alpine   node2     Running         Running 16 minutes ago           
w630bshgl3ga    \_ redis.1   redis:6.0-alpine      node2     Shutdown        Shutdown 16 minutes ago         
mkm0o152ns3e   redis.2       redis:6.0.20-alpine   master    Running         Running 16 minutes ago           
sqmmetgn9mqy    \_ redis.2   redis:6.0-alpine      master    Shutdown        Shutdown 16 minutes ago         
ezwkml7u8gih   redis.3       redis:6.0.20-alpine   node1     Running         Running 16 minutes ago           
qgvbqgmcmu7i    \_ redis.3   redis:6.0-alpine      node1     Shutdown        Shutdown 17 minutes ago  

2 、运行docker node update --availability drain <NODE-ID>以耗尽已分配任务的节点:

docker node update --availability drain node2 我们将 node2 节点停掉,并查看当前服务以及节点信息:

从图中看到node2 的节点状态已经是 Drain 并且分配在 node2 上的服务状态已经是 Shutdown

管理器通过在具有 drain 状态的节点上结束任务,并在Active 状态的节点上创建新的任务来保持所需要的状态。

3 、启用节点的可用性

sh 复制代码
docker node update --availability active node2

如图所示, 节点 Node2 的状态 已经更新为 Active

当节点设置回Active可用性时,它可以接收新任务:

  • 在服务更新期间进行扩展
  • 在滚动更新期间
  • 将另一个节点设置为Drain可用性时
  • 当任务在另一个活动节点上失败时

使用swarm模式路由网格

Docker Engine集群模式可以轻松发布服务端口,以使其可用于集群之外的资源。所有节点都参与入口路由网格。路由网格使群中的每个节点能够接受群中运行的任何服务在已发布端口上的连接,即使节点上没有运行的任务。路由网格将所有传入的请求路由到可用节点上的已发布端口到活动容器。

要在集群中使用网络,在启用群模式之前,我们需要在群节点之间打开以下端口:

  • Port 7946 TCP/UDP 用于容器网络发现。
  • Port 4789 UDP 可为容器入口网络配置。

我们还必须在集群的节点和任何需要访问端口的外部资源(如外部负载平衡器)之间打开已发布的端口。

发布服务端口

创建服务时,使用--publish标识指定要对外发布端口。target用于指定容器内的端口,而published用于指定在路由网格上绑定的端口。如果没有配置published`端口,则每个服务任务都会绑定一个随机的高编号端口。

sh 复制代码
docker service create --replicas 3 --name redis --publish published=63790,target=6379 --update-delay 5s redis:6.0.20-alpine
sh 复制代码
root@master:~# docker service create --replicas 3 --name redis --publish published=63790,target=6379 --update-delay 5s re
dis:6.0.20-alpine                                                                                             
vmmg8v0zr26os5johw7187mla                                                                                     
overall progress: 3 out of 3 tasks                                                                           
1/3: running   [==================================================>]                                         
2/3: running   [==================================================>]                                         
3/3: running   [==================================================>]                                                     
verify: Service converged 
bash 复制代码
root@master:~# docker service ls                                                                             
ID             NAME      MODE         REPLICAS   IMAGE                 PORTS                                 
vmmg8v0zr26o   redis     replicated   3/3        redis:6.0.20-alpine   *:63790->6379/tcp 

如图我们已经看到了端口为 *:63790->6379/tcp

<PUBLISHED-PORT>swarm提供服务的端口。如果省略它,将绑定一个随机的高编号端口。<CONTAINER-PORT>是容器侦听的端口。此参数是必需的。

例如,以下命令将nginx容器中的端口80发布到群中任何节点的端口8080:

sh 复制代码
docker service create \
  --name web \
  --publish published=8080,target=80 \
  --replicas 2 \
  nginx

当访问任何节点上的端口8080时,Docker会将请求路由到活动容器。在群节点本身上,端口8080实际上可能没有绑定,但路由网格知道如何路由流量并防止任何端口冲突的发生。

路由网格在已发布的端口上监听分配给节点的任何IP地址。对于外部可路由的IP地址,该端口可从主机外部获得。对于所有其他IP地址,只能从主机内部访问。

部署完成后我们在宿主机上面访问对应的地址和端口:

如果所示,服务正常访问。并且我们访问部署了的服务节点都可以访问到 nginx 服务。

同样对于已经部署的 service,我们也可以重新给他发布端口:

sh 复制代码
 docker service update \
  --publish-add published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
  <SERVICE>

您可以使用docker service inspect来查看服务的已发布端口。例如:

sh 复制代码
docker service inspect --format="{{json .Endpoint.Spec.Ports}}" web
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080}]

输出显示来自容器的<CONTAINER-PORT>(标记为TargetPort)和<PUBLISHED-PORT>(标记为PublishedPort),其中节点侦听对服务的请求。

使用上面的命令我们查看部署的 nginx web 服务:

sh 复制代码
root@master:~# docker service inspect --format="{{json .Endpoint.Spec.Ports}}" web                                       
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080,"PublishMode":"ingress"}]  

仅为TCP或仅UDP发布端口

默认情况下,当您发布端口时,它是一个TCP端口。您可以专门发布UDP端口,而不是或除了TCP端口。当您同时发布TCP和UDP端口时,如果您省略协议说明符,该端口将作为TCP端口发布。如果您使用较长的语法(推荐),请将protocol密钥设置为tcpudp

仅限TCP

长语法:

sh 复制代码
docker service create --name dns-cache \
  --publish published=53,target=53 \
  dns-cache

简短的语法:

sh 复制代码
docker service create --name dns-cache \
  -p 53:53 \
  dns-cache

TCP和UDP

长语法:

sh 复制代码
docker service create --name dns-cache \
  --publish published=53,target=53 \
  --publish published=53,target=53,protocol=udp \
  dns-cache

简短的语法:

sh 复制代码
docker service create --name dns-cache \
  -p 53:53 \
  -p 53:53/udp \
  dns-cache

仅限UDP

长语法:

sh 复制代码
docker service create --name dns-cache \
  --publish published=53,target=53,protocol=udp \
  dns-cache

简短的语法:

sh 复制代码
docker service create --name dns-cache \
  -p 53:53/udp \
  dns-cache

绕过路由网格

我们可以绕过路由网格,这样当访问给定节点上的绑定端口时,我们总是访问该节点上运行的服务实例。这被称为host模式。这种情况下需要注意:

  • 如果访问未运行服务任务的节点,则该服务不会监听该端口。有可能什么都没有,或者访问到了一个完全不同的应用程序。
  • 如果希望在每个节点上运行多个服务任务(例如,当您有5个节点但运行10个副本时),则无法指定静态目标端口。要么允许Docker分配一个随机的高编号端口(通过关闭published端口),要么通过使用全局服务而不是复制服务,或使用放置约束,确保服务仅在给定节点上运行。

要绕过路由,必须使用长--publish服务并将mode设置为host。如果省略mode或将其设置为ingress,则使用路由网格。以下命令使用host模式并绕过路由网格创建全局服务。

sh 复制代码
 docker service create --name dns-cache \
  --publish published=53,target=53,protocol=udp,mode=host \
  --mode global \
  dns-cache

配置外部负载均衡器

我们可以为集群服务配置外部负载平衡器,要么与路由网格结合使用,要么根本不使用路由网格。

使用路由网格

我们可以配置外部负载均衡器将请求路由到集群服务。可以配置HAProxy来均衡对发布到端口8080nginx服务的请求。

上图的 来源于网络,仅供参考

在这种情况下,端口8080必须在负载平衡器和集群中的节点之间打开。集群节点可以在代理服务器可以访问的专用网络上,但不能公开访问。

可以配置负载平衡器,以均衡集群中每个节点之间的请求,即使节点上没有安排任务。例如,我们可以在/etc/haproxy/haproxy.cfg中拥有以下HAProxy配置:

sh 复制代码
global
        log /dev/log    local0
        log /dev/log    local1 notice
...snip...

# Configure HAProxy to listen on port 80
frontend http_front
   bind *:80
   stats uri /haproxy?stats
   default_backend http_back

# Configure HAProxy to route requests to swarm nodes on port 8080
backend http_back
   balance roundrobin
   server master 172.16.95.137:8080 check
   server node2 172.16.95.138:8080 check
   server node3 172.16.95.139:8080 check

当访问端口80上的HAProxy负载均衡器时,它会将请求转发到群中的节点。群路由网格将请求路由到活动任务。集群调度器将任务发送到不同的节点,则无需重新配置负载均衡器。

不使用路由网格

要使用没有路由网格的外部负载均衡器,请将--endpoint-mode设置为dnsrr,而不是vip的默认值。在这种情况下,没有一个虚拟IP。相反,Docker为服务设置DNS条目,使服务名称的DNS查询返回IP地址列表,客户端直接连接到其中一个地址。

相关推荐
jjkkzzzz3 小时前
docker介绍与常用命令汇总
docker
wasteland~3 小时前
Dubbo:Docker部署Zookeeper、Dubbo Admin的详细教程和SpringBoot整合Dubbo的实战与演练
docker·zookeeper·dubbo·dubbo-admin
少陽君3 小时前
使用shell快速删除Docker容器、镜像和存储内容
linux·docker
川川籽3 小时前
minikube dashboard ImagePullBackOff 失败问题的解决方法
docker·k8s
默心3 小时前
centos7安装mysql8.0
运维·mysql·adb·centos·devops
努力的搬砖人.6 小时前
Linux在防火墙中添加开放端口
java·linux·docker
肖永威8 小时前
HomeAssistant开源的智能家居docker快速部署实践笔记(CentOS7)
linux·docker
程序员老王wd9 小时前
Jenkins+Docker+Harbor快速部署Spring Boot项目详解
spring boot·docker·jenkins
前进的程序员9 小时前
Linux 移植 Docker 详解
linux·运维·docker
bjwuzh10 小时前
安装完dockers后就无法联网了,执行sudo nmcli con up Company-WiFi,一直在加载中
docker