---
# System prepended metadata

title: Docker Swarm
tags: [docker, Linux]

---

# Docker Swarm 容器集群

## Docker Swarm 安裝
* 三台 VM
    * manager
    * node1
    * node2

### Manager

* 初始化 (會給連線用的Token)
```bash=
$ sudo docker swarm init --advertise-addr 192.168.68.201
Swarm initialized: current node (lu170z4o841cvflz6kgfqv3dd) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5ze0djseob3b2sk5658p4lfh317t5ht6oty0qc513n0oyejufz-659xe4d52mn4a0sslqj242q5t 192.168.68.201:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

```
* 查看連線
```bash=
$ sudo docker node ls
[sudo] password for user: 
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
lu170z4o841cvflz6kgfqv3dd *   docker1    Ready     Active         Leader           24.0.7
9e58igu1n2rs4q6cz8p4k6nrh     docker2    Ready     Active                          24.0.7
n2zbpzslirsjrazhql3iir30k     docker3    Ready     Active                          24.0.7
```
* 查看Token
```bash=
$ sudo docker swarm join-token worker
To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5ze0djseob3b2sk5658p4lfh317t5ht6oty0qc513n0oyejufz-659xe4d52mn4a0sslqj242q5t 192.168.68.201:2377

$ sudo docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5ze0djseob3b2sk5658p4lfh317t5ht6oty0qc513n0oyejufz-3o35ghm8dh6oujo8ctjqvqeyz 192.168.68.201:2377

```

### Node1

* 用建立時給的 Token 連線
```bash=
$ sudo docker swarm join --token SWMTKN-1-5ze0djseob3b2sk5658p4lfh317t5ht6oty0qc513n0oyejufz-659xe4d52mn4a0sslqj242q5t 192.168.68.201:2377
This node joined a swarm as a worker.
```

### Node2

* 用建立時給的 Token 連線
```bash=
$ sudo docker swarm join --token SWMTKN-1-5ze0djseob3b2sk5658p4lfh317t5ht6oty0qc513n0oyejufz-659xe4d52mn4a0sslqj242q5t 192.168.68.201:2377
This node joined a swarm as a worker.
```

* 執行container
```
sudo docker run -itd -p 8888:8080 -e HOST=192.168.68.201 -e PORT=8080 -v /var/run/docker.sock:/var/run/docker.sock --name visualizer dockersamples/visualizer
```

* 用 Docker Visualizer 查看
方法: [Docker Visualizer](https://hackmd.io/eMFnW-DZT56SKquf7mPY1g)
![image](https://hackmd.io/_uploads/Hy-Rm5NBp.png)


## Docker Swarm Service

* 啟動一個服務, 用 httpd:latest
```bash=
$ sudo docker service create --name myweb httpd
wbejpmbng04dac6oasnm8wod6
overall progress: 1 out of 1 tasks 
1/1: running   
verify: Service converged 
$ sudo docker service ls
ID             NAME      MODE         REPLICAS   IMAGE          PORTS
wbejpmbng04d   myweb     replicated   1/1        httpd:latest   
```
* 再回到 Visualizer
**會自動分配工作給底下的 node (再Swarm中, Manager也會被分配任務)**
![image](https://hackmd.io/_uploads/rJR7rqVr6.png)

## Docker Swarm Service 擴容
* 擴容
```bash=
$ sudo docker service scale myweb=3
[sudo] password for user: 
myweb scaled to 3
overall progress: 3 out of 3 tasks 
1/3: running   
2/3: running   
3/3: running   
verify: Service converged

$ sudo docker service ls
ID             NAME      MODE         REPLICAS   IMAGE          PORTS
wbejpmbng04d   myweb     replicated   3/3        httpd:latest  
```
![image](https://hackmd.io/_uploads/HJpYU9EBT.png)

* 再擴
```bash=
$ sudo docker service scale myweb=5
myweb scaled to 5
overall progress: 5 out of 5 tasks 
1/5: running   
2/5: running   
3/5: running   
4/5: running   
5/5: running   
verify: Service converged

$ sudo docker service ls
ID             NAME      MODE         REPLICAS   IMAGE          PORTS
wbejpmbng04d   myweb     replicated   5/5        httpd:latest   
```
![image](https://hackmd.io/_uploads/H1pbv5EH6.png)

* 縮減
```bash=
$ sudo docker service scale myweb=2
myweb scaled to 2
overall progress: 2 out of 2 tasks 
1/2: running   
2/2: running   
verify: Service converged 

$ sudo docker service ls
ID             NAME      MODE         REPLICAS   IMAGE          PORTS
wbejpmbng04d   myweb     replicated   2/2        httpd:latest   
```
![image](https://hackmd.io/_uploads/BJFdw9ES6.png)

## 工作分配

* 讓指定節點不接收工作
```bash=
$ sudo docker node ls    # 三個節點都有工作
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
p8xeslmh8e26b9tlzxwgn6qsf *   docker1    Ready     Active         Leader           24.0.7
d5kukh9qwfaesvgtki2nshfao     docker2    Ready     Active                          24.0.7
8l061ghpaouhsyeej7xj7glko     docker3    Ready     Active                          24.0.7
$ sudo docker node update --availability drain docker1    # 不分配工作給 docker1
docker1
$ sudo docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
p8xeslmh8e26b9tlzxwgn6qsf *   docker1    Ready     Drain          Leader           24.0.7
d5kukh9qwfaesvgtki2nshfao     docker2    Ready     Active                          24.0.7
8l061ghpaouhsyeej7xj7glko     docker3    Ready     Active                          24.0.7
```
```bash=
$ sudo docker service ps myweb
ID             NAME          IMAGE          NODE      DESIRED STATE   CURRENT STATE            ERROR     PORTS
vlpmxy0pp1yr   myweb.1       httpd:latest   docker2   Running         Running 5 minutes ago              
sap5oh1wdab0   myweb.2       httpd:latest   docker3   Running         Running 4 minutes ago              
3fcu5n7kq1h0   myweb.3       httpd:latest   docker3   Running         Running 2 minutes ago              
2oitxiwosmle    \_ myweb.3   httpd:latest   docker1   Shutdown        Shutdown 2 minutes ago             
```
![image](https://hackmd.io/_uploads/Hkfr9OSHT.png)
* 恢復
```bash=
$ sudo docker node update --availability active docker1
docker1
```


* 機器故障

**把 docker3 關機**
```bash=
$ sudo docker service ps myweb
ID             NAME          IMAGE          NODE      DESIRED STATE   CURRENT STATE            ERROR     PORTS
vlpmxy0pp1yr   myweb.1       httpd:latest   docker2   Running         Running 10 minutes ago             
xkd4pdxfpibg   myweb.2       httpd:latest   docker2   Running         Running 35 seconds ago             
sap5oh1wdab0    \_ myweb.2   httpd:latest   docker3   Shutdown        Running 9 minutes ago              
rojvqyeuwhym   myweb.3       httpd:latest   docker2   Running         Running 35 seconds ago             
3fcu5n7kq1h0    \_ myweb.3   httpd:latest   docker3   Shutdown        Running 7 minutes ago              
2oitxiwosmle    \_ myweb.3   httpd:latest   docker1   Shutdown        Shutdown 7 minutes ago             
```
![image](https://hackmd.io/_uploads/SyvR5uSBT.png)

:::info
就算機器重新上線, 原有工作內容也不會自動分配出去
:::

## port 開放

* 原本只有開啟 httpd 的 image (port 的欄位是空的)
```bash=
$ sudo docker service ls
ID             NAME      MODE         REPLICAS   IMAGE          PORTS
gvuabs89xy5k   myweb     replicated   3/3        httpd:latest   
```
* 更新 port 的開放
```bash=
$ sudo docker service update --publish-add 8080:80 myweb
myweb
overall progress: 3 out of 3 tasks 
1/3: running   
2/3: running   
3/3: running   
verify: Service converged 
```
* 更新結果
```bash=
$ sudo docker service ls
ID             NAME      MODE         REPLICAS   IMAGE          PORTS
gvuabs89xy5k   myweb     replicated   3/3        httpd:latest   *:8080->80/tcp
```
![1701318572532](https://hackmd.io/_uploads/r1X01qrH6.gif)



## 附載均衡 (Docker Swarm Load Balancer)

![photo_2023-12-01_14-19-33](https://hackmd.io/_uploads/SyVWigPHa.jpg)

* 編輯網頁 方便顯示內容
```bash=
$ sudo docker exec -it 533 bash
root@533e636e901f:/usr/local/apache2# cd htdocs/
root@533e636e901f:/usr/local/apache2/htdocs# echo "HI docker1" > hi.htm
```
* 在三台都編輯完後, 簡單測試
```bash=
$ for i in `seq 100`; do curl http://192.168.68.202:8080/hi.htm; done
```
* haproxy
```bash=
# 在第四台安裝 haproxy
$ sudo yum install -y epel-release
$ sudo yum install -y haproxy
```

* 編輯檔案 /etc/haproxy/haproxy.cfg
```shell=
global
daemon
chroot /var/lib/haproxy
user haproxy
group haproxy
stats timeout 30s

defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000

frontend http_front
bind *:80
stats uri /haproxy?stats
default_backend http_back

backend http_back
balance roundrobin
server user4-1 192.168.68.201:8080 check    # 改自己IP
server user4-2 192.168.68.202:8080 check
server user4-3 192.168.68.203:8080 check
```
* 啟動 & 連線
```bash=
$ sudo systemctl restart haproxy
```


## mysql + php

* 透過NFS掛載3台虛擬機目錄
    :::info
    getenforce 跟 firewall 先關閉
    :::
    
    * 安裝:
        `sudo yum install nfs-utils`    
        `sudo systemctl enable rpcbind`
        `sudo systemctl enable nfs`
    
    * Master 端
        `sudo vim /etc/exports` 在這裡新增要共享的資料夾跟權限等
        **/home/user/Desktop/mydb 192.168.68.0/24(rw,sync,no_root_squash,no_all_squash)**
        **/home/user/Desktop/myphp 192.168.68.0/24(rw,sync,no_root_squash,no_all_squash)**
        
        `sudo systemctl restart nfs`

        查看共享目錄:
        `showmount -e localhost`
        
    * Worker 端
         查看共享目錄:
        `showmount -e localhost`

        建立連結的目錄(名稱可以不一樣):
        `mkdir /home/user/Desktop/mydb`
        `mkdir /home/user/Desktop/myphp`

        掛載:
            `mount -t nfs 192.168.68.148:/home/user/Desktop/mydb /home/user/Desktop/mydb`
            `mount -t nfs 192.168.68.148:/home/user/Desktop/myphp /home/user/Desktop/myphp`

* 建立 overlay 網路
`docker network create -d overlay mynet`

* 啟動 db 服務
`docker service create --name mydb --network mynet --mount type=bind,source=/home/user/Desktop/mydb,target=/var/lib/mysql --env MYSQL_ROOT_PASSWORD=123456 --publish published=3306,target=3306 mysql`

* db
    `docker exec -it <ID> bash`
    `mysql -u root -p`
    ```sql=
    show databases;            # 顯示目前有的資料庫
    create database testdb;    # 創建資料庫
    use testdb;                # 使用資料庫
    create table addrbook(name varchar(50) not null, phone char(10));    # 創建資料表
    insert into addrbook(name, phone) values ("tom", "0912123456");      # 加入資料
    insert into addrbook(name, phone) values ("mary", "0912123567");     # 加入資料
    select name,phone from addrbook;          # 選擇資料
    update addrbook set phone="0987465123";    # 更新資料
    ```

* 啟動 apache(php) 服務
`docker service create --name myphp --network mynet --mount type=bind,source=/home/user/Desktop/myphp,target=/var/www/html --publish published=8888,target=80 radys/php-apache:7.4`

* php
    * 與資料庫連結
    ```php=
    <?php
    $servername="192.168.68.148";
    $username="root";
    $password="123456";
    $dbname="testdb";

    $conn = new mysqli($servername, $username, $password, $dbname);

    if($conn->connect_error){
        die("connection failed: " . $conn->connect_error);
    }
    else{
        echo "connect OK!" . "<br>";
    }

    $sql="select name, phone from addrbook";
    $result=$conn->query($sql);

    if($result->num_rows>0){
        while($row=$result->fetch_assoc()){
            echo "name: " . $row["name"] . "\tphone: " . $row["phone"] . "<br>";
        }
    } else {
        echo "0 record";
    }
    ?>
    ```

![image](https://hackmd.io/_uploads/S1VKpGj7C.png)
