# Docker Swarm

Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台 Docker 主机抽象为一个整体,并且通过一个入口统一管理这些Docker主机上的各种Docker资源。

  1. 推荐阅读:Docker Swarm简介 (opens new window)
  2. 部署docker swarm集群监控 (opens new window)
  3. 生产环境中使用Docker Swarm的一些建议 (opens new window)

# 哪些适合上Swarm

  1. 无状态的容器就非常适合部署在Swarm集群,它们可以由环境变量进行配置(使用ENV指令)。建议为开源工具构建镜像,例如,可以将Nginx的配置文件放到Docker镜像中。
  2. 部署在Swarm集群之外的容器:数据库(MySQL)、缓存(Redis)
  3. 当你开启一个服务的端口之后,在Swarm集群中的任何一个节点都可以访问它。负载均衡也是由Swarm提供的。

# 基本概念

  1. 单引擎(Single-Engine)模式: 不包含在任何 Swarm 中的 Docker 节点。
  2. Swarm 模式: 一旦被加入 Swarm 集群。在单引擎模式下的 Docker 主机上运行 docker swarm init 会将其切换到 Swarm 模式,并创建一个新的 Swarm,将自身设置为 Swarm 的第一个管理节点。
  • Swarm网络
    • 2377/tcp:用于客户端与 Swarm 进行安全通信。
    • 7946/tcp 与 7946/udp:用于控制面 gossip 分发。
    • 4789/udp:用于基于 VXLAN 的覆盖网络。

Swarm 模式下的dnsrr只对内部访问,vip:虚拟专用网,会进行负载均衡,各个节点都能访问。一个服务可以定义多个网络,这个服务就可以被多个网络访问

搭建步骤:初始化第一个管理节点 -> 加入额外的管理节点 -> 加入工作节点 -> 完成


# 初始化swarm管理节点
docker swarm init \
--advertise-addr 10.0.0.1:2377 \
--listen-addr 10.0.0.1:2377

## 工作节点

# 列出 Swarm 中的节点。
docker node ls

1
2
3
4
5
6
7
8
9
10
11
## 本地测试环境
## 注意:管理不能有集群存储模式,例如--cluster-store and --cluster-advertise daemon
## 如果有,需要修改,修改命令参考(vim /lib/systemd/system/docker.service | systemctl daemon-reload && systemctl restart docker)
docker swarm init \
--advertise-addr 192.168.2.106:2377 \
--listen-addr 192.168.2.106:2377

## 初始化完成后会有提示:
# Swarm initialized: current node (q6jiy9n8hydc71dqdv9wl2kd2) is now a manager.
# To add a worker to this swarm, run the following command:
# docker swarm join --token SWMTKN-1-05dvket41nnkhwiuijshcx98visd9uq3s5dnlnsosw6wof8ptp-1dxk9zpeopgb6d9zk8tmcmfhv 192.168.2.106:2377
# To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

## swarm添加work节点(docker swarm join-token worker: 展示添加工作节点命令)
docker swarm join --token SWMTKN-1-05dvket41nnkhwiuijshcx98visd9uq3s5dnlnsosw6wof8ptp-1dxk9zpeopgb6d9zk8tmcmfhv 192.168.2.106:2377

## swarm添加管理节点(docker swarm join-token manager: 展示添加管理节点命令, 尽可能指出advertise-addr与listen-addr实际IP地址)
docker swarm join --token SWMTKN-1-05dvket41nnkhwiuijshcx98visd9uq3s5dnlnsosw6wof8ptp-ccyr5f9trow8v313mj7g4218p 192.168.2.106:2377 \
--advertise-addr 192.168.2.109:2377 \
--listen-addr 192.168.2.109:2377

#### 注意:管理节点也可以同时作为work节点

## 查看swarm集群节点
docker node ls 

## 在swarm集群中创建网络(只能在管理节点上创建)
docker network create -d overlay --attachable ctfx

## 查看刚创建的网络信息
docker network inspect ctfx

## 测试网络通信,任意两个节点上和子节点上分别执行
docker run -itd --name=leo_zhou1 --net=ctfx  busybox  # 启动容器,加入刚创建的ctfx网络
docker run -itd --name=leo_zhou2 --net=ctfx  busybox  # 启动容器,加入刚创建的ctfx网络

docker exec -it leo_zhou1 ping leo_zhou2

## 将work节点提升为管理节点(高可用的集群中至少要有三个节点)
docker node promote vm511  

## 取消管理节点,将其降为work节点
docker node demote vm512

#############################################
## 在swarm集群中创建服务
docker service create --name test1 alpine ping www.baidu.com

## 查看swarm集群中服务
docker service ls
docker service inspect test1

## 查看swarm集群中服务日志
docker service logs -f test1

## 测试nginx服务
docker service create --name nginx --detach=false nginx

## 更新服务(开放nginx的8080端口,开放后,任意节点的http://IP:8080都可以访问到nginx)
docker service update --publish-add 8080:80 --detach=false nginx

## 扩充服务
docker service scale nginx=3

## 查看服务进程,编辑页面,测试其负载均衡。测试结论:轮训在三个节点上访问
docker service ps nginx
docker exec -it 807c27a26ba5 bash
echo "I am the test nginx" > /usr/share/nginx/html/index.html

## 关闭swarm服务
docker service rm nginx test1

## 测试swarm自定义网络
docker service create --network ctfx --name nginx -p 8080:80 --detach=false nginx
docker service create --network ctfx --name test1 --detach=false alpine ping www.baidu.com

docker service ps alpine


docker service create --name nginx-b --endpoint-mode dnsrr --detach=false nginx

## 更新nginx-b网络服务
docker service update --network-add ctfx --detach=false nginx-b
#############################################
docker stack  # 可以定义一组服务,相当于compose

docker stack deploy -c service.yaml test   # 在swarm上部署service.yaml
docker stack deploy --compose-file docker-stack.yml vote	# 在swarm上部署docker-stack.yml
docker stack services test  # 查看服务 
docker stack ls  # 展示服务
docker stack ps test # 展示进程部署
docker stack rm test  # 查看服务 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
version: "3.4"
services:
   alpine:
    image: alpine
    command:
       - "ping"
       - "www.baidu.com"
    networks:
       - "ctfx"
    deploy:
	   endpoint_mode: dnsrr
       replicas: 2
       restart_policy:
          condition: on-failure
       resources:
          limits:
            cpus: "0.1"
            memory: 50M
	   placement:
	      constraints: [ node.role == manager ]  # 这个服务只会放在管理节点上,参考下面的部署约束
    depends_on:
        - nginx
   nginx:
    image: nginx
    networks:
      - "ctfx"
    ports:
      - "8080:80"

networks:
    ctfx:
     external: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 服务编排

  1. 服务部署 && 服务发现
  2. 服务更新: docker service update ...
  3. 服务扩缩容: docker service scale ...

swarm网络模式: ingress、ingress-link、dnsrr

vip: 负载均衡模式,可以通过浏览器访问
dnsrr: 只能开放给那些在容器间通过名字来进行互相访问提供服务的。不对外提供服务。

如果我们的服务不需要提供给客户端访问,可以定义为dnsrr的方式。

以交互方式,进入nginx镜像内看看
docker run -it --entrypoint bash nginx

查看swarm证书:
openssl x509 -in /var/lib/docker/swarm/certificates/swarm-node.crt -text

注意

mysql、redis、nsq等基础服务,使用docker单机模式配置在固定的节点上。不要放在集群中。

部署约束(可以指定服务部署在哪一台节点上或者具有哪一类特征的节点上):

  1. 节点ID,如 node.id==o2p4kw2uuw2a。
  2. 节点名称,如 node.hostname==wrk-12。
  3. 节点角色,如 node.role!=manager。
  4. 节点引擎标签,如 engine.labels.operatingsystem==ubuntu16.04。
  5. 节点自定义标签,如 node.labels.zone==prod1。

配置文件中四类顶级关键词理解:

  1. version: 版本,在没有yaml文件中第一行都会有,指定compose编写时对应的版本(例如:2.2、3.2、3.4),不要理解为该文档编辑的不同版本。
  2. services: 服务,定义整个应用服务,是必需要自定义给的,描述了各个应用服务需要的各种资源等信息。
  3. networks: 网络,定义该compose使用的网络,网络可提前创建,也可以启动时创建,可以使用加密网络。
  4. secrets: 密钥,定义出各种密钥,用于给服务、网络使用。

# Docker密钥管理和使用

  1. Docker-Secret管理和使用 (opens new window)
  • 什么可以作为Secret(将密码用文件保存后,给文件起个名称,然后用docker管理,编写services时使用)
    • 用户名密码
    • SSH Key
    • TLS认证
    • 任何不想让别人看到的数据

创建密钥命令 docker secret

# 查看docker中已经创建的密钥
docker secret ls
 
## 把密码放入一个文件里,例如password 
echo "adminadmin" > ./password

# 创建一个名为my-pwd的密钥
docker secret create my-pwd password

# 然后就可以在compose中根据名称来进行引用。
1
2
3
4
5
6
7
8
9
10

# Docker-Swarm部署总结

一、NSQ集群部署示例及总结

  1. docker-stack-nsq.yml如下
version: '3.4'
services:
  nsqlookupd:
    image: nsqio/nsq
    command: /nsqlookupd
    ports:
      - "4160:4160"
      - "4161:4161"
	#  - "4160"    # 系统自动分配端口
    #  - "4161"	   # 系统自动分配端口
    #deploy:
    #  placement:
    #     constraints: [ node.hostname==vm511 ]
    networks:
      - nsq

  nsqd:
    image: nsqio/nsq
    command: /nsqd --lookupd-tcp-address=nsqlookupd:4160
    depends_on:
      - nsqlookupd
    ports:
      - "4150:4150"		# TCP端口
    #  - "4151:4151"	# HTTP端口
    deploy:
      replicas: 1
    networks:
      - nsq

  nsqadmin:
    image: nsqio/nsq
    command: /nsqadmin --lookupd-http-address=nsqlookupd:4161
    depends_on:
      - nsqlookupd
    ports:
      - "4171:4171"
    #deploy:
    #  placement:
    #     constraints: [ node.hostname==vm511 ]   # 该服务只会部署在vm511主机上
    networks:
      - nsq

networks:
    nsq:
     driver: overlay
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  1. 总结如下:

NSQ集群总结

  1. docker stack的Yaml文件中,每个service配置中不需要配置容器名称,因为部署后,stack会根据service自动分配名称,例如上述nsqadmin,启动后会生成nsqadmin.1名称
  2. 部署后,整个集群内的节点都可以访问(即使某个节点没有服务启动,只要这个节点属于集群,该节点也会自动开放已启动的服务端口,例如nsqadmin只在vm511节点启动的,在vm512、vm513都可以访问该服务)
  3. 一个stack配置文件作为一个整体,只需配置具体某个服务,如果服务需要部署多个,只需配置副本数(replicas),在每个节点都可以看到开放的端口。
  4. 在stack中,尽量明确哪些端口需要对外暴露,如果不需要,则不要在配置中对外开放。如要的话,对外标清楚。如果出现30002、30003类似端口,这个是系统随机端口,尽量不要独自分配端口。

实现零宕机部署也非常简单。这样也可以方便地实现持续部署:

# 构建新镜像
docker build -t hub.docker.com/image .
 
# 将新镜像上传到Docker仓库
docker push hub.docker.com/image
 
# 更新服务的镜像
docker service update --image hub.docker.com/image service
1
2
3
4
5
6
7
8
上次更新: 2020-04-18 11:00:56