前言
單機模式下,我們可以使用 Docker Compose 來編排多個服務,而在 上一篇文章 中介紹的 Docker Swarm 只能實現(xiàn)對單個服務的簡單部署。于是就引出了本文的主角 Docker Stack ,通過 Docker Stack 我們只需對已有的 docker-compose.yml 配置文件稍加改造就可以完成 Docker 集群環(huán)境下的多服務編排。
正文
-
首先創(chuàng)建一個 docker-compose.yml 文件,使用 Docker Compose v3 語法
內容比較簡單,一個有四個實例的 nginx 服務,兩個只部署在 manager 節(jié)點上的單實例監(jiān)控工具服務:portainer 和 visualizer
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
- 在瀏覽器中訪問監(jiān)控工具,對應端口如下:
portainer ——→ ip:9000
visualizer ——→ ip:9001
注意:如果有多個 manager 節(jié)點,portainer 和 visualizer 可能分別部署在兩臺機器上,所以ip可能會不一樣。
- 修改 docker-compose.yml 文件后重新部署即可完成對修改內容的更新
$ docker stack deploy -c docker-compose.yml stack-demo
關于負載均衡
評論區(qū)有小伙伴提到,容器間通過服務名 ( 比如文中的 nginx ) 通訊時,對應的 IP 卻和容器的實際 IP 對不上。出現(xiàn)這個情況是因為負載均衡( 對外表現(xiàn)為一個服務,內部為多個服務 )。下面是我做的試驗,希望能幫助大家理解。
按上面的配置啟動集群 ( 由兩臺服務器構成 )
-
在 manager 節(jié)點服務器中看下運行的服務
$ 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 -
在這臺服務器上啟動了兩個 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發(fā)現(xiàn)容器中綁了3個網(wǎng)卡 eth0 、eth1 、eth2 ,我猜想分別對應 整個 Swarm 集群的局域網(wǎng)、當前 Stack 集群的局域網(wǎng) 、當前主機下 Compose 服務的局域網(wǎng) 三個網(wǎng)絡。
-
查看當前主機下的 docker 網(wǎng)絡
$ 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 -
上一步看到有兩個 swarm 的網(wǎng)絡,進去看下具體信息
$ 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", } }內容太多就省略其他無關內容了,從上面的信息已經(jīng)可以證明第三步的猜想了 ( ingress 對應 Swarm 集群 , stack-demo_default 對應 Stack 集群 ) ,要進一步確認可以再看下另一臺服務器上的網(wǎng)絡。
-
通過容器 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發(fā)現(xiàn) IP 是 10.0.6.5 ,屬于 stack-demo_default 網(wǎng)絡,但是在兩臺服務器的 docker 網(wǎng)絡詳情里面都找不到這個實例。
-
在容器 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可以看到訪問成功,再多調用幾次。
-
打印 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 網(wǎng)絡中一個名為
lb-開頭的實例,它就是這個負載均衡的入口。"lb-stack-demo_default": { "Name": "stack-demo_default-endpoint", "IPv4Address": "10.0.6.4/24", } -
再去另一臺服務器看下 nginx 的日志
發(fā)現(xiàn)并沒有訪問記錄,說明這個負載均衡僅限于 當前服務器 、相同服務 的 多個實例,不會跨服務器負載均衡。
-
在另一臺服務器上再重復 7 & 8 兩個步驟
發(fā)現(xiàn)另一臺服務器上不存在名為
lb-開頭的實例,而負載均衡的入口是其中一個普通的 nginx 實例。
總結下:
整個請求的調用流程應該就是:通過服務名 nginx 訪問 -- 指向 --> stack 集群網(wǎng)關 ( 10.0.6.5 ) -- 轉發(fā) --> stack 集群中,位于當前服務器的負載均衡實例 ( 10.0.6.4 ) -- 分發(fā) --> 最終的應用 。
相關命令
| 命令 | 描述 |
|---|---|
| docker stack deploy | 部署新的堆?;蚋卢F(xiàn)有堆棧 |
| docker stack ls | 列出現(xiàn)有堆棧 |
| docker stack ps | 列出堆棧中的任務 |
| docker stack rm | 刪除一個或多個堆棧 |
| docker stack services | 列出堆棧中的服務 |