MENU

docker进阶:docker swarm

2018 年 12 月 04 日 • 应用服务器

简介

docker三剑客之一,属于原生态的容器集群管理平台,可以提供集群和调度能力的编排工具,在docker 1.12 swarm mode已经嵌入Docker 引擎,不需要去额外安装,Swarm优点。

1、高性能,一些性能测试,Swarm可扩展极限在1000个节点上运行50000个部署容器,每个容器的启动时间为亚秒,同时性能无减损。

2、灵活的容器调度,Swarm的内置调度器(scheduler)支持多种过滤器,包括节点标签,亲和性,和多种容器策略,如binpack、spread、random等等,根据调度权重值,权重值包含cpu利用率,内存利用率,储存空间来进行相关的调度。

3、服务的可持续性,Docker SwarmSwarm Manager 提供高可用性,通过创建多个Swarm master节点和定制主master 节点宕机时的备选策略,如果一个master节点宕机,那么一个slave节点就会被升格为master节点,直到原来的master节点恢复正常。

Docker swarm架构

Manager: 接收客户端服务定义,将任务发送到worker节点,维护集群期望状态和集群管理功能及leader选举。默认情况下,Manager节点也会运行任务,也可以配置只做管理任务。

worker:接收并执行从管理节点非配的任务,并报告任务当前状态,以便管理节点维护每个服务的期望状态。

Swarm 特点

  • Docker Swarm 采用集群管理,统一部署
  • 弹性伸缩,可以策略的方式随意增加、删减容器数量
  • 多主机网络:Swarm内置多主机网络,实现多主机中的容器互通(overlay网络)
  • 服务发现:可以通过Swarm内置的DNS服务器查询集群中每个运行的容器。
  • 负载均衡:实现服务副本负载均衡,提供入口访问,也可以将服务入口暴露给外部负载均衡再次负载均衡。
  • 滚动升级:升级时,逐步将应用服务更新到节点,如果出现问题,可以将任务回滚到先前版本。
  • 安全传输:Swarm中的每个节点使用验证方式加入集群,确保安全的其他节点通讯。

Docker swarm 两个重点

重点一:任务 (task):

任务是Swarm中最小的调度单位,目前来说就是单一的容器

重点二:服务(services):

服务是指定一组任务的集合,服务定义了任务的属性,服务有两种模式

  • replicated services

    • 复制,按照一定规则在各个工作节点上运行指定个数的任务
  • global services

    • 全局服务,每个工作节点上运行一个任务

创建Docker Swarm 集群

Swarm 集群由管理节点和工作节点组成,首先来创建一个包含一个管理节点和一个工作节点的集群,也就是最小的集群。

前提条件:保证集群节点之间TCP2377、TCP/UDP7946&UDP4789端口通讯

  1. 2377 (TCP) 端口-集群管理
  2. 7946 (TCP&UDP) - 节点通信
  3. 4789 (TCP&UDP) - 覆盖网络流量

建议将iptables&firewall全部关闭

测试节点规划:

管理节点:192.168.1.93 (docker-1)

NODE节点:192.168.1.230 (docker-2)

启动集群

通过docker swarm启动集群,指定Manager节点,也就是93

[root@docker-1 ~]# docker swarm init --advertise-addr 192.168.1.93
Swarm initialized: current node (vppq6inxg38vpg1s0r5u1qncy) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-4p96v6jekuw4ngzhwt1ilj1pppmnfluin6hpbclupk106psv8c-9tpv4fylbvk96ofebqlmtewl4 192.168.1.93:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

查看一下状态,捡重点贴出来了。

[root@docker-1 ~]# docker info
Swarm: active                         (是否启用了swarm)
 NodeID: vppq6inxg38vpg1s0r5u1qncy    (节点ID)
 Is Manager: true                     (是否为Manager)
 ClusterID: lkekvgebbt4vk6wjzxxfw34dx (集群ID) 
 Managers: 1                           (当前集群Managers个数)
 Nodes: 1                              (worker个数,默认情况下Managers节点也是worker节点)
  Node Address: 192.168.1.93           (节点IP)
 Manager Addresses:
  192.168.1.93:2377                    (管理地址)

可以看到上面生成了一个令牌环,不用记住,可以用下面的命令随时查看,下面是查看manager的令牌环

[root@docker-1 ~]# docker swarm join-token manager 
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-4p96v6jekuw4ngzhwt1ilj1pppmnfluin6hpbclupk106psv8c-akp56kqqlxkkq3tredfav7hmr 192.168.1.93:2377

查看worker的令牌环,工作节点

[root@docker-1 ~]# docker swarm join-token worker 
To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-4p96v6jekuw4ngzhwt1ilj1pppmnfluin6hpbclupk106psv8c-9tpv4fylbvk96ofebqlmtewl4 192.168.1.93:2377

不一样的撒,将230加入到集群

节点加入集群

[root@docker-2 ~]# docker swarm join --token SWMTKN-1-4p96v6jekuw4ngzhwt1ilj1pppmnfluin6hpbclupk106psv8c-9tpv4fylbvk96ofebqlmtewl4 192.168.1.93:2377
This node joined a swarm as a worker.
[root@docker-2 ~]# docker info
Swarm: active
 NodeID: qmf0nmf8xtdxr0di53z27sz05
 Is Manager: false
 Node Address: 192.168.1.230
 Manager Addresses:
  192.168.1.93:2377

可以看到现在docker-2已经加入到集群中了,管理节点看一下,Nodes现在显示的是2就对了。

[root@docker-1 ~]# docker info
 Managers: 1
 Nodes: 2

Docker swarm 服务部署及管理

创建replicated服务

首先来创建一个replicated服务,也就是复制模式

[root@docker-1 ~]# docker service create --replicas 2 --name nginx nginx:latest 
jnru8eu7r2s1bqjgd83fcypvz

创建一个名为nginx的服务,启动两份,使用nginx镜像,现在应该是在管理节点和工作节点各有一个,看一下撒。

查看服务的详细信息

[root@docker-1 ~]# docker service inspect --pretty  nginx

这种创建的算是replicated服务,现在创建一个global服务

创建global服务

全局服务,也就是在全部的节点上运行一个服务,有新的节点加进来会自动也启动一个,适用场景收集日志

[root@docker-manager ~]# docker service create --mode global --name nginx nginx:1.15.8
[root@docker-manager ~]# docker service ls | grep nginx:1.15.8
0dqka1wg1fa1        nginx                  global              12/12               nginx:1.15.8                                                      

扩展服务实例

刚刚创建的nginx服务只是用用了两个节点,现在扩展一下撒,扩展到四个,输出我就不贴了,直接看图吧。

[root@docker-1 ~]# docker service ps nginx    ##查看当前的nginx服务运行情况
[root@docker-1 ~]# docker service scale nginx=4  ## 扩展实例到4个
[root@docker-1 ~]# docker service ps nginx ## 再次查看nginx服务运行情况
[root@docker-1 ~]# docker ps               ## 查看本机运行容器
[root@docker-1 ~]# ansible docker -m shell -a "docker ps" ## 这个不多BB了

既然能扩展也就可以缩减,也是使用scale,现在是四个,想缩减到两个就写scale nginx=2就行了,全局服务不能扩展撒。

检索容器

也就是通过各种条件去检索服务里的容器,可以是容器的状态,容器ID,容器名字,或是节点,一个最简单的栗子,检索状态是Running的容器,刚刚启动了四个容器实例,随便先停一个。

[root@docker-1 ~]# ansible docker -m shell -a "docker stop nginx.1.tc7e9ss2gojs81ip3iwblzhwq"
192.168.1.230 | CHANGED | rc=0 >>
nginx.1.tc7e9ss2gojs81ip3iwblzhwq
[root@docker-1 ~]# docker service ps --filter desired-state=running  nginx
[root@docker-1 ~]# docker service ps --filter desired-state=shutdown  nginx 

可以看到running状态的容器是四个,一个处于shutdown状态,也就是我手动关闭的那个,至于为毛我刚刚手动停了一个,现在还有四个,常理来说应该是3个running才对,原因就是我之前指定了扩展到四个,所以就会永远有四个在线,只要有容器宕掉就会有新的容器启动,通过条件去检索用命令docker service ps --filter去看吧,很简单,过。

滚动更新服务

更新服务有两种方式,第一种是直接去更新,第二种是创建我们服务的时候添加相关的更新策略,先来试试第一种,直接去更新。

直接更新

正常情况下是直接使用images去更新,我这里测试,我刚刚用的nginx:laster去创建的服务,正常来说nginx版本应该是1.15.6,现在pull一个1.14.1,用这个去更新,先看一下当前nginx版本

[root@docker-1 ~]# docker exec -it nginx.1.3818r8sk87zpujdxdfjty4f3u nginx -v
nginx version: nginx/1.15.6

1.15.6的,现在去pull一个1.14.1

[root@docker-1 ~]# docker pull nginx:1.14.1
[root@docker-1 ~]# docker service ps nginx

开始更新

[root@docker-1 ~]# docker service update --image nginx:1.14.1 nginx
[root@docker-1 ~]# docker service ps nginx
[root@docker-1 ~]# docker exec -it nginx.1.hti2w3utjqexdqvev2jtkysfg nginx -v

可以看到更新成功了,细扒一下容器状态,看一下状态是running

[root@docker-1 ~]# docker service ps --filter desired-state=running  nginx 

再看一下是shutdown状态的

阔以看到使用我最开始使用nginx:latest镜像创建的容器都被停掉了,新启动的都是以nginx:1.14.1镜像创建的容器,这样就算更新完成了。

创建更新策略

做这个之前先把现有的都删掉了,在创建的时候需要设定一下策略,现在测试环境升级,新加三个节点到集群中,所以现在一共是五个worker节点,一个manager节点。

[root@docker-1 ~]# ansible docker -m shell -a "docker swarm join --token SWMTKN-1-4p96v6jekuw4ngzhwt1ilj1pppmnfluin6hpbclupk106psv8c-9tpv4fylbvk96ofebqlmtewl4 192.168.1.93:2377"
[root@docker-1 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
vppq6inxg38vpg1s0r5u1qncy *   docker-1            Ready               Active              Leader              18.09.0
qmf0nmf8xtdxr0di53z27sz05     docker-2            Ready               Active                                  18.09.0
49vekokqujpzqlymp9ymgf2dx     docker-3            Ready               Active                                  18.09.0
s0y8yd0ysl5xo2v9c90f6kp8c     docker-4            Ready               Active                                  18.09.0
4j8lr6sh1qrovsrm5e1zgm3jw     docker-5            Ready               Active                                  18.09.0

开始创建,我添加了几个常用的参数,更详细的用docker service create --help去看吧

[root@docker-1 ~]# docker service create --name nginx --update-delay 5s --update-parallelism 2 --update-monitor 3s --update-failure-action rollback --rollback-delay 5s --rollback-monitor 3s --rollback-parallelism 2 --rollback-failure-action continue --replicas 10 nginx:1.14.1

参数说明

参数含义
--update-delay更新延迟时间,默认0秒并行
--update-parallelism允许更新并行最大值,默认1
--update-monitor监控时间,默认5s
--update-failure-action更新失败执行的操作,默认pause
--rollback-delay回滚延时时间,默认0并行
--rollback-monitor监控时间,默认五秒
--rollback-parallelism运行并行回滚最大值,默认1
--rollback-failure-action回滚失败执行的操作,默认pause

测试环境随便写的,主要注意的就是更新延时时间那里,像我们有时候跑jar包,jar包不可能在五秒之内启动完成,我们现在线上用jenkins发布的时候,各个服务器更新延时都在一分钟,而且有相关的逻辑判断去判断jar包是否正常启动,如果检测失败直接回滚,下面手动更新一下试试。

服务更新

上面提过一次了,也就是update,先给nginx:latest打个标签吧,刚刚创建用的是nginx:1.14.1的镜像,现在更新成nginx:1.15.6

[root@docker-1 ~]# docker tag nginx:latest nginx:1.15.6
[root@docker-1 ~]# docker service update --image nginx:1.15.6 nginx 

这里的更新就会按着上面创建的更新策略去执行了,阔以看到以nginx:1.14.1镜像构建的容器全部被shutdown了,running状态的都是1.15.6的,下面是手动回滚。

手动回滚

这里的回滚也是按着上面所定义的策略去执行了,现在回滚到1.14.1

[root@docker-1 ~]# docker service update --rollback nginx
[root@docker-1 ~]# docker service ps nginx --filter desired-state=running

木有问题撒,继续。

指定服务约束

说白了就是指定某一个容器运行在某一个节点上,一下面是使用update的方法。

使用update

我现在有5个节点,运行了十个nginx,先砍一半。

[root@docker-1 ~]# docker service scale nginx=5
[root@docker-1 ~]# docker service ps nginx --filter desired-state=running

然后去指定docker-5节点去运行这个nginx

[root@docker-1 ~]# docker service update --constraint-add node.hostname==docker-5 nginx
[root@docker-1 ~]# docker service ps nginx --filter desired-state=running

可以看到已经切过来了,我刚刚指定的主机名,也可以用别的方法去指定,如下。

属性匹配栗子
node.id节点IDnode.id==vppq6inxg38vp
node.hostname节点主机名node.hostname!=docker-5
node.role节点角色node.role==manager
node.labels用户定义节点labelsnode.labels.regin==zookeeper

在创建时候指定

再就是在创建的去指定容器运行的节点,栗子,运行五个redisworker节点上,不放在manager上。

[root@docker-1 ~]# docker service create --name redis --constraint node.role==worker --replicas 5 redis
[root@docker-1 ~]# docker service ps redis

可以看到运行了5个,没有在docker-1上的,顺便提一下使用各种属性指定的办法,上面已经提到两个了,一个是使用主机名,在一个是使用节点角色,下面来看看用labels的方法

使用labels

要使用labels需要先给节点打个标签,这个词翻译过来就是标签,哈哈,先把现在服务全部删掉了,有点乱,先随便给三个节点打个zookeeper的标签吧,这个东西很常用。

[root@docker-1 ~]# docker node update --label-add regin=zookeeper docker-2 
[root@docker-1 ~]# docker node update --label-add regin=zookeeper docker-3 
[root@docker-1 ~]# docker node update --label-add regin=zookeeper docker-4
[root@docker-1 ~]# docker node inspect docker-{2..4} | grep "regin"

然后启动四个zookeeper,指定运行节点为带有zookeeper标签的,正常来说就是在docker{2..4}上面

[root@docker-1 ~]# docker service create --name zookeeper --constraint node.labels.regin==zookeeper --replicas 4 zookeeper
[root@docker-1 ~]# docker service ps zookeeper

这样就可以了,一般情况下用打标签就够了,删除标签的话用--label-rm regin就行了,不演示,下一步。

修改节点状态

目前节点状态氛围三种,分别是active&drain&pauseactive表示正常状态,drain表示当前节点凉凉,pause表示节点不再参与swarm的容器部署,但是不会影响当前的东西,来试一下看效果,

查看节点状态的方法

[root@docker-1 ~]# docker node ls

现在都是Active的状态,现在把docker-4的状态改为drain看看效果,现在docker-4上面是跑着东西呢

[root@docker-1 ~]# docker node update --availability drain docker-4
[root@docker-1 ~]# docker service ps zookeeper

可以看到docker-4上面的zookeeper全部宕掉了,在2&3上分别有创建了一个,现在改回正常。

[root@docker-1 ~]# docker node update --availability active docker-4

改成pause试试

[root@docker-1 ~]# docker node update --availability pause docker-4

然后扩容zookeeper,现在是四个,扩展到10个。

[root@docker-1 ~]# docker node ls
[root@docker-1 ~]# docker service scale zookeeper=10
[root@docker-1 ~]# docker service ps --filter desired-state=running zookeeper
[root@docker-1 ~]# docker service ps --filter desired-state=shutdown zookeeper

大概就是这样,pause状态效果不明显,重新来,看下面的。

[root@docker-1 ~]# docker service create --name redis --constraint node.hostname==docker-4 --replicas 2 redis
[root@docker-1 ~]# docker node update --availability pause docker-4
[root@docker-1 ~]# docker service create --name zookeeper --constraint node.labels.regin==zookeeper --replicas 4 zookeeper
[root@docker-1 ~]# docker service ps zookeeper

Swarm 使用Compose文件

也就是Swarm使用docker-compose.yml文件去构建服务,就用之前写过改一下试试。

[root@docker-1 /docker-compose]# cat lnmp.yml 
version: '3'
services: 
  nginx: 
    image: nginx
    hostname: nginx
    ports:
      - "80:80"
      - "443:443"
    networks: 
      - lnmp

  mysql: 
    image: mysql:5.7
    hostname: mysql
    ports: 
      - "3306:3306"
    environment: 
      MYSQL_ROOT_PASSWORD: Sowhat?
    networks: 
      - lnmp

  php: 
    image: php:7.2-fpm
    hostname: php
    networks: 
      - lnmp

networks: 
  lnmp:
[root@docker-1 /docker-compose]# docker stack deploy --compose-file lnmp.yml lnmp
[root@docker-1 /docker-compose]# docker stack ps lnmp 

阔以看到三个服务分别分布到了docker-{3..5}节点上,还创建了一个叫lnmp_lnmp的网络,看一下这个网络的类型。

[root@docker-1 ~]# docker network ls | grep "lnmp_lnmp"
1b6rxh0v0pbz        lnmp_lnmp                overlay             swarm

是一个overlay网络,支持跨主机通讯的网络,一会儿会细说,详细的看一下这个服务的信息。

[root@docker-1 ~]# docker stack services lnmp 
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
3uix9jxvgw6x        lnmp_nginx          replicated          1/1                 nginx:latest        *:80->80/tcp, *:443->443/tcp
jp70z56jycgr        lnmp_php            replicated          1/1                 php:7.2-fpm         
kann48c0i8gw        lnmp_mysql          replicated          1/1                 mysql:5.7           *:3306->3306/tcp

一共是映射三个端口,至于怎么去访问,只要访问这个manage就可以了,试一下。

[root@docker-2 ~]# curl -I 192.168.1.93
[root@docker-2 ~]# mysql -uroot -pSowhat? -h192.168.1.93 -e "show databases ;"

阔以访问到,上面可以看到容器并不是放到docker-1上的,是分布在docker-{3..5}上的,顺便提一下REPLICAS,能看到显示的1,只要不删除这个服务,REPLICAS就会永远存在,譬如nginx容器宕了,swarm会帮你再启一个去顶替,保证了服务的可持续性,这是一个最简单的跨主机通信,下面说说网络,先把这个服务删了吧。

[root@docker-1 ~]# docker stack rm lnmp 
Removing service lnmp_mysql
Removing service lnmp_nginx
Removing service lnmp_php
Removing network lnmp_lnmp

Docker Swarm 网络管理

网络描述

主要作用:主要作用就是将不同主机之前的容器进行互联或隔离,主要是分为两种流量。

  1. 控制和管理计划流量:包括集群管理消息,譬如节点请求加入或离开集群,该流量是加密的
  2. 应用程序数据流量:包括来自外部客户端的集装箱运输和流量

要让不同主机上运行的容器互通,唯一的办法就是使用覆盖网络,也就是overlay网络,默认提供一套,其负责配合libnetworklibkv实现一套基于Vxlan的解决方案,相当于一个插件的方式集成到了Swarm中,不需要安装任何东西了。

Docker Engine Swarm模式当中,我们可以单纯立足管理节点创建一套覆盖网络。

网络概念

有三个网络概念,分别如下

  1. Overlay Network:管理参与集群中Docker守护进程之间的通讯,使用Overlay networks驱动程序
  2. Ingress Network:入口网络,一种特殊的覆盖网络,助于在服务节点之间实现负载均衡,当收到请求时,会将请求发给一个名为IPVS的模块,IPVS跟踪参与该服务的所有IP地址,选择其中一个,并将请求路由到它,在初始化或加入一个集群的时候,ingress网络会自动创建。
  3. Docker gwbridge:对于单个docker守护进程的物理网络

Overlay网络创建及部署

现在创建一个名为rj-baiOverlay网络,只能在管理节点去创建撒。

[root@docker-1 ~]# docker network create --driver overlay --scope swarm --subnet 13.14.15.0/24 --ip-range 13.14.15.0/24 --gateway 13.14.15.1 rj-bai
参数含义
--driver指定驱动类型
--scope指定范围
--subnet配置网段
--ip-range指定IP范围
--gateway设置网关

查看一下网络详情

[root@docker-1 ~]# docker network inspect rj-bai

使用这个网络

我们新建一个服务,去使用这个网络。

[root@docker-1 ~]# docker service create --name tomcat --network rj-bai --constraint node.role==worker --replicas 5 tomcat
[root@docker-1 ~]# docker service ls
[root@docker-1 ~]# docker service ps tomcat 

一些参数上面都说过了撒,就不多BB了,就是使用--network去指定网络,看一下效果。

看上去没什么问题,验证一下,先看一下服务的虚拟IP地址。

[root@docker-1 ~]# docker service inspect tomcat -f "{{.Endpoint.VirtualIPs}}"
[{pbxucdl2om1hosbh0txj4amwg 13.14.15.2/24}]

再看一下容器的IP地址,分别看看docker-{2..3}的吧

[root@docker-1 ~]# ansible 192.168.1.230 -m shell -a "docker inspect tomcat.1.s7b72ze2q68b7r9k1oy85nzcc | grep -i "ipaddress""
192.168.1.230 | CHANGED | rc=0 >>
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "13.14.15.3",

[root@docker-1 ~]# ansible 192.168.1.195 -m shell -a "docker inspect tomcat.3.tmc2cjod14g2l8i32dku5dhvf | grep -i "ipaddress""
192.168.1.195 | CHANGED | rc=0 >>
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "13.14.15.5",

阔以看到运行在两个不同节点的容器IP是在一个地址段的,下面进到容器里面,230容器ping195容器试试

[root@docker-1 ~]# ssh 192.168.1.230
[root@docker-2 ~]# docker exec -it tomcat.1.s7b72ze2q68b7r9k1oy85nzcc /bin/bash
root@15b068646bed:/usr/local/tomcat# ping -c 2 192.168.1.93
root@15b068646bed:/usr/local/tomcat# ping -c 2 13.14.15.5
root@15b068646bed:/usr/local/tomcat# ip addr

可以看到没问题,别的不用试了,肯定可以的撒,还有就是如果现有服务要切换到rj-bai网络,可以用update去改,栗子。

[root@docker-1 ~]# docker service create --name redis --constraint node.hostname==docker-2 --replicas 1 redis
[root@docker-1 ~]# docker service ps redis
[root@docker-1 ~]# ansible 192.168.1.230 -m shell -a "docker ps "
[root@docker-1 ~]# ansible 192.168.1.230 -m shell -a "docker inspect 2e6151efc98d | grep -i "ipaddress""

阔以看到在没指定网络的情况下使用的是172.17.0.0网段,也就是bridge,现在把它的网络切换到rj-bai网络。

[root@docker-1 ~]# docker service update --network-add rj-bai redis
[root@docker-1 ~]# ansible 192.168.1.230 -m shell -a "docker ps "
[root@docker-1 ~]# ansible 192.168.1.230 -m shell -a "docker inspect d1d8c54746b1 | grep -i "ipaddress""

没问题,过,接下来负载均衡。

Docker Swarm 网络负载均衡

上面扯了一堆,没提到集群的访问方式,现在来看看负载均衡这块,其实就是利用了一些组件

  1. DNS组件,可以自动为集群中的每个服务分配DNS记录,Swarm Manager使用内部负载均衡器,根据服务的DNS名称在集群内的服务之前分发请求。
  2. Swarm Manager 使用 ingress load blancing 暴露你想从外部访问集群提供的服务。 Swarm Manager自动为服务分配一个范围30000-32767端口的Published Port,也可以为该服务指定一个Published Port
  3. ingress network 是一个特殊的overlay 网络,便于服务器的节点直接负载均衡。当任何swarm 节点在已发布的端口上就收请求时,它将该请求转发给调用的IPVS模块,IPVS跟踪参与该服务器的所有容器IP地址,选择其中一个,并通过ingress network 将请求路由给它。

Swarm网络负载方式

负载方式有两种,分别是VIP&dnsrr,有雷同也有区别,雷同是两种都可以进行IP轮训,不通是VIP也可以进行端口查找,dnsrr只能进行IP轮训。

VIP负载均衡数据流量

例如有一个主机端口为8080=> 容器Ingress-sbox(例如13.14.15.2/24,如上Ingress配置)=> IPVS分发到containers

访问主机之后数据包流到了一个特殊的Sandbox容器里,这个容器和我们的容器共享一个Ingress网络,通过iptables和IPVS等重定向到了最终容器之上,达到了服务在任何一台主机的8080端口都可达的目的。

DNS负载均衡和VIP负载不一样,dnsrr主要依赖用户自定义的overlay网络,实例。

使用dnsrr

[root@docker-1 ~]# docker service create --name tomcat --network rj-bai --endpoint-mode dnsrr --constraint node.role==worker --replicas 4 -p 8080:8080 tomcat
Error response from daemon: rpc error: code = InvalidArgument desc = EndpointSpec: port published with ingress mode can't be used with dnsrr mode

抛错了撒,dnsrr不支持-p参数,现在把它去掉,

[root@docker-1 ~]# docker service create --name tomcat --network rj-bai --endpoint-mode dnsrr --constraint node.role==worker --replicas 4 tomcat
[root@docker-1 ~]# ssh docker-2
[root@docker-2 ~]# docker exec -it tomcat.3.nvq1vujj2yjgjq7moaabo4pr9 /bin/bash
root@f76681799177:/usr/local/tomcat# apt-get update && apt-get install -y dnsutils
root@f76681799177:/usr/local/tomcat# nslookup tomcat

大概是这种轮训效果,至于怎么暴露端口出来,用dnsrr模式得用你自己的负载均衡器哦,譬如nginx

使用VIP

现在将dnsrr模式切到VIP模式,并把8080端口暴露出来。

[root@docker-1 ~]# curl -I 192.168.1.93:8080
curl: (7) Failed connect to 192.168.1.93:8080; 拒绝连接
[root@docker-1 ~]# docker service update --endpoint-mode vip tomcat
[root@docker-1 ~]# docker service update --publish-add 8080:8080 tomcat
[root@docker-1 ~]# curl -I 192.168.1.93:8080
HTTP/1.1 200 

大概就是这样,看不到啥子效果,我改一下tomcat的默认页面,循环访问就知道了。

[root@docker-1 ~]# for ((i=1; i<=4; i++))
> do 
>  curl 192.168.1.93:8080
> done
docker-3
docker-2
docker-5
docker-4

现在就是在manager提供了一个入口,去访问节点的8080也是能通的,现在就是轮训撒,新建服务指定如下。

[root@docker-1 ~]# docker service create --name tomcat --network rj-bai --endpoint-mode vip --constraint node.role==worker --replicas 4 -p 8080:8080/tcp tomcat
[root@docker-1 ~]# docker service inspect tomcat
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 8080,
                        "PublishedPort": 8080,
                        "PublishMode": "ingress"
                    }
                ]

就是酱紫,没问题,下面是管理敏感数据。

Docker Swarm 集群敏感数据管理

说白了就是我们的一些密码或密钥证书等进行加密,并允许多个Docker容器之间共享访问指定敏感数据,也就是创建secret,有两种方式,一种是明文,一种是密文。

使用密文

栗子,使用openssl,来加密MySQLroot密码,首先使用openssl创建加密密码。

[root@docker-1 ~]# openssl rand -base64 20 | docker secret create mysql_root_password -
k9g2c97ff2dwjx1g26piqtbt0
[root@docker-1 ~]# docker secret ls
ID                          NAME                  DRIVER              CREATED             UPDATED
k9g2c97ff2dwjx1g26piqtbt0   mysql_root_password                       9 seconds ago       9 seconds ago

创建好了撒,下面需要启动一个MySQL服务,将加密密码应用起来。

[root@docker-1 ~]# docker service create --name mysql --network rj-bai \
> --constraint node.role==manager --replicas 1 \
> --secret source=mysql_root_password,target=mysql_root_password \
> -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password \
> mysql:5.7
[root@docker-1 ~]# docker service ps mysql 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
kxh2v4een4l0        mysql.1             mysql:5.7           docker-1            Running             Running 14 seconds ago                       

source指定容器使用secret后,secret会被解密并存放到容器的文件系统中,默认位置为/run/secret/<secret_name>--secret source=mysql_root_password,target=mysql_root_password的作用就是指定使用secret my_root_password,然后把容器解密后的内容保存到容器的/run/secrets/mysql_root_password中,文件名称由target指定,具体现在MySQLroot密码是啥子,我也不知道,需要登录到容器里面去看。

[root@docker-1 ~]# docker exec -it mysql.1.kxh2v4een4l0qo4oczwvyvy8e /bin/bash
root@290b4d6dafc1:/# cat /run/secrets/mysql_root_password 
q0gNaMr+eHnt0HLdnDelAXEfyLI=
root@290b4d6dafc1:/# mysql -uroot -pq0gNaMr+eHnt0HLdnDelAXEfyLI= -e "show databases ;"

大概就是这种效果。

使用明文

明文的话就是这样。

[root@docker-1 ~]# docker service rm mysql 
[root@docker-1 ~]# docker secret rm  mysql_root_password
[root@docker-1 ~]# echo "Sowhat?" | docker secret create mysql_root_password -
[root@docker-1 ~]# docker service create --name mysql --network rj-bai \
> --constraint node.role==manager --replicas 1 \
> --secret source=mysql_root_password,target=mysql_root_password \
> -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password \
> mysql:5.7
[root@docker-1 ~]# docker exec -it mysql.1.jh6z2scnnqsxx4gm6z6d3hgsd /bin/bash
root@11503395df51:/# mysql -uroot -pSowhat? -e "show databases ;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

效果就是这样,活用吧,栗子就在上面,下面硕鼠数据存储。

Docker Swarm 数据持久化

Swarm集群中,我们可以创建本地卷或是全局卷来挂载到容器,用来保存数据,全局卷可以被挂载到Swarm集群的任意节点,本地数据卷是通过本地硬盘,本地数据卷只能存在某个节点本地的一个挂载卷。也可以使用NFS共享存储作为数据卷,主要说一下用本地数据卷。

Volume类型

有两种类型,一种是Volume类型,一种是bind类型,Volume类型会应用到多个节点,而bind只是绑定一个目录提供容器使用,只能运行在本NODE节点,不能跨NODE进行。

创建Volume

创建一个Volume

[root@docker-1 ~]# docker volume create data
data
[root@docker-1 ~]# docker volume ls
local               data

嘤用到节点撒

[root@docker-1 ~]# docker service create --name nginx --mount type=volume,src=data,dst=/data --constraint node.role==worker --network rj-bai --replicas 4 nginx

类型volume,源data数据卷,容器挂载目标/data,登录到容器上看一下

[root@docker-2 ~]# docker exec -it nginx.1.z13ruoru6t5lqnmlp5rfcgghf /bin/bash
root@7e1a30ba81b3:/# ls -d data/
data/
root@7e1a30ba81b3:/# cd /data/
root@7e1a30ba81b3:/data# ls

现在没有任何数据,现在随便写一条,在这里会看到。

root@7e1a30ba81b3:/data# echo rj-bai > rj-bai
root@7e1a30ba81b3:/data# cat rj-bai 
rj-bai
root@7e1a30ba81b3:/data# exit
[root@docker-2 ~]# cd /var/lib/docker/volumes/data/
[root@docker-2 /var/lib/docker/volumes/data]# ls
_data
[root@docker-2 /var/lib/docker/volumes/data]# cd _data/
[root@docker-2 /var/lib/docker/volumes/data/_data]# ls
rj-bai

现在这个是在docker-2节点创建的,去看看的别的有没有这个文件。

[root@docker-3 ~]# ls /var/lib/docker/volumes/data/_data/
[root@docker-3 ~]# docker exec -it nginx.3.wkcshtvpfwxsqrcn5e2jn6wah /bin/bash
root@a085a4d8cc73:/# ls /data/

很明显木有撒,在docker-3上创建一个文件试试。

root@a085a4d8cc73:/# cd data/
root@a085a4d8cc73:/data# echo docker-3 > docker-3
root@a085a4d8cc73:/data# exit
[root@docker-3 ~]# ls /var/lib/docker/volumes/data/_data/
docker-3

再去docker-2看一眼。

[root@docker-2 ~]# docker exec -it nginx.1.z13ruoru6t5lqnmlp5rfcgghf ls /data/
rj-bai
[root@docker-2 ~]# ls /var/lib/docker/volumes/data/_data/
rj-bai

无法同步,别的不用看,肯定是空的,这个就是volume的机制,只能做到本地卷,没有做到全局卷。

设置成只读权限

[root@docker-2 ~]# docker service create --name nginx --mount type=volume,src=data,dst=/data,readonly --constraint node.role==worker --network rj-bai --replicas 4 nginx

就是加了一个readonly,查看这个数据卷的详细信息。

[root@docker-1 ~]# docker volume inspect rj-bai 
[
    {
        "CreatedAt": "2018-11-29T14:24:24+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/rj-bai/_data",
        "Name": "rj-bai",
        "Options": {},
        "Scope": "local"
    }
]

下面使用bind类型.

bind类型

首先要创建目录

[root@docker-1 ~]# ansible docker -m file -a "path=/www/nginx state=directory"

开始挂载

[root@docker-1 ~]# docker service create --name nginx --mount type=bind,src=/www/nginx,dst=/usr/share/nginx/html --constraint node.role==worker -p 80:80  --network rj-bai --replicas 4 nginx

写一个默认页面进去,看效果。

[root@docker-1 ~]# ansible docker -m shell -a "echo "\$HOSTNAME" > /www/nginx/index.html"
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 192.168.1.93; done

就是这种效果,一般情况下这个用的比较多,老版本的docker使用bind模式挂载可能只能运行在manager上,无法使用NODE,看这个服务的详情阔以看到如下信息。

[root@docker-1 ~]# docker service inspect nginx
                    "Mounts": [
                        {
                            "Type": "bind",
                            "Source": "/www/nginx",
                            "Target": "/usr/share/nginx/html"
                        }
                    ],

为原有的服务添加volume

先新建一个服务,就用tomcat吧

[root@docker-1 ~]# docker service create --name tomcat --constraint node.role==worker -p 8080:8080  --network rj-bai --replicas 4 tomcat

更新服务。

[root@docker-1 ~]# docker volume create tomcat
tomcat
[root@docker-1 ~]# docker service update --mount-add type=volume,source=tomcat,target=/usr/local/tomcat/webapps tomcat

修改页面测试。

[root@docker-1 ~]# ansible docker -m shell -a "echo tomcat-"\$HOSTNAME" > /var/lib/docker/volumes/tomcat/_data/ROOT/index.jsp"
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 192.168.1.93:8080; done

木有问题,下面用bind试试。

[root@docker-1 ~]# docker service update --mount-rm type=bind,source=/www/tomcat,target=/usr/local/tomcat/webapps tomcat
[root@docker-1 ~]# ansible docker -m file -a "path=/www/tomcat/ROOT state=directory"
[root@docker-1 ~]# ansible docker -m shell -a "echo data-"\$HOSTNAME" > /www/tomcat/ROOT/index.jsp"
[root@docker-1 ~]# docker service update --mount-add type=bind,source=/www/tomcat,target=/usr/local/tomcat/webapps tomcat
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 192.168.1.93:8080; done

木问题撒,达到我想要的效果了。过。

Docker Swarm 配置文件存储

将我们的所需要的配置文件保存到docker配置中,可以应用到实例当中,先手写一个nginx的配置文件吧,先看一下现在的情况。

[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1; done
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1:81; done

创建配置文件

下面是一个最简单的栗子。

[root@docker-1 ~]# cat rj-bai.conf 
server {
  listen 81;

  server_name rj-bai.com;

  location / {
  root /usr/share/nginx/html/rj-bai ;
  index index.html;
  }
}
[root@docker-1 ~]# ansible docker -m file -a "path=/www/nginx/rj-bai state=directory"
[root@docker-1 ~]# ansible docker -m shell -a "echo rj-bai-"\$HOSTNAME" > /www/nginx/rj-bai/index.html"

保存到配置中

[root@docker-1 ~]# docker config create rj-bai.conf rj-bai.conf

应用到服务中

[root@docker-1 ~]# docker service update --config-add source=rj-bai.conf,target=/etc/nginx/conf.d/rj-bai.conf --publish-add 81:81 nginx

然后发现了一个很严重的问题,大概这样。

[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1; done
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1:81; done
[root@docker-1 ~]# docker service ps nginx --filter desired-state=running

负载方式乱了,全部走到了docker-3节点了,所以这个不适合来做和端口有关的东西,像是传点文件之类的还是蛮方便的,栗子。

[root@docker-1 ~]# docker service rm nginx
[root@docker-1 ~]# docker service create --name nginx --mount type=bind,src=/www/nginx,dst=/usr/share/nginx/html --constraint node.role==worker --replicas 4 -p 80:80 nginx
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1; done
[root@docker-1 ~]# cat default.conf 
server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html/rj-bai;
        index  index.html index.htm;
    }

    }
[root@docker-1 ~]# docker config create default.conf default.conf 
[root@docker-1 ~]# docker service update --config-add source=default.conf,target=/etc/nginx/conf.d/default.conf nginx 
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1; done

就是这种作用,下面看看新建服务使用配置文件

新建服务使用配置文件

先把这个删了。

[root@docker-1 ~]# docker service  rm nginx 
[root@docker-1 ~]# docker service create --name nginx --mount type=bind,src=/www/nginx,dst=/usr/share/nginx/html --config source=rj-bai.conf,target=/etc/nginx/conf.d/rj-bai.conf --constraint node.role==worker --replicas 4 -p 80:80 -p 81:81 nginx
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1; done
[root@docker-1 ~]# for ((i=1; i<=4; i++)); do   curl 127.0.0.1:81; done

这个就木有问题撒,使用方法就是这样,过。

Docker Swarm 高可用性

目前管理节点只有一个,看一下。

[root@docker-1 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
vppq6inxg38vpg1s0r5u1qncy *   docker-1            Ready               Active              Leader              18.09.0
qmf0nmf8xtdxr0di53z27sz05     docker-2            Ready               Active                                  18.09.0
49vekokqujpzqlymp9ymgf2dx     docker-3            Ready               Active                                  18.09.0
s0y8yd0ysl5xo2v9c90f6kp8c     docker-4            Ready               Active                                  18.09.0
4j8lr6sh1qrovsrm5e1zgm3jw     docker-5            Ready               Active                                  18.09.0

也就是leader节点,现在只有一个,如果这个崩了节点上的任务会继续运行,不受影响,但是不能执行管理任务,包括扩展更新服务、加入或删除节点,恢复的最佳方式就是丢失的leader重新联机,如果不能,只能重建集群了,所以做一下高可用吧。

首先增加两个manager节点,方法一,在现有manager节点操作,就指定docker-{2..3}

[root@docker-1 ~]# docker node promote docker-{2..3}
[root@docker-1 ~]# docker node ls

阔以看到docker-{2..3}变的MANAGER STATUS变成了Reachable,这是一种方式,还有就是查看manager token,去docker -{2..3}手动执行一下,查看方式。

[root@docker-1 ~]# docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-266xrsu4ynat4rnvyl0xc9o1avy3w9iabbl857ljrdy4h5vx4w-8psp4c307jma24djp8wln99qp 192.168.1.93:2377

如果去docker-{2..3}手动加入,在加入之前请先离开集群才能加,也就是用docker swarm leave,现在加进去了,去docker-{2..3}看一下。

[root@docker-1 ~]# vim /etc/ansible/hosts
[docker]
docker-2
docker-3
#docker-4
#docker-5
[root@docker-1 ~]# ansible docker -m shell -a "docker node ls"
[root@docker-1 ~]# ansible docker-2 -m shell -a "docker service update --host-add rj-bai:127.0.0.1 nginx"
[root@docker-1 ~]# ansible docker-3 -m shell -a "docker service update --host-rm rj-bai:127.0.0.1 nginx"

没问题,可以进行常规操作,如果现在docker-1宕了,会是什么效果,来看看。

[root@docker-1 ~]# systemctl stop docker.service
[root@docker-2 ~]# ansible docker -m shell -a "docker node ls"

现在docker-3通过选举变成了新的leaderdocker-1成了Down状态。

访问一下服务有没有受到影响。

[root@docker-1 ~]# ansible docker -m shell -a "for ((i=1; i<=4; i++)); do   curl -s 127.0.0.1; done"
[root@docker-1 ~]# ansible docker -m shell -a "for ((i=1; i<=4; i++)); do   curl -s 127.0.0.1:8080; done"

没有,现在服务应该都分布在docker-{4..5}上了,看一下撒。

[root@docker-1 ~]# ansible docker-3 -m shell -a "docker service ps nginx --filter desired-state=running"
[root@docker-1 ~]# ansible docker-3 -m shell -a "docker service ps tomcat --filter desired-state=running"

没啥子问题,要注意的是,如果做高可用,请确保有一个Leader在线,最少两个Reachable在线,如果是一个Leader和一个Reachable在线,leader崩了,而且恢复不了,那么这个集群就算完了,会抛这种错。

Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.

这种情况只能强制重建集群,重建的后果就是集群重新初始化,服务全没,大概就这样,现在把docker-1起了吧。

[root@docker-1 ~]# systemctl start docker.service 
[root@docker-1 ~]# docker node ls 
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
34l90zrmclxp6ic1t8m3gt7ip *   docker-1            Ready               Active              Reachable           18.09.0
mlzu2j5vo7xm1apxmv9ocyt2k     docker-2            Ready               Active              Reachable           18.09.0
avd68nziciq0sq5spwkz3xicx     docker-3            Ready               Active              Leader              18.09.0

现在docker-1变成了ReachableLeader还是docker-3,没影响,关于Swarm这块暂时就这些吧,实例现在没有练手的东西,后期再加。

最后编辑于: 2019 年 03 月 30 日
返回文章列表 文章二维码 打赏
本页链接的二维码
打赏二维码