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