接下來通過實例演示一下如何使用 Swarm 來創建安全的集群。
實例中包含 3 個管理節點和 3 個工作節點,如下圖所示,可以根據需要自行調整管理節點和工作節點的數量、名稱和 IP。
每個節點都需要安裝 Docker,并且能夠與 Swarm 的其他節點通信。
如果配置有域名解析就更好了,這樣在命令的輸出中更容易識別出節點,也更有利于排除故障。
在網絡方面,需要在路由器和防火墻中開放如下端口。
? 2377/tcp:用于客戶端與 Swarm 進行安全通信。
? 7946/tcp 與 7946/udp:用于控制面 gossip 分發。
? 4789/udp:用于基于 VXLAN 的覆蓋網絡。
如果滿足以上前提,就可以著手開始搭建 Swarm 集群了。
搭建 Swarm 的過程有時也被稱為初始化 Swarm,大體流程包括初始化第一個管理節點 -> 加入額外的管理節點 -> 加入工作節點 -> 完成。
不包含在任何 Swarm 中的 Docker 節點,稱為運行于單引擎(Single-Engine)模式。一旦被加入 Swarm 集群,則切換為 Swarm 模式,如下圖所示。
在單引擎模式下的 Docker 主機上運行 docker swarm init會將其切換到 Swarm 模式,并創建一個新的 Swarm,將自身設置為 Swarm 的第一個管理節點。
更多的節點可以作為管理節點或工作節點加入進來。這一操作也會將新加入的節點切換為 Swarm 模式。
以下的步驟會將 mgr1 切換為 Swarm 模式,并初始化一個新的 Swarm。接下來將 wrk1、wrk2 和 wrk3 作為工作節點接入,自動將它們切換為Swarm模式。然后將 mgr2 和 mgr3 作為額外的管理節點接入,并同樣切換為 Swarm 模式。最終有 6 個節點切換到 Swarm 模式,并運行于同一個 Swarm 中。
⒈ 登錄到 mgr1 并初始化一個新的 Swarm
如果在 Windows 的 PowerShell 終端執行如下命令的話,不要忘了將反斜杠替換為反引號。
$ docker swarm init \
--advertise-addr 10.0.0.1:2377 \
--listen-addr 10.0.0.1:2377
Swarm initialized: current node (d21lyz...c79qzkx) is now a manager.
將這條命令拆開分析如下。
docker swarm init會通知 Docker 來初始化一個新的 Swarm,并將自身設置為第一個管理節點。同時也會使該節點開啟 Swarm 模式。
--advertise-addr 指定其他節點用來連接到當前管理節點的 IP 和端口。這一屬性是可選的,當節點上有多個 IP 時,可以用于指定使用哪個IP。此外,還可以用于指定一個節點上沒有的 IP,比如一個負載均衡的 IP。
--listen-addr 指定用于承載 Swarm 流量的 IP 和端口。其設置通常與 --advertise-addr 相匹配,但是當節點上有多個 IP 的時候,可用于指定具體某個 IP。并且,如果 --advertise-addr 設置了一個遠程 IP 地址(如負載均衡的IP地址),該屬性也是需要設置的。建議執行命令時總是使用這兩個屬性來指定具體 IP 和端口。
Swarm 模式下的操作默認運行于 2337 端口。雖然它是可配置的,但 2377/tcp 是用于客戶端與 Swarm 進行安全(HTTPS)通信的約定俗成的端口配置。
⒉ 列出 Swarm 中的節點。
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
d21...qzkx * mgr1 Ready Active Leader
注意到 mgr1 是 Swarm 中唯一的節點,并且作為 Leader 列出。
⒊ 在 mgr1 上執行 docker swarm join-token 命令
docker swarm join-token 命令用來獲取添加新的工作節點和管理節點到 Swarm 的命令和 Token。
$ docker swarm join-token worker
To add a manager to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-0uahebax...c87tu8dx2c \
10.0.0.1:2377
$ docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-0uahebax...ue4hv6ps3p \
10.0.0.1:2377
請注意,工作節點和管理節點的接入命令中使用的接入 Token(SWMTKN...)是不同的。因此,一個節點是作為工作節點還是管理節點接入,完全依賴于使用了哪個 Token。接入 Token 應該被妥善保管,因為這是將一個節點加入 Swarm 的唯一所需!
⒋ 登錄到 wrk1,并使用包含工作節點接入 Token 的 docker swarm join 命令將其接入 Swarm。
$ docker swarm join \
--token SWMTKN-1-0uahebax...c87tu8dx2c \
10.0.0.1:2377 \
--advertise-addr 10.0.0.4:2377 \
--listen-addr 10.0.0.4:2377
This node joined a swarm as a worker.
--advertise-addr 與 --listen-addr 屬性是可選的。在網絡配置方面,請盡量明確指定相關參數,這是一種好的實踐。
⒌ 在 wrk2 和 wrk3 上重復上一步驟來將這兩個節點作為工作節點加入 Swarm。
確保使用 --advertise-addr 與 --listen-addr 屬性來指定各自的 IP 地址。
⒍ 登錄到 mgr2,然后使用含有管理節點接入 Token 的 docker swarm join 命令,將該節點作為工作節點接入 Swarm。
$ docker swarm join \
--token SWMTKN-1-0uahebax...ue4hv6ps3p \
10.0.0.1:2377 \
--advertise-addr 10.0.0.2:2377 \
--listen-addr 10.0.0.1:2377
This node joined a swarm as a manager.
⒎ 在 mgr3 上重復以上步驟,記得在 --advertise-addr 與 --listen-addr 屬性中指定 mgr3 的 IP 地址。
⒏ 在任意一個管理節點上執行 docker node ls 命令來列出 Swarm 節點。
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
0g4rl...babl8 * mgr2 Ready Active Reachable
2xlti...l0nyp mgr3 Ready Active Reachable
8yv0b...wmr67 wrk1 Ready Active
9mzwf...e4m4n wrk3 Ready Active
d21ly...9qzkx mgr1 Ready Active Leader
e62gf...l5wt6 wrk2 Ready Active
至此,已經成功創建了 6 個節點的 Swarm,其中包含 3 個管理節點和 3 個工作節點。
在這個過程中,每個節點的 Docker 引擎都被切換到 Swarm 模式下。并且,Swarm 已經自動啟用了 TLS 以策安全。
注意,mgr2 的 ID 列還顯示了一個星號(*),這個星號會告知用戶執行docker node ls 命令所在的節點。本例中,命令是在 mgr2 節點執行的。
每次將節點加入 Swarm 都指定 --advertise-addr 與 --listen-addr 屬性是痛苦的。然而,一旦 Swarm 中的網絡配置出現問題將會更加痛苦。況且,手動將節點加入 Swarm 也不是一種日常操作,所以在執行該命令時額外指定這兩個屬性是值得的。
現在已經有一個運行中的 Swarm 了,下面看一下如何進行高可用(HA)管理。
Swarm 的管理節點內置有對 HA 的支持。這意味著,即使一個或多個節點發生故障,剩余管理節點也會繼續保證 Swarm 的運轉。
從技術上來說,Swarm 實現了一種主從方式的多管理節點的 HA。這意味著,即使你可能有多個管理節點,也總是僅有一個節點處于活動狀態。
通常處于活動狀態的管理節點被稱為“主節點”(leader),而主節點也是唯一一個會對 Swarm 發送控制命令的節點。也就是說,只有主節點才會變更配置,或發送任務到工作節點。如果一個備用(非活動)管理節點接收到了 Swarm 命令,則它會將其轉發給主節點。
這一過程如下圖所示。步驟 ① 指命令從一個遠程的 Docker 客戶端發送給一個管理節點;步驟 ② 指非主節點將命令轉發給主節點;步驟 ③ 指主節點對 Swarm 執行命令。
仔細觀察上圖會發現,管理節點或者是 Leader 或者是 Follower。這是 Raft 的術語,因為 Swarm 使用了 Raft 共識算法的一種具體實現來支持管理節點的HA。
關于 HA,有以下兩條最佳實踐原則。
? 部署奇數個管理節點。
? 不要部署太多管理節點(建議 3 個或 5 個)。
部署奇數個管理節點有利于減少腦裂(Split-Brain)情況的出現機會。假如有 4 個管理節點,當網絡發生分區時,可能會在每個分區有兩個管理節點。這種情況被稱為腦裂。
每個分區都知道曾經有 4 個節點,但是當前網絡中僅有兩個節點,糟糕的是,每個分區都無法知道其余兩個節點是否運行,也無從得知本分區是否掌握大多數(Quorum)。
雖然在腦裂情況下集群依然在運行,但是已經無法變更配置,或增加和管理應用負載了。不過,如果部署有 3 個或 5 個管理節點,并且也發生了網絡分區,就不會出現每個分區擁有同樣數量的管理節點的情況。
這意味著掌握多數管理節點的分區能夠繼續對集群進行管理。下圖中右側的例子,闡釋了這種情況,左側的分區知道自己掌握了多數的管理節點。
對于所有的共識算法來說,更多的參與節點就意味著需要花費更多的時間來達成共識。這就像決定去哪吃飯,只有3個人的時候總是比有33個人的時候能更快確定。
考慮到這一點,最佳的實踐原則是部署 3 個或 5 個節點用于 HA。7 個節點可以工作,但是通常認為 3 個或 5 個是更優的選擇。當然絕對不要多于 7 個,因為需要花費更長的時間來達成共識。
關于管理節點的 HA 再補充一點。顯然將管理節點分布到不同的可用域(Availability Zone)中是一種不錯的實踐方式,但是一定要確保它們之間的網絡連接是可靠的,否則由于底層網絡分區導致的問題將是令人痛苦的。
Swarm 集群內置有繁多的安全機制,并提供了開箱即用的合理的默認配置——如 CA 設置、接入 Token、公用 TLS、加密集群存儲、加密網絡、加密節點 ID 等。
盡管內置有如此多的原生安全機制,重啟一個舊的管理節點或進行備份恢復仍有可能對集群造成影響。
一個舊的管理節點重新接入 Swarm 會自動解密并獲得 Raft 數據庫中長時間序列的訪問權,這會帶來安全隱患。
進行備份恢復可能會抹掉最新的 Swarm 配置。
為了規避以上問題,Docker 提供了自動鎖機制來鎖定 Swarm,這會強制要求重啟的管理節點在提供一個集群解鎖碼之后才有權從新接入集群。
通過在執行 docker swarm init 命令來創建一個新的 Swarm 集群時傳入 --autolock 參數可以直接啟用鎖。
然而,前面已經搭建了一個 Swarm 集群,這時也可以使用 docker swarm update 命令來啟用鎖。
在某個 Swarm 管理節點上運行如下命令。
$ docker swarm update --autolock=true
Swarm updated.
To unlock a swarm manager after it restarts, run the
`docker swarm unlock`command and provide the following key:
SWMKEY-1-5+ICW2kRxPxZrVyBDWzBkzZdSd0Yc7Cl2o4Uuf9NPU4
Please remember to store this key in a password manager, since without
it you will not be able to restart the manager.
請確保將解鎖碼妥善保管在安全的地方!
重啟某一個管理節點,以便觀察其是否能夠自動重新接入集群。
$ service docker restart
嘗試列出Swarm中的節點。
$ docker node ls
Error response from daemon: Swarm is encrypted and needs to be unlocked
before it can be used.
盡管 Docker 服務已經重啟,該管理節點仍然未被允許重新接入集群。
為了進一步驗證,可以到其他管理節點執行 docker node ls 命令,會發現重啟的管理節點會顯示 down 以及 unreachable。
執行 docker swarm unlock 命令來為重啟的管理節點解鎖 Swarm。該命令需要在重啟的節點上執行,同時需要提供解鎖碼。
$ docker swarm unlock
Please enter unlock key: <enter your key>
該節點將被允許重新接入 Swarm,并且再次執行 docker node ls 命令會顯示 ready 和 reachable。
至此,Swarm 集群已經搭建起來,相信大家已經對主節點和管理節點 HA 有了一定了解。