作者:不洗碗工作室 - Marklux
出處:marklux.cn/blog/55
版權歸作者所有,轉載請註明出處
前言
DOCKER技術在推出後掀起了一陣容器化技術的熱潮,容器化使得服務的部署變得極其簡易,這為微服務和分散式計算提供了很大的便利。
為了把容器化技術的優點發揮到極致,docker公司先後推出了三大技術:docker-machine
,docker-compose
,docker-swarm
,可以說是幾乎實現了容器化技術中所有可能需要的底層技術手段。
在使用go語言實現了判題引擎並打包好docker映象後,就需要進行分散式判題的編寫,這次就讓我們手動實踐,嘗試使用docker的三大殺器來部署一個多機器構成的判題服務叢集。
三劍客簡介
docker-machine
docker技術是基於Linux核心的cgroup
技術實現的,那麼問題來了,在非Linux平臺上是否就不能使用docker技術了呢?答案是可以的,不過顯然需要藉助虛擬機器去模擬出Linux環境來。
docker-machine就是docker公司官方提出的,用於在各種平臺上快速建立具有docker服務的虛擬機器的技術,甚至可以通過指定driver來定製虛擬機器的實現原理(一般是virtualbox)。
docker-compose
docker映象在建立之後,往往需要自己手動pull來獲取映象,然後執行run命令來執行。當服務需要用到多種容器,容器之間又產生了各種依賴和連線的時候,部署一個服務的手動操作是令人感到十分厭煩的。
dcoker-compose技術,就是通過一個.yml
配置檔案,將所有的容器的部署方法、檔案對映、容器連線等等一系列的配置寫在一個配置檔案裡,最後只需要執行docker-compose up
命令就會像執行指令碼一樣的去一個個安裝容器並自動部署他們,極大的便利了複雜服務的部署。
docker-swarm
swarm是基於docker平臺實現的叢集技術,他可以通過幾條簡單的指令快速的建立一個docker叢集,接著在叢集的共享網路上部署應用,最終實現分散式的服務。
相比起zookeeper等叢集管理框架來說,swarm顯得十分輕量,作為一個工具,它把節點的加入、管理、發現等複雜的操作都濃縮為幾句簡單的命令,並且具有自動發現節點和排程的演算法,還支援自定製。雖然swarm技術現在還不是非常成熟,但其威力已經可見一般。
淺談docker服務架構和遠端API
在正式使用docker技術部署叢集應用時,我們應該先來了解一下docker工作的一些底層原理,和docker遠端呼叫的API,這樣才能大體瞭解叢集究竟是如何運作的。
daemon
之前的docker入門文章中講過,docker的基礎服務,比如容器的建立、檢視、停止、映象的管理,其實都是由docker的守護程式(daemon)來實現的。
每次執行的docker指令其實都是通過向daemon傳送請求來實現的。
daemon的運作(通訊模式)主要有兩種,一種是通過unix套接字(預設,但只能在本地訪問到,比較安全),一種是通過監聽tcp協議地址和埠來實現(這個可以實現在遠端呼叫到docker服務)。
遠端API
除了通過遠端tcp協議訪問遠端主機上的docker服務外,docker還提供了一套基於HTTP的API,可以使用curl來實現操作遠端主機上的docker服務,這為開發基於WEB的docker服務提供了便利。
遠端docker使用示例
最終實現叢集的時候實際是使用docker的遠端呼叫來將不同的docker主機連線成一個整體的(通過tcp協議)。
我們不妨先來手動模擬嘗試一下docker服務的遠端呼叫吧。
首先需要在提供服務的主機上將docker的執行方式改為tcp,具體方法為修改/etc/default/docker
中的DOCKER_OPTS
為如下內容
-H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock複製程式碼
-H 後的引數是自己定義的要繫結的tcp地址和埠,成功繫結後重啟docker服務就可以在該埠訪問到docker的daemon服務。
不幸的是:
- 在OSX平臺上,並沒有找到docker的daemon配置檔案
- 在OSX平臺上,使用
docker -H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock -d
這樣的命令來嘗試以tcp的方式啟動docker daemon也是失敗的,並沒有任何作用 - 目前推測除了Linux平臺上,其他平臺上的配置方法都不太一樣,但是在網路上暫時沒有找到解決方案,所以後面的操作我只能通過在本地建立多個docker-machine的方式來模擬實現遠端呼叫。
假設我們在192.168.1.123
這臺主機上開啟了docker服務,監聽了2375
埠,那麼我們就可以在同一網段的其他主機上(比如192.168.1.233
)通過docker -H tcp://192.168.1.123:2345 <command>
的方式呼叫到該主機上的docker服務。
比如
docker -H tcp://192.168.1.123:2345 ps
docker -H tcp://192.168.1.123:2345 images
docker -H tcp://192.168.1.123:2345 run ...複製程式碼
最終swarm構建叢集的時候,就是通過這樣的遠端服務呼叫來排程各個節點的。
叢集和分散式運算
在正式開始實踐叢集之前,我們有必要了解究竟什麼是叢集,什麼是分散式計算。
首先,這兩者有一個共同點,就是他們都是使用了多個服務節點的,通俗的說,就是要用到多臺伺服器協同工作(不一定是實體,也可能是虛擬機器)。
而兩者的區別在於:
- 叢集是多臺機器執行同一個業務,每次根據排程演算法尋找最合適的節點來執行該業務
- 分散式計算是將一個業務拆分成多個獨立的部分,由多臺機器共同協作完成
叢集的優點在於,當業務的需要的資源比較大時,可以避免由一個伺服器去獨自承擔壓力,而且即便有一個節點當機了,業務仍然可以繼續正常執行。這有點類似於負載均衡。
分散式的優點則是在計算上,可以協同多臺機器發揮計算的威力,進行需要超高效能的運算。
構建叢集
說現在我們正式開始構建叢集。
使用docker-machine建立節點
由於實體機器的缺乏以及在osx上無法正常開啟tcp的docker服務,我們基於docker-machine來建立多個虛擬機器,作為叢集中的節點。
執行下面的命令就可以建立一個新的docker-machine虛擬機器manager1
docker-machine create --driver virtualbox manager1複製程式碼
在建立了虛擬機器後,可以使用docker-machine env manager1
來檢視虛擬機器manager1
的相關資訊,包括IP地址等
現在我們繼續執行命令建立worker1
和worker2
兩個節點,使用docker-machine ls
命令可以檢視到所有正在工作的虛擬機器:
docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
manager1 - virtualbox Running tcp://192.168.99.100:2376 v17.06.1-ce
worker1 - virtualbox Running tcp://192.168.99.101:2376 v17.06.1-ce
worker2 - virtualbox Running tcp://192.168.99.102:2376 v17.06.1-ce複製程式碼
建立docker machine後,可以通過docker-machine ssh manager1 <command>
的方式來訪問虛擬機器,執行指令。
建立swarm叢集
初始化一個swarm叢集的命令為:
docker swarm init --listen-addr <MANAGER-IP>:<PORT> --advertise-addr <IP>複製程式碼
--listen-addr
引數是管理者節點的docker服務所在的IP:PORT,也就是說,可以通過這個組合訪問到該節點的docker服務。
--advertise-addr
是廣播地址,也就是其他節點加入該swarm叢集時,需要訪問的IP
現在我們在manager1
節點裡建立swarm網路,執行
docker-machine ssh manager1 docker swarm init --listen-addr 192.168.99.100:2377 --advertise-addr 192.168.99.100複製程式碼
返回響應:
Swarm initialized: current node (23lkbq7uovqsg550qfzup59t6) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-3z5rzoey0u6onkvvm58f7vgkser5d7z8sfshlu7s4oz2gztlvj-c036gwrakjejql06klrfc585r \
192.168.99.100:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.複製程式碼
這樣便建立了一個swarm叢集,並且manager1
節點目前是以管理者的身份加入在節點中的。
現在我們把worker1
和worker2
兩個節點加入到swarm叢集中去,分別在兩個節點的虛擬機器中執行docker swarm join --token ..
即可:
docker-machine ssh worker1 docker swarm join --token \
SWMTKN-1-3z5rzoey0u6onkvvm58f7vgkser5d7z8sfshlu7s4oz2gztlvj-c036gwrakjejql06klrfc585r \
192.168.99.100:2377
This node joined a swarm as a worker.複製程式碼
docker-machine ssh worker2 docker swarm join --token \
SWMTKN-1-3z5rzoey0u6onkvvm58f7vgkser5d7z8sfshlu7s4oz2gztlvj-c036gwrakjejql06klrfc585r \
192.168.99.100:2377
This node joined a swarm as a worker.複製程式碼
在任何一個節點上執行docker node ls
都可以檢視到當前整個叢集中的所有節點:
docker-machine ssh manager1 docker node ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
manager1 - virtualbox Running tcp://192.168.99.100:2376 v1.12.3
worker1 - virtualbox Running tcp://192.168.99.101:2376 v1.12.3
worker2 - virtualbox Running tcp://192.168.99.102:2376 v1.12.3複製程式碼
建立跨主機網路
叢集建立完畢之後我們要做的就是在叢集上部署我們的服務。但是首先應該讓所有的節點處在一個共享的網路中,這樣當我們把服務部署在這個共享網路中,就相當於部署在整個叢集中了。
使用docker network ls
可以檢視到當前主機所參與的所有網路:
docker-machine ssh manager1 docker network ls
NETWORK ID NAME DRIVER SCOPE
764ff31881e5 bridge bridge local
fbd9a977aa03 host host local
6p6xlousvsy2 ingress overlay swarm
e81af24d643d none null local複製程式碼
其中SCOPE為swarm,DRIVER為overlay的即為叢集節點中的共享網路。叢集建立後會有一個預設的ingress共享網路,現在我們來再建立一個:
docker-machine ssh manager1 docker network create --driver overlay swarm_test複製程式碼
在跨主機網路上部署服務
在叢集上部署應用,就是在共享網路上部署服務(service)。
但首先要保證每個節點上都已經有所需的映象和環境了,這點便可以通過將同一份docker-compose配置檔案共享到每個主機上,使用docker-compose在每個節點上下載映象和搭建環境的工作。
由於judge_server的服務架構很簡單,就一個映象,所以我在這裡直接在每臺主機上把它pull下來就好了:
docker-machine ssh manager1 docker pull registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0
docker-machine ssh worker1 docker pull registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0
docker-machine ssh worker2 docker pull registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0複製程式碼
接下來便是重頭戲,我們使用manager1
節點,在共享網路上啟動我們的服務
docker service create --replicas 3 --name judge_swarm -p 8090:8090 --network=swarm_test registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0複製程式碼
這個命令看起來是不是很像docker run
?沒錯,swarm最終的目的就是把操作叢集變得像操作單一的docker服務端一樣簡單!
--replicas 用於指定服務需要的節點數量,也就是叢集的規模,這個值是彈性的,你可以在後續動態的更改它。
當服務中某個節點掛掉時,swarm將會搜尋叢集中剩餘的可用節點,頂替上去。也就是說,swarm會動態的排程,總是保持服務是由3個節點執行著的。
-p 用於暴露埠到宿主機,這樣我們就能訪問到了。
--network用於指定部署service的網路是哪一個
現在在manager1
節點中使用docker service ls
來檢視叢集中的服務:
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
kofcno637cmq judge_swarm replicated 3/3 registry.cn-qingdao.aliyuncs.com/marklux/judge_server:1.0 *:8090->8090/tcp複製程式碼
現在我們嘗試在本地訪問192.168.99.100:8090/ping,就可以得到響應了,事實上,現在無論將ip換為worker1
或者worker2
的,響應的結果都是一樣,因為此時所有節點已經處在一個共同的叢集網路下了
經過大量的訪問測試,可以看到hostname
是在變化著的,這說明每次請求,都由swarm動態的排程,選擇了不同的節點來進行處理。
遺留問題
至此叢集的部署已經完成,但是我們還遺留了幾個問題沒有解決:
- 叢集節點的動態新增刪除不是很方便,這導致在web端管理判題服務機有一定的難度,當然可以通過docker的REMOTE API來實現,不過複雜度比較高
- 叢集節點間的檔案同步不太好實現,可能需要自己寫指令碼同步或是使用
rsync
之類的服務來實現 - swarm非常適合快速構建大量叢集來實現業務的處理,不過對於只有幾臺機器的情況而言,有些"殺雞用牛刀"的感覺