Docker Swarm Mode
Docker v1.12 是一個非常重要的版本瓦阐,Docker 重新實(shí)現(xiàn)了集群的編排方式。在此之前,提供集群功能的 Docker Swarm 是一個單獨(dú)的軟件,而且依賴外部數(shù)據(jù)庫(比如 Consul适贸、etcd 或 Zookeeper)昧廷。
從 v1.12 開始彪薛,Docker Swarm 的功能已經(jīng)完全與 Docker Engine 集成,要管理集群,只需要啟動 Swarm Mode。安裝好 Docker翼悴,Swarm 就已經(jīng)在那里了,服務(wù)發(fā)現(xiàn)也在那里了(不需要安裝 Consul 等外部數(shù)據(jù)庫)幔妨。
相比 Kubernetes鹦赎,用 Docker Swarm 創(chuàng)建集群非常簡單,不需要額外安裝任何軟件误堡,也不需要做任何額外的配置钙姊。很適合作為學(xué)習(xí)容器編排引擎的起點(diǎn)。
重要概念
- swarm
swarm 運(yùn)行 Docker Engine 的多個主機(jī)組成的集群埂伦。從 v1.12 開始,集群管理和編排功能已經(jīng)集成進(jìn) Docker Engine思恐。當(dāng) Docker Engine 初始化了一個 swarm 或者加入到一個存在的 swarm 時沾谜,它就啟動了 swarm mode膊毁。沒啟動 swarm mode 時,Docker 執(zhí)行的是容器命令基跑;運(yùn)行 swarm mode 后婚温,Docker 增加了編排 service 的能力。Docker 允許在同一個 Docker 主機(jī)上既運(yùn)行 swarm service媳否,又運(yùn)行單獨(dú)的容器栅螟。
- node
swarm 中的每個 Docker Engine 都是一個 node,有兩種類型的 node:manager 和 worker篱竭。為了向 swarm 中部署應(yīng)用力图,我們需要在 manager node 上執(zhí)行部署命令,manager node 會將部署任務(wù)拆解并分配給一個或多個 worker node 完成部署掺逼。manager node 負(fù)責(zé)執(zhí)行編排和集群管理工作吃媒,保持并維護(hù) swarm 處于期望的狀態(tài)。swarm 中如果有多個 manager node吕喘,它們會自動協(xié)商并選舉出一個 leader 執(zhí)行編排任務(wù)赘那。woker node 接受并執(zhí)行由 manager node 派發(fā)的任務(wù)。默認(rèn)配置下 manager node 同時也是一個 worker node氯质,不過可以將其配置成 manager-only node募舟,讓其專職負(fù)責(zé)編排和集群管理工作。work node 會定期向 manager node 報告自己的狀態(tài)和它正在執(zhí)行的任務(wù)的狀態(tài)闻察,這樣 manager 就可以維護(hù)整個集群的狀態(tài)拱礁。
- service
service 定義了 worker node 上要執(zhí)行的任務(wù)。swarm 的主要編排任務(wù)就是保證 service 處于期望的狀態(tài)下蜓陌。舉一個 service 的例子:在 swarm 中啟動一個 http 服務(wù)觅彰,使用的鏡像是 httpd:latest,副本數(shù)為 3钮热。manager node 負(fù)責(zé)創(chuàng)建這個 service填抬,經(jīng)過分析知道需要啟動 3 個 httpd 容器,根據(jù)當(dāng)前各 worker node 的狀態(tài)將運(yùn)行容器的任務(wù)分配下去隧期,比如 worker1 上運(yùn)行兩個容器飒责,worker2 上運(yùn)行一個容器。運(yùn)行了一段時間仆潮,worker2 突然宕機(jī)了宏蛉,manager 監(jiān)控到這個故障,于是立即在 worker3 上啟動了一個新的 httpd 容器性置。這樣就保證了 service 處于期望的三個副本狀態(tài)拾并。
創(chuàng)建swarm 集群
swarm-manager 是 manager node,swarm-worker1 和 swarm-worker2 是 worker node。創(chuàng)建三節(jié)點(diǎn)的 swarm 集群嗅义。
在 swarm-manager 上執(zhí)行如下命令創(chuàng)建 swarm屏歹。
docker swarm init --advertise-addr 10.10.8.125
--advertise-addr 指定與其他 node 通信的地址。
執(zhí)行 docker node ls 查看當(dāng)前 swarm 的 node之碗,目前只有一個 manager蝙眶。復(fù)制前面的 docker swarm join 命令,在 swarm-worker1 和 swarm-worker2 上執(zhí)行褪那,將它們添加到 swarm 中幽纷。命令輸出如下:
docker swarm join --token SWMTKN-1-3rx3jnumdxja3rm4k5h8pijqbttoctjvy4p3fo7t0kdxwffcoz-5uwscs6wh2s9lpqg3lclv00y7 10.10.8.125:2377
docker node ls 可以看到兩個 worker node 已經(jīng)添加進(jìn)來了。如果當(dāng)時沒有記錄下 docker swarm init 提示的添加 worker 的完整命令博敬,可以通過 docker swarm join-token worker 查看友浸。
運(yùn)行service
現(xiàn)在部署一個運(yùn)行 httpd 鏡像的 service,執(zhí)行如下命令:
docker service create --name web_server httpd
部署 service 的命令形式與運(yùn)行容器的 docker run 很相似冶忱,--name 為 service 命名尾菇,httpd 為鏡像的名字。
通過 docker service ls 可以查看當(dāng)前 swarm 中的 service囚枪。REPLICAS 顯示當(dāng)前副本信息派诬,0/1 的意思是 web_server 這個 service 期望的容器副本數(shù)量為 1,目前已經(jīng)啟動的副本數(shù)量為 0链沼。也就是當(dāng)前 service 還沒有部署完成默赂。命令 docker service ps 可以查看 service 每個副本的狀態(tài)。
可以看到 service 唯一的副本被分派到 swarm-worker1括勺,當(dāng)前的狀態(tài)是 Preparing缆八,還沒達(dá)到期望的狀態(tài) Running,我們不僅要問疾捍,這個副本在 Preparing 什么呢奈辰?其實(shí)答案很簡單,swarm-worker1 是在 pull 鏡像乱豆,下載完成后奖恰,副本就會處于 Running 狀態(tài)了。
service伸縮
對于 web 服務(wù)宛裕,我們通常會運(yùn)行多個實(shí)例瑟啃。這樣可以負(fù)載均衡,同時也能提供高可用揩尸。swarm 要實(shí)現(xiàn)這個目標(biāo)非常簡單蛹屿,增加 service 的副本數(shù)就可以了。在 swarm-manager 上執(zhí)行如下命令:docker service scale web_server=5岩榆。副本數(shù)增加到 5错负,通過 docker service ls 和 docker service ps 查看副本的詳細(xì)信息坟瓢。
默認(rèn)配置下 manager node 也是 worker node,所以 swarm-manager 上也運(yùn)行了副本湿颅。如果不希望在 manager 上運(yùn)行 service载绿,可以執(zhí)行如下命令:docker node update --availability drain swarm-manager
我們還可以 scale down,減少副本數(shù)油航,運(yùn)行下面的命令:
docker service scale web_server=3
實(shí)現(xiàn)failover
創(chuàng)建 service 的時候,我們沒有告訴 swarm 發(fā)生故障時該如何處理怀浆,只是說明了我們期望的狀態(tài)(比如運(yùn)行3個副本)谊囚,swarm 會盡最大的努力達(dá)成這個期望狀態(tài),無論發(fā)生什么狀況执赡。
現(xiàn)在swarm-worker1上有兩個副本镰踏,swarm-worker2上有一個副本,當(dāng)我們關(guān)閉 swarm-worker1沙合。Swarm 會檢測到 swarm-worker1 的故障奠伪,并標(biāo)記為 Down。Swarm 會將 swarm-worker1 上的副本調(diào)度到其他可用節(jié)點(diǎn)首懈。我們可以通過 docker service ps 觀察這個 failover 過程绊率。可以看到究履,web_server.1 和 web_server.2 已經(jīng)從 swarm-worker1 遷移到了 swarm-worker2滤否,之前運(yùn)行在故障節(jié)點(diǎn) swarm-worker1 上的副本狀態(tài)被標(biāo)記為 Shutdown。
訪問service
服務(wù)并沒有暴露給外部網(wǎng)絡(luò)最仑,只能在 Docker 主機(jī)上訪問藐俺。換句話說,當(dāng)前配置下泥彤,我們無法訪問 service web_server欲芹。
要將 service 暴露到外部,方法其實(shí)很簡單吟吝,執(zhí)行下面的命令:
docker service update --publish-add 8080:80 web_server
如果是新建 service菱父,可以直接用使用 --publish 參數(shù),比如:
docker service create --name web_server --publish 8080:80 --replicas=2 httpd
器在 80 端口上監(jiān)聽 http 請求爸黄,--publish-add 8080:80 將容器的 80 映射到主機(jī)的 8080 端口滞伟,這樣外部網(wǎng)絡(luò)就能訪問到 service 了。curl 集群中任何一個節(jié)點(diǎn)的 8080 端口炕贵,都能夠訪問到 web_server梆奈。
routing mesh
當(dāng)我們訪問任何節(jié)點(diǎn)的 8080 端口時,swarm 內(nèi)部的 load balancer 會將請求轉(zhuǎn)發(fā)給 web_server 其中的一個副本称开。這就是 routing mesh 的作用亩钟。
所以乓梨,無論訪問哪個節(jié)點(diǎn),即使該節(jié)點(diǎn)上沒有運(yùn)行 service 的副本清酥,最終都能訪問到 service扶镀。另外,我們還可以配置一個外部 load balancer焰轻,將請求路由到 swarm service臭觉。比如配置 HAProxy,將請求分發(fā)到各個節(jié)點(diǎn)的 8080 端口辱志。
ingress 網(wǎng)絡(luò)
當(dāng)我們應(yīng)用 --publish-add 8080:80 時蝠筑,swarm 會重新配置 service,之前的所有副本都被 Shutdown揩懒,然后啟動了新的副本什乙。
查看新副本的容器網(wǎng)絡(luò)配置:
# docker exec web_server.2.r2d9wt0ik82dmczxy5p2qwnv8 ip r
default via 172.18.0.1 dev eth1
10.255.0.0/16 dev eth0 proto kernel scope link src 10.255.0.7
172.18.0.0/16 dev eth1 proto kernel scope link src 172.18.0.3
容器的網(wǎng)絡(luò)與 --publish-add 之前已經(jīng)大不一樣了,現(xiàn)在有兩塊網(wǎng)卡已球,每塊網(wǎng)卡連接不同的 Docker 網(wǎng)絡(luò)臣镣。
實(shí)際上:
1、eth0 連接的是一個 overlay 類型的網(wǎng)絡(luò)智亮,名字為 ingress忆某,其作用是讓運(yùn)行在不同主機(jī)上的容器可以相互通信。
2鸽素、eth1 連接的是一個 bridge 類型的網(wǎng)絡(luò)褒繁,名字為 docker_gwbridge,其作用是讓容器能夠訪問到外網(wǎng)馍忽。
# docker network ls
NETWORK ID NAME DRIVER SCOPE
cffcec359069 bridge bridge local
15a7c1fbba33 docker_gwbridge bridge local
bae92bff8199 host host local
l0usihpnydn3 ingress overlay swarm
b7ec19400b21 mac_net1 macvlan local
cdc188b05e3c mac_net10 macvlan local
870eae5eb0c3 mac_net20 macvlan local
deb4cbe62ba0 none null local
ingress 網(wǎng)絡(luò)是 swarm 創(chuàng)建時 Docker 為自動我們創(chuàng)建的棒坏,swarm 中的每個 node 都能使用 ingress。通過 overlay 網(wǎng)絡(luò)遭笋,主機(jī)與容器坝冕、容器與容器之間可以相互訪問;同時瓦呼,routing mesh 將外部請求路由到不同主機(jī)的容器喂窟,從而實(shí)現(xiàn)了外部網(wǎng)絡(luò)對 service 的訪問。
service之間通信
微服務(wù)架構(gòu)的應(yīng)用由若干 service 組成央串。比如有運(yùn)行 httpd 的 web 前端磨澡,有提供緩存的 memcached,有存放數(shù)據(jù)的 mysql质和,每一層都是 swarm 的一個 service稳摄,每個 service 運(yùn)行了若干容器。在這樣的架構(gòu)中饲宿,service 之間是必然要通信的厦酬。
- 服務(wù)發(fā)現(xiàn)
一種實(shí)現(xiàn)方法是將所有 service 都 publish 出去胆描,然后通過 routing mesh 訪問。但明顯的缺點(diǎn)是把 memcached 和 mysql 也暴露到外網(wǎng)仗阅,增加了安全隱患昌讲。
如果不 publish,那么 swarm 就要提供一種機(jī)制减噪,能夠:
1短绸、讓 service 通過簡單的方法訪問到其他 service。
2旋廷、當(dāng) service 副本的 IP 發(fā)生變化時鸠按,不會影響訪問該 service 的其他 service。
3饶碘、當(dāng) service 的副本數(shù)發(fā)生變化時,不會影響訪問該 service 的其他 service馒吴。
這其實(shí)就是服務(wù)發(fā)現(xiàn)(service discovery)扎运。Docker Swarm 原生就提供了這項(xiàng)功能,通過服務(wù)發(fā)現(xiàn)饮戳,service 的使用者不需要知道 service 運(yùn)行在哪里豪治,IP 是多少,有多少個副本扯罐,就能與 service 通信负拟。
- 創(chuàng)建 overlay 網(wǎng)絡(luò)
要使用服務(wù)發(fā)現(xiàn),需要相互通信的 service 必須屬于同一個 overlay 網(wǎng)絡(luò)歹河,所以我們先得創(chuàng)建一個新的 overlay 網(wǎng)絡(luò)掩浙。
docker network create --driver overlay myapp_net
直接使用 ingress 行不行?很遺憾秸歧,目前 ingress 沒有提供服務(wù)發(fā)現(xiàn)厨姚,必須創(chuàng)建自己的 overlay 網(wǎng)絡(luò)。
- 部署 service 到 overlay
部署一個 web 服務(wù)键菱,并將其掛載到新創(chuàng)建的 overlay 網(wǎng)絡(luò)谬墙。
docker service create --name my_web --replicas=3 --network myapp_net httpd
部署一個 util 服務(wù)用于測試,掛載到同一個 overlay 網(wǎng)絡(luò)经备。
docker service create --name util --network myapp_net busybox sleep 10000000
sleep 10000000 的作用是保持 busybox 容器處于運(yùn)行的狀態(tài)拭抬,我們才能夠進(jìn)入到容器中訪問 service my_web。
- 驗(yàn)證
通過 docker service ps util 確認(rèn) util 所在的節(jié)點(diǎn)為node1侵蒙。登錄到 swarm-worker1造虎,在容器 util.1 中 ping 服務(wù) my_web。
# docker exec util.1.irfxosry2tzefrmvchqyanari ping -c 3 my_web
PING my_web (10.0.0.5): 56 data bytes
64 bytes from 10.0.0.5: seq=0 ttl=64 time=0.142 ms
64 bytes from 10.0.0.5: seq=1 ttl=64 time=0.104 ms
64 bytes from 10.0.0.5: seq=2 ttl=64 time=0.092 ms
可以看到 my_web 的 IP 為 10.0.0.2蘑志,這是哪個副本的 IP 呢累奈?
其實(shí)哪個副本的 IP 都不是贬派。10.0.0.2 是 my_web service 的 VIP(Virtual IP),swarm 會將對 VIP 的訪問負(fù)載均衡到每一個副本澎媒。
我們可以執(zhí)行下面的命令查看每個副本的 IP搞乏。
# docker exec util.1.irfxosry2tzefrmvchqyanari nslookup tasks.my_web
Server: 127.0.0.11
Address: 127.0.0.11:53
Non-authoritative answer:
Name: tasks.my_web
Address: 10.0.0.8
Name: tasks.my_web
Address: 10.0.0.7
Name: tasks.my_web
Address: 10.0.0.6
10.0.0.6、10.0.0.7戒努、10.0.0.8 才是各個副本自己的 IP请敦。不過對于服務(wù)的使用者(這里是 util.1),根本不需要知道 my_web副本的 IP储玫,也不需要知道 my_web 的 VIP侍筛,只需直接用 service 的名字 my_web 就能訪問服務(wù)。
滾動更新service
滾動更新降低了應(yīng)用更新的風(fēng)險撒穷,如果某個副本更新失敗匣椰,整個更新將暫停,其他副本則可以繼續(xù)提供服務(wù)端礼。同時禽笑,在更新的過程中,總是有副本在運(yùn)行的蛤奥,因此也保證了業(yè)務(wù)的連續(xù)性佳镜。
下面我們將部署三副本的服務(wù),鏡像使用 httpd:2.2.31凡桥,然后將其更新到 httpd:2.2.32蟀伸。
創(chuàng)建服務(wù):
docker service create --name my_web --replicas=3 httpd:2.2.31
將 service 更新到 httpd:2.2.32:
docker service update --image httpd:2.2.32 my_web
--image 指定新的鏡像。
Swarm 將按照如下步驟執(zhí)行滾動更新:
1缅刽、停止第一個副本啊掏。
2、調(diào)度任務(wù)拷恨,選擇 worker node脖律。
3、在 worker 上用新的鏡像啟動副本腕侄。
4小泉、如果副本(容器)運(yùn)行成功,繼續(xù)更新下一個副本冕杠;如果失敗微姊,暫停整個更新過程。
docker service ps 查看更新結(jié)果分预。默認(rèn)配置下兢交,Swarm 一次只更新一個副本,并且兩個副本之間沒有等待時間笼痹。我們可以通過 --update-parallelism 設(shè)置并行更新的副本數(shù)目配喳,通過 --update-delay 指定滾動更新的間隔時間酪穿。
比如執(zhí)行如下命令:
docker service update --replicas 6 --update-parallelism 2 --update-delay 1m30s my_web
service 增加到六個副本,每次更新兩個副本晴裹,間隔時間一分半鐘被济。docker service inspect 查看 service 的當(dāng)前配置。
Swarm 還有個方便的功能是回滾涧团,如果更新后效果不理想只磷,可以通過 --rollback 快速恢復(fù)到更新之前的狀態(tài)。請注意泌绣,--rollback 只能回滾到上一次執(zhí)行 docker service update 之前的狀態(tài)钮追,并不能無限制地回滾。
docker service update --rollback my_web
管理數(shù)據(jù)
service 的容器副本會 scale up/down阿迈,會 failover元媚,會在不同的主機(jī)上創(chuàng)建和銷毀,這就引出一個問題苗沧,如果 service 有要管理的數(shù)據(jù)惠毁,那么這些數(shù)據(jù)應(yīng)該如何存放呢?
選項(xiàng)一:打包在容器里崎页。
顯然不行。除非數(shù)據(jù)不會發(fā)生變化腰埂,否則飒焦,如何在多個副本直接保持同步呢?
選項(xiàng)二:數(shù)據(jù)放在 Docker 主機(jī)的本地目錄中屿笼,通過 volume 映射到容器里牺荠。
位于同一個主機(jī)的副本倒是能夠共享這個 volume,但不同主機(jī)中的副本如何同步呢驴一?
選項(xiàng)三:利用 Docker 的 volume driver休雌,由外部 storage provider 管理和提供 volume,所有 Docker 主機(jī) volume 將掛載到各個副本肝断。
這是目前最佳的方案杈曲。volume 不依賴 Docker 主機(jī)和容器,生命周期由 storage provider 管理胸懈,volume 的高可用和數(shù)據(jù)有效性也全權(quán)由 provider 負(fù)責(zé)担扑,Docker 只管使用。
replicated mode VS global mode
Swarm 可以在 service 創(chuàng)建或運(yùn)行過程中靈活地通過 --replicas 調(diào)整容器副本的數(shù)量趣钱,內(nèi)部調(diào)度器則會根據(jù)當(dāng)前集群的資源使用狀況在不同 node 上啟停容器涌献,這就是 service 默認(rèn)的 replicated mode。在此模式下首有,node 上運(yùn)行的副本數(shù)有多有少燕垃,一般情況下枢劝,資源更豐富的 node 運(yùn)行的副本數(shù)更多,反之亦然卜壕。
除了 replicated mode您旁,service 還提供了一個 globalmode,其作用是強(qiáng)制在每個 node 上都運(yùn)行一個且最多一個副本印叁。
此模式特別適合需要運(yùn)行 daemon 的集群環(huán)境被冒。比如要收集所有容器的日志,就可以 global mode 創(chuàng)建 service轮蜕,在所有 node 上都運(yùn)行 gliderlabs/logspout 容器昨悼,即使之后有新的 node 加入,swarm 也會自動在新 node 上啟動一個 gliderlabs/logspout 副本跃洛。
docker service create \
> --mode global \
> --name logspout \
> --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \
> gliderlabs/logspout
可以通過 docker service inspect 查看 service 的 mode率触。
# docker service inspect logspout --pretty
ID: bj4kj5c2egao5zu7c42tjc7uz
Name: logspout
Service Mode: Global
Placement:
UpdateConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Update order: stop-first
RollbackConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Rollback order: stop-first
ContainerSpec:
Image: gliderlabs/logspout:latest@sha256:90847ec896aa9f96cd558afc80e690a48d4861f4a54f92f1d56efd3ade1e5b3a
Init: false
Mounts:
Target: /var/run/docker.sock
Source: /var/run/docker.sock
ReadOnly: false
Type: bind
Resources:
Endpoint Mode: vip
這里是 Global,如果創(chuàng)建 service 時不指定汇竭,默認(rèn)是 Replicated葱蝗。
使用Label控制service的位置
使用 label可以精細(xì)控制 Service 的運(yùn)行位置呢。
邏輯分兩步:
1细燎、為每個 node 定義 label两曼。
2、設(shè)置 service 運(yùn)行在指定 label 的 node 上玻驻。
label 可以靈活描述 node 的屬性悼凑,其形式是 key=value,用戶可以任意指定璧瞬,例如將 swarm-worker1 作為測試環(huán)境户辫,為其添加 label env=test:
docker node update --label-add env=test swarm-worker1
對應(yīng)的,將 swarm-worker2 作為生產(chǎn)環(huán)境嗤锉,添加 label env=prod:
docker node update --label-add env=prod swarm-worker2
現(xiàn)在部署 service 到測試環(huán)境:
docker service create \
--constraint node.labels.env==test \
--replicas 3 \
--name my_web \
--publish 8080:80 \
httpd
--constraint node.labels.env==test 限制將 service 部署到 label=test 的 node渔欢,即 swarm-worker1。
更新 service瘟忱,將其遷移到生產(chǎn)環(huán)境:
docker service update --constraint-rm node.labels.env==test my_web
docker service update --constraint-add node.labels.env==prod my_web
刪除并添加新的 constraint奥额,設(shè)置 node.labels.env==prod,最終所有副本都遷移到了 swarm-worker2酷誓。
label 還可以跟 global 模式配合起來使用披坏,比如只收集生產(chǎn)環(huán)境中容器的日志。
docker service create \
--mode global \
--constraint node.labels.env==prod \
--name logspout \
--mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \
gliderlabs/logspout
只有 swarm-worker2 節(jié)點(diǎn)上才會運(yùn)行 logspout盐数。
配置Health Check
Docker 只能從容器啟動進(jìn)程的返回代碼判斷其狀態(tài)棒拂,而對于容器內(nèi)部應(yīng)用的運(yùn)行情況基本沒有了解。
執(zhí)行 docker run 命令時,通常會根據(jù) Dockerfile 中的 CMD 或 ENTRYPOINT 啟動一個進(jìn)程帚屉,這個進(jìn)程的狀態(tài)就是 docker ps STATUS 列顯示容器的狀態(tài)谜诫。
命令顯示:
1、有的容器正在運(yùn)行攻旦,狀態(tài)為 UP喻旷。
2、有的容器已經(jīng)正常停止了牢屋,狀態(tài)是 Exited (0)且预。
3、有的則因發(fā)生故障停止了烙无,退出代碼為非 0锋谐,例如 Exited (137)、Exited (1) 等截酷。
Docker 支持的 Health Check 可以是任何一個單獨(dú)的命令涮拗,Docker 會在容器中執(zhí)行該命令,如果返回 0迂苛,容器被認(rèn)為是 healthy三热,如果返回 1,則為 unhealthy三幻。
對于提供 HTTP 服務(wù)接口的應(yīng)用就漾,常用的 Health Check 是通過 curl 檢查 HTTP 狀態(tài)碼,比如:
curl --fail http://localhost:8080/ || exit 1
如果 curl 命令檢測到任何一個錯誤的 HTTP 狀態(tài)碼念搬,則返回 1从藤,Health Check 失敗。
下面我們通過例子來演示 Health Check 在 swarm 中的應(yīng)用锁蠕。
docker service create --name my_db \
--health-cmd "curl --fail http://localhost:8091/pools || exit 1" \
couchbase
--health-cmd Health Check 的命令,還有幾個相關(guān)的參數(shù):
--timeout 命令超時的時間懊蒸,默認(rèn) 30s荣倾。
--interval 命令執(zhí)行的間隔時間,默認(rèn) 30s骑丸。
--retries 命令失敗重試的次數(shù)舌仍,默認(rèn)為 3,如果 3 次都失敗了則會將容器標(biāo)記為 unhealthy通危。swarm 會銷毀并重建 unhealthy 的副本铸豁。
使用Secret
我們經(jīng)常要向容器傳遞敏感信息,最常見的莫過于密碼了菊碟。比如:
docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
在啟動 MySQL 容器時我們通過環(huán)境變量 MYSQL_ROOT_PASSWORD 設(shè)置了 MySQL 的管理員密碼节芥。不過密碼是以明文的形式寫在 docker run 命令中,有潛在的安全隱患。
為了解決這個問題头镊,docker swarm 提供了 secret 機(jī)制蚣驼,允許將敏感信息加密后保存到 secret 中,用戶可以指定哪些容器可以使用此 secret相艇。
如果使用 secret 啟動 MySQL 容器颖杏,方法是:
1、在 swarm manager 中創(chuàng)建 secret my_secret_data坛芽,將密碼保存其中留储。
echo "my-secret-pw" | docker secret create my_secret_data -
2、啟動 MySQL service咙轩,并指定使用 secret my_secret_data获讳。
docker service create \
--name mysql \
--secret source=my_secret_data,target=mysql_root_password \
-e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
mysql:latest
① source 指定容器使用 secret 后,secret 會被解密并存放到容器的文件系統(tǒng)中臭墨,默認(rèn)位置為 /run/secrets/<secret_name>赔嚎。--secret source=my_secret_data,target=mysql_root_password 的作用就是指定使用 secret my_secret_data,然后把器解密后的內(nèi)容保存到容器 /run/secrets/mysql_root_password 文件中胧弛,文件名稱 mysql_root_password 由 target 指定尤误。
② 環(huán)境變量 MYSQL_ROOT_PASSWORD_FILE 指定從 /run/secrets/mysql_root_password 中讀取并設(shè)置 MySQL 的管理員密碼。
Secret使用場景
我們可以用 secret 管理任何敏感數(shù)據(jù)结缚。這些敏感數(shù)據(jù)是容器在運(yùn)行時需要的损晤,同時我們不又想將這些數(shù)據(jù)保存到鏡像中。
secret 可用于管理:
1红竭、用戶名和密碼尤勋。
2、TLS 證書茵宪。
3最冰、SSH 秘鑰。
4稀火、其他小于 500 KB 的數(shù)據(jù)暖哨。
secret 只能在 swarm service 中使用。普通容器想使用 secret凰狞,可以將其包裝成副本數(shù)為 1 的 service篇裁。
這里我們再舉一個使用 secret 的典型場景:
數(shù)據(jù)中心有三套 swarm 環(huán)境,分別用于開發(fā)赡若、測試和生產(chǎn)达布。對于同一個應(yīng)用,在不同的環(huán)境中使用不同的用戶名密碼逾冬。我們可以在三個環(huán)境中分別創(chuàng)建 secret黍聂,不過使用相同的名字,比如 username 和 password。應(yīng)用部署時只需要指定 secret 名字分冈,這樣我們就可以用同一套腳本在不同的環(huán)境中部署應(yīng)用了圾另。
除了敏感數(shù)據(jù),secret 當(dāng)然也可以用于非敏感數(shù)據(jù)雕沉,比如配置文件集乔。不過目前新版本的 Docker 提供了 config 子命令來管理不需要加密的數(shù)據(jù)。config 與 secret 命令的使用方法完全一致坡椒。
secret的安全性
當(dāng)在 swarm 中創(chuàng)建 secret 時扰路,Docker 通過 TLS 連接將加密后的 secret 發(fā)送給所以的 manager 節(jié)點(diǎn)。
secret 創(chuàng)建后倔叼,即使是 swarm manager 也無法查看 secret 的明文數(shù)據(jù)汗唱,只能通過 docker secret inspect 查看 secret 的一般信息。
只有當(dāng) secret 被指定的 service 使用是丈攒,Docker 才會將解密后的 secret 以文件的形式 mount 到容器中哩罪,默認(rèn)的路徑為/run/secrets/<secret_name>。例如在前面 MySQL 的例子中巡验,我們可以在容器中查看 secret际插。
當(dāng)容器停止運(yùn)行,Docker 會 unmount secret显设,并從節(jié)點(diǎn)上清除框弛。
stack
stack 包含一系列 service,這些 service 組成了應(yīng)用捕捂。stack 通過一個 YAML 文件定義每個 service瑟枫,并描述 service 使用的資源和各種依賴。
WordPress 的 stack 版本
如果將前面 WordPress 用 stack 來定義指攒,YAML 文件可以是這樣:
YAML 是一種閱讀性很強(qiáng)的文本格式慷妙,上面這個 stack 中定義了三種資源:service、secret 和 volume允悦。
① services
定義了兩個 service:db
和 wordpress
景殷。
② secrets
定義了兩個 secret:db_password
和 db_root_password
,在 service db
和 wordpress
的定義中引用了這兩個 secret澡屡。
③ volumes
定義了一個 volume:db_data
,service db
使用了此 volume咐旧。
④ wordpress
通過 depends_on
指定自己依賴 db
這個 service驶鹉。Docker 會保證當(dāng) db
正常運(yùn)行后再啟動 wordpress
。
可以在 YAML 中定義的元素遠(yuǎn)遠(yuǎn)不止這里看到的這幾個铣墨,完整列表和使用方法可參考文檔 https://docs.docker.com/compose/compose-file/
stack的優(yōu)點(diǎn)
stack 將應(yīng)用所包含的 service室埋,依賴的 secret、voluem 等資源,以及它們之間的關(guān)系定義在一個 YAML 文件中姚淆。相比較手工執(zhí)行命令或是腳本孕蝉,stack 有明顯的優(yōu)勢。
1腌逢、YAML 描述的是 What降淮,是 stack 最終要達(dá)到的狀態(tài)。
比如 service 有幾個副本搏讶?使用哪個 image佳鳖?映射的端口是什么?而腳本則是描述如何執(zhí)行命令來達(dá)到這個狀態(tài)媒惕,也就是 How系吩。顯而易見,What 更直觀妒蔚,也更容易理解穿挨。至于如何將 What 翻譯成 How,這就是 Docker swarm 的任務(wù)了肴盏,用戶只需要告訴 Docker 想達(dá)到什么效果科盛。
2、重復(fù)部署應(yīng)用變得非常容易叁鉴。
部署應(yīng)用所需要的一切信息都已經(jīng)寫在 YAML 中土涝,要部署應(yīng)用只需一條命令 docker stack deploy。stack 的這種自包含特性使得在不同的 Docker 環(huán)境中部署應(yīng)用變得極其簡單幌墓。在開發(fā)但壮、測試和生成環(huán)境中部署可以完全采用同一份 YAML,而且每次部署的結(jié)果都是一致的常侣。
3蜡饵、可以像管理代碼一樣管理部署。
YAML 本質(zhì)上將應(yīng)用的部署代碼化了胳施,任何對應(yīng)用部署環(huán)境的修改都可以通過修改 YAML 來實(shí)現(xiàn)溯祸。可以將 YAML 納入到版本控制系統(tǒng)中進(jìn)行管理舞肆,任何對 YAML 的修改都會被記錄和跟蹤坐昙,甚至可以像評審代碼一樣對 YAML 執(zhí)行 code review。應(yīng)用部署不再是一個黑盒子屡谐,也不再是經(jīng)驗(yàn)豐富的工程師專有的技能魂爪,所以的細(xì)節(jié)都在 YAML 中,清晰可見哩盲。