DockerのHEALTHCHECKでnginxとphp-fpmの生死を判定する

  • Docker
  • nginx
  • php-fpm

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

DockerのHEALTHCHECK命令にはいくつかオプションがあって、意味を理解しないまま--interval=10s --timeout=3s --retries=3をコピペしてる人が多い気がする(自分もそうだった)。一回ちゃんと整理して、nginxとphp-fpmそれぞれで実際に組んでみた。

HEALTHCHECKのオプション

HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD <command>
オプション 意味
--interval ヘルスチェックを実行する間隔(秒)
--timeout 1回のチェックがこの時間内に終わらないと失敗扱いになる
--retries 何回連続で失敗したらunhealthyにするか
--start-period コンテナ起動直後の猶予期間。ここでの失敗は--retriesのカウントに含まれない(今回のDockerfileでは未使用)

--start-periodを付けていない場合、起動が遅いアプリだと立ち上がりきる前にunhealthy判定を食らうことがあるので、起動に時間がかかるプロセスでは意識しておいた方がいい。

nginxの場合

curlで自前のヘルスチェック用エンドポイントを叩くだけ。

################################################################################
# Health check #
################################################################################
RUN apk add --no-cache curl
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -f http://localhost/hc || exit 1

/hcは200を返すだけの適当なエンドポイントを用意しておけばいい。curlが失敗(非2xx)するとexit 1になってunhealthyにカウントされる。

php-fpmの場合

php-fpmは単体だとHTTPを喋らないので、curlは使えない。php-fpm自体が持ってるpingモジュールをFastCGI経由で叩く。

まずpool設定でpingを有効化する。

; /usr/local/etc/php-fpm.d/zzz-app.conf
ping.path = /ping

そのうえでDockerfile側はこう。

################################################################################
# Health check #
################################################################################
RUN apk add --no-cache fcgi
HEALTHCHECK --interval=10s --timeout=3s \
CMD \
SCRIPT_NAME=/ping \
SCRIPT_FILENAME=/ping \
REQUEST_METHOD=GET \
cgi-fcgi -bind -connect 127.0.0.1:9000 || exit 1

cgi-fcgiでFastCGIソケット(127.0.0.1:9000)に直接繋いで、ping.pathで設定したパスを叩く。php-fpmが生きていればpongが返ってくる。curlみたいにHTTPサーバー越しじゃなくて、php-fpmプロセスそのものの生死を見れるのがポイント。

動作確認

$ docker run -itd php-fpm:healthcheck
$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
877b31bf9f6a php-fpm:healthcheck ... Up 3 seconds (health: starting) 9000/tcp pensive_ramanujan

数秒待つとhealth: startingからhealthyに変わる。

$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
877b31bf9f6a php-fpm:healthcheck ... Up 13 seconds (healthy) 9000/tcp pensive_ramanujan

詳細なログはdocker inspectで見れる。

$ docker inspect --format='{{json .State.Health}}' <container_id> | jq
{
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2020-06-05T00:38:54Z",
"End": "2020-06-05T00:38:54Z",
"ExitCode": 0,
"Output": "{\"status\":\"OK\"}"
}
]
}

Logには過去のチェック結果が直近何件か残る。ExitCodeが0以外になったらそこが失敗としてカウントされていく。

nginx側はcurlでHTTP、php-fpm側はcgi-fcgiでFastCGIと、プロトコルが違うだけで考え方は同じ。「コンテナの中で完結する形でヘルスチェック用のコマンドを1つ用意する」というのがHEALTHCHECKの基本形。