893
技術社區[雲棲]
Docker 容器健康檢查機製
在分布式係統中,經常需要利用健康檢查機製來檢查服務的可用性,防止其他服務調用時出現異常。
對於容器而言,最簡單的健康檢查是進程級的健康檢查,即檢驗進程是否存活。Docker Daemon會自動監控容器中的PID1進程,如果docker run命令中指明了restart policy,可以根據策略自動重啟已結束的容器。在很多實際場景下,僅使用進程級健康檢查機製還遠遠不夠。比如,容器進程雖然依舊運行卻由於應用死鎖無法繼續響應用戶請求,這樣的問題是無法通過進程監控發現的。
在Kubernetes提供了Liveness與Readness探針分別對Container及其服務健康狀態進行檢查。阿裏雲容器服務也提供了類似的服務健康檢查機製。
Docker 原生健康檢查能力
而自 1.12 版本之後,Docker 引入了原生的健康檢查實現,可以在Dockerfile中聲明應用自身的健康檢測配置。 HEALTHCHECK
指令聲明了健康檢測命令,用這個命令來判斷容器主進程的服務狀態是否正常,從而比較真實的反應容器實際狀態。
HEALTHCHECK
指令格式:
-
HEALTHCHECK [選項] CMD <命令>
:設置檢查容器健康狀況的命令 -
HEALTHCHECK NONE
:如果基礎鏡像有健康檢查指令,使用這行可以屏蔽掉
注:在Dockerfile中 HEALTHCHECK
隻可以出現一次,如果寫了多個,隻有最後一個生效。
使用包含 HEALTHCHECK
指令的dockerfile構建出來的鏡像,在實例化Docker容器的時候,就具備了健康狀態檢查的功能。啟動容器後會自動進行健康檢查。
HEALTHCHECK
支持下列選項:
-
--interval=<間隔>
:兩次健康檢查的間隔,默認為 30 秒; -
--timeout=<間隔>
:健康檢查命令運行超時時間,如果超過這個時間,本次健康檢查就被視為失敗,默認 30 秒; -
--retries=<次數>
:當連續失敗指定次數後,則將容器狀態視為unhealthy
,默認 3 次。 -
--start-period=<間隔>
: 應用的啟動的初始化時間,在啟動過程中的健康檢查失效不會計入,默認 0 秒; (從17.05)引入
在 HEALTHCHECK [選項] CMD
後麵的命令,格式和 ENTRYPOINT
一樣,分為 shell
格式,和 exec
格式。命令的返回值決定了該次健康檢查的成功與否:
-
0
:成功; -
1
:失敗; -
2
:保留值,不要使用
容器啟動之後,初始狀態會為 starting
(啟動中)。Docker Engine會等待 interval
時間,開始執行健康檢查命令,並周期性執行。如果單次檢查返回值非0或者運行需要比指定 timeout
時間還長,則本次檢查被認為失敗。如果健康檢查連續失敗超過了 retries
重試次數,狀態就會變為 unhealthy
(不健康)。
注:
- 一旦有一次健康檢查成功,Docker會將容器置回
healthy
(健康)狀態 - 當容器的健康狀態發生變化時,Docker Engine會發出一個
health_status
事件。
假設我們有個鏡像是個最簡單的 Web 服務,我們希望增加健康檢查來判斷其 Web 服務是否在正常工作,我們可以用 curl
來幫助判斷,其 Dockerfile
的 HEALTHCHECK
可以這麼寫:
FROM elasticsearch:5.5
HEALTHCHECK --interval=5s --timeout=2s --retries=12 \
CMD curl --silent --fail localhost:9200/_cluster/health || exit 1
docker build -t test/elasticsearch:5.5 .
docker run --rm -d \
--name=elasticsearch \
test/elasticsearch:5.5
我們可以通過 docker ps,來發現過了幾秒之後,Elasticsearch容器從 starting
狀態進入了 healthy
狀態
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c9a6e68d4a7f test/elasticsearch:5.5 "/docker-entrypoin..." 2 seconds ago Up 2 seconds (health: starting) 9200/tcp, 9300/tcp elasticsearch
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c9a6e68d4a7f test/elasticsearch:5.5 "/docker-entrypoin..." 14 seconds ago Up 13 seconds (healthy) 9200/tcp, 9300/tcp elasticsearch
另外一種方法是在 docker run
命令中,直接指明healthcheck相關策略。
$ docker run --rm -d \
--name=elasticsearch \
--health-cmd="curl --silent --fail localhost:9200/_cluster/health || exit 1" \
--health-interval=5s \
--health-retries=12 \
--health-timeout=2s \
elasticsearch:5.5
為了幫助排障,健康檢查命令的輸出(包括 stdout 以及 stderr)都會被存儲於健康狀態裏,可以用 docker inspect 來查看。我們可以通過如下命令,來獲取過去5個容器的健康檢查結果
docker inspect --format='{{json .State.Health}}' elasticsearch
或
docker inspect elasticsearch | jq ".[].State.Health"
示例結果如下
{
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2017-08-19T09:12:53.393598805Z",
"End": "2017-08-19T09:12:53.452931792Z",
"ExitCode": 0,
"Output": "..."
},
...
}
由於應用的開發者會更加了解應用的SLA,一般建議在Dockerfile中聲明相應的健康檢查策略,這樣可以方便鏡像的使用。對於應用的部署和運維人員,可以通過命令行參數和REST API針對部署場景對健康檢查策略按需進行調整。
Docker社區為提供了一些包含健康檢查的實例鏡像,我們可以在如下項目中獲取 https://github.com/docker-library/healthcheck
注:
- 阿裏雲容器服務同時支持Docker原生健康檢測機製和阿裏雲的擴展檢查機製
- 目前Kubernetes還不提供對Docker原生健康檢查機製的支持。
Docker Swarm mode中的服務健康檢查能力
在Docker 1.13之後,在Docker Swarm mode中提供了對健康檢查策略的支持
可以在 docker service create
命令中指明健康檢查策略
$ docker service create -d \
--name=elasticsearch \
--health-cmd="curl --silent --fail localhost:9200/_cluster/health || exit 1" \
--health-interval=5s \
--health-retries=12 \
--health-timeout=2s \
elasticsearch
在Swarm模式下,Swarm manager會監控服務task的健康狀態,如果容器進入 unhealthy
狀態,它會停止容器並且重新啟動一個新容器來取代它。這個過程中會自動更新服務的 load balancer (routing mesh) 後端或者 DNS記錄,可以保障服務的可用性。
在1.13版本之後,在服務更新階段也增加了對健康檢查的支持,這樣在新容器完全啟動成功並進入健康狀態之前,load balancer/DNS解析不會將請求發送給它。這樣可以保證應用在更新過程中請求不會中斷。
下麵是在服務更新過程的時序圖
總結
在企業生產環境中,合理的健康檢查設置可以保證應用的可用性。現在很多應用框架已經內置了監控檢查能力,比如Spring Boot Actuator。配合Docker內置的健康檢測機製,可以非常簡潔實現應用可用性監控,自動故障處理,和零宕機更新。
最後更新:2017-08-24 11:33:10