kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Dockerfile に HEALTHCHECK を設定すると「ヘルスチェック機能」が使えるようになる

今回は Docker で使える「ヘルスチェック機能」を試す.Release Note を読むと,機能としては Docker 1.12 から使えるらしく,3年前からあったなんて...!仕組みとしては,Docker デーモンからコンテナに指定したコマンドを定期的に実行する.

Dockerfile 構文

「ヘルスチェック機能」を使う場合,まず DockerfileHEALTHCHECK を設定する.実行するコマンド以外に以下のオプションも設定できる.注意点として,--retries 以外は秒数を指定するため,表記は 5s のように単位も付ける.

  • --interval=DURATION (default: 30s)
  • --timeout=DURATION (default: 30s)
  • --start-period=DURATION (default: 0s)
  • --retries=N (default: 3)

以下にサンプルを載せる.

HEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/ || exit 1

docs.docker.com

検証用コンテナイメージ

今回は検証用コンテナイメージを「計3種類」作成した.Alpine を使うため,事前に curl をインストールしておく必要がある.

  • kakakakakku/nginx:base
    • nginx:alpinecurl を追加した
  • kakakakakku/nginx:ok
    • nginx:alpinecurlHEALTHCHECK を追加した(ヘルスチェック成功
  • kakakakakku/nginx:ng
    • nginx:alpinecurlHEALTHCHECK を追加した(ヘルスチェック失敗

Dockerfile : kakakakakku/nginx:ok

nginx を使うため,DockerfileHEALTHCHECK を設定し,curl を実行する.今回は検証として,オプションは --interval=5s--timeout=3s にした.kakakakakku/nginx:ok は,デフォルトの index.html にアクセスできるため,ヘルスチェックに成功する.

FROM nginx:alpine

RUN apk add curl

HEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/ || exit 1

Dockerfile : kakakakakku/nginx:ng

kakakakakku/nginx:ng は,誤った URL に curl を実行するため「終了コード 1」となり,ヘルスチェックに失敗する.

FROM nginx:alpine

RUN apk add curl

HEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/ng || exit 1

詳細は GitHub を見てもらえればと!

github.com

コンテナを起動する

コンテナを起動し,docker ps コマンドを実行すると,以下のように STATUS「ヘルスチェック結果」を確認できる.通常は非表示だけど,今回は (healthy)(unhealthy) が表示されている.なお,起動直後は (health: starting) という表記になっていた.

$ docker run -d -p 8000:80 --name nginx01 kakakakakku/nginx:base
$ docker run -d -p 8001:80 --name nginx02 kakakakakku/nginx:ok
$ docker run -d -p 8002:80 --name nginx03 kakakakakku/nginx:ng

$ docker ps --format "table {{.Image}}\t{{.Status}}\t{{.Names}}"
IMAGE                    STATUS                     NAMES
kakakakakku/nginx:ng     Up 3 minutes (unhealthy)   nginx03
kakakakakku/nginx:ok     Up 3 minutes (healthy)     nginx02
kakakakakku/nginx:base   Up 3 minutes               nginx01

コンテナの起動時にヘルスチェックを設定する

実は docker run コマンドのオプションでヘルスチェックを指定することもできる.よって,DockerfileHEALTHCHECK が設定されていなくても「ヘルスチェック機能」は使える.オプションは以下のドキュメントに載っている.

  • --health-cmd
  • --health-interval
  • --health-retries
  • --health-start-period
  • --health-timeout

docs.docker.com

さっそく kakakakakku/nginx:base を使って,「ヘルスチェックに成功するコンテナ」「ヘルスチェックに失敗するコンテナ」を起動する.すると,DockerfileHEALTHCHECK を記載したときと同様に STATUS「ヘルスチェック結果」が表示された.オプションを柔軟に設定できるため,Dockerfile にベタ書きするよりも使えそう.

$ docker run -d -p 8003:80 --name nginx04 \
  --health-cmd 'curl -f http://localhost/ || exit 1' \
  --health-interval 5s \
  --health-timeout 3s \
  kakakakakku/nginx:base

$ docker run -d -p 8004:80 --name nginx05 \
  --health-cmd 'curl -f http://localhost/ng || exit 1' \
  --health-interval 5s \
  --health-timeout 3s \
  kakakakakku/nginx:base

$ docker ps --format "table {{.Image}}\t{{.Status}}\t{{.Names}}"
IMAGE                    STATUS                      NAMES
kakakakakku/nginx:base   Up 19 seconds (unhealthy)   nginx05
kakakakakku/nginx:base   Up 27 seconds (healthy)     nginx04
kakakakakku/nginx:ng     Up 11 minutes (unhealthy)   nginx03
kakakakakku/nginx:ok     Up 11 minutes (healthy)     nginx02
kakakakakku/nginx:base   Up 11 minutes               nginx01

ヘルスチェック結果でフィルタリングする

docker ps コマンドと --filter を組み合わせることにより,ヘルスチェック結果でコンテナをフィルタリングすることもできる.

$ docker ps --format "table {{.Image}}\t{{.Status}}\t{{.Names}}" --filter 'health = healthy'
IMAGE                    STATUS                    NAMES
kakakakakku/nginx:base   Up 2 minutes (healthy)    nginx04
kakakakakku/nginx:ok     Up 13 minutes (healthy)   nginx02

$ docker ps --format "table {{.Image}}\t{{.Status}}\t{{.Names}}" --filter 'health = unhealthy'
IMAGE                    STATUS                      NAMES
kakakakakku/nginx:base   Up 3 minutes (unhealthy)    nginx05
kakakakakku/nginx:ng     Up 14 minutes (unhealthy)   nginx03

docs.docker.com

ヘルスチェックログを確認する

docker inspect コマンドと --format を組み合わせることにより,ヘルスチェックログ(直近5件)を確認できる.以下はヘルスチェックに失敗した nginx03 のログとなり,curl の結果 404「終了コード 1」になっている(Output の結果が長く,横スクロールをする必要がある).

$ docker inspect --format='{{json .State.Health}}' nginx03 | jq .
{
  "Status": "unhealthy",
  "FailingStreak": 242,
  "Log": [
    {
      "Start": "2019-11-21T06:22:46.3344018Z",
      "End": "2019-11-21T06:22:46.611824Z",
      "ExitCode": 1,
      "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: 404 Not Found\n"
    },
    {
      "Start": "2019-11-21T06:22:51.622651Z",
      "End": "2019-11-21T06:22:51.8756001Z",
      "ExitCode": 1,
      "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: 404 Not Found\n"
    },
    {
      "Start": "2019-11-21T06:22:56.8871398Z",
      "End": "2019-11-21T06:22:57.0339383Z",
      "ExitCode": 1,
      "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: 404 Not Found\n"
    },
    {
      "Start": "2019-11-21T06:23:02.046402Z",
      "End": "2019-11-21T06:23:02.4194353Z",
      "ExitCode": 1,
      "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: 404 Not Found\n"
    },
    {
      "Start": "2019-11-21T06:23:07.4317862Z",
      "End": "2019-11-21T06:23:07.6609581Z",
      "ExitCode": 1,
      "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: 404 Not Found\n"
    }
  ]
}

docs.docker.com

ヘルスチェック結果の変化を検知する

docker events コマンドと --filter を組み合わせることにより,Docker のイベント情報から「ヘルスチェック結果の変化」を検知できる.以下は nginx05 コンテナを起動し,unhealthy となったタイミングに取得したイベント情報となる.

$ docker events --filter 'type=container' --filter event=health_status
2019-11-21T15:44:18.141166300+09:00 container health_status: unhealthy xxxxx (image=kakakakakku/nginx:base, maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>, name=nginx05)

docs.docker.com

まとめ

  • Docker で使える「ヘルスチェック機能」を試した
    • Dockerfile に設定することもできるし,docker run コマンドのオプションに設定することもできる
  • docker psdocker inspectdocker events コマンドを使うと「ヘルスチェック」に関する情報を取得できる
  • 注意点として,Docker 単体だとヘルスチェックに失敗して unhealthy になったとしても,コンテナ自体はそのまま残る