跳至主要内容

7.2 Swarm 模式下的容器

在前幾個章節,都是使用 docker container run 來運行一個獨立的容器,而在 Docker Swarm 的模式之下,有了新的物件可以來運行容器。

這邊先回到 Swarm 模式:

$ docker swarm init
Swarm initialized: current node (ne5cwloz00by50gvyh3hj74qt) is now a manager.

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

docker swarm join --token SWMTKN-1-1mmrll6hucyytwlv3p06lvbap84ihgm2rquql0952mhloczirw-9fcoakk5hvwwonw7q5cko7p4q 192.168.65.3:2377

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

這個新的物件叫做 Service ( 服務 ),在前幾章節的 docker container run 只能夠一次啟動一個容器,缺乏彈性,而 docker service create 則是拿來解決這個問題的答案。

輸入下方的指令試試看感覺如何:

$ docker service create --name nginx --publish 8080:80 --detach --replicas 3 nginx
sltqjrnlc9cflvm2a4hqgmbqq

接著使用 docker service list 指令來列出 service 物件:

$ docker service list
ID NAME MODE REPLICAS IMAGE PORTS
sltqjrnlc9cf nginx replicated 3/3 nginx *:8080->80/tcp

在啟動 service 時,加入了一個之前沒有看過的參數 --replicas,也就是副本的意思,這邊要求要複製三個 nginx 的映像檔運行而成的容器,就如同下圖所示:

Docker Servcie

在 Manager Node 上面執行了建立 Service 的指令,這個指令將會根據副本的數量而拆分成該數量的任務,以這個例子來說,就是建立了一個 Servcie,而這個 Service 裡面則有 3 個任務,每一個任務裡面都是 nginx:latest 映像檔運行而成的容器。

Manager Node 會想按照擁有的節點數量想辦法分配到每一個節點上,而因為現在在自己的電腦上執行,所以只有一個節點,這樣子就會把三個容器都坐落在這個節點上;這邊就可以使用以前所學,列出所有的容器來看看。

$ docker container list
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4d535277f940 nginx "/docker" 8 min Up 8.. 80/tcp nginx.1..
208142908eee nginx "/docker" 8 min Up 8.. 80/tcp nginx.2..
627ecb54c131 nginx "/docker" 8 min Up 8.. 80/tcp ngnix.3..

這樣就有稍微覺得安心一點了吧,至少它還是熟悉的樣子,只是透過 Docker Swarm 的 API 來做一些之前沒辦法做的事。

Docker Swarm 分配任務的流程

前面有提到 Docker Swarm 會平均的分配任務 ( 容器 ) 到每一個節點上,那它是如何做到的呢?可以根據下方 Docker 官方的圖片來解釋一下流程。

Swarm 分配的簡易流程圖

透過上方的圖片,在 Client 端,也就是我的電腦,把 docker service create 的指令送到了 Docker Swarm 之中,而 Manager Node 則會接受這個 Service 的預期狀態,以前面的例子來說,預期狀態就是對應到本機的 8080 port 以及命名為 nginx 還要在背景執行,並且要有 3 個副本

接著會進入到 Orchestrator ( 調度者 ),負責建立 Service,會不斷地監視著其建立的 Service 物件,並且想辦法維持著 Servie 的預期狀態,假設今天某一個節點的 nginx 容器壞掉了,這時候 Orchestrator 就會發現,並且在補上另外一個 nginx 容器來滿足預期狀態中的 3 個副本。

下面可以實驗一下 Orchestrator 有沒有在偷懶,剛剛有確認過本機的 nginx 容器有 3 個,而 docker service list 中也顯示 REPLICAS 是 3/3,也就是滿足預期狀態的 3 個副本。

這時候有一個推薦的工具叫做 watch,可以自動刷新某一個指令的結果,macOS 的讀者們可以透過 brew install watch 來安裝這個工具;Linux 的用戶則根據你們的套件管理工具安裝 watch。

接著可以打開兩個終端機,其中一個輸入下方指令,就可以不用手動一直去輸入docker service list 來看狀態的改變。

# 第一個終端機拿來監測 docker service list

$ watch docker service list
Every 2.0s: docker service ls Roberts-MacBook-Pro.local: Tue Oct 4 01:12:56 2022

ID NAME MODE REPLICAS IMAGE PORTS
sltqjrnlc9cf nginx replicated 3/3 nginx *:8080->80/tcp

而另一個終端機則是先列出所有容器,並且用 Container ID 強制刪除掉其中一個。

# 第二個終端機拿來執行動作
$ docker container list
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4d535277f940 nginx "/docker" 8 min Up 8.. 80/tcp nginx.1..
208142908eee nginx "/docker" 8 min Up 8.. 80/tcp nginx.2..
627ecb54c131 nginx "/docker" 8 min Up 8.. 80/tcp ngnix.3..

$ docker container rm --force 4d535
4d535

刪除掉的瞬間,會看到另外一個終端機的畫面有所變動,如下方所示,REPLICAS 變成了 2 / 3,代表 Orchestrator 沒有在偷懶,它有檢查到突然少了一個副本。

# 第一個終端機拿來監測 docker service list
$ watch docker service list
Every 2.0s: docker service ls Roberts-MacBook-Pro.local: Tue Oct 4 01:12:56 2022

ID NAME MODE REPLICAS IMAGE PORTS
sltqjrnlc9cf nginx replicated 2/3 nginx *:8080->80/tcp

大概過了 3 秒左右,神奇的事情發生了,會發現第一個終端機又變回了 3 / 3 的 REPLICAS。

# 第一個終端機拿來監測 docker service list
$ watch docker service list
Every 2.0s: docker service ls Roberts-MacBook-Pro.local: Tue Oct 4 01:12:56 2022

ID NAME MODE REPLICAS IMAGE PORTS
sltqjrnlc9cf nginx replicated 3/3 nginx *:8080->80/tcp

這就是 Orchestrator 在做的事情,不斷地檢查其建立的 Service 物件有沒有出現不符合預期狀態的情況發生,一但發現,就會重複上一張圖片中 Manager Node 的流程,透過 allocator 來分配 IP 位置給任務 ( Task ),接著是 dispatcher 則會把任務傳遞給 Orchestrator 分配好的節點,最後則是 scheduler 會告訴 Worker Node 該如何執行這個任務。

而 Worker Node 除了乖乖地執行任務之外,還會回報任務的執行狀況給 Manager Node,讓整個 Swarm 保持在一個不斷同步資訊的情境下。