這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
標題: 部署docker swarm
這部分,我們啟動我們的accountservice,運行在本地的docker swarm叢集中.同時討論幾個容器部署的重要概念
這篇部落客要講一下幾點:
- docker swarm和容器部署
- 用docker作為容器運行accountservice
- 建立一個本地的docker swarm叢集
- 將accountservice作為swarm服務部署
- 基測和結果
其實寫完這一章,我發現這一章和go沒有關係.但希望你喜歡.
什麼是容器部署
在實踐開始之前,一個容器部署的簡單介紹:
當一個應用越來越複雜,並且開始有更高的負載,將會有成百個服務在很多硬體上運行.容器部署讓我們在一個節點上管理我們的硬體
一篇文章上總結的:
抽象主機的基礎結構,部署工具允許使用者在一個部署目標上控制整個叢集.
這總結的很好.用kubernetes或者docker swarm這種容器部署工具來部署我們的各個服務在不同的節點上.對於docker來說,swarm模式管理docker engine的叢集.kubernetes用一種稍微不同的抽象方法,但是整體概念上是一致的.
容器部署不僅控制我們服務的生命週期,也提供其他服務,例如:服務發現,負載平衡,內部地址和日誌.
docker swarm核心概念
在docker swarm中,有三個核心概念:
- 節點: 一個節點就是一個docker engine的執行個體.理論上講,他是一個擁有cpu資源,記憶體和網路介面的主機.一個節點可以是一個manager節點或者worker節點.
- 服務: 服務就是worker節點上啟動並執行指令.一個服務可以是複製的或者全域的.一個服務可以抽象的看成是任意數量的容器組成的邏輯上的服務.這個服務可以用它的名字來調用,而不需要知道它內部的網路結構.
- 任務: 一個任務可以是docker容器,docker檔案定義任務為:擁有並運行docker容器和指令.manager節點分發任務給worker節點的服務.
展示一個簡單的微服務架構.兩個服務accountservice和quotes-service抽象為兩個節點,運行在五個容器的執行個體中.
![圖片上傳中...]
代碼
這部分沒有改go方面的代碼.
cker.你可以得到這一章完整的代碼
git checkout P5
容器化我們的accountservice
docker安裝
你需要安裝docker.我用的是docker toolbox 和vitualbox.但是你可以直接用docker.
建立dockerfile
一個dockerfile可以看成是你想建立什麼樣的docker鏡像的配方.讓我們在accountservice檔案夾中建立檔案Dockerfile:
FROM iron/baseEXPOSE 6767ADD accountservice-linux-amd64 /ENTRYPOINT ["./accountservice-linux-amd64"]
解釋:
- FROM-定義我們的基礎鏡像.我們會在這個鏡像上開始.iron/base是一個可以運行go程式的小巧的鏡像
- EXPOSE-定義一個連接埠,作為外部請求的連接埠
- ADD-增加一個檔案accountservice-linux-amd64到root(/)目錄下
- ENTRYPOINT-定義啟動哪一個程式,當docker開啟這個鏡像容器
不同作業系統下的編譯
我們的檔案名稱字包含linux-amd64.我們可以叫他任何名字,但是我喜歡把作業系統和cpu型號放進執行檔案名稱字中.我用的是mac OSX 系統.所以我如果直接編譯go的執行檔案,用go build的話,這產生一個執行檔案在同一個檔案夾中.然而這個執行檔案不能再docker上運行,因為docker容器的環境是linux.因此,我們需要設定一些環境參數,這樣我們的編譯器才知道我們要給其他的系統或者cpu環境編譯檔案.
在goblog/accountservice檔案夾下面運行:
export GOOS=linuxgo build -o accountservice-linux-amd64export GOOS=darwin
-o表示產生二進位執行檔案.我經常用指令檔自動做這些東西(後面會有)
因為OS X和linux容器都在AMD64 cpu架構上,我們不需要設定GOARCH參數.但是你如果用32位系統,或者ARM處理器,你要設定GOARCH參數
建立docker鏡像
現在我們建立第一個docker鏡像,包含我們的執行檔案.去accountservice的上層檔案夾,就是$GOPATH/src/github.com/callistaenterprise/goblog.
對於docker鏡像,我們經常用一個首碼來標註名字.我經常用我的github名字作為首碼,例如eriklupander/myservicename.這裡,我用someprefix作為首碼.執行下面的命令來建立Docker鏡像:
> docker build -t someprefix/accountservice accountservice/Sending build context to Docker daemon 13.17 MBStep 1/4 : FROM iron/base ---> b65946736b2cStep 2/4 : EXPOSE 6767 ---> Using cache ---> f1147fd9abcfStep 3/4 : ADD accountservice-linux-amd64 / ---> 0841289965c6Removing intermediate container db3176c5e1e1Step 4/4 : ENTRYPOINT ./accountservice-linux-amd64 ---> Running in f99a911fd551 ---> e5700191acf2Removing intermediate container f99a911fd551Successfully built e5700191acf2
好了,我們有啦一個someprefix/accountservice鏡像.如果我們要在多節點下運行或者分享鏡像,我們可以用docker push來使我們的鏡像被別的主機pull.
我們現在運行鏡像:
> docker run --rm someprefix/accountserviceStarting accountserviceSeeded 100 fake accounts...2017/02/05 11:52:01 Starting HTTP service at 6767
然而,我們的容器不是在你的主機系統下運行,他運行在自己的網路中,我們不能直接從我們的主機來請求他.有辦法來解決這個問題,但現在我們放一放,我們繼續組建我們的docker swarm和部署accountservice.
建立單節點Docker swarm叢集
一個docker swarm叢集包括至少一個swarm manager和零到多個swarm worker.我的例子會包含一個swarm manager.這節過後,你會有一個swarm manager運行.
你可以參考別的文章來看如何運行swarm.下面這條命令初始化docker主機為swarm-manager-1作為一個swarm節點,同時讓swarm-manager-1節點地址和主機一樣
> docker $(docker-machine config swarm-manager-1) swarm init --advertise-addr $(docker-machine ip swarm-manager-1)
如果我們要建立多節點的swarm叢集,我們要把這條命令產生的join-token記錄下來,這樣我們可以加入其他的節點到這個swarm中.
建立網路
一個docker網路的用處是,當我們想請求同一個swarm叢集上的其他的容器,並不需要知道真實的叢集分布.
docker network create --driver overlay my_network
my_network是我們的網路名稱
部署accountservice
現在我們要部署我們的accountservice進Docker swarm服務中.這個docker服務命令有很多參數設定,但不要怕.這裡我們來部署accountservice
docker service create --name=accountservice --replicas=1 --network=my_network -p=6767:6767 someprefix/accountservicentg3zsgb3f7ah4l90sfh43kud
快速看一下這些參數
- -name:給服務的名字.這也是叢集中其他服務要求我們的名字.所以另一個服務來請求accountservice的話,這個服務只需要Get請求http://accountservice:6767/accounts/10000
- -replicas: 我們服務的執行個體數量.如果我們有多節點的docker swarm叢集,swarm engine會自動分發執行個體到不同的節點上.
- -network: 這裡我們告訴我們的服務用剛剛我們建立的網路my_network
- -p: 映射[內部連接埠]:[外部連接埠].這裡我們用6767:6767.如果我們用6767:80,從外部請求要用80連接埠.注意這部分使我們的服務可以被外界請求.大多數情況,你不應該讓你的服務暴露給外界.你應該用一個EDGE-server(例如反向 Proxy),包括路由機制和安全檢查,所以外界不能隨意請求你的服務
- someprefix/accountservice: 指明我們想讓容器運行哪一個鏡像.
讓我們看看我們的服務是否運行了
> docker service ls
太好了,我們應該可以curl或者用瀏覽器請求我們的api.唯一要知道的就是我們swarm的ip地址.即使我們只運行一個服務執行個體,我們的網路和swarm也會需要我們的服務外部連接埠,這意味著兩個服務不能用同一個外部連接埠.他們可以有同樣的內部連接埠,但對於外部來說,swarm是一個整體.
> echo $ManagerIP192.168.99.100
如果你換了terminal,你可以重新匯出:
> export ManagerIP=`docker-machine ip swarm-manager-0`
curl請求:
> curl $ManagerIP:6767/accounts/10000{"id":"10000","name":"Person_0"}
部署可視化
用docker的命令來查看swarm的狀態不容易看,一個圖形化的方法比較好.例如manomarks docker swarm visualizer可以被部署為一個Docker swarm的服務.這可以給我們提供我們叢集分布的圖形,同事確保我們在叢集中外部暴露的服務可不可以請求到.
初始化這個可視器在容器鏡像中
docker service create \ --name=viz \ --publish=8080:8000/tcp \ --constraint=node.role==manager \ --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ manomarks/visualizer
這將在8000連接埠產生一個服務.讓我們用瀏覽器瀏覽http://$ManagerIP:8000
額外內容
我也做了一個swarm的可視化介面叫做dvizz,展示docker 遠程api和D3.js force圖.你可以安裝她
docker service create \ --constraint=node.role==manager \ --replicas 1 --name dvizz -p 6969:6969 \ --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ --network my_network \ eriklupander/dvizz
瀏覽 http://$ManagerIP:6969
加入quote-service
只有一個服務的微服務不能看出微服務的全貌.讓我們部署一個基於spring boot的quotes-service.我把這個容器鏡像放在docker hub中的eriklupander/quotes-service.
> docker service create --name=quotes-service --replicas=1 --network=my_network eriklupander/quotes-service
如果你輸入docker ps來看那些docker容器在運行:
> docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES98867f3514a1 eriklupander/quotes-service "java -Djava.security" 12 seconds ago Up 10 seconds
注意,我們沒有暴露一個外界連接埠給這個服務,所以我們只能在叢集內部的連接埠8080內請求.我們會整合這個服務在第七部分同時看一下服務探索和負載平衡.
如果你加入了dvizz,你應該能看到quotes-service和accountservice
copyall.sh指令碼
我們來做一個指令碼協助我們編譯和部署.在root/goblog檔案夾中,建立一個指令檔叫做copyall.sh
#!/bin/bashexport GOOS=linuxexport CGO_ENABLE=0cd accountservice; go get; go build -o accountservice-linux-amd64;echo build `pwd`;cd ..export GOOS=darwindocker build -t someprefix/accountservice accountservice/docker service rm accountservicedocker service create --name=accountservice --replicas=1 --network=my_network -p=6767:6767someprefix/accountservice
這段指令碼編譯執行檔案,重新編譯docker鏡像,部署到docker swarm服務上.
我喜歡指令碼的簡化,雖然有時我用gradle plugin.
效能
現在開始,所有的基測都在docker swarm上進行.這意味之前的結果不能用來和之後的比較
cpu使用率和記憶體使用量會用 docker stats來收集.我們也會用gatling測試.
如果你喜歡壓力測試,第二節的仍然可以用,但需要改變-baseUrl參數
> mvn gatling:execute -dusers=1000 -Dduration=30 -DbaseUrl=http://$ManagerIP:6767
記憶體使用量率
> docker stats $(docker ps | awk '{if(NR>1) print $NF}')CONTAINER CPU % MEM USAGE / LIMIT accountservice.1.k8vyt3dulvng9l6y4mj14ncw9 0.00% 5.621 MiB / 1.955 GiBquotes-service.1.h07fde0ejxru4pqwwgms9qt00 0.06% 293.9 MiB / 1.955 GiB
啟動後,包含linux和我們accountservice的容器用5.6mb的記憶體,java開發的quotes-service用了300mb.雖然這可以通過調整jvm來降低.
cpu和記憶體使用量壓力測試
CONTAINER CPU % MEM USAGE / LIMIT accountservice.1.k8vyt3dulvng9l6y4mj14ncw9 25.50% 35.15 MiB / 1.955 GiBB
在1K req/s下,虛擬機器中啟動並執行swarm和在第二三節中的OS x系統相比,記憶體稍微升高,cpu大略相同.
效能
![圖片上傳中...]
延遲上升到4ms.這和直接運行有所升高,原因有幾點.我認為gatling測試通過橋接的網路和swarm上的路由會有一些延遲.但是4ms的延遲也不錯.畢竟我們從boltDB讀資料,序列化到json並輸出到HTTP.
總結
我們學習如何啟動docker swarm和部署accountservice到swarm上.下一節,我們會給我們的微服務加入healthcheck.