聲明:所有的實(shí)驗(yàn)示例大部分來(lái)自《learn-docker-in-a-month-of-lunches》的作者Elton Stoneman,但運(yùn)行結(jié)果并不都是照搬,大部分實(shí)驗(yàn)結(jié)果可能與原書(shū)不同
一、健康檢查(health checks)和依賴(lài)檢查(dependency checks)概述
Q--什么是健康檢查(health checks)?有什么用?
A--健康檢查用于判斷一個(gè)容器是否正常工作,容器處于運(yùn)行狀態(tài)并不代表容器是正常工作的,因?yàn)槿萜鞅憩F(xiàn)的行為可能完全不符合預(yù)期,這樣得容器我們稱(chēng)之為不正常工作的容器,而健康檢查就用于這方面的檢查。健康檢查可以?xún)H僅只是一個(gè)普通指令,也可以是一個(gè)腳本,一串邏輯,具體什么樣的返回結(jié)果才算正常,由你自己決定。如果容器被標(biāo)記為不健康后,會(huì)產(chǎn)生一個(gè)事件,這樣運(yùn)行該容器的平臺(tái)就可以根據(jù)該事件做出相應(yīng)的響應(yīng),比如啟動(dòng)一個(gè)新的容器替換這個(gè)不健康的容器。
Q--什么是依賴(lài)檢查(denpendency checks)?有什么用?
A--依賴(lài)檢查用于在容器啟動(dòng)時(shí)(沒(méi)錯(cuò)!只在啟動(dòng)時(shí))檢查該容器依賴(lài)的服務(wù)是否已經(jīng)運(yùn)行了,如果沒(méi)有,該容器進(jìn)入退出狀態(tài)。作用就是保持容器與容器之間的依賴(lài)關(guān)系以及啟動(dòng)順序。依賴(lài)檢查可以很簡(jiǎn)單,比如單純的通過(guò)curl指令訪問(wèn)一下目標(biāo)服務(wù),如果有正確的響應(yīng)則算通過(guò),當(dāng)然實(shí)際上檢測(cè)標(biāo)準(zhǔn)應(yīng)當(dāng)更加完善才好。
Q--Docker Compose有depends_on設(shè)置項(xiàng)設(shè)置容器間的依賴(lài)關(guān)系以及啟動(dòng)順序,為什么還需要依賴(lài)檢查?
A--因?yàn)镈ocker Compose僅僅只能在一臺(tái)機(jī)器上控制依賴(lài)關(guān)系。如果應(yīng)用太大,就需要被分布在許多臺(tái)機(jī)器上,這時(shí)候啟動(dòng)時(shí)的依賴(lài)關(guān)系可就很難維護(hù)了。再者強(qiáng)硬的使用依賴(lài)關(guān)系規(guī)定A容器必須運(yùn)行在B容器之前,這樣也不是很好,比如,假設(shè)A有50個(gè)容器,那么B想要運(yùn)行則必須等這50個(gè)全部運(yùn)行后才可以運(yùn)行,然而此時(shí)恰恰就有1個(gè)容器怎么都運(yùn)行不起來(lái),那么這段時(shí)間B也運(yùn)行不起來(lái),整個(gè)應(yīng)用在這段時(shí)間都是不正常的。若是使用依賴(lài)檢查的話,只要通過(guò)了依賴(lài)檢查容器就可以啟動(dòng),而不需要管其他什么亂七八糟的,比如,只要有20個(gè)A容器啟動(dòng)了,A服務(wù)就"表現(xiàn)"正常了,那么B在啟動(dòng)時(shí)就可以通過(guò)依賴(lài)檢查,因此就可以啟動(dòng)了。
二、健康檢查
- HEALTHCHECK指令
//...
# app image
FROM diamol/dotnet-aspnet
ENTRYPOINT ["dotnet", "/app/Numbers.Api.dll"]
HEALTHCHECK CMD curl --fail http://localhost/health
WORKDIR /app
//...
這是某個(gè)鏡像的Dockerfile中的一部分,健康檢查相關(guān)的部分就定義在這里,HEALTHCHECK后面接的指令就是用于健康檢查的指令。該指令每隔一段時(shí)間就執(zhí)行一次,如果執(zhí)行的返回值異常超過(guò)一定次數(shù),則該容器會(huì)被視為不健康的。默認(rèn)是每30秒執(zhí)行一次,3次連續(xù)異常則被判斷為不健康的。
- 運(yùn)行一個(gè)帶健康檢查的容器
$ cd diamol/ch08/exercises/numbers
$ docker image build -t ch08-numbers-api -f ./numbers-api/Dockerfile.v2 .
$ docker container run -d -p 8080:80 --health-interval 5s ch08-numbers-api:v2
docker image build命令中的-f選項(xiàng)用于指定創(chuàng)建鏡像所需要依據(jù)的Dockerfile
docker container run命令中其實(shí)可以指定和健康檢查的相關(guān)的配置,比如:
--health-cmd指定要執(zhí)行的健康檢查的命令
--health-interval指定健康檢查命令執(zhí)行的時(shí)間間隔,默認(rèn)ms
--health-retries指定失敗多少次,容器會(huì)被標(biāo)記為不健康的
--health-start-period指定在多少秒后才正式開(kāi)始計(jì)算失敗次數(shù)
--health-timeout健康檢查的超時(shí)時(shí)間,超時(shí)會(huì)被認(rèn)為是檢查失敗
- 查看容器的狀態(tài)
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2589f5acff74 ch08-numbers-api:v2 "dotnet /app/Numbers…" 24 minutes ago Up 24 minutes (healthy) 0.0.0.0:8080->80/tcp competent_sanderson
在STATUS一欄中能看到(healthy)的狀態(tài),則代表該容器是健康的。
因?yàn)樵搼?yīng)用有一個(gè)人為的bug,即訪問(wèn)localhost:8080/rng三次后再訪問(wèn)會(huì)報(bào)錯(cuò),所以接下來(lái)我們將人為觸發(fā)這個(gè)bug,然后再查看容器的狀態(tài)
- 觸發(fā)bug,查看容器狀態(tài)
$ curl localhost:8080/rng
$ curl localhost:8080/rng
$ curl localhost:8080/rng
//等待15秒左右
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2589f5acff74 ch08-numbers-api:v2 "dotnet /app/Numbers…" 37 minutes ago Up 37 minutes (unhealthy) 0.0.0.0:8080->80/tcp competent_sanderson
可以看到該容器被標(biāo)記為不健康了
- 查看健康檢查的日志
$ docker container inspect $(docker container ls --last 1 -q)
//...
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 32116,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-03-17T14:07:33.190455031Z",
"FinishedAt": "0001-01-01T00:00:00Z",
"Health": {
"Status": "unhealthy",
"FailingStreak": 128,
"Log": [
{
"Start": "2020-03-17T22:55:33.399820121+08:00",
"End": "2020-03-17T22:55:33.542807814+08:00",
"ExitCode": 22,
"Output": " % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\ncurl: (22) The requested URL returned error: 500 Internal Server Error\n"
},
{
"Start": "2020-03-17T22:55:38.572044692+08:00",
"End": "2020-03-17T22:55:38.715793476+08:00",
"ExitCode": 22,
"Output": " % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\ncurl: (22) The requested URL returned error: 500 Internal Server Error\n"
},
{
"Start": "2020-03-17T22:55:43.74781023+08:00",
"End": "2020-03-17T22:55:43.887389369+08:00",
"ExitCode": 22,
"Output": " % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\ncurl: (22) The requested URL returned error: 500 Internal Server Error\n"
},
//...
--last是選擇最近運(yùn)行的那個(gè)容器,在State下的Health下的Log項(xiàng)中記錄了每次檢查失敗的記錄
三、依賴(lài)檢查
注意:依賴(lài)檢查不像健康檢查一樣有特殊的指令可以指定,所以依賴(lài)檢查實(shí)際上就是一個(gè)簡(jiǎn)單的條件語(yǔ)句,如果檢查通過(guò),則執(zhí)行運(yùn)行服務(wù)所需要的關(guān)鍵命令,如果沒(méi)通過(guò),則會(huì)因?yàn)闆](méi)有什么其他命令可執(zhí)行而進(jìn)入到退出狀態(tài)。比如:
CMD curl --fail http://numbers-api/rng && dotnet Numbers.Web.dll
&&表示如果
curl --fail http://numbers-api/rng(即依賴(lài)檢查)沒(méi)有出錯(cuò),則執(zhí)行dotnet Numbers.Web.dll,否則不執(zhí)行該命令,由于該命令是整個(gè)該容器(服務(wù))的啟動(dòng)命令,不執(zhí)行該命令就等效于容器任務(wù)執(zhí)行完成(因?yàn)闆](méi)有其他命令可以執(zhí)行),故而進(jìn)入到退出狀態(tài)。
四、健康檢查與依賴(lài)檢查在Docker Compose工具中的應(yīng)用
- Docker Compose中健康檢查的配置
services:
numbers-api:
image: ch08-numbers-api:vmtk
ports:
- "8087:80"
healthcheck:
test: ["CMD" , "curl" , "--fail" , "http://localhost/health"]
interval: 5s
timeout: 1s
retries: 2
start_period: 5s
networks:
- app-net
//...
這是compose文件中的一部分,healthcheck項(xiàng)下就是關(guān)于健康檢查的配置:
test后面接一個(gè)數(shù)組,放的是健康檢查的命令,每個(gè)字符串占用一個(gè)數(shù)組元素
interval、timeout、retries、start_period前面已經(jīng)介紹過(guò)了,不再贅述
- Docker Compose中依賴(lài)檢查的配置
//...
numbers-web:
image: ch08-numbers-web:v3
restart: on-failure
environment:
- RngApi__Url=http://numbers-api/rng
ports:
- "8088:80"
healthcheck:
test: ["CMD", "dotnet", "Utilities.HttpCheck.dll", "-t", "150"]
interval: 5s
timeout: 1s
retries: 2
start_period: 10s
networks:
- app-net
//...
numbers-web服務(wù)是依賴(lài)于numbers-api服務(wù)的,但是我們可以發(fā)現(xiàn)它并沒(méi)有配置
depends_on設(shè)置項(xiàng),原因在開(kāi)頭已經(jīng)解釋過(guò)了。這里最關(guān)鍵的配置在于restart: on-failure一項(xiàng),它告訴Docker Compose如果該容器啟動(dòng)失敗則重啟該容器,這樣當(dāng)容器因?yàn)橐蕾?lài)檢查失敗而進(jìn)入到退出狀態(tài)時(shí),Docker Compose會(huì)重啟它,而此時(shí)該容器所依賴(lài)的容器可能已經(jīng)成功運(yùn)行了,因此該容器也就會(huì)通過(guò)依賴(lài)檢查而成功啟動(dòng)。
五、其他
- 注意事項(xiàng):
- 健康檢查是會(huì)耗費(fèi)計(jì)算機(jī)資源的,所以務(wù)必注意健康檢查在資源占用和使用效果上的平衡,如果健康檢查太嚴(yán)格,那么將過(guò)于耗費(fèi)計(jì)算機(jī)資源,如果健康檢查太寬松,那么健康檢查的實(shí)際效果可能會(huì)大打折扣,而由于沒(méi)有及時(shí)檢測(cè)到問(wèn)題,用戶(hù)的體驗(yàn)將會(huì)持續(xù)下降。
- 建議在程序中編寫(xiě)一套自己的工具用于健康檢查和依賴(lài)檢查,而不是使用外部工具,比如
curl。理由如下:- 減少了需要添加的額外工具,越多的外部工具意味著越多的風(fēng)險(xiǎn)
- 你自己應(yīng)用的狀態(tài)如何只有你自己最清楚,利用外部工具所能達(dá)到的效果可能并不如意
- 你自己編寫(xiě)的工具,你可以檢測(cè)任何你想要的東西,并根據(jù)這些數(shù)據(jù)來(lái)利用更完善的邏輯去判斷服務(wù)的健康狀態(tài)
- 因?yàn)槭菍儆诔绦騼?nèi)部的工具,所以使用的庫(kù)將是一個(gè)統(tǒng)一的庫(kù),使用的配置將是一個(gè)統(tǒng)一的配置,管理方便
- 將會(huì)得到Docker跨平臺(tái)的優(yōu)點(diǎn),檢查結(jié)果不會(huì)因?yàn)槠脚_(tái)而影響
參考文檔:
[1] learn-docker-in-a-month-of-lunches
[2] 官方文檔