环境说明:
使用docker-machine的实验环境,创建swarm集群
IP | Hostname | Node |
---|---|---|
192.168.20.201 | host1 | worker |
192.168.20.202 | host2 | worker |
192.168.20.203 | master | manager |
[root@master ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
tnrfe0xadx6ry9dp1genwnkyr host1 Ready Active 18.09.0
jpx8fdobbc8pr4kd7bpieogug host2 Ready Active 18.09.0
l3uepmyv6bhafawmljop5ek9c * master Ready Active Leader 18.09.0
在swarm集群里通过service部署wordpress
所有操作都在管理节点192.168.20.203上进行
1.创建一个overlay的network:
# docker network create -d overlay demo
1iuc6sxtqxdm74jktgq91ybop
# docker network ls
NETWORK ID NAME DRIVER SCOPE
bb036d9a6179 bridge bridge local
1iuc6sxtqxdm demo overlay swarm
dea77c20e6d4 docker_gwbridge bridge local
2de632b4004c host host local
ty3kfj24k0go ingress overlay swarm
a56a6ac11428 none null local
2.运行service:
# mysql
# docker service create --name mysql --env MYSQL_ROOT_PASSWORD=root --env MYSQL_DATABASE=wordpress --network demo --mount type=volume,source=mysql-data,destination=/var/lib/mysql mysql:5.7
# wordpress
# docker service create --name wordpress -p 80:80 --env WORDPRESS_DB_PASSWORD=root --env WORDPRESS_DB_HOST=mysql --network demo wordpress
--mount type=volume,source=mysql-data,destination=/var/lib/mysql
等价于在docker run
时指定-v mysql-data:/var/lib/mysql
。
3.查看service:
# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
4mgu43o5z5wa mysql replicated 1/1 mysql:5.7
pdt70uonc44p wordpress replicated 1/1 wordpress:latest *:80->80/tcp
# docker service ps wordpress
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
5z32wlxzqafc wordpress.1 wordpress:latest host1 Running Running 3 minutes ago
登录到host1上查看
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c95b340e8659 wordpress:latest "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 80/tcp wordpress.1.5z32wlxzqafcp16xve8lkivap
到此,我们的wordpress就部署好了,可以发现访问集群中任何一个节点的80端口都能够访问wordpress,这其实是swarm的 routing mesh的作用
`
集群服务间通信之Routing Mesh
Routing Mesh
的两种体现:
Internal
:Container和Container之间访问通过overlay网络(通过VIP)Ingress
:如果服务有绑定接口,则此服务可以通过任意swarm节点的相应接口访问
容器间互访
微服务架构的应用由若干 service 组成。比如有运行 httpd 的 web 前端,有提供缓存的 memcached,有存放数据的 mysql,每一层都是 swarm 的一个 service,每个 service 运行了若干容器。在这样的架构中,service 之间是必然要通信的。
服务发现:
一种实现方法是将所有 service 都 publish 出去,然后通过 routing mesh 访问。但明显的缺点是把 memcached 和 mysql 也暴露到外网,增加了安全隐患。
如果不 publish,那么 swarm 就要提供一种机制,能够:
- 让 service 通过简单的方法访问到其他 service。
- 当 service 副本的 IP 发生变化时,不会影响访问该 service 的其他 service。
- 当 service 的副本数发生变化时,不会影响访问该 service 的其他 service。
这其实就是服务发现(service discovery
)。Docker Swarm 原生就提供了这项功能,通过服务发现,service 的使用者不需要知道 service 运行在哪里,IP 是多少,有多少个副本,就能与 service 通信。
创建 overlay 网络:
要使用服务发现,需要相互通信的 service 必须属于同一个 overlay 网络,所以我们先得创建一个新的 overlay 网络。
# docker network create -d overlay demo
部署 service 到 overlay:
部署一个 web 服务,并将其挂载到新创建的 overlay 网络。
# docker service create --name web --replicas=3 --network demo nginx
部署一个 test 服务用于测试,挂载到同一个 overlay 网络。
# docker service create --name test --network demo busybox:1.28.3 sleep 100000000
sleep 100000000 的作用是保持 busybox 容器处于运行的状态
验证:
# docker service ps test
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
nrjyalzghbtl test.1 busybox:1.28.3 host2 Running Running 28 seconds ago
登录到test所在节点
[root@host2 ~]# docker exec test.1.nrjyalzghbtlhxez9rx6jb068 ping -c 3 web
PING web (10.0.0.2): 56 data bytes
64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.194 ms
64 bytes from 10.0.0.2: seq=1 ttl=64 time=0.128 ms
64 bytes from 10.0.0.2: seq=2 ttl=64 time=0.119 ms
可以看到web的IP是10.0.0.2
然后可以执行nslookup
命令查看每个副本的 IP
[root@host2 ~]# docker exec test.1.nrjyalzghbtlhxez9rx6jb068 nslookup tasks.web
Server: 127.0.0.11
Address 1: 127.0.0.11
Name: tasks.web
Address 1: 10.0.0.5 web.3.a0ne2us0dg78xuswgd1av75yx.demo
Address 2: 10.0.0.4 web.2.9df3f8fejnj9xcpejgnk6htvm.demo
Address 3: 10.0.0.3 web.1.5dmbr5f35dc4n8o9fb5m3cd68.demo
注意:新版busybox镜像执行nslookup会报错
可以看到10.0.0.3、10.0.0.4、10.0.0.5 才是各个副本自己的 IP。10.0.0.2 是 web service 的 VIP(Virtual IP),swarm 会将对 VIP 的访问负载均衡到每一个副本。不过对于服务的使用者(这里是test.1),根本不需要知道 web副本的 IP,也不需要知道 web 的 VIP,只需直接用 service 的名字 web 就能访问服务。
外部访问容器
Docker Stack部署wordpress
在 Swarm 集群中也可以使用 compose 文件(docker-compose.yml
) 来配置、启动多个服务。
在swarm下使用docker-compose文件部署需要使用 docker stack
命令。
# docker stack --help
Usage: docker stack [OPTIONS] COMMAND
Manage Docker stacks
Options:
--orchestrator string Orchestrator to use (swarm|kubernetes|all)
Commands:
deploy Deploy a new stack or update an existing stack
ls List stacks
ps List the tasks in the stack
rm Remove one or more stacks
services List the services in the stack
Run 'docker stack COMMAND --help' for more information on a command.
对之前创建wordpress的yml文件做如下修改:
version: '3'
services:
web:
image: wordpress
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: root
networks:
- my-network
depends_on:
- mysql
deploy:
mode: replicated #初始化3个副本
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:
- my-network
deploy: #设置mysql只能部署到manager节点,且只能运行一个副本
mode: global
placement:
constraints:
- node.role == manager
volumes:
mysql-data:
networks:
my-network:
driver: overlay #使用overlay网络
depoly参数用法:https://docs.docker.com/compose/compose-file/#deploy
使用 compose 文件创建service:
# docker stack deploy wordpress --compose-file=docker-compose.yml
Creating network wordpress_my-network
Creating service wordpress_web
Creating service wordpress_mysql
# docker stack ls
NAME SERVICES ORCHESTRATOR
wordpress 2 Swarm
# docker stack ps wordpress
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
teexafxvv2cf wordpress_mysql.l3uepmyv6bhafawmljop5ek9c mysql:5.7 master Running Running 29 seconds ago
mzop59r1uyd3 wordpress_web.1 wordpress:latest host2 Running Running 29 seconds ago
o0cttjj5tnv6 wordpress_web.2 wordpress:latest master Running Running 30 seconds ago
yjf0po2jglw2 wordpress_web.3 wordpress:latest host1 Running Running 29 seconds ago
# docker stack services wordpress
ID NAME MODE REPLICAS IMAGE PORTS
rhgsre2kmc31 wordpress_web replicated 3/3 wordpress:latest *:8080->80/tcp
zpejq396z4k7 wordpress_mysql global 1/1 mysql:5.7
Docker Secret管理和使用
Docker 提供了 secrets 管理功能,用户可以在 Swarm 集群中安全地管理密码、密钥证书等敏感数据,并允许在多个 Docker 容器实例之间共享访问指定的敏感数据。
Secret management:
- 存在swarm manager节点Raft database里
- Secret可以assign给一个service,这个service就能看到这个Secret
- 在container内部Secret看起来像文件,但是实际是在内存中
可以用 docker secret
命令来管理敏感信息
创建Secret有两种方式:
# docker secret create --help
Usage: docker secret create [OPTIONS] SECRET [file|-]
Create a secret from a file or STDIN as content
Options:
-d, --driver string Secret driver
-l, --label list Secret labels
--template-driver string Template driver
第一种:通过文件创建:
# cat password
adminadmin
# docker secret create my-pw password
xpspv8d47tbtk9zp95f1i9j15
# docker secret ls
ID NAME DRIVER CREATED UPDATED
xpspv8d47tbtk9zp95f1i9j15 my-pw About a minute ago About a minute ago
第二种:从标准输入创建
# echo "adminadmin" | docker secret create my-pw2 -
eoodcgcq94jvrhzuu35kgwks3
# docker secret ls
ID NAME DRIVER CREATED UPDATED
xpspv8d47tbtk9zp95f1i9j15 my-pw 2 minutes ago 2 minutes ago
eoodcgcq94jvrhzuu35kgwks3 my-pw2 5 seconds ago 5 seconds ago
在service中使用secret:
# docker service create --name db --secret my-pw -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/my-pw mysql:5.7
登录到db所在节点,进入容器查看:
# docker exec -it 2dbd3d8259a9 sh
# cat /run/secrets/my-pw
adminadmin
#
# mysql -uroot -padminadmin
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.24 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
# docker service create --name db --secret source=my-pw,target=mysql_root_password -e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" mysql:5.7
--secret source=my-pw,target=mysql_root_password
的作用就是指定使用 secret my-pw,然后把器解密后的内容保存到容器 /run/secrets/mysql_root_password
文件中,文件名称 mysql_root_password
由 target
指定。
相比于在环境变量中直接指定密码,使用secret可以将密码和容器解耦和,secret可以由管理员创建,而运行容器的用户只需使用 secret 而不需要知道 secret 的内容。
案例:创建一个 MySQL service,将密码保存到 secret 中。我们还会创建一个 WordPress service,它将使用 secret 连接 MySQL。
使用 secret 避免在 image 中存放敏感信息,或者在命令行中直接传递敏感数据
$ docker service create \
--name mysql \
--replicas 1 \
--network mysql_private \
--mount type=volume,source=mydata,destination=/var/lib/mysql \
--secret source=mysql_root_password,target=mysql_root_password \
--secret source=mysql_password,target=mysql_password \
-e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
-e MYSQL_PASSWORD_FILE="/run/secrets/mysql_password" \
-e MYSQL_USER="wordpress" \
-e MYSQL_DATABASE="wordpress" \
mysql:latest
$ docker service create \
--name wordpress \
--replicas 1 \
--network mysql_private \
--publish target=30000,port=80 \
--mount type=volume,source=wpdata,destination=/var/www/html \
--secret source=mysql_password,target=wp_db_password,mode=0400 \
-e WORDPRESS_DB_USER="wordpress" \
-e WORDPRESS_DB_PASSWORD_FILE="/run/secrets/wp_db_password" \
-e WORDPRESS_DB_HOST="mysql:3306" \
-e WORDPRESS_DB_NAME="wordpress" \
wordpress:latest
Secret在stack中的使用
version: '3'
services:
web:
image: wordpress
ports:
- 8080:80
secrets:
- my-pw
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/my-pw
networks:
- my-network
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:
- my-pw
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my-pw
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- my-network
deploy:
mode: global
placement:
constraints:
- node.role == manager
volumes:
mysql-data:
networks:
my-network:
driver: overlay
#secrets: #如果secret没有事先创建可以在yml中指定(不建议)
# my-pw:
# file: ./password
Visualizer
Visualizer是一个图形化显示docker swarm集群中各个节点状态以及运行了什么容器和负载情况的监控工具
注意:
docker.sock
这个文件必须要挂载到容器里面去,不然读取不到集群的状态
启动:
# docker run -d --name visualizer -p 80:8080 -v /var/run/docker.sock:/var/run/docker.sock dockersamples/visualizer
或者通过配置文件创建:
# cat visualizer.yml
version: '3'
services:
visualizer:
image: dockersamples/visualizer:stable
ports:
- 80:8080
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints:
- node.role == manager
# docker stack deploy visualizer --compose-file=visualizer.yml
可以横向扩展web services,查看效果:
# docker service scale wordpress_web=4
# docker service ps wordpress_web
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
srtm9te7yqbj wordpress_web.1 wordpress:latest host1 Running Running 6 minutes ago
151hhhiqtcqg wordpress_web.2 wordpress:latest host2 Running Running 6 minutes ago
nt28i3esyij4 wordpress_web.3 wordpress:latest master Running Running 6 minutes ago
itz5hexoop8w wordpress_web.4 wordpress:latest host1 Running Running about a minute ago
service滚动更新
滚动更新降低了应用更新的风险,如果某个副本更新失败,整个更新将暂停,其他副本则可以继续提供服务。同时,在更新的过程中,总是有副本在运行的,因此也保证了业务的连续性。
示例:部署两副本的服务,镜像使用 httpd:2.2.31,然后将其更新到 httpd:2.2.32。
# docker service create --name myweb --replicas=2 httpd:2.2.31
# docker service ps myweb
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
lq7g3n0keb7z myweb.1 httpd:2.2.31 host2 Running Running 2 minutes ago
7gtztdiy5hs4 myweb.2 httpd:2.2.31 host1 Running Running about a minute ago
将 service 更新到 httpd:2.2.32:
# docker service update --image httpd:2.2.32 myweb
--image 指定新的镜像。
# docker service ps myweb
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
e4s92g98ssn9 myweb.1 httpd:2.2.32 host2 Running Running about a minute ago
lq7g3n0keb7z \_ myweb.1 httpd:2.2.31 host2 Shutdown Shutdown 4 minutes ago
fazxhm2t1s4z myweb.2 httpd:2.2.32 master Running Running 4 minutes ago
7gtztdiy5hs4 \_ myweb.2 httpd:2.2.31 host1 Shutdown Shutdown 6 minutes ago
注意:在更新的过程中,用户可能访问到新的内容也可能访问到旧的内容
Swarm 将按照如下步骤执行滚动更新:
- 停止第一个副本。
- 调度任务,选择 worker node。
- 在 worker 上用新的镜像启动副本。
- 如果副本(容器)运行成功,继续更新下一个副本;如果失败,暂停整个更新过程。
默认配置下,Swarm 一次只更新一个副本,并且两个副本之间没有等待时间。我们可以通过 --update-parallelism
设置并行更新的副本数目,通过 --update-delay
指定滚动更新的间隔时间。
# docker service update --replicas 6 --update-parallelism 2 --update-delay 1m30s myweb
service 增加到六个副本,每次更新两个副本,间隔时间一分半钟。
Swarm 还有个方便的功能是回滚,如果更新后效果不理想,可以通过 --rollback
快速恢复到更新之前的状态。
注意,
--rollback
只能回滚到上一次执行 docker service update 之前的状态,并不能无限制地回滚。
其他更新可以查看帮助:
# docker service update --help
如端口更新:
docker service update --publish-rm 8080:80 --publish-add 80:80 web