2016年7月14日にリリースされる予定の Docker 1.12 ですが RC3 に HEALTHCHECK 機能が入ったようなのでこれを試してみます。(1.12 のその他の目玉機能はDocker 1.12 の衝撃 [slideshare] をどうぞ)
New HEALTHCHECK Dockerfile instruction to support user-defined healthchecks #23218
RC版のインストールも簡単で
curl -fsSL https://test.docker.com/ | sh
だけ。
バージョン情報はこちら
Client: Version: 1.12.0-rc3 API version: 1.24 Go version: go1.6.2 Git commit: 91e29e8 Built: Sat Jul 2 00:28:53 2016 OS/Arch: linux/amd64 Server: Version: 1.12.0-rc3 API version: 1.24 Go version: go1.6.2 Git commit: 91e29e8 Built: Sat Jul 2 00:28:53 2016 OS/Arch: linux/amd64
DigitalOcean を使って3台の swarm mode cluster をセットアップします。
root@docker01:~# docker swarm init --listen-addr 10.130.13.161 Swarm initialized: current node (8a0gb55owih94q8lwum4b61us) is now a manager. root@docker02:~# docker swarm join --listen-addr 10.130.27.157 10.130.13.161 This node joined a Swarm as a worker. root@docker03:~# docker swarm join --listen-addr 10.130.44.213 10.130.13.161 This node joined a Swarm as a worker. root@docker01:~# docker node ls ID HOSTNAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS 3tv5prg22lgebh0rvjp8gu4o4 docker03 Accepted Ready Active 8a0gb55owih94q8lwum4b61us * docker01 Accepted Ready Active Leader ciezynqpmqa9ok5egpuf2sdjy docker02 Accepted Ready Active
簡単ですねぇ、たったこれだけで3台のクラスタができちゃいます。
(あら?worker node って RC2 の時から auto accept だったっけ?)
テストにあたって nginx:1.11.1-alpine をベースに HEALTHCHECK の有効な docker image を build してみます。
まず、healthcheck.sh というスクリプトを準備
#!/bin/sh status=`curl -so /dev/null -w %{http_code} http://127.0.0.1/healthcheck` if [ $? -eq 0 -a "$status" = "200" ] ; then exit 0 else exit 1 fi
healthcheck コマンドは OK の場合は 0、NG の場合は 1 で終了させます。起動中の場合は 2 で終了させますが、一度 healthy となった後は 1 も 2 も同じです。
このスクリプトでは nginx に GET /healthcheck でテストします。初期状態ではこの URL は 404 となるため 1 で終了します。
次に Dockerfile を準備
FROM nginx:1.11.1-alpine COPY healthcheck.sh / RUN apk update && apk add curl && chmod 755 /healthcheck.sh HEALTHCHECK --interval=5s --timeout=3s --retries=1 CMD /healthcheck.sh
大事なのは HEALTHCHECK --interval=5s --timeout=3s --retries=1 CMD /healthcheck.sh
の部分ですね。5秒おきに /healthcheck.sh が実行されます。ポートへの connect だけの確認であれば HEALTHCHECK CONNECT TCP 7000
という書き方もできるようです。interval や timeout, retries は省略可能。
build して Docker Hub にアップロードします
root@docker01:~# docker build --tag yteraoka/nginx-healthcheck . root@docker01:~# docker login root@docker01:~# docker push yteraoka/nginx-healthcheck
docker image の準備ができたところでコンテナ3つを維持するように service を起動させます
root@docker01:~# docker service create --name nginx --replicas 3 -p 80 yteraoka/nginx-healthcheck 7dxakcqlnsqcoq4g9sqq6qyvc root@docker01:~# docker service ls ID NAME REPLICAS IMAGE COMMAND 7dxakcqlnsqc nginx 3/3 yteraoka/nginx-healthcheck root@docker01:~# docker service tasks nginx ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE 0qwcjtu4pni4i5r8qn37d6nj4 nginx.1 nginx yteraoka/nginx-healthcheck Running About a minute Running docker01 9ajup0heqn1i9j894ntn7e17j nginx.2 nginx yteraoka/nginx-healthcheck Running About a minute Running docker03 0zbp7nniwimnj89v3f2d3i644 nginx.3 nginx yteraoka/nginx-healthcheck Running About a minute Running docker02
あら簡単!
healthcheck が機能しているかどうかログを確認します
root@docker01:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ac6a04048f48 yteraoka/nginx-healthcheck:latest "nginx -g 'daemon off" About a minute ago Up About a minute (unhealthy) 80/tcp, 443/tcp nginx.1.4t4708e2hhijpk3i0oh6udoav root@docker01:~# docker logs --tail 5 ac6a04048f48 127.0.0.1 - - [05/Jul/2016:14:21:46 +0000] "GET /healthcheck HTTP/1.1" 404 169 "-" "curl/7.49.1" "-" 2016/07/05 14:21:51 [error] 6#6: *22 open() "/usr/share/nginx/html/healthcheck" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /healthcheck HTTP/1.1", host: "127.0.0.1" 127.0.0.1 - - [05/Jul/2016:14:21:51 +0000] "GET /healthcheck HTTP/1.1" 404 169 "-" "curl/7.49.1" "-" 2016/07/05 14:21:56 [error] 6#6: *23 open() "/usr/share/nginx/html/healthcheck" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /healthcheck HTTP/1.1", host: "127.0.0.1" 127.0.0.1 - - [05/Jul/2016:14:21:56 +0000] "GET /healthcheck HTTP/1.1" 404 169 "-" "curl/7.49.1" "-"
404 だから unhealthy のはずですね。さて、これはサービスにどう影響するのでしょう?
期待としては unhealthy の状態では Load Balancer のメンバーに組み込まれないという状態ですが・・・
わかりやすいように /index.html にそれぞれ docker01, docker02, docker03 を書き込みます。
docker exec -it ... /bin/sh
で echo docker01 > /usr/share/nginx/html/index.html
てな具合に。
それから Load Balancer のポート番号を確認
root@docker01:~# docker service inspect nginx -f '{{range .Endpoint.Ports}}{{.PublishedPort}}{{end}}' 30000
30000 番なので http://localhost:30000/ に何度かアクセスしてみると
root@docker01:~# curl http://localhost:30000/ docker02 root@docker01:~# curl http://localhost:30000/ docker03 root@docker01:~# curl http://localhost:30000/ docker01 root@docker01:~# curl http://localhost:30000/ docker02 root@docker01:~# curl http://localhost:30000/ docker03 root@docker01:~#
あれ?3つとも有効になってるぞ
1つでも healthy にしてみたらなにか変わるだろうか?また docker exec で touch /usr/share/nginx/html/healthcheck して再度 curl で 30000 番にアクセスしてみるも変化なし
おやおや?期待の動作じゃありませんね
docker inspect
で healthcheck の状態が確認できるようなので試してみます
healthy な場合
root@docker01:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ac6a04048f48 yteraoka/nginx-healthcheck:latest "nginx -g 'daemon off" 9 minutes ago Up 9 minutes (healthy) 80/tcp, 443/tcp nginx.1.4t4708e2hhijpk3i0oh6udoav root@docker01:~# docker inspect -f {{.State.Health.Status}} ac6a04048f48 healthy root@docker01:~# docker inspect -f '{{json .State.Health}}' ac6a04048f48 { "Status":"healthy", "FailingStreak":0, "Log":[ { "Start":"2016-07-05T10:30:19.247502323-04:00", "End":"2016-07-05T10:30:19.300936843-04:00", "ExitCode":0, "Output":"" }, { "Start":"2016-07-05T10:30:24.301484104-04:00", "End":"2016-07-05T10:30:24.386685529-04:00", "ExitCode":0, "Output":"" }, { "Start":"2016-07-05T10:30:29.387079189-04:00", "End":"2016-07-05T10:30:29.468869919-04:00", "ExitCode":0, "Output":"" }, { "Start":"2016-07-05T10:30:34.469089038-04:00", "End":"2016-07-05T10:30:34.545193782-04:00", "ExitCode":0, "Output":"" }, { "Start":"2016-07-05T10:30:39.545724519-04:00", "End":"2016-07-05T10:30:39.621370916-04:00", "ExitCode":0, "Output":"" } ] } root@docker01:~#
unhealthy な場合
root@docker02:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5c20b551e3b9 yteraoka/nginx-healthcheck:latest "nginx -g 'daemon off" 9 minutes ago Up 9 minutes (unhealthy) 80/tcp, 443/tcp nginx.2.8bvv14ynpt9wvnruo6oukoy5u root@docker02:~# docker inspect -f {{.State.Health.Status}} 5c20b551e3b9 unhealthy root@docker02:~# docker inspect -f '{{json .State.Health}}' 5c20b551e3b9 { "Status":"unhealthy", "FailingStreak":122, "Log":[ { "Start":"2016-07-05T10:29:58.986077293-04:00", "End":"2016-07-05T10:29:59.074625863-04:00", "ExitCode":1, "Output":"" }, { "Start":"2016-07-05T10:30:04.075129735-04:00", "End":"2016-07-05T10:30:04.160529007-04:00", "ExitCode":1, "Output":"" }, { "Start":"2016-07-05T10:30:09.160904985-04:00", "End":"2016-07-05T10:30:09.253501465-04:00", "ExitCode":1, "Output":"" }, { "Start":"2016-07-05T10:30:14.253976633-04:00", "End":"2016-07-05T10:30:14.32434033-04:00", "ExitCode":1, "Output":"" }, { "Start":"2016-07-05T10:30:19.324644872-04:00", "End":"2016-07-05T10:30:19.395827854-04:00", "ExitCode":1, "Output":"" } ] } root@docker02:~#
healthcheck 自体は機能しているが、現状はまだこれによて Load Balancer の状態に反映されたりはしないようだ。コード読めってことですね、はい。
event としては通知されます。ドキュメントにもまだ event のことしか書かれていない。docker events
コマンドで event を monitor できます。
2016-07-05T11:32:19.612553558-04:00 container health_status: unhealthy ac6a04048f488d66457edea65b168d5b51671cc9da9aab7a63719c78c6bb8d45 (com.docker.swarm.node.id=8a0gb55owih94q8lwum4b61us, com.docker.swarm.service.id=ecbqm2dd25ibcsf6fmwqbt6yl, com.docker.swarm.service.name=nginx, com.docker.swarm.task=, com.docker.swarm.task.id=4t4708e2hhijpk3i0oh6udoav, com.docker.swarm.task.name=nginx.1, image=yteraoka/nginx-healthcheck:latest, name=nginx.1.4t4708e2hhijpk3i0oh6udoav) 2016-07-05T11:32:29.796809926-04:00 container health_status: healthy ac6a04048f488d66457edea65b168d5b51671cc9da9aab7a63719c78c6bb8d45 (com.docker.swarm.node.id=8a0gb55owih94q8lwum4b61us, com.docker.swarm.service.id=ecbqm2dd25ibcsf6fmwqbt6yl, com.docker.swarm.service.name=nginx, com.docker.swarm.task=, com.docker.swarm.task.id=4t4708e2hhijpk3i0oh6udoav, com.docker.swarm.task.name=nginx.1, image=yteraoka/nginx-healthcheck:latest, name=nginx.1.4t4708e2hhijpk3i0oh6udoav)
docker run
にも --health-cmd
, --health-interval
, --health-retries
, --health-timeout
, --no-healthcheck
というオプションが追加されています。
※更新※
1.12.0 では Service で HEALTHCHECK が FAIL すると、その TASK は停止させれら、新しく起動されるようになっています
おまけ