Dockers Compose 可以非常方便的实现单机多容器应用的部署和管理,甚至可以通过 —scale 参数实现水平扩展。但是对应多机之间容器的部署和管理就需要用到 Swarm。
Swarm 是Docker 官方提供的集群管理工具,其主要作用是把若干台 Docker 主机抽象为一个整体,并且通过一个入口统一管理这些 Docker 主机上的各种 Docker 资源。
基础架构
节点
Swarm 是一种集群架构, 集群中包含 Manager 和 Worker 两类节点(Node)。
- Manager:是管理节点,管理 cluster 的状态。通过 Manager 节点去部署service;
- Worker:是工作节点,manager在水平扩展 service 数量时,会将 service 平均分配至每个 Worker 节点上面。Worker节点不具备管理功能。
- Manager 节点的数据存储在一个内置分布式存储数据库中( Internal distributed state store ),不同的 Manager 之间通过 Raft 协议同步数据,Raft 协议能保证 Manager 节点的数据是对称的。
-
服务和任务
任务(Task) 是 Swarm 中的最小调度单位,目前来说就是一个单一的容器,服务(Service)是指一组任务的集合,服务定义了任务的属性。
服务有两种模式: replicated:按照一定规则在各个节点运行指定个数的任务,Service 可以水平扩展
- global:每个节点上运行一个任务 ,Service 不可以水平扩展
两种可以模式通过 docker service create 的 —mode 参数指定。
容器、任务、服务的关系
基本操作
搭建实验环境
实验环境:创建一个三个节点的 swarm 集群
# 初始化一个 Manager 节点
docker swarm init --advertise-addr=192.168.240.119
# 查看加入集群命令
docker swarm join-token worker/manager
# worker 节点
docker swarm join --token SWMTKN-1-4mrd0oqwl9ch3ygdwwhglsto9wbrpvvvus7rs8t57c6ktaa34m-6m4xy67hortwmqd420yu8xt9d 192.168.240.119:2377
# manager 节点
docker swarm join --token SWMTKN-1-4mrd0oqwl9ch3ygdwwhglsto9wbrpvvvus7rs8t57c6ktaa34m-53x7qw7leybbfbhqxusaskq97 192.168.240.119:2377
# 离开集群
docker swarm leave
# 查看集群中节点
docker node ls
service 的创建和水平扩展
# 创建一个 service
docker service create -d --name test busybox /bin/sh -c "while true; do sleep 3600; done"
# 进入 service 中的某个容器
docker exec -it test sh
# 查看 service 中的容器
docker service ps test
# 水平扩展,servie 中的某个容器 down 掉了之后,会自动启动一个新的容器
docker service scale test=5
# 删除某个 service
docker service rm test
service 部署 wordpress
# 部署 mysql
docker service create -d --name mysql --mount type=volume,source=mysql,destination=/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wordpress --network demo mysql:5.7
# 部署 wordpress
docker service create -d --name wordpress -e WORDPRESS_DB_HOST=mysql -e WORDPRESS_DB_PASSWORD=root --network demo -p 8080:80 wordpress
集群服务间通信
实验:创建两个服务,测试服务间通信情况
# 创建 service test1
docker service create -d --name test1 --network demo busybox:1.28.3 /bin/sh -c "while true; do sleep 3600; done"
# 创建 service test2
docker service create -d --name test2 --network demo busybox:1.28.3 /bin/sh -c "while true; do sleep 3600; done"
# 扩展 service test2
docker service scale test2=3
# 查看 test1 部署在哪个节点
docker service ps test1
# 进入 test1 中的容器
docker exec -it sh
# 测试 ping 和 nslookup
ping test2
nslookup test2
nslookup tasks.test2
# 结论
Swarm 模式下 Service 之间的通信是通过 VIP (虚拟IP) 实现的
注意:上面两个服务能够通信,是因为他们在同一个网络下,不同网络下的服务是无法直接通信的。
每个服务都有一个 虚拟的 IP 地址,并且该 IP 地址映射到与该服务关联的多个容器的 IP 地址。在这种情况下,与服务关联的服务 IP 不会改变,即使与该服务关联的容器挂掉并重新启动。
在上面的例子中,总共有两个服务 myservice 和 client,其中 myservice 有两个容器,这两个服务在同一个网络中。在 client 里针对 docker.com 和 myservice 各执行了一个 curl 操作,下面是执行的流程:
- 首先会对DNS 查询进行初始化
- 容器内置的域名解析器在 127.0.0.11:53 拦截到这个 DNS 查询请求,并把请求转发到宿主机上 Docker 引擎的 DNS 服务中
- myservice 被解析成服务对应的 VIP,在接下来的 内部负载均衡阶段再被解析成一个具体容器的IP地址。如果是 myservice 是一个容器名称这一步会直接解析成容器对应的 IP 地址。
- docker.com 在 mynet 网络上不能被解析成服务,所以这个请求被转发到配置好的默认DNS服务器上
ingress network
初始化或加入 Swarm 集群时会自动创建ingress
网络,ingress
网络是一个特殊的overlay
网络,用于服务节点间的负载均衡。当任何 Swarm 节点在发布的端口上接收到请求时,它将该请求交给一个名为IPVS
的模块。IPVS
跟踪参与该服务的所有容器的IP地址,并将请求转发到其中某一个容器上。Docker Stack 多服务集群部署
部署 Wordpress ```yamldocker-compose.yml
version: ‘3’
services:
web: image: wordpress ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: root
networks:
- demo
depends_on:
- mysql
deploy:
mode: replicated
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress volumes:
- mysql-data:/var/lib/mysql
networks:
- demo
deploy:
mode: global
placement:
constraints:
- node.role == manager
volumes: mysql-data:
networks: demo: driver: overlay
部署
docker stack deploy wordpress -c=docker-compose.yml # -c 等价于 —compose-file
删除
docker stack rm wordpress
查看 stack 下的服务
docker stack services wordpress
<a name="UcPH6"></a>
## Docker Secret
Docker 中的 Secret 包含:
- 用户名、密码
- SSH Key
- TLS 认证
- 任何不想让别人看到的数据
<a name="mRgNN"></a>
### Secret 管理和使用
- Secret 存储在 Swram Manager 节点的 Raft database 中(数据默认是加密的)
- Secret 可以 assign 给一个 **service**,这个 service 就能使用这个 secret
- **在 container 内部 Secret 看起来像文件,但实际是存在内存中的**
```bash
# password.txt
a1234567
# 从文件创建 secret
docker secret create pwd password.txt
# 从标准输入创建 secret
echo "admin" | docker secret create pwd2 -
# 查看当前 secret
docker secret ls
# 创建一个 service 并传入 secret
docker service create -d --name test --secret pwd busybox /bin/sh -c "while true; do sleep 3600; done"
# 进入容器中
docker exec -it 8224bbd0d8cc sh
# 进入容器中 secret 存在的目录
cd /run/secrets
# 查看 secret
cat pwd # a1234567
# 创建一个 mysql,通过 secret 指定 root 用户的密码
docker service create -d --name db --secret pwd2 -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/pwd2 mysql:5.7
Secret 在 Stack 中的使用
# docker-compose.yml
version: '3.6'
services:
web:
image: wordpress
ports:
- 8080:80
secrets:
- db-pw
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: /run/secrets/db-pw
networks:
- demo
depends_on:
- mysql
deploy:
mode: replicated
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
mysql:
image: mysql:5.7
secrets:
- db-pw
environment:
MYSQL_ROOT_PASSWORD: /run/secrets/db-pw
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- demo
deploy:
mode: global
placement:
constraints:
- node.role == manager
volumes:
mysql-data:
networks:
demo:
driver: overlay
# 不建议在这里指定 secrets 参数,建议创建好了 secret 之后在创建服务
secrets:
db-pw:
file: ./password.txt
# 部署
docker stack deploy wordpress -c=docker-compose.yml # -c 等价于 --compose-file
# 删除
docker stack rm wordpress
# 查看 stack 下的服务
docker stack services wordpress
注意:版本号小于 3.1 会报错:secrets Additional property secrets is not allowed