使用 Docker Swarm 來運行服務
Docker的最新版本v1.12,包含了很多更新和Docker Swarm提供的功能。在今天的文章中,我們將探討如何使用Docker Swarm來部署服務。
在Ubuntu 16.04中激活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加載卷的功能可能受到影響。
安裝Docker Engine
在Apt配置好和 linux-image-extra 安裝好之后,我們可以繼續安裝Docker Engine了。我們可以使用 apt-get 命令的 install 選項來安裝 docker-engine 包。
# apt-get install docker-engine
此時,我們應該已經安裝好了 Docker Engine v1.12.0 或者更新版本。我們可以執行 docker 命令的 version 選項來驗證我們已經安裝了最新版本。
# docker version Client: Version: 1.12.0 API version: 1.24 Go version: go1.6.3 Git commit: 8eab29e Built: Thu Jul 28 22:11:10 2016 OS/Arch: linux/amd64 Server: Version: 1.12.0 API version: 1.24 Go version: go1.6.3 Git commit: 8eab29e Built: Thu Jul 28 22:11:10 2016 OS/Arch: linux/amd64
我們可以看到,Server版本和Client版本都是 1.12.0 。接下來,我們會創建Swarm集群。
創建一個Docker 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.1 Swarm 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:2377 To 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:2377 This 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 ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS 13evr7hmiujjanbnu3n92dphk 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 redis er238pvukeqdev10nfmh9q1kr
除了 service create 選項之外,我們還指定了 --name 為 redis , --replicas 表示該服務需要運行在 2 個不同的節點上。我們可以運行 docker 命令的 service ls 選項來驗證該服務是運行在兩個節點上的。
root@swarm-01:~# docker service ls ID NAME REPLICAS IMAGE COMMAND er238pvukeqd redis 2/2 redis
從輸出中可以看到, 2 個副本都在運行。如果我們想看到這些任務的更多細節,我們可以運行 docker 命令的 service ps 選項。
root@swarm-01:~# docker service ps redis ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR 5lr10nbpy91csmc91cew5cul1 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 6379 swarm-01.example.com:6379>
從上面的連接可以看到,我們已經成功地連接上了 redis 服務。這意味著我們的服務已經運行起來了。
Docker Swarm是如何發布服務的
當我們創建了 redis 服務時,我們使用了 --publish 選項。該選項用來告知Docker將端口 6379 發布為 redis 服務的可用端口。
當Docker發布了服務端口時,它在Swarm集群上的所有節點上監聽該端口。當流量到達該端口時,該流量將被路由到運行該服務的容器上。如果所有節點都運行著一個服務的容器,那么概念是相對標準的;然而,當我們的節點數比副本多時,概念就變得有趣了。
添加第三個worker節點
為了添加另一個worker節點,我們只要簡單地重復第一部分中的安裝步驟。因為我們已經做過這些步驟了,所以我們直接跳到3節點的Swarm集群。再一次地,我們可以運行 docker 命令來檢查集群狀態。
root@swarm-01:~# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS 13evr7hmiujjanbnu3n92dphk swarm-02.example.com Ready Active awwiap1z5vtxponawdqndl0e7 * swarm-01.example.com Ready Active Leader e4ymm89082ooms0gs3iyn8vtl swarm-03.example.com Ready Active
我們可以看到集群包含三個主機:
-
swarm-01
-
swarm-02
-
swarm-03
當 我們創建了兩個副本的服務時,它在 swarm-01 和 swarm-02 上分別創建了任務(容器)。即便我們添加了另一個worker節點,我們可以看到情況仍然是這樣的。
root@swarm-01:~# docker service ps redis ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR 5lr10nbpy91csmc91cew5cul1 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 6379 swarm-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 redis 5o8m338zmsped0cmqe0guh2to
同樣是 docker service create 命令,唯一的區別是指定了 --mode 參數為 global 。
服務建立好之后,運行 docker 命令的 service ps 選項,我們可以看到,Docker是如何分發該服務的。
root@swarm-01:~# docker service ps redis ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR 27s6q5yvmyjvty8jvp5k067ul 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可能是一個更容易的選擇。
來自:https://mp.weixin.qq.com/s?__biz=MzA5OTAyNzQ2OA==&mid=2649692097&idx=1&sn=2db0f8b1300555ef21dbb7125019ce43&chksm=889328a2bfe4a1b4c84121085440b31c5b22e42bfa55ac5acf1ab6832f749bb59d9dde895b61&mpshare=1&scene=1&srcid=1010gpPnRQbWwM7awqPz2NgV&pass_ticket=PKMfvvjspn1QaORVofj8eoWT8aU4iRZRyGTuowHhhkI%3D#rd