跳至主要内容

7.4 正式建立叢集

這一個章節將會用 Digital Ocean 這個雲端架構供應商所提供的虛擬伺服器當作叢集的教學,也正式的脫離本機來到真正的網際網路世界!

註冊帳號

前往 Digital Ocean 的首頁,點擊右上角的 Sign Up,如下圖所示:

DigitalOcean 首頁

接著經過註冊基本資訊以及輸入信用卡號後,會進到下圖的頁面,也就是整個帳號的主控台,如果不想輸入信用卡的人也可以不要註冊,就看接下來的實作細節就可以了!

DigitalOcean 主控台

接著點擊 Get started with a Droplet,接著開始選擇作業系統以及伺服器需要的硬體設施,這邊要有多便宜選多便宜,因為只是拿來練習 Docker Swarm,而且避免讀者們不小心忘記關掉伺服器,導致被 DigitalOcean 收費,就算忘了被收最便宜的也不會太心痛 ( 切身之痛 )

選擇伺服器規格

接著往下滑,會有一個 Choose Authentication Method 的選項,我們選擇 SSH Key,在點擊 Add SSH Key,就會看到以下畫面:

新增公開的 SSH Key

而 SSH 的全名為 Secure Shell,是一個網路的通訊協定,簡單來說能夠讓本地的電腦與遠端的伺服器進行加密的連線,而如何加密呢?就是將自己電腦產生的公鑰交給對方,以上面的例子來說,就是讓 DigitalOcean 能夠確認真的是我們這個使用者要和伺服器建立連線,而不是一個隨便的路人。

至於詳細內容是如何加密這邊則不多加贅述,只要知道 SSH 是一個現代非常常用的通訊協定,而且非常安全。

雖然 DigitalOcean 旁邊有教學如何產生公鑰以及私鑰,但這邊還是簡單地帶過,輸入下方指令:

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 按 Enter
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 按 Enter
Enter same passphrase again: 按 Enter
Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:+fTrUo3UY4vezwwa9SJP6cwx1sERns62DjQqSn4haRo root@d56dc36e1123
The key's randomart image is:
+---[RSA 3072]----+
| . |
| . o|
| . + |
| . . B .|
| S .. B.O |
| E + + .*.=+o|
| +...o++oO.o|
| .o ..o .%+B |
| o. o+ =o+|
+----[SHA256]-----+

# 以上就已經產生完公鑰以及私鑰了

root@d56dc36e1123:/# cat ~/.ssh/id_rsa.pub <- 複製你的公鑰
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQIcJ5lthUaon/bwJPS5eXFsIrUwjiwPzRTHhPCtDkk4Nlt2VWkFVOnkBvr5xYO6UQ8o0Wbhw9jWtHE/ZE7/SNUC0p1xzu0gucuygqS5aIivn+P0aOaOJW7pB9Ca6Z74XrnZAKgTiDMFhBwTJ78amx5sU1CvV8u90ENtX3oG7KpZsyHOtsVzpLCKKZopfMr8//mjI5bPc8mp1+rGcIi9h3elfknug9mL2plpui0ckENs9rp8qLbUYICPVXXglQuXhA6pjDp2Qj922w7aJ3cc6PXfFKh1YYU2udixKE27gRrQhw0iTauv6xFLKKe1ksFHa+cgGFeFswVidrKxxUMiYkpoyhdKWtpbZurD8ttEzoYQkUUkA4yN9IGdka6TYEaealpq2KvJ48Es02+qj6G8F9xNNro1oabdmz2o58aXlRqJn0INfyMa5DLPEzaIlRXFieD79/OZOSWY7d/AcEZCLDxY+r6aUNGpvB3dRZYXIX9+QsLDfdEEzNOCpuNp9vSAE= root@d56dc36e1123

# 把上面那一整串貼到 DigitalOcean,不是貼我的,貼你自己電腦的

貼完之後,記得輸入這組 Key 放在 DigitalOcean 的名字,像我就取叫做 MacBook,其實都可以,看你心情。

接著選擇數量,一次直接給他開三台,這樣才有叢集的感覺,其實也可以開個十台,反正練習完就關掉了。

伺服器數量選擇

接著按下右下角的 Create 後,就會回到主控台,等待伺服器的安裝,安裝好後,就會看到每一台機器都有屬於自己的 IP 位置。

伺服器安裝完成

接著就透過 SSH 的方式來連接到這三台伺服器吧!

$ ssh root@178.128.127.229 <- 格式為 使用者名稱@伺服器的 IP 位置
The authenticity of host '178.128.127.229 (178.128.127.229)' can't be established.
ED25519 key fingerprint is SHA256:0CQngjOObUEtqAmG2sg1AwN37V8UuF2Ylg3pDugi/cs.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? 這邊輸入 yes
Warning: Permanently added '178.128.127.229' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04 LTS (GNU/Linux 5.15.0-41-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Tue Oct 4 16:50:29 UTC 2022

System load: 0.0 Users logged in: 0
Usage of /: 16.0% of 9.51GB IPv4 address for eth0: 178.128.127.229
Memory usage: 44% IPv4 address for eth0: 10.15.0.6
Swap usage: 0% IPv4 address for eth1: 10.104.0.3
Processes: 93

0 updates can be applied immediately.



The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~#

到這邊就正式的進入到 DigitalOcean 的伺服器中,其他兩個伺服器也是比照辦理,這邊就不示範了。

接著要在每一台伺服器上安裝 Docker,就像回到一開始的 安裝 Docker ,因為這邊的作業系統都是 Ubuntu,所以我們直接使用腳本來安裝最新版本的 Docker。

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# curl -fsSL https://get.docker.com -o get-docker.sh <- 不換行
# 沒有反應是正常的,這邊只是下載了腳本

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# sh get-docker.sh
# Executing docker install script, commit: 4f282167c425347a931ccfd95cc91fab041d414f
+ sh -c apt-get update -qq >/dev/null
+ sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null
....
To run the Docker daemon as a fully privileged service, but granting non-root
users access, refer to https://docs.docker.com/go/daemon-access/

WARNING: Access to the remote API on a privileged Docker daemon is equivalent
to root access on the host. Refer to the 'Docker daemon attack surface'
documentation for details: https://docs.docker.com/go/attack-surface/

================================================================

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker version
Client: Docker Engine - Community
Version: 20.10.18
API version: 1.41
Go version: go1.18.6
Git commit: b40c2f6
Built: Thu Sep 8 23:11:43 2022
OS/Arch: linux/amd64
Context: default
Experimental: true

Server: Docker Engine - Community
Engine:
Version: 20.10.18
API version: 1.41 (minimum version 1.12)
Go version: go1.18.6
Git commit: e42327a
Built: Thu Sep 8 23:09:30 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.8
GitCommit: 9cd3357b7fd7218e4aec3eae239db1f68a5a6ec6
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d
docker-init:
Version: 0.19.0
GitCommit: de40ad0

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker compose version
Docker Compose version v2.10.2

安裝完後,養成好習慣,使用 docker version 以及 docker compose version 兩個指令來確認安裝結果。

三台伺服器都安裝完 Docker 後,就可以挑其中一台來執行 docker swarm init 了!

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker swarm init
Error response from daemon: could not choose an IP address to advertise since this system has multiple addresses on interface eth0 (178.128.127.229 and 10.15.0.6) - specify one with --advertise-addr

這邊出現了錯誤訊息,提示說這個機器的網路介面上有太多的 IP 位置,叫我們綁定其中一個,就使用顯示在主控台上的那一個,並且搭配 --advertise--addr 參數來使用。

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker swarm init --advertise-addr 178.128.127.229
Swarm initialized: current node (wuqi9exd0f71uah8dcpdvgtw4) is now a manager.

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

docker swarm join --token SWMTKN-1-3wcrndo9pz9akrgdfxnlqpp2fibyt4xaat13nqn5b0fmkqfrv3-d8yd0nwji5dulrfbp3g5d0ibq 178.128.127.229:2377

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

成功的開啟了 Swarm 模式,接著就是把其他兩個伺服器也加入 Swarm 之中,複製整段指令貼到另外兩台伺服器。

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-02:~# docker swarm join --token SWMTKN-1-3wcrndo9pz9akrgdfxnlqpp2fibyt4xaat13nqn5b0fmkqfrv3-d8yd0nwji5dulrfbp3g5d0ibq 178.128.127.229:2377
This node joined a swarm as a worker.

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-03:~# docker swarm join --token SWMTKN-1-3wcrndo9pz9akrgdfxnlqpp2fibyt4xaat13nqn5b0fmkqfrv3-d8yd0nwji5dulrfbp3g5d0ibq 178.128.127.229:2377
This node joined a swarm as a worker.

接著回到第一台第一台的伺服器輸入 docker node list,這次就會和在本機練習的感受不同了。

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker node list
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS E.V
wuqi... * ubun.... Ready Active Leader 20.10.18
png7... ubun.... Ready Active 20.10.18
fdmx... ubun.... Ready Active 20.10.18

接著在其他的伺服器輸入 docker node list 試試看:

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-02:~# docker node list
Error response from daemon: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.

這些被加入的節點不是 manager 的角色,所以不能夠查看節點的列表,Docker 指示我們去 manager 的節點執行這個指令,或是把現在這個節點升級成 manager,那就讓我們來試著替這個節點升職吧!

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker node promote png7umuzpg64fvxoa5ynxgmn6
Node png7umuzpg64fvxoa5ynxgmn6 promoted to a manager in the swarm.

docker node promote 後面要接的是節點的 ID,接著在節點的列表中,就可以看到第二台伺服器被升級成了 Reachable 的狀態,這和 Leader 差在哪呢?

這就和我們前面所提過的 Raft 演算法 ( 共識演算法 ) 有關,Raft 演算法需要一個 Leader 來接受資訊並且調度,之後再將資訊和其他節點同步,而其他的節點則為 Reachable 的節點,但實際上它們的職位都是 Manager,可以做的事情也一樣,只是在底層運作的角色有些不同罷了。

再把三個機器都升級成 Manager 後,就來建立 Nginx 的 Service,並且透過 docker service ps 的指令,可以看到 Swarm 確實把每一個 Task 都分配到不同的節點上,而不像本機因為沒有地方可以分配,而全部塞再一起。

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker service create --publish 8080:80 --detach --name nginx --replicas 3 nginx
um6dcfyyecxlxfv1xwkjn47dy

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker service ps nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS p415.. nginx.1 nginx 03 Running Runnin...
uhgk.. nginx.2 nginx 01 Running Runnin...
3418.. nginx.3 nginx 02 Running Runnin...

如果在這台 Leader 上輸入 docker container list 的話,也會看到確實只有一個容器在機器上。

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker container list
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2819affa.. nginx:lat "/docke.." 7 mi.. Up 7... 80/tcp nginx.2

接著透過 DigitalOcean 的 IP 位置,可以直接使用輸入下方網址看到熟悉的 nginx 畫面,畫面我就不貼出來了,相信這本書的 nginx 範例大家也沒有少看。

http://178.128.127.229:8080/ <- 您的 IP 位置不會一樣

內建的 Load Balance

現在我們認知到每一個節點都有一個 nginx 的容器,而 Docker Swarm 其實會自動幫我們做 Load Balance,如果單一節點流量過大的時候,會把請求分配到比較閒的節點,那要如何驗證這件事呢?

首先可以透過 docker service logs --follow 的指令來觀看整個 Service 的 Log,也就是三個容器一次滿足的意思。

root@ubuntu-s-1vcpu-512mb-10gb-sgp1-01:~# docker service logs --follow nginx <- 不換行
nginx.1.ieqxxsom5j23@ubuntu-s-1vcpu-512mb-10gb-sgp1-03 | 10.0.0.2 - - [05/Oct/2022:17:01:37 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"
nginx.1.ieqxxsom5j23@ubuntu-s-1vcpu-512mb-10gb-sgp1-03 | 10.0.0.2 - - [05/Oct/2022:17:01:37 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" "-"

可以看到最前面,是 nginx.1 這個容器接受到的請求,但我們回頭想一下,這個 IP 位置的容器是 nginx.2 才對,怎麼回應的會是別的伺服器的容器呢?

透過下方的圖片,我們可以知道原來是 ingress 這個虛擬網路做的好事。

ingress network 示意圖

圖片中顯示出,即使輸入的 IP 位置沒有容器的存在,也會透過 Docker Swarm 的 Load Balancer 把請求導過去在同個 port 有服務的節點。

而我們的例子則是透過 Load Balancer,即使輸入的 IP 位置是 nginx.2 容器的節點,它也不像是傳統的 1 對 1 這麼的單純,而是會自動的導流,可以開個 docker service logs --follow nginx,並且不斷地重新整理網頁,會看到 Log 不斷地切換到不同的容器來接受請求。

至於 ingress network 是什麼呢?我們下一章節解釋。