# 利用 Docker Swarm 一次部屬多台主機自動做HA機制 ## Docker Swarm 測試配置規劃 正常來說要做到好的 HA 機制可能需要7台以上的 server,然後設定2台為 manager。5台為 worker 之類的。 但因為範例簡化:只準備了2台 server,分別為 manager 的 Server1 和 worker 的 Server2。 ## 1. Docker Swarm init 第一步驟,請先確保這些server的網路是互通的 接著在你要當作 manager 的 Server1 輸入 swarm init 命令,這時 Server1 會切換到 Swarm 模式,並被設置為 manager `--advertise-addr`參數是當該主機有多張網卡、虛擬網卡 ip 時,要指定哪一個作為露出。 ``` docker swarm init --advertise-addr {ip} ``` 成功後出現以下輸出 ```= [root@server1 /]# docker swarm init --advertise-addr 10.20.250.55 Swarm initialized: current node (02fdsjkjkjgkjsd93jok53jr5g12) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-4k6ab89ervnewkoioi1ojg6t7pxeczrk5x2f2nmiivbm88vi73-dk2yw470fjlcc6ys3b30yyv0u 10.20.250.55:2377 To add a manager to this swarm, run `docker swarm join-token manager` and follow the instructions. ``` ## 2. 其他主機加入建立好的 Swarm 叢集 1. 進入 Server2 想要將 Server2 設定為該 Swarm 叢集的 **worker** ,把剛才的指令複製下來貼上執行。 :::success ```= docker swarm join --token SWMTKN-1-4k6ab89ervnewkoioi1ojg6t7pxeczrk5x2f2nmiivbm88vi73-dk2yw470fjlcc6ys3b30yyv0u 10.20.250.55:2377 ``` 成功後會出現輸出畫面 ``` This node joined a swarm as a worker. ``` ::: 2. 若是要設定某台主機為該 Swarm 叢集的另一個 **manager**,則在原 mamager (這邊是 Server1)使用指令 :::info ```= docker swarm join-token manager ``` 接著就會產生讓其他主機 join magager 用的 token ```= [root@server1 /]# docker swarm join-token manager To add a manager to this swarm, run the following command: docker swarm join --token SWMTKN-1-dijk4uv9ervnewkoioi1ojg6t766gocrk5x2f2nmiivbm88vi-3ddfk2yw470fjlcc6ys3b30yynffd 10.20.250.55:2377 ``` 然後將指令複製下來,進入其他主機輸入即可 ```cmd= docker swarm join --token SWMTKN-1-dijk4uv9ervnewkoioi1ojg6t766gocrk5x2f2nmiivbm88vi-3ddfk2yw470fjlcc6ys3b30yynffd 10.20.250.55:2377 ``` 成功後會出現輸出畫面 ``` This node joined a swarm as a manager. ``` ::: ## 3. 加入完畢後,請在 manager 輸入以下指令,查看該 Swarm 中所有的 Node ```gherkin= docker node ls ``` 可以看到輸出如下 ```= [root@Server1 /]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION jbbgvht2vi03rdqbohv3ux0i8 * Server1 Ready Active Leader 23.0.4 mb65pbdvyhcqcu3d5c2kyb0z5 Server2 Ready Active 23.0.4 ``` :::danger ### **這樣就完成了初步的 Swarm 環境設定,除非有要變動環境不然之後都不需要再重跑 1~3 步驟** ::: ## 4. 配置 Swarm 的 docker-compose.yml 1. 先自己生出一個 docker-compose , 我這邊取名為 `swarm-test.yml` swarm-test.yml 內容如下: ```gherkin= version: "3.3" services: my-backend: image: com.company.mybackend/api-server volumes: - /app/mybackend/log:/mybackend-log ports: - 7890:7890 deploy: replicas: 2 resources: limits: cpus: "0.50" memory: 4096M ``` 這邊的範例是一個後端的服務,但如果想要有複數個服務(例如:前端、後端、DB)包在一起也是可以的 例如: `swarm-test-multiple.yml`: ```gherkin= version: "3.3" services: my-backend: image: com.company.mybackend/api-server volumes: - /app/mybackend/log:/mybackend-log ports: - 7890:7890 deploy: replicas: 2 resources: limits: cpus: "0.50" memory: 4096M my-frontend: image: com.company.myfrontend/vue-website ports: - 7777:7777 deploy: replicas: 2 resources: limits: cpus: "0.20" memory: 2048M (...略) ``` docker-compose 詳細的內容可以自行 google my-backend 是我原先就包好的後端程式 image `com.company.mybackend/api-server` 在 volumes 部分是指 我要掛載該 server 的絕對路徑資料夾 `/app/mybackend/log` 至 my-backend 這個服務 container 的 `/mybackend-log` 資料夾。 我的`/mybackend-log`資料夾,是我的程式寫好的 log4j 的 log 存放位置。可以自行替換要掛載那些 container 資料夾到外部 server。 :::danger (請確保每台 server 都有預先建立好 `/app/mybackend/log` 資料夾,這邊 docker 不會自動幫你建,掛載不到的話那個 Node 就無法正常啟用) ::: 2. 請將這個`swarm-test.yml`丟到 Server1 (master) 主機上 3. 部屬 Stack 執行指令 ```cmd= docker stack deploy -c /opt/docker/swarm-test.yml MY-BACKEND ``` 最後面的`MY-BACKEND`是我取的 Stack Name 這邊`/opt/docker/swarm-test.yml`是我放置 docker-compose 的絕對路徑,如果你在`/opt/docker`這層下指令就只需要這樣 ```cmd= docker stack deploy -c swarm-test.yml MY-BACKEND ``` 成功後會看到 ``` [root@Server1 /]# docker stack deploy -c /opt/docker/swarm-test.yml MY-BACKEND Creating network MY-BACKEND_default Creating service MY-BACKEND_my-backend ``` 4. 確認服務 ```= docker stack ls ``` 基本上就可以看到 (使用 `swarm-test.yml`) ``` [root@Service1 /root]# docker stack ls NAME SERVICES MY-BACKEND 1 ``` 或是複數服務的話會看到 (使用 `swarm-test-multiple.yml`) ``` [root@Service1 /root]# docker stack ls NAME SERVICES MY-BACKEND_AND_FRONTEND 2 ``` 使用 `docker ps` 可以看到個別的 container :::warning 注意!! 我們在 `docker.compose.yml` 內設定的 `replicas` 是指在整個 swarm 架構下生成幾個container。但會掛在哪一台上靠的是內建的 load balance 機制。 以範例`swarm-test-multiple.yml`來說,我們故意不把`my_frontend:latest`的 image 放在 Server2上,導致 Server2 無法啟動 container 時,load balance 機制就會自動分配這個replica到其他 Node 上: Server1 (manager)的`docker ps` ``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c549fbd3d6b7 my_frontend:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp, 7777/tcp MY_BACKEND_AND_FRONTEND_my_frontend.1.sbqvnr7uqdp6z4gag9syuh1iq e79549a77594 my_frontend:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp, 7777/tcp MY_BACKEND_AND_FRONTEND_my_frontend.2.dw2i4j23nakhftbunr3zt39xt f810d500b96c my_backend:latest "java -Djava.securit…" 2 hours ago Up 2 hours MY_BACKEND_AND_FRONTEND_my_backend.94n7hiiojkp25x9fmsgw669l4 ``` Server2 (worker)的`docker ps` ``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ebbb3cd7e461 my_backend:latest "java -Djava.securit…" 2 hours ago Up 2 hours MY_BACKEND_AND_FRONTEND_my_backend.98myklsxuq2uysqhna4uae28y ``` ::: 基本上這樣就OK了!! ## 5. 踩雷補充 如果都設定好之後看似一切正常,但發現 load balance 機制沒有執行時: 1. 確認每個 node 主機內的 docker 服務是 actuve(running) 的,如果不是的話請重啟 docker。 ``` systemctl status docker ``` 2. 網路網卡環境問題,先查出該 node 主機的網卡名稱 ``` #linux環境 ifconfig ``` ```cmd= [root@Service1 /root]# ifconfig ens192: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.20.250.55 netmask 255.255.255.0 broadcast 10.20.250.255 ether 00:50:56:92:f4:58 txqueuelen 1000 (Ethernet) RX packets 2922745 bytes 3336899257 (3.1 GiB) RX errors 0 dropped 24 overruns 0 frame 0 TX packets 1661264 bytes 530241681 (505.6 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ``` 可以看到我的網卡名稱是 ens192 下指令``` ethtool -K {網卡名稱} tx-checksum-ip-generic off``` ```= ethtool -K ens192 tx-checksum-ip-generic off ``` ## 6. 【補充 Swarm 常用指令】 * 列出所有 Stack ``` docker stack ls ``` * 移除 stack 服務 ``` docker stack rm {StackName} ``` * 列出 Stack 內的所有 Service ``` docker stack services {StackName} ``` * 查看 service 服務 ``` docker service ls ``` * 查看 {某 service 服務} 詳細啟動節點 log ``` docker service ps {docker service ls 看到的name} ``` * 進入 service container terminal ``` docker exec -it {docker ps 看到的CONTAINER ID} bash ``` * 直接查看 Service logs ``` docker logs -f {docker ps 看到的CONTAINER ID} ``` * 到 worker 內查看該 Stack (就是普通的查 container 方式) ``` docker ps ``` # 參考來源: * [Docker] 使用 Docker Swarm 管理多台 Server 的服務 https://dotblogs.com.tw/fire/2023/01/15/171645 * Docker Swarm - Issue with Routing Mesh not routing Published Port on all Nodes https://forums.docker.com/t/docker-swarm-issue-with-routing-mesh-not-routing-published-port-on-all-nodes/123780/12