DockerSwarm 实践及原理
基本原理
Swarm 是使用 Docker 引擎内置的集群管理和编排工具。Swarm 集群的框架与 Hadoop 集群或其他分布式系统类似,它也是由节点构成,每一个节点就是一台主机或者虚拟机。工作的机制也是主从模式(master/slaver),节点分为两种,一种是负责管理的 Manager 另一种是具体干活的 Worker。
管理节点: 用于 Swarm 集群的管理,docker swarm 命令基本只能在管理节点执行(节点退出集群命令 docker swarm leave 可以在工作节点执行)。为了避免单点故障,一个 Swarm 集群可以有多个管理节点,但只有一个管理节点可以成为 leader,leader 通过 raft 协议实现。
工作节点: 是任务执行节点,管理节点将任务 (Task) 下发至工作节点执行。管理节点默认也作为工作节点,也可以通过配置让服务只运行在管理节点。
多个 Docker 主机就被抽象为单个大型的虚拟 Docker 主机,在管理节点上,用户可以像在单机一样在集群上操作容器或服务
基本概念
Swarm 集群中管理的对象主要由三个,Task、Service 与 Node,其中 Node 上面已经介绍过,这里解释下 Task 与 Service 的概念
任务
Swarm 中的最小的调度单位,目前一个 Task 就是一个容器
服务
Service 一般是由一组相同的 Task 组成,Service 是这组 Task 要完成的任务的一个抽象。按照其包含的 Task 的布署方式分为两种:
这两种模式是在服务创建时通过创建命令 docker service create 的 --mode 参数指定的
Service 与 Task 以及容器的关系如下:
总结成一句话就是,swarm 集群(cluster)是由节点(node)组成;服务(service)一般包含若干个任务(Task),一个 Task 就是运行一个容器,所有这些 Task 都是在节点上执行的,具体在那个个节点上执行是由管理节点调度的。
初始化 swarm 集群
安装 docker 环境
curl -sL https://gitee.com/YunFeiGuoJi/Cnblog/raw/master/shell/Scripts/docker_install.sh|sh -x -
复制代码
修改 docker 配置文件
# 修改 /etc/docker/damean.json 添加配置
"live-restore": false
# 重启
systemctl restart dockerd
复制代码
初始化 docker swarm manager
docker swarm init --advertise-addr 192.168.56.101
--advertise-addr 指定与其他 node 通信的地址
复制代码
docker swarm init 输出告诉我们:
① swarm 创建成功,swarm-manager 成为 manager node。
② 添加 worker node 需要执行的命令。
③ 添加 manager node 需要执行的命令。
如果当时没有记录下 docker swarm init 提示的添加 worker 的完整命令,可以通过 docker swarm join-token worker 查看
管理 docker swarm
节点管理
[root@master ~]# docker node --help
Usage: docker node COMMAND
Manage Swarm nodes
Commands:
demote Demote one or more nodes from manager in the swarm
inspect Display detailed information on one or more nodes
ls List nodes in the swarm
promote Promote one or more nodes to manager in the swarm
ps List tasks running on one or more nodes, defaults to current node
rm Remove one or more nodes from the swarm
update Update a node
Run 'docker node COMMAND --help' for more information on a command.
复制代码
# 查看节点
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
8x5iogo0c2d7cl9edjvbacxio * master Ready Active Leader 19.03.12
shihkplhlozcttq3m8va4129c node01 Ready Active 19.03.12
# 删除节点
docker node rm shihkplhlozcttq3m8va4129c
复制代码
服务管理
docker service create --name app1 --network yfgj --replicas 2 --entrypoint "sleep 1d" busybox
docker service create --name app2 --network yfgj --replicas 2 nginx
# 查看生成的vip
docker service inspect --format='{{json .Endpoint.VirtualIPs}}' app1
[{"NetworkID":"9ttgtpwyvnssbfnw33wchbgwi","Addr":"10.0.15.5/24"}]
docker service inspect --format='{{json .Endpoint.VirtualIPs}}' app2
复制代码
docker service scale web1=2
复制代码
docker service rm service_name
复制代码
Gui 管理界面安装
# docker-compose yml文件
version: "3.8"
services:
portainer:
image: portainer/portainer-ce:latest
ports:
- "9000:9000"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
networks:
- "tarfik-public"
deploy:
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik-public"
- "traefik.http.routers.portainer.rule=Host(`portainer.local.cluster`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls=true"
- "traefik.http.routers.portainer.tls.certresolver=foo"
- "traefik.http.routers.portainer.tls.domains[0].main=local.cluster"
- "traefik.http.routers.portainer.tls.domains[0].sans=*.local.cluster"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
replicas: 1
placement:
constraints: [node.role == manager]
networks:
traefik-public:
external: true
# 执行命令
docker stack deploy -c docker-compose.yaml docker-swarm-manager
# 访问
## 本地访问
http://localhost:9000
复制代码
服务发现
实现功能:
让 service 通过简单的方法访问到其他 service。
当 service 副本的 IP 发生变化时,不会影响访问该 service 的其他 service。
当 service 的副本数发生变化时,不会影响访问该 service 的其他 service
从使用者角度看,一个 Service 相当于它的所有 Task 的一个反向代理,它主要使用了 Linux 内核 iptables 和 IPVS 的功能来实现服务发现和负载均衡
Swarm 支持三种模式的负载均衡,它们的使用方式如下:
docker service create --endpoint-mode dnsrr --network overlay1 --replicas 3 --name nginx nginx
复制代码
docker service create --network overlay1 --replicas 3 --name nginx nginx
复制代码
docker service create --network overlay1 --replicas 3 -p 80:80 --name nginx nginx
复制代码
创建覆盖网络
docker network create \
--driver overlay \
--gateway 10.0.4.1 \
--subnet 10.0.4.0/22 \
--ip-range 10.0.4.0/24 \
--attachable yfgj_net
复制代码
暴露服务外部访问
安装 traefik 代理服务
version: "3.8"
secrets:
aliyun_region_id:
#external: true
file: "/root/.ssl/aliyun_region_id"
aliyun_access_key:
#external: true
file: "/root/.ssl/aliyun_access_key"
aliyun_secret_key:
#external: true
file: "/root/.ssl/aliyun_secret_key"
services:
traefik:
image: traefik:v2.4.8
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
#- "--entrypoints.webnossl.address=:8443"
- "--api=true"
- "--api.dashboard=true"
- "--api.debug=true"
- "--accesslog=true"
- "--log=true"
- "--log.filepath=/tmp/traefik.err.log"
- "--accesslog.filepath=/tmp/traefik.access.log"
- "--providers.docker.network=yfgj_net"
- "--certificatesresolvers.foo.acme.dnschallenge=true"
- "--certificatesresolvers.foo.acme.dnschallenge.provider=alidns"
- "--certificatesresolvers.foo.acme.dnschallenge.resolvers=114.114.114.114:53,8.8.8.8:53"
- "--certificatesresolvers.foo.acme.keytype=EC256" #RSA4096
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.foo.acme.email=xxxx"
- "--certificatesresolvers.foo.acme.storage=/letsencrypt/acme.json"
- "--metrics.prometheus=true"
- "--metrics.prometheus.buckets=0.100000, 0.300000, 1.200000, 5.000000"
- "--metrics.prometheus.addEntryPointsLabels=true"
- "--metrics.prometheus.addServicesLabels=true"
- "--entryPoints.metrics.address=:8082"
- "--metrics.prometheus.entryPoint=metrics"
ports:
- "80:80"
- "443:443"
- "8080:8080"
- "8020:8020"
secrets:
- "aliyun_region_id"
- "aliyun_access_key"
- "aliyun_secret_key"
environment:
- "ALICLOUD_REGION_ID_FILE=/run/secrets/aliyun_region_id"
- "ALICLOUD_ACCESS_KEY_FILE=/run/secrets/aliyun_access_key"
- "ALICLOUD_SECRET_KEY_FILE=/run/secrets/aliyun_secret_key"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/root/letsencrypt:/letsencrypt"
networks:
- "yfgj_net"
deploy:
mode: replicated
replicas: 1
labels:
- "traefik.enable=true"
- "traefik.docker.network=yfgj_net"
- "traefik.http.services.traefik_traefik.loadbalancer.server.port=8080"
# http 80
- "traefik.http.routers.traefik-ui-80.rule=Host(`traefik.ctq6.cn`)"
- "traefik.http.routers.traefik-ui-80.entrypoints=web"
# http 8443
- "traefik.http.routers.traefik-ui-8443.rule=Host(`traefik-8443.ctq6.cn`)"
- "traefik.http.routers.traefik-ui-8443.entrypoints=webnossl"
- "traefik.http.routers.traefik-ui-8443.tls=true"
- "traefik.http.routers.traefik-ui-8443.tls.certresolver=foo"
- "traefik.http.routers.traefik-ui-8443.tls.domains[0].main=ctq6.cn"
- "traefik.http.routers.traefik-ui-8443.tls.domains[0].sans=*.ctq6.cn"
- "traefik.http.routers.traefik-ui-8443.middlewares=redirect-https"
# https 443
- "traefik.http.middlewares.redirect-https.redirectScheme.scheme=https"
- "traefik.http.routers.traefik-ui-443.rule=Host(`traefik-443.ctq6.cn`)"
- "traefik.http.routers.traefik-ui-443.tls=true"
- "traefik.http.routers.traefik-ui-443.tls.certresolver=foo"
- "traefik.http.routers.traefik-ui-443.tls.domains[0].main=ctq6.cn"
- "traefik.http.routers.traefik-ui-443.tls.domains[0].sans=*.ctq6.cn"
- "traefik.http.routers.traefik-ui-443.middlewares=redirect-https"
- "traefik.http.routers.traefik-ui-443.entrypoints=websecure"
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
networks:
yfgj_net:
external: true
name: yfgj_net
复制代码
# 配置域名
阿里云或者腾讯云上配置域名指向安装traefik所在节点,并将443,80,8443防火强配置为允许公网访问,配置需要访问的服务域名
# 部署traefik 服务
docker stack deploy -c docker-compose-traefik.yml traefik
# 测试traefik 域名是否生成
curl -v localhost:8080/api/http/routers|jq .
# 测试是否能访问对应的服务
curl -v -H "Host:traefik.ctq6.cn" localhost
curl -v -H "Host:traefik-443.ctq6.cn" localhost:443
curl -v -H "Host:traefik-8443.ctq6.cn" localhost:8443
# 浏览器访问
http://traefik.ctq6.cn
https://traefik-443.ctq6.cn
http://traefik-8443.ctq6.cn:8443
复制代码
安装 nginx web 服务
version: "3.8"
services:
nginx:
image: nginx:latest
logging:
driver: "json-file"
options:
max-size: "200k"
max-file: "10"
ports:
- 8088:80
networks:
- yfgj_net
volumes:
- /root/yunfeiguoji:/usr/share/nginx/html/blog/yunfeiguoji:rw
- ./default.conf:/etc/nginx/conf.d/default.conf:rw
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
deploy:
mode: replicated
replicas: 1
labels:
- "traefik.enable=true"
- "traefik.docker.network=yfgj_net"
- "traefik.http.services.nginx_nginx.loadbalancer.server.port=80"
# https 443
- "traefik.http.middlewares.redirect-https.redirectScheme.scheme=https"
- "traefik.http.routers.nginx-ui-443.rule=Host(`www.ctq6.cn`)"
- "traefik.http.routers.nginx-ui-443.tls=true"
- "traefik.http.routers.nginx-ui-443.tls.certresolver=foo"
- "traefik.http.routers.nginx-ui-443.tls.domains[0].main=ctq6.cn"
- "traefik.http.routers.nginx-ui-443.tls.domains[0].sans=*.ctq6.cn"
- "traefik.http.routers.nginx-ui-443.middlewares=redirect-https"
- "traefik.http.routers.nginx-ui-443.entrypoints=websecure"
placement:
constraints: [node.role == manager]
resources:
limits:
cpus: '0.50'
memory: 50M
reservations:
cpus: '0.25'
memory: 20M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
networks:
yfgj_net:
external: true
name: yfgj_net
复制代码
docker stack deploy nginx -c docker-conpose-nginx.yml
复制代码
评论