前言
DOCKER技術在推出后掀起了一陣容器化技術的熱潮,容器化使得服務的部署變得極其簡易,這為微服務和分布式計算提供了很大的便利。
為了把容器化技術的優(yōu)點發(fā)揮到極致,docker公司先后推出了三大技術:docker-machine
,docker-compose
,docker-swarm
,可以說是幾乎實現(xiàn)了容器化技術中所有可能需要的底層技術手段。
在使用go語言實現(xiàn)了判題引擎并打包好docker鏡像后,就需要進行分布式判題的編寫,這次就讓我們手動實踐,嘗試使用docker的三大殺器來部署一個多機器構成的判題服務集群。
三劍客簡介
docker-machine
docker技術是基于Linux內核的cgroup
技術實現(xiàn)的,那么問題來了,在非Linux平臺上是否就不能使用docker技術了呢?答案是可以的,不過顯然需要借助虛擬機去模擬出Linux環(huán)境來。
docker-machine就是docker公司官方提出的,用于在各種平臺上快速創(chuàng)建具有docker服務的虛擬機的技術,甚至可以通過指定driver來定制虛擬機的實現(xiàn)原理(一般是virtualbox)。
docker-compose
docker鏡像在創(chuàng)建之后,往往需要自己手動pull來獲取鏡像,然后執(zhí)行run命令來運行。當服務需要用到多種容器,容器之間又產(chǎn)生了各種依賴和連接的時候,部署一個服務的手動操作是令人感到十分厭煩的。
dcoker-compose技術,就是通過一個.yml
配置文件,將所有的容器的部署方法、文件映射、容器連接等等一系列的配置寫在一個配置文件里,最后只需要執(zhí)行docker-compose up
命令就會像執(zhí)行腳本一樣的去一個個安裝容器并自動部署他們,極大的便利了復雜服務的部署。
docker-swarm
swarm是基于docker平臺實現(xiàn)的集群技術,他可以通過幾條簡單的指令快速的創(chuàng)建一個docker集群,接著在集群的共享網(wǎng)絡上部署應用,最終實現(xiàn)分布式的服務。
相比起zookeeper等集群管理框架來說,swarm顯得十分輕量,作為一個工具,它把節(jié)點的加入、管理、發(fā)現(xiàn)等復雜的操作都濃縮為幾句簡單的命令,并且具有自動發(fā)現(xiàn)節(jié)點和調度的算法,還支持自定制。雖然swarm技術現(xiàn)在還不是非常成熟,但其威力已經(jīng)可見一般。
淺談docker服務架構和遠程API
在正式使用docker技術部署集群應用時,我們應該先來了解一下docker工作的一些底層原理,和docker遠程調用的API,這樣才能大體了解集群究竟是如何運作的。
daemon
之前的docker入門文章中講過,docker的基礎服務,比如容器的創(chuàng)建、查看、停止、鏡像的管理,其實都是由docker的守護進程(daemon)來實現(xiàn)的。
每次執(zhí)行的docker指令其實都是通過向daemon發(fā)送請求來實現(xiàn)的。
daemon的運作(通信模式)主要有兩種,一種是通過unix套接字(默認,但只能在本地訪問到,比較安全),一種是通過監(jiān)聽tcp協(xié)議地址和端口來實現(xiàn)(這個可以實現(xiàn)在遠程調用到docker服務)。
遠程API
除了通過遠程tcp協(xié)議訪問遠程主機上的docker服務外,docker還提供了一套基于HTTP的API,可以使用curl來實現(xiàn)操作遠程主機上的docker服務,這為開發(fā)基于WEB的docker服務提供了便利。
遠程docker使用示例
最終實現(xiàn)集群的時候實際是使用docker的遠程調用來將不同的docker主機連接成一個整體的(通過tcp協(xié)議)。
我們不妨先來手動模擬嘗試一下docker服務的遠程調用吧。
首先需要在提供服務的主機上將docker的運行方式改為tcp,具體方法為修改/etc/default/docker
中的DOCKER_OPTS
為如下內容
-H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock
-H 后的參數(shù)是自己定義的要綁定的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平臺上,其他平臺上的配置方法都不太一樣,但是在網(wǎng)絡上暫時沒有找到解決方案,所以后面的操作我只能通過在本地創(chuàng)建多個docker-machine的方式來模擬實現(xiàn)遠程調用。
假設我們在192.168.1.123
這臺主機上開啟了docker服務,監(jiān)聽了2375
端口,那么我們就可以在同一網(wǎng)段的其他主機上(比如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構建集群的時候,就是通過這樣的遠程服務調用來調度各個節(jié)點的。
集群和分布式運算
在正式開始實踐集群之前,我們有必要了解究竟什么是集群,什么是分布式計算。
首先,這兩者有一個共同點,就是他們都是使用了多個服務節(jié)點的,通俗的說,就是要用到多臺服務器協(xié)同工作(不一定是實體,也可能是虛擬機)。
而兩者的區(qū)別在于:
- 集群是多臺機器執(zhí)行同一個業(yè)務,每次根據(jù)調度算法尋找最合適的節(jié)點來執(zhí)行該業(yè)務
- 分布式計算是將一個業(yè)務拆分成多個獨立的部分,由多臺機器共同協(xié)作完成
集群的優(yōu)點在于,當業(yè)務的需要的資源比較大時,可以避免由一個服務器去獨自承擔壓力,而且即便有一個節(jié)點宕機了,業(yè)務仍然可以繼續(xù)正常運行。這有點類似于負載均衡。
分布式的優(yōu)點則是在計算上,可以協(xié)同多臺機器發(fā)揮計算的威力,進行需要超高性能的運算。
構建集群
說現(xiàn)在我們正式開始構建集群。
使用docker-machine創(chuàng)建節(jié)點
由于實體機器的缺乏以及在osx上無法正常開啟tcp的docker服務,我們基于docker-machine來創(chuàng)建多個虛擬機,作為集群中的節(jié)點。
執(zhí)行下面的命令就可以創(chuàng)建一個新的docker-machine虛擬機manager1
docker-machine create --driver virtualbox manager1
在創(chuàng)建了虛擬機后,可以使用docker-machine env manager1
來查看虛擬機manager1
的相關信息,包括IP地址等
現(xiàn)在我們繼續(xù)執(zhí)行命令創(chuàng)建worker1
和worker2
兩個節(jié)點,使用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
創(chuàng)建docker machine后,可以通過docker-machine ssh manager1 <command>
的方式來訪問虛擬機,執(zhí)行指令。
創(chuàng)建swarm集群
初始化一個swarm集群的命令為:
docker swarm init --listen-addr <MANAGER-IP>:<PORT> --advertise-addr <IP>
--listen-addr
參數(shù)是管理者節(jié)點的docker服務所在的IP:PORT,也就是說,可以通過這個組合訪問到該節(jié)點的docker服務。
--advertise-addr
是廣播地址,也就是其他節(jié)點加入該swarm集群時,需要訪問的IP
現(xiàn)在我們在manager1
節(jié)點里創(chuàng)建swarm網(wǎng)絡,執(zhí)行
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.
這樣便創(chuàng)建了一個swarm集群,并且manager1
節(jié)點目前是以管理者的身份加入在節(jié)點中的。
現(xiàn)在我們把worker1
和worker2
兩個節(jié)點加入到swarm集群中去,分別在兩個節(jié)點的虛擬機中執(zhí)行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.
在任何一個節(jié)點上執(zhí)行docker node ls
都可以查看到當前整個集群中的所有節(jié)點:
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
創(chuàng)建跨主機網(wǎng)絡
集群建立完畢之后我們要做的就是在集群上部署我們的服務。但是首先應該讓所有的節(jié)點處在一個共享的網(wǎng)絡中,這樣當我們把服務部署在這個共享網(wǎng)絡中,就相當于部署在整個集群中了。
使用docker network ls
可以查看到當前主機所參與的所有網(wǎng)絡:
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的即為集群節(jié)點中的共享網(wǎng)絡。集群建立后會有一個默認的ingress共享網(wǎng)絡,現(xiàn)在我們來再創(chuàng)建一個:
docker-machine ssh manager1 docker network create --driver overlay swarm_test
在跨主機網(wǎng)絡上部署服務
在集群上部署應用,就是在共享網(wǎng)絡上部署服務(service)。
但首先要保證每個節(jié)點上都已經(jīng)有所需的鏡像和環(huán)境了,這點便可以通過將同一份docker-compose配置文件共享到每個主機上,使用docker-compose在每個節(jié)點上下載鏡像和搭建環(huán)境的工作。
由于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
節(jié)點,在共享網(wǎng)絡上啟動我們的服務
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 用于指定服務需要的節(jié)點數(shù)量,也就是集群的規(guī)模,這個值是彈性的,你可以在后續(xù)動態(tài)的更改它。
當服務中某個節(jié)點掛掉時,swarm將會搜尋集群中剩余的可用節(jié)點,頂替上去。也就是說,swarm會動態(tài)的調度,總是保持服務是由3個節(jié)點運行著的。
-p 用于暴露端口到宿主機,這樣我們就能訪問到了。
--network用于指定部署service的網(wǎng)絡是哪一個
現(xiàn)在在manager1
節(jié)點中使用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
現(xiàn)在我們嘗試在本地訪問192.168.99.100:8090/ping,就可以得到響應了,事實上,現(xiàn)在無論將ip換為worker1
或者worker2
的,響應的結果都是一樣,因為此時所有節(jié)點已經(jīng)處在一個共同的集群網(wǎng)絡下了
經(jīng)過大量的訪問測試,可以看到hostname
是在變化著的,這說明每次請求,都由swarm動態(tài)的調度,選擇了不同的節(jié)點來進行處理。
遺留問題
至此集群的部署已經(jīng)完成,但是我們還遺留了幾個問題沒有解決:
- 集群節(jié)點的動態(tài)添加刪除不是很方便,這導致在web端管理判題服務機有一定的難度,當然可以通過docker的REMOTE API來實現(xiàn),不過復雜度比較高
- 集群節(jié)點間的文件同步不太好實現(xiàn),可能需要自己寫腳本同步或是使用
rsync
之類的服務來實現(xiàn) - swarm非常適合快速構建大量集群來實現(xiàn)業(yè)務的處理,不過對于只有幾臺機器的情況而言,有些"殺雞用牛刀"的感覺