(轉) Docker swarm - 使用體驗 1+2


 

 

 

  背景

  憑借敏捷開發部署理念的推行,相信對於很多人來說docker這項容器技術已經並不陌生,Docker 1.12引擎發布了快兩個月,新引擎中包含了許多特性。諸如: Swarm模式,容器集群的健康檢查,節點的身份加密,docker Service API調用,容器啟動的過濾匹配方式(constraint), docker的內建路由,以及支持在多平台系統上運行docker(MAC、Windows、AWS、AZURE),以及一些插件升級等等. 特性之多,就連Docker 自己的產品經理也表示這次的新版本可能是公司有史以來變化最大的一次產品發布。

  很長一段時間里,docker在集群模式的管理上一直廣受外界詬病。Docker服務自身只能在單台host上進行操作,官方並沒有真正意義上的集群管理方案。直到現在1.12的出現, 引擎在多主機、多容器的集群管理上才有了進一步的改進和完善,版本自身內嵌了swarm mode集群管理模式。

  本文主要是介紹一下swarm 集群管理模式的新特性,以及如何該模式下如何實現集群的搭建和服務部署。

  Swarm cluster 模式新特性介紹

  1. 批量創建服務

  1.12引擎中多了docker service命令,和之前的docker run命令類似,但不同的是它能同時對多主機中的容器進行管理操作。下面就以1台manager節點,5台worker節點的swarm集群來闡述這些特性。

  首先看下容器的創建:

  $ docker network create -d overlay mynet

  $ docker service create –replicas 3 –name frontend –network mynet –publish 80:80/tcp frontend_image:latest

  $ docker service create –name redis –network mynet redis:latest

  建立容器之前先創建一個overlay的網絡,用來保證在不同主機上的容器網絡互通的網絡模式,后面兩條命令用來在同一個名叫mynet的overlay網絡里新建三個相同的web容器副本,和一個 redis副本,並且每個web容器都提供統一的端口映射關系。就像這樣:

  2. 強大的集群的容錯性

  既然是集群,當然難免會出現某幾個節點故障的情況:

  當三個web副本中的其中兩台web節點宕機后,cluster會根據自己的服務注冊發現機制,以及之前設定的值–replicas 3,在集群中剩余的空閑節點上,重新拉起兩個web副本。不難看出,docker service其實不僅僅是批量啟動服務這么簡單,而是在集群中定義了一種狀態。Cluster會持續檢測服務的健康狀態並維護集群的高可用性。

  新節點的分布示意圖如下:

  

  3. 服務節點的可擴展性

  Swarm Cluster不光只是提供了優秀的高可用性,同時也提供了節點彈性擴展的功能。當web這個容器組想動態擴展至六個節點個數時,只需執行$ docker service scale frontend=6就能立刻復制出三個新的副本出來。

  眼尖的朋友可能注意到了,所有擴展出來的新web副本節點都run在原先的web節點下面,如果有需求想在每台節點上都run一個相同的副本有沒有辦法呢?答案也是肯定的:

  $ docker service create –mode=global –name extend_frontend frontend_image:latest

  一條命令分分鍾搞定!

  4. 調度機制

  Docker1.12的調度機制也值得一提。

  所謂的調度其主要功能是cluster的server端去選擇在哪個服務器節點上創建並啟動一個容器實例的動作。它是由一個裝箱算法和過濾器組合而成。每次通過過濾器(constraint)啟動容器的時候,swarm cluster 都會調用調度機制篩選出匹配約束條件的服務器,並在這上面運行容器。

  還是拿剛剛那個例子來說,再加上–constraint參數,就能指定容器只run在服務器硬盤是SSD的節點上(前提是加入到cluster的節點,在啟動daemon時,本身需要加上參數 --label com.example.storage=”ssd”):

  $ docker service create –replicas 3 –name frontend –network mynet –publish 80:80/tcp –constraint engine.labels.com.example.storage=ssd frontend_image:lastest

  搭建一個swarm集群

  有了以上這些介紹,我們對swarm cluster 的一些新特性應該有了初步的了解 ,下面再看一個模擬網站rolling_update的實例,相信這也是許多平時做版本發布的devops們真正想要看到的東西。

  1. 搭建一個swarm集群

  准備三台機器

  Node1:192.168.133.129

  Node2:192.168.133.137

  Node3:192.168.133.139

  在構建一個swarm cluster前,需在cluster節點的防火牆上放行2377/tcp(cluster 管理端口)、7946/udp(節點間通信端口)、4789/udp(overlay 網絡端口)

  首先在node1上運行 $docker swarm init 去啟動一台cluster manager節點,然后在任意需要添加進集群的節點上運行docker swarm join –token *** 192.168.133.129:2377 就能將節點加入到cluser(加入到集群里的節點身份可在后面自由設置成worker或manager)。現在swarm cluster的節點就像下面的圖一樣,箱子都准備好了,就差貨物往里面裝了。

  通過$docker node ls能看到所有swarm節點的運行狀態:

  P.S.Swarm cluster的創建過程包含以下三個步驟:

  1. 發現Docker集群中的各個節點,收集節點狀態、角色信息,並監視節點狀態的變化

  2. 初始化內部調度(scheduler)模塊

  3. 創建並啟動API監聽服務模塊

  一旦創建好這個cluster,就可以用命令docker service批量對集群內的容器進行操作。搭建cluster只有兩步,是不是非常酷?

  2. 制作一個演示用的demo鏡像

  鏡像中存放一個python寫的簡單的http web服務:env.py,目的是顯示容器的containerID:

  from flask import Flask

  import os

  app = Flask(__name__)

  @app.route("/")

  def env():

  return os.environ["HOSTNAME"]

  app.run(host="0.0.0.0")

  3. 用swarm mode創建service task

  有了這個鏡像,然后通過docker service create命令去創建一個名叫test的task:

  $ docker service create --name test -p 5000:5000 demo python env.py

  用docker ps看一眼

  欸?為什么沒有起來呢?再用docker service ls 查看task的狀態:

  

  注意這個REPOLICAS的值,0/1說明docker create 已經創建了一個副本但是還沒有起來,稍等一會再運行一遍命令:

  補充:

  一些情況下已經運行了容器,可是運行docker ps在本機還是看不到容器,為什么呢?

  其實,docker 會根據當前每個swarm節點的負載判斷,在負載最優的節點運行這個task任務,用docker service ps + taskID 可以看到任務運行在哪個節點上。

  好了container已經起來了並且運行在node1上

  用瀏覽器打開地址能看到容器對應的ID:

  4. 增加service節點

  有了單個容器實例之后,下一步再嘗試下動態擴展實例個數

  $ docker service scale test=6

  Node1:

  Node2

  Node3

  一條命令讓現在swarm cluster里三台節點,每台都運行了兩個test副本實例。

  此時你是不是已經留意到,一個天然的HA集群出現了。docker會把對每個host的http請求依據輪詢算法,均勻地發送到每個task副本上。

  5. 模擬其中一個swarm cluster節點離線的情況

  正常來講讓一個swarm cluster中的一個node退出集群的方式,是在要推出的節點上運行$ docker swarm leave命令,但是為了讓實驗更瘋狂,我在node3上直接stop docker的daemon

  再去剩余兩個節點上任意一個查看task狀態:

  原本在node3上運行的兩個test任務:test3、test4,分別在node1和node2兩台host上被來起來了。整個副本遷移的過程無需人工干預,遷移后原本的集群的load balance依舊好使!

 

--- 第二篇 ---

 

01
上篇對node1、node2、node3的請求是通過綁host的方式測試的,本期我們接着往下聊。

負載均衡和服務發現

測試中只是每個host節點中的containers之間實現了負載均衡,生產環境在做rolling_update時,必須確保持在同一時刻,至少有一個容器能正常提供服務。

那么問題來了,有沒有辦法能自定義檢測到每個節點中應用的運行狀態,如果其中一個服務運行不正常,則立即通知前面做反向代理的HTTP服務器,讓它自動摘除不正常的節點,等到節點修復后又重新自動注冊節點信息到負載均衡器上呢?並且全程沒有人工干預。

答案是肯定的。這里介紹兩種實現服務注冊發現的方式:

1. docker1.12內置的服務注冊發現機制

講到docker的服務發現機制之前,不得不提overlay網絡,這個特性最早出現在docker1.9版本發布的功能特性中,他的特點就是能夠使不同宿主機上的容器進行網絡互通。

而在此之前,如果要做到位於不同主機的容器之間通信,一般有幾種方法:

使用端口映射:直接把容器的服務端口映射到主機上,主機直接通過映射出來的端口通信

把容器放到主機所在的網段

通過第三方工具flannel,weave 或者 pipework 等,這些方案一般都是通過 SDN 搭建 overlay 網絡達到容器通信的

Docker1.12中依然繼承了這個overlay的網絡模型,並且為自己的服務注冊發現提供了強有力的網絡保障。

02

Docke的注冊發現原理其實是采用一個分布式的Key-Value Storage作為存儲的抽象層。Docker 1.12 提供了內置的 Discovery 服務, 這樣集群的搭建不需要再依賴外部的 Discovery 服務, 比如 consul 或 etcd。(當然swarm mode下也可以使用這些Discovery 服務,具體的下個小節會詳細介紹)。目前Swarm mode提供了6種discovery機制:Token(默認)、Node、File、Consul、Etcd、Zookeeper。其中有兩種方式的節點需要對配置文件或者其他相關內容進行維護,其他服務發現僅需要通過join命令行來完成。這兩種方式分別是Node和File discovery。

好了繼續實驗,首先創建一個自定義的overlay網絡:

$docker network create -d overlay test

03

然后在同一個網絡上分別吧應用容器和http服務容器起來:

$ docker service create --name test -p 5000:5000 --replicas 6 –network test demo python env.py

$ docker service create --name nginx --replicas 3 --network test -p 80:80 nginx-2

Nginx容器的default.conf配置如下,其中test:5000對應之前由docker service create出來的test任務,docker 引擎已經將task name對應的ip關系映射成內部的DNS解析。

server {

listen 80;

server_name localhost;

access_log /var/log/nginx/log/host.access.log main;

location / {

# root /usr/share/nginx/html;

# index index.html index.htm;

proxy_pass http://test:5000;

}

error_page 500 502 503 504 /50x.html;

location = /50x.html {

root /usr/share/nginx/html;

}

}

至此全部操作完成,當瀏覽器訪問http://node2后,http請求根據VIP負載均衡算法均勻的分配至3個swarm cluster node上的6個python容器去響應請求,並且無論哪個后端容器掛了,只要三台docker swarm cluster的節點不同時出事,都不會影響正常 的網站服務。

對於上述的VIP負載均衡算法做下補充說明:docker1.12使用的是linux自身的IPVS作為負載均衡方式。IPVS實則linux內核中一個叫做ip_vs的負載均衡模塊。不同於DNS負載均衡將IP列表順序輪詢,IPVS會將負載均勻的分發到每個容器。IPVS是四層的轉發者,能夠轉發TCP、UDP、DNS並且支持八種負載均衡算法。

04

2. docker結合外置的配置存儲服務

這類的服務有多種選擇,consul和etcd,zookeeper,這里以consul為例。consul是一款服務注冊發現的軟件,自身是一個key/value的store。在docker1.12發布之前,許多人選擇用它和docker一起結合來提供一個高可擴展性的web服務。

開始實驗前要先修改docker的主配置文件,使用consul替換缺省的docker自身的key/value store中心

ExecStart=/usr/bin/dockerd --registry-mirror=http://057aa18c.m.daocloud.io -H unix:///var/run/docker.sock --cluster-store=consul://192.168.133.137:8500

1)還是在上面演示的幾台機器中選一台node2來做consul的server(consul的server最好也配置成cluster的模式,實現consul自己的HA,本文為了快速介紹功能就不搭建了,只起一個節點)。

還需注意一點,本文中選用了一台業務節點作為配置存儲服務的運行位置,不過通常建議是這種base service能與運行業務容器的節點分開,使用獨立的服務節點,這樣才能確保所有運行業務容器的節點是無狀態的,可以被平等的調度和分配運算任務。

2)$docker run –d --restart=always -h node -p 8500:8500 -p 8600:53/udp progrium/consul -server -bootstrap -advertise 192.168.133.137 -log-level debug

Concul自帶UI,打開192.168.133.137:8500你就能看到,consul啟動后會開啟兩個端口,一個事53/udp,還有一個是8500/tcp,從dashboard上都能看到他們的狀況。

05
3)啟動 registrator 容器,目的是注冊 docker container 的信息到consul 集群中

$docker run -d --restart=always -v /var/run/docker.sock:/tmp/docker.sock -h 192.168.133.137 gliderlabs/registrator consul://192.168.133.137:8500

4)啟動一個最簡單的http服務器驗證是否已經將自身信息注冊到了consul中,實現了自動發現的功能:

$docker run -d -p 7070:80 --name httpd httpd

06

5)最后在測試機上安裝 consul-template 來從 consul 獲取數據,更新本地的某個模板配置文件。

安裝consul-template:

$ curl https://releases.hashicorp.com/consul-template/0.15.0/consul-template_0.15.0_linux_amd64.zip -o consul-template.zip && unzip consul-template.zip && mv ./consul-template /usr/bin/

生成模板文件:

$ echo -e '{{range service "httpd"}}\nserver {{.Address}}:{{.Port}}{{end}}' > /tmp/consul.ctmpl
07

填寫模板:

$consul-template -consul 192.168.133.137:8500 -template "/tmp/consul.ctmpl:/tmp/consul.result" --once

08

現在再把httpd容器stop掉,重新執行填寫模板的命令。

可以看到注冊進consul的容器信息被填寫進模板當中了! 如果把模板做成nginx的配置文件,就能依據consul來檢測容器是否啟動從而動態更新nginx的配置文件了。

upstream consul_nodes {

server 192.168.133.137:7070;

server 192.168.133.139:7070;

}

location / {

root html;

index index.html index.htm;

proxy_pass http://consul_nodes;

}

以上兩種是實現服務注冊發現的方式,都列出來給各位看看,對比之下看得出來在配置的容易程度方面,docker1.12自帶的還是要占有比較明顯的優勢的。

滾動部署

從前docker的舊版本下,容器必須手動藍綠部署,或者手寫腳本實現滾動升級。1.12有了滾動更新以后,我們就不需要把更新規則寫成腳本去實現透明部署。Swarm mode中,服務可以更新逐步節點,並且控制服務的部署之間的延遲到不同的節點集合。如果出現任何錯誤,能夠馬上回滾上一個任務,回到先前版本的服務。

當現在要更新test這個task所引用的鏡像時,可以這么操作:

$docker service update --update-parallelism 2 --image demo:2.0 --update-delay 10s test

其中--update-parallelism參數用來指定最大同步更新任務數。這意味着我們可以安全透明的更新容器副本。關於透明,當然要確保你的容器是向后兼容的,否則最好銷毀舊的容器,再去更新所有的容器。

然后容器就會每隔10秒跟新2個容器,直至30秒后此次更新操作完畢。

010

011

最后提醒一句,docker 1.12的swarm cluster的功能選項是可開始,不是必須選項。原先的單主機運行方式依然保留。但是看到了這些炫酷的新特性你還舍得關閉這個選項嗎?

Docker生態已經逐漸從單純的鏡像生態,衍生到容器集群的管理,docker的前途依舊一片光明。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM