# 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)

## 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也會被分配任務)**

## 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
```

* 再擴
```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
```

* 縮減
```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
```

## 工作分配
* 讓指定節點不接收工作
```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
```

* 恢復
```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
```

:::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
```

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

* 編輯網頁 方便顯示內容
```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";
}
?>
```

{"description":"高可靠","contributors":"[{\"id\":\"2aa6933a-ff5f-468f-81d7-6752f397faaf\",\"add\":12081,\"del\":1122}]","title":"Docker Swarm"}