docker进阶:docker swarm
简介
docker
三剑客之一,属于原生态的容器集群管理平台,可以提供集群和调度能力的编排工具,在docker 1.12 swarm mode
已经嵌入Docker
引擎,不需要去额外安装,Swarm
优点。
1、高性能,一些性能测试,Swarm
可扩展极限在1000
个节点上运行50000
个部署容器,每个容器的启动时间为亚秒,同时性能无减损。
2、灵活的容器调度,Swarm
的内置调度器(scheduler)支持多种过滤器,包括节点标签,亲和性,和多种容器策略,如binpack、spread、random等等,根据调度权重值,权重值包含cpu利用率,内存利用率,储存空间来进行相关的调度。
3、服务的可持续性,Docker Swarm
由 Swarm 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
端口通讯
- 2377 (TCP) 端口-集群管理
- 7946 (TCP&UDP) - 节点通信
- 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 | 节点ID | node.id==vppq6inxg38vp | |
node.hostname | 节点主机名 | node.hostname!=docker-5 | |
node.role | 节点角色 | node.role==manager | |
node.labels | 用户定义节点labels | node.labels.regin==zookeeper |
在创建时候指定
再就是在创建的去指定容器运行的节点,栗子,运行五个redis
在worker
节点上,不放在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&pause
,active
表示正常状态,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 网络管理
网络描述
主要作用:主要作用就是将不同主机之前的容器进行互联或隔离,主要是分为两种流量。
- 控制和管理计划流量:包括集群管理消息,譬如节点请求加入或离开集群,该流量是加密的
- 应用程序数据流量:包括来自外部客户端的集装箱运输和流量
要让不同主机上运行的容器互通,唯一的办法就是使用覆盖网络,也就是overlay
网络,默认提供一套,其负责配合libnetwork
与libkv
实现一套基于Vxlan
的解决方案,相当于一个插件的方式集成到了Swarm
中,不需要安装任何东西了。
在Docker Engine Swarm
模式当中,我们可以单纯立足管理节点创建一套覆盖网络。
网络概念
有三个网络概念,分别如下
- Overlay Network:管理参与集群中
Docker
守护进程之间的通讯,使用Overlay networks
驱动程序 - Ingress Network:入口网络,一种特殊的覆盖网络,助于在服务节点之间实现负载均衡,当收到请求时,会将请求发给一个名为
IPVS
的模块,IPVS
跟踪参与该服务的所有IP地址,选择其中一个,并将请求路由到它,在初始化或加入一个集群的时候,ingress
网络会自动创建。 - Docker gwbridge:对于单个
docker
守护进程的物理网络
Overlay网络创建及部署
现在创建一个名为rj-bai
的Overlay
网络,只能在管理节点去创建撒。
[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容器ping
195容器试试
[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 网络负载均衡
上面扯了一堆,没提到集群的访问方式,现在来看看负载均衡这块,其实就是利用了一些组件
- DNS组件,可以自动为集群中的每个服务分配DNS记录,
Swarm Manager
使用内部负载均衡器,根据服务的DNS名称在集群内的服务之前分发请求。 - Swarm Manager 使用 ingress load blancing 暴露你想从外部访问集群提供的服务。
Swarm Manager
自动为服务分配一个范围30000-32767
端口的Published Port
,也可以为该服务指定一个Published Port
。 - 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
,来加密MySQL
的root
密码,首先使用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
指定,具体现在MySQL
的root
密码是啥子,我也不知道,需要登录到容器里面去看。
[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
通过选举变成了新的leader
,docker-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
变成了Reachable
,Leader
还是docker-3
,没影响,关于Swarm
这块暂时就这些吧,实例现在没有练手的东西,后期再加。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。