使用 Docker Stack 部署多服务集群

1,182 阅读4分钟

前言


单机模式下,我们可以使用 Docker Compose 来编排多个服务,而在 上一篇文章 中介绍的 Docker Swarm 只能实现对单个服务的简单部署。于是就引出了本文的主角 Docker Stack ,通过 Docker Stack 我们只需对已有的 docker-compose.yml 配置文件稍加改造就可以完成 Docker 集群环境下的多服务编排。

正文


  • 首先创建一个 docker-compose.yml 文件,使用 Docker Compose v3 语法

    内容比较简单,一个有四个实例的 nginx 服务,两个只部署在 manager 节点上的单实例监控工具服务:portainervisualizer

version: "3"

services:
  nginx:
    image: nginx:alpine
    ports:
      - 80:80
    deploy:
      mode: replicated
      replicas: 4

  visualizer:
    image: dockersamples/visualizer
    ports:
      - "9001:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]

  portainer:
    image: portainer/portainer
    ports:
      - "9000:9000"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]
  • 部署服务
$ docker stack deploy -c docker-compose.yml stack-demo
  • 部署成功之后查看详情
$ docker stack services stack-demo 
ID                  NAME                    MODE                REPLICAS            IMAGE                             PORTS
4yb35ywqvo49        stack-demo_portainer    replicated          1/1                 portainer/portainer:latest        *:9000->9000/tcp
mzd2volqug28        stack-demo_nginx        replicated          4/4                 nginx:alpine                      *:80->80/tcp
r0zlzpp3wujg        stack-demo_visualizer   replicated          1/1                 dockersamples/visualizer:latest   *:9001->8080/tcp
  • 在浏览器中访问监控工具,对应端口如下:
portainer  ——→ ip:9000
visualizer ——→ ip:9001

注意:如果有多个 manager 节点,portainer 和 visualizer 可能分别部署在两台机器上,所以ip可能会不一样。

  • 修改 docker-compose.yml 文件后重新部署即可完成对修改内容的更新
$ docker stack deploy -c docker-compose.yml stack-demo

关于负载均衡


评论区有小伙伴提到,容器间通过服务名 ( 比如文中的 nginx ) 通讯时,对应的 IP 却和容器的实际 IP 对不上。出现这个情况是因为负载均衡( 对外表现为一个服务,内部为多个服务 )。下面是我做的试验,希望能帮助大家理解。

  1. 按上面的配置启动集群 ( 由两台服务器构成 )

  2. manager 节点服务器中看下运行的服务

    $ docker ps
    CONTAINER ID IMAGE NAMES
    9b96f07bbb91 dockersamples/visualizer:latest stack-demo_visualizer.1.p5hy7gsc50vbm0wkxm1c17rl6
    942dd34d024e nginx:alpine stack-demo_nginx.4.tp6u05jmg9iuookqc9i9e11kz
    706ae42e0089 nginx:alpine stack-demo_nginx.2.vnlmlky5m5qy7l8qxq6k5nllk
    6dba55dd7d63 portainer/portainer:latest stack-demo_portainer.1.yv76gf0i7gou2awen44kshm1j
    
  3. 在这台服务器上启动了两个 nginx 容器实例,随便进一个实例看下 IP

    $ docker exec -it stack-demo_nginx.2.vnlmlky5m5qy7l8qxq6k5nllk ifconfig
    eth0 Link encap:Ethernet HWaddr 02:42:0A:FF:00:3E
    inet addr:10.255.0.62 Bcast:10.255.255.255 Mask:255.255.0.0
    eth1 Link encap:Ethernet HWaddr 02:42:0A:00:06:07
    inet addr:10.0.6.7 Bcast:10.0.6.255 Mask:255.255.255.0
    eth2 Link encap:Ethernet HWaddr 02:42:AC:13:00:04
    inet addr:172.19.0.4 Bcast:172.19.255.255 Mask:255.255.0.0
    lo Link encap:Local Loopback
    inet addr:127.0.0.1 Mask:255.0.0.0
    

    发现容器中绑了3个网卡 eth0eth1eth2 ,我猜想分别对应 整个 Swarm 集群的局域网当前 Stack 集群的局域网当前主机下 Compose 服务的局域网 三个网络。

  4. 查看当前主机下的 docker 网络

    $ docker network ls
    NETWORK ID NAME DRIVER SCOPE
    bd4fa8219483 bridge bridge local
    e51735fef0d6 docker_gwbridge bridge local
    26360437865a host host local
    yvupj4ex3odl ingress overlay swarm
    f0a0190c3b1f none null local
    oft930l7jpdn stack-demo_default overlay swarm
    
  5. 上一步看到有两个 swarm 的网络,进去看下具体信息

    $ docker network inspect ingress
    "IPAM": {
        "Config": [
            {
                "Subnet": "10.255.0.0/16",
                "Gateway": "10.255.0.1"
            }
        ]
    },
    "Containers": {
        "6dba55dd7d63f7166e2e0ee3afed8e427089b7140d62f39a835d3145a058b868": {
            "Name": "stack-demo_portainer.1.yv76gf0i7gou2awen44kshm1j",
            "IPv4Address": "10.255.0.59/16",
        },
        "706ae42e00890444087aa6d51ccb966b76b5ad4c985b48fdf5215c192bcf0836": {
            "Name": "stack-demo_nginx.2.vnlmlky5m5qy7l8qxq6k5nllk",
            "IPv4Address": "10.255.0.62/16",
        },
        "942dd34d024e218aad4e5034e1194a2cfa1d9be81a839ec86403cf237d41368b": {
            "Name": "stack-demo_nginx.4.tp6u05jmg9iuookqc9i9e11kz",
            "IPv4Address": "10.255.0.64/16",
        },
        "9b96f07bbb91a570bb8d26945996d77151c5633c0a6057361ab4474b393da364": {
            "Name": "stack-demo_visualizer.1.p5hy7gsc50vbm0wkxm1c17rl6",
            "IPv4Address": "10.255.0.66/16",
        },
        "ingress-sbox": {
            "Name": "ingress-endpoint",
            "IPv4Address": "10.255.0.2/16",
        }
    }
    

    内容太多就省略其他无关内容了,从上面的信息已经可以证明第三步的猜想了 ( ingress 对应 Swarm 集群 , stack-demo_default 对应 Stack 集群 ) ,要进一步确认可以再看下另一台服务器上的网络。

  6. 通过容器 nginx.4 使用服务名的方式 ping 一下 nginx

    $ docker exec -it stack-demo_nginx.4.tp6u05jmg9iuookqc9i9e11kz ping nginx
    PING nginx (10.0.6.5): 56 data bytes
    64 bytes from 10.0.6.5: seq=0 ttl=64 time=0.121 ms
    

    发现 IP10.0.6.5 ,属于 stack-demo_default 网络,但是在两台服务器的 docker 网络详情里面都找不到这个实例。

  7. 在容器 nginx.4 中安装 curl 然后再访问 nginx 看下效果

    $ docker exec -it stack-demo_nginx.4.tp6u05jmg9iuookqc9i9e11kz sh -c 'echo -e "https://mirrors.ustc.edu.cn/alpine/latest-stable/main\nhttps://mirrors.ustc.edu.cn/alpine/latest-stable/community" > /etc/apk/repositories && apk --update add curl'
    $ docker exec -it stack-demo_nginx.4.tp6u05jmg9iuookqc9i9e11kz curl nginx
    

    可以看到访问成功,再多调用几次。

  8. 打印 nginx 容器的日志 ( 可以多开几个终端打印日志,再访问 nginx 看下实时日志的效果,这样更直观 )

    $ docker logs stack-demo_nginx.2.vnlmlky5m5qy7l8qxq6k5nllk && echo "---分界线---" && docker logs stack-demo_nginx.4.tp6u05jmg9iuookqc9i9e11kz
    10.0.6.4 - - [06/Dec/2018:10:15:18 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.60.0" "-"
    ---分界线---
    10.0.6.4 - - [06/Dec/2018:10:13:16 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.60.0" "-"
    10.0.6.4 - - [06/Dec/2018:10:15:19 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.60.0" "-"
    

    可以看到明显的负载均衡的效果,请求都来自 10.0.6.4,它就是 stack-demo_default 网络中一个名为 lb- 开头的实例,它就是这个负载均衡的入口。

    "lb-stack-demo_default": {
    	"Name": "stack-demo_default-endpoint",
    	"IPv4Address": "10.0.6.4/24",
    }
    
  9. 再去另一台服务器看下 nginx 的日志

    发现并没有访问记录,说明这个负载均衡仅限于 当前服务器相同服务多个实例,不会跨服务器负载均衡。

  10. 在另一台服务器上再重复 7 & 8 两个步骤

    发现另一台服务器上不存在名为 lb- 开头的实例,而负载均衡的入口是其中一个普通的 nginx 实例。

总结下:

整个请求的调用流程应该就是:通过服务名 nginx 访问 -- 指向 --> stack 集群网关 ( 10.0.6.5 ) -- 转发 --> stack 集群中,位于当前服务器的负载均衡实例 ( 10.0.6.4 ) -- 分发 --> 最终的应用

相关命令


命令描述
docker stack deploy部署新的堆栈或更新现有堆栈
docker stack ls列出现有堆栈
docker stack ps列出堆栈中的任务
docker stack rm删除一个或多个堆栈
docker stack services列出堆栈中的服务

参考文章



系列文章


Docker 学习总结

Docker 常用指令详解

使用 Dockerfile 构建镜像

使用 Docker Compose 构建容器

Docker Daemon 连接方式详解

Docker 下的网络模式


转载请注明出处:www.jianshu.com/p/1db6f0150…