Docker的最新版本v1.12,包含了很多更新和Docker Swarm提供的功能。在今天的文章中,我們將探討如何使用Docker Swarm來部署服務。
在我們使用Docker Engine Swarm部署服務之前,我們需要建立一個Swarm集群。因為我們需要1.12新添加的功能,所以我們也要安裝Docker Engine的最新版本。
以下步驟將指導你在Ubuntu 16.04上安裝Docker Engine。對於其他平臺和版本,你可以參考Docker的官方安裝文檔(https://docs.docker.com/engine/installation/#installation)。
設置Docker Apt倉庫我們會使用Ubuntu的標準安裝方式,依賴於Apt包管理器。因為我們需要安裝最新版本的Docker Engine,所以我們需要配置Apt,從Docker官方Apt倉庫來安裝docker-engine,而不是從系統預配置的倉庫。
添加Docker公鑰配置Apt來使用新倉庫的第一步是想Apt緩存中添加該庫的公鑰。使用apt-key命令:
# apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
以上的apt-key命令向密鑰伺服器p80.pool.sks-keyservers.net請求一個特定的密鑰(58118E89F3A912897C070ADBF76221572C52609D)。公鑰將會被用來驗證從新倉庫下載的所有包。
指定Docker倉庫的位置引入Docker的公鑰,我們可以配置Apt使用Docker的倉庫伺服器。我們可以在/etc/apt/sources.list.d/目錄中添加一個條目。
# echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" >> /etc/apt/sources.list.d/docker.list
當我們刷新Apt緩存時,Apt將會搜索sources.list.d/目錄下的所有文件,來尋找新的包倉庫。上述命令會創建一個新文件docker.list,其中包含了一個添加了apt.dockerproject.org倉庫的條目。
更新Apt包緩存運行apt-get命令的update選項,來刷新Apt包緩存。
# apt-get update
這會觸發Apt重新讀取配置文件,刷新倉庫列表,包含進我們添加的那個倉庫。該命令也會查詢這些倉庫來緩存可用的包列表。
安裝linux-image-extra在安裝Docker Engine之前,我們需要安裝一個先決軟體包(prerequisite package)。linux-image-extra包是一個內核相關的包,Ubuntu系統需要它來支持aufs存儲設備驅動。Docker會使用該驅動來加載卷。
為了安裝該包,我們將使用apt-get命令的install選項。
# apt-get install linux-image-extra-$(uname -r)
在apt-get命令中,$(uname -r)將返回正在運行的內核的版本。任何對於該系統的內核更新應當包括安裝linux-image-extra,它的版本需要與新內核版本相一致。如果該包沒有正確更新的話,Docker加載卷的功能可能受到影響。
在Apt配置好和linux-image-extra安裝好之後,我們可以繼續安裝Docker Engine了。我們可以使用apt-get命令的install選項來安裝docker-engine包。
# apt-get install docker-engine
此時,我們應該已經安裝好了Docker Engine v1.12.0或者更新版本。我們可以執行docker命令的version選項來驗證我們已經安裝了最新版本。
# docker versionClient:Version: 1.12.0API version: 1.24Go version: go1.6.3Git commit: 8eab29eBuilt: Thu Jul 28 22:11:10 2016OS/Arch: linux/amd64Server:Version: 1.12.0API version: 1.24Go version: go1.6.3Git commit: 8eab29eBuilt: Thu Jul 28 22:11:10 2016OS/Arch: linux/amd64
我們可以看到,Server版本和Client版本都是1.12.0。接下來,我們會創建Swarm集群。
在這一小節中,我們將在多臺機器上執行多個任務。為了更清楚地表述,我會在例子中包含主機名。
我們會使用兩個節點來啟動Swarm集群。此時,兩個節點都按照上述步驟安裝了Docker Engine。
當創建Swarm集群時,我們需要指定一個manager節點。在這個例子中,我們會使用主機名為swarm-01的主機作為manager節點。為了使swarm-01成為manager節點,我們需要首先在swarm-01執行命令來創建Swarm集群。這個命令就是docker命令的swarm init選項。
root@swarm-01:~# docker swarm init --advertise-addr 10.0.0.1Swarm initialized: current node (awwiap1z5vtxponawdqndl0e7) is now a manager.To add a worker to this swarm, run the following command:docker swarm join \--token SWMTKN-1-51pzs5ax8dmp3h0ic72m9wq9vtagevp1ncrgik115qwo058ie6-3fokbd3onl2i8r7dowtlwh7kb \10.0.0.1:2377To add a manager to this swarm, run the following command:docker swarm join \--token SWMTKN-1-51pzs5ax8dmp3h0ic72m9wq9vtagevp1ncrgik115qwo058ie6-bwex7fd4u5aov4naa5trcxs34 \10.0.0.1:2377
在上述命令中,除了swarm init之外,我們還指定了--advertise-addr為10.0.0.1。Swarmmanager節點會使用該IP位址來廣告Swarm集群服務。雖然該地址可以是私有地址,重要的是,為了使節點加入該集群,那些節點需要能通過該IP的2377埠來訪問manager節點。
在運行docker swarm init命令之後,我們可以看到swarm-01被賦予了一個節點名字(awwiap1z5vtxponawdqndl0e7),並被選為Swarm集群的管理器。輸出中也提供了兩個命令:一個命令可以添加worker節點到swarm中,另一個命令可以添加另一個manager節點到該Swarm中。
Docker Swarm Mode可以支持多個manager節點。然而,其中的一個會被選舉為主節點伺服器,它會負責Swarm的編排。
添加worker節點到Swarm集群中Swarm集群建立之後,我們需要添加一個新的worker節點。
root@swarm-02:~# docker swarm join \> --token SWMTKN-1-51pzs5ax8dmp3h0ic72m9wq9vtagevp1ncrgik115qwo058ie6-3fokbd3onl2i8r7dowtlwh7kb \> 10.0.0.1:2377This node joined a swarm as a worker.
在本例中,我們將swarm-02添加到了swarm中,作為worker節點。Swarm集群中的worker節點的角色是用來運行任務(tasks)的;在該例中,任務(tasks)就是容器(containers)。另一方面,manager節點的角色是管理任務(容器)的編排,並維護Swarm集群本身。
除此之外,manager節點本身也是worker節點,也可以運行任務。
查看當前的Swarm節點我們現在有了一個最基本的兩節點的Swarm集群。我們可以執行docker命令的node ls選項來驗證集群的狀態。
root@swarm-01:~# docker node lsID HOSTNAME STATUS AVAILABILITY MANAGER STATUS13evr7hmiujjanbnu3n92dphk swarm-02.example.com Ready Active awwiap1z5vtxponawdqndl0e7 * swarm-01.example.com Ready Active Leader
從輸出中可以看到,swarm-01和swarm-02都處於Ready和Active狀態。因此,我們可以繼續在Swarm集群上部署服務了。
在Docker Swarm Mode中,服務是指一個長期運行(long-running)的Docker容器,它可以被部署到任意一臺worker節點上,可以被遠端系統或者Swarm中其他容器連接和消費(consume)的。
在本例中,我們會部署一個Redis服務。
部署一個有副本的服務一個有副本的服務是一個Docker Swarm服務,運行了特定數目的副本(replicas)。這些副本是由多個Docker容器的實例組成的。在本例中,每個副本都是一個獨立的Redis實例。
為了創建新服務,我們會使用docker命令的service create選項。以下命令將創建一個名為redis的服務,包含2個副本,並在集群中發布6379埠。
root@swarm-01:~# docker service create --name redis --replicas 2 --publish 6379:6379 rediser238pvukeqdev10nfmh9q1kr
除了service create選項之外,我們還指定了--name為redis,--replicas表示該服務需要運行在2個不同的節點上。我們可以運行docker命令的service ls選項來驗證該服務是運行在兩個節點上的。
root@swarm-01:~# docker service lsID NAME REPLICAS IMAGE COMMANDer238pvukeqd redis 2/2 redis
從輸出中可以看到,2個副本都在運行。如果我們想看到這些任務的更多細節,我們可以運行docker命令的service ps選項。
root@swarm-01:~# docker service ps redisID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR5lr10nbpy91csmc91cew5cul1 redis.1 redis swarm-02.example.com Running Running 40 minutes ago 1t77jsgo1qajxxdekbenl4pgk redis.2 redis swarm-01.example.com Running Running 40 minutes ago
service ps選項會顯示特定服務的任務(容器)。在本例中,我們可以看到redis服務有一個任務(容器)運行在兩個Swarm節點上。
連接Redis服務我們已經驗證了服務正在運行,我們可以嘗試從遠端系統,使用redis-cli客戶端來連接該服務。
vagrant@vagrant:~$ redis-cli -h swarm-01.example.com -p 6379swarm-01.example.com:6379>
從上面的連接可以看到,我們已經成功地連接上了redis服務。這意味著我們的服務已經運行起來了。
Docker Swarm是如何發布服務的當我們創建了redis服務時,我們使用了--publish選項。該選項用來告知Docker將埠6379發布為redis服務的可用埠。
當Docker發布了服務埠時,它在Swarm集群上的所有節點上監聽該埠。當流量到達該埠時,該流量將被路由到運行該服務的容器上。如果所有節點都運行著一個服務的容器,那麼概念是相對標準的;然而,當我們的節點數比副本多時,概念就變得有趣了。
添加第三個worker節點為了添加另一個worker節點,我們只要簡單地重複第一部分中的安裝步驟。因為我們已經做過這些步驟了,所以我們直接跳到3節點的Swarm集群。再一次地,我們可以運行docker命令來檢查集群狀態。
root@swarm-01:~# docker node lsID HOSTNAME STATUS AVAILABILITY MANAGER STATUS13evr7hmiujjanbnu3n92dphk swarm-02.example.com Ready Active awwiap1z5vtxponawdqndl0e7 * swarm-01.example.com Ready Active Leadere4ymm89082ooms0gs3iyn8vtl swarm-03.example.com Ready Active
我們可以看到集群包含三個主機:
當我們創建了兩個副本的服務時,它在swarm-01和swarm-02上分別創建了任務(容器)。即便我們添加了另一個worker節點,我們可以看到情況仍然是這樣的。
root@swarm-01:~# docker service ps redisID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR5lr10nbpy91csmc91cew5cul1 redis.1 redis swarm-02.example.com Running Running 55 minutes ago 1t77jsgo1qajxxdekbenl4pgk redis.2 redis swarm-01.example.com Running Running 55 minutes ago
Docker Swarm通過replicated服務,可以保證對於每個指定的副本,都運行了一個任務(容器)。當我們創建redis服務時,我們指定了2個副本。這就意味著,即便我們有了第三個節點,Docker也沒有理由在新節點上創建一個新任務。
此時,我們遇到了一個有趣的情形:我們在3個Swarm節點中的2個上運行了服務。在non-swarm的世界中,這就意味著當連接第三個Swarm節點時,redis服務將變得不可用。然而,Swarm Mode中,情況卻不是這樣的。
在一個無任務運行的worker節點上連接服務之前,我們提到Docker是如何發布服務埠的,Swarm在所有節點上都發布了該埠。有趣的是,當我們連接一個並未運行任何容器的worker節點時,會發生什麼呢。
讓我們看一下,當我們連接swarm-03的redis埠時,會發生什麼呢。
vagrant@vagrant:~$ redis-cli -h swarm-03.example.com -p 6379swarm-03.example.com:6379>
有趣的是,連接竟然成功了。儘管swarm-03上並未運行任何redis容器,但是連接成功了。這是因為,在內部,Docker將redis服務流量重路由到運行了redis容器的worker節點。
Docker稱之為入口負載均衡(ingress load balancing)。它的工作方式是,所有worker節點都監聽在發布的服務埠上。當該服務被外部系統調用時,收到流量的節點會通過Docker提供的內部DNS服務,將流量負載均衡該流量。
因此,即便我們將Swarm集群擴展至100個worker節點時,redis服務的終端用戶可以連接到任意一個worker節點。他們會被重定向到運行了任務(容器)的兩個Docker宿主機之一。
這些重路由和負載均衡對於終端用戶是完全透明的。
讓服務global化此時,我們已經建立了redis服務,運行了2個副本,這意味著,3個節點中的2個正在運行容器。
如果我們希望redis服務在每一個worker節點上運行一個實例,我們可以簡單地修改服務的副本數目,從2增加到3。這意味著,如果我們增加或者減少worker節點數目,我們需要調整副本數目。
我們可以自動化地做這件事,只要把我們的服務變成一個Global Service。Docker Swarm Mode中的Global Service使用了創建一個服務,該服務會自動地在每個worker節點上運行任務。這種方法對於像Redis這樣的一般服務都是有效的。
讓我們重新創建redis服務為Global Service。
root@swarm-01:~# docker service create --name redis --mode global --publish 6379:6379 redis5o8m338zmsped0cmqe0guh2to
同樣是docker service create命令,唯一的區別是指定了--mode參數為global。
服務建立好之後,運行docker命令的service ps選項,我們可以看到,Docker是如何分發該服務的。
root@swarm-01:~# docker service ps redisID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR27s6q5yvmyjvty8jvp5k067ul redis redis swarm-03.example.com Running Running 26 seconds ago 2xohhkqvlw7969qj6j0ca70xx \_ redis redis swarm-02.example.com Running Running 38 seconds ago 22wrdkun5f5t9lku6sbprqi1k \_ redis redis swarm-01.example.com Running Running 38 seconds ago
我們可以看到,一旦該服務被創建為Global Service,那麼每個worker節點上都會運行一個任務。
在本文中,我們不僅安裝了Docker Engine,也創建了一個Swarm集群,部署了一個有副本的服務,然後創建了Global Service。
在最近的一篇文章(https://blog.codeship.com/getting-started-with-kubernetes/)中,我不僅安裝了Kubernetes,也創建了Kubernetes服務。通過比較Docker Swarm Mode服務和Kubernetes服務,我發現Swarm Mode服務更容易創建。如果只想使用Kubernetes的服務特性,而不需要Kubernetes的其他功能,那麼Docker Swarm Mode可能是一個更容易的選擇。
本文為翻譯文章,點擊閱讀原文連結可查看原文。