Docker(docker-compose)のhealthcheckを試してみた

背景

自作のDocker, docker-composeを利用したアプリケーションで、docker-compose内でdepends_onを利用 + アプリケーション側でチェックする仕組みを作っていましたが、これだけでは不足してたことと、healthcheckというものがあると聞いたので試してみました。

結論

  • healthcheckが通ったからといってもアプリケーション側で接続できるようになっているかどうかは断定できない(=一時的にhealthcheckが通っただけという可能性もある)
  • なので、アプリケーション側でもチェックする仕組みはあった方が良さそう
  • もしかしたら、wait-for-itといった他のツールの方が良いのかもしれない

ちょっと曖昧なところがありますが、こういう結論でした。

なぜdepends_onでは駄目なのか?

depends_onはサービス間の起動順番と終了順番の依存関係を表すものです。

https://docs.docker.jp/compose/compose-file/index.html#depends-on

記述することで

  • DBコンテナよりも後に起動
  • DBコンテナよりも後に削除

といったことをすることが出来ます。 ただし、これはあくまでも順番を制御するのみ、なので実際にコンテナがアプリケーションから利用可能かどうかとは異なるようでした。

そのため、docker-composeでは

  • ツールを使う
  • 自分でラッパースクリプトを書いてアプリケーション用のヘルスチェックを処理する

といったことをやるように書かれています。

https://docs.docker.jp/compose/startup-order.html

healthcheckとは

https://docs.docker.jp/compose/compose-file/index.html#healthcheck によると、サービスがHealthyかどうかを指定したコマンドでチェックする機能のようです。

docker-composeのversion2.1から追加されたようでした。 https://docs.docker.com/compose/compose-file/compose-file-v2/#depends_on

以下はDjangoMySQLのコンテナをそれぞれ利用するdocker-composeです。 この書き方だと、webがdbの後に起動、後に削除されるようになっています。

before

version: '2.1'  
  
services:  
  db:  
    container_name: docker_healthcheck_db  
    image: mysql:8.0.32  
    volumes:  
      - db_data:/var/lib/mysql  
    environment:  
      - MYSQL_DATABASE=docker_healthcheck  
      - MYSQL_USER=docker_healthcheck  
      - MYSQL_PASSWORD=docker_healthcheck  
      - MYSQL_ROOT_PASSWORD=docker_healthcheck  
  web:  
    build:  
      context: ..  
      dockerfile: docker/Dockerfile  
    container_name: docker_healthcheck_web  
    command: python manage.py runserver 0.0.0.0:8000  
    volumes:  
      - ..:/docker_healthcheck  
    ports:  
      - "8000:8000"  
    environment:  
      - MYSQL_DATABASE=docker_healthcheck  
      - MYSQL_USER=docker_healthcheck  
      - MYSQL_PASSWORD=docker_healthcheck  
      - MYSQL_HOST=db  
      - MYSQL_PORT=3306  
      - DJANGO_SETTINGS_MODULE=config.settings  
    depends_on:  
      - db
volumes:  
    db_data:

上記を以下のように書き換えて、db側にhealthcheckとチェック内容, webdepends_oncondition: service_healthy を追記します。

after

version: '2.1'  
  
services:  
  db:  
    container_name: docker_healthcheck_db  
    image: mysql:8.0.32  
    volumes:  
      - db_data:/var/lib/mysql  
    environment:  
      - MYSQL_DATABASE=docker_healthcheck  
      - MYSQL_USER=docker_healthcheck  
      - MYSQL_PASSWORD=docker_healthcheck  
      - MYSQL_ROOT_PASSWORD=docker_healthcheck  
    healthcheck:  
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]  
      interval: 10s  
      timeout: 5s  
      retries: 5  
  web:  
    build:  
      context: ..  
      dockerfile: docker/Dockerfile  
    container_name: docker_healthcheck_web  
    command: python manage.py runserver 0.0.0.0:8000  
    volumes:  
      - ..:/docker_healthcheck  
    ports:  
      - "8000:8000"  
    environment:  
      - MYSQL_DATABASE=docker_healthcheck  
      - MYSQL_USER=docker_healthcheck  
      - MYSQL_PASSWORD=docker_healthcheck  
      - MYSQL_HOST=db  
      - MYSQL_PORT=3306  
      - DJANGO_SETTINGS_MODULE=config.settings  
    depends_on:  
      db:  
        condition: service_healthy  
volumes:  
    db_data:

beforeだと、このようにStartedになったタイミングで次の処理に移ります(私だと、Djangoが立ち上がるようになって、最初のMySQLへの接続に失敗する状態でした)。

[+] Running 4/4
 ✔ Network docker_default            Created                                                                                                  0.0s 
 ✔ Volume "docker_db_data"           Created                                                                                                  0.0s 
 ✔ Container docker_healthcheck_db   Started                                                                                                  0.0s 
 ✔ Container docker_healthcheck_web  Started                                                                                                  0.0s 

afterだと、Startedになった後に

[+] Running 4/4
 ✔ Network docker_default            Created                                                                                                  0.0s 
 ✔ Volume "docker_db_data"           Created                                                                                                  0.0s 
 ✔ Container docker_healthcheck_db   Started                                                                                                  0.0s 
 ✔ Container docker_healthcheck_web  Created                                                                                                  0.0s 

Healthyであることをチェックして、次の処理へ進むようになりました。

[+] Running 4/4
 ✔ Network docker_default            Created                                                                                                  0.0s 
 ✔ Volume "docker_db_data"           Created                                                                                                  0.0s 
 ✔ Container docker_healthcheck_db   Healthy                                                                                                  0.0s 
 ✔ Container docker_healthcheck_web  Started                                                                                                  0.0s 

のようになります。

ただし、私の環境だとHealthyであってもDjango側で接続に失敗してOperationalErrorが発生しました。恐らくinterval: 10s20sなど広めに取れば大丈夫ではありそうでした。 ありそう、というのは実際に試してOKではあったものの、それが常に大丈夫だと言える保証もないかなーと考えたからです。

上記より、あくまでもHealthyかどうかチェックできるだけで、実際にDjango側から接続可能なのかどうかは別問題かなと感じました。

以降は実際に出たエラーやもう少し追加で調べた記録です。

実際のエラー

ちょっと長いですが、こういう感じでした。

[+] Running 4/4
 ✔ Network docker_default            Created                                                                                                  0.0s 
 ✔ Volume "docker_db_data"           Created                                                                                                  0.0s 
 ✔ Container docker_healthcheck_db   Created                                                                                                  0.0s 
 ✔ Container docker_healthcheck_web  Created                                                                                                  0.0s 
Attaching to docker_healthcheck_db, docker_healthcheck_web
docker_healthcheck_db   | 2024-01-02 23:19:55+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.32-1.el8 started.
docker_healthcheck_db   | 2024-01-02 23:19:56+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
docker_healthcheck_db   | 2024-01-02 23:19:57+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.32-1.el8 started.
docker_healthcheck_db   | 2024-01-02 23:19:58+00:00 [Note] [Entrypoint]: Initializing database files
docker_healthcheck_db   | 2024-01-02T23:19:58.491975Z 0 [Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead.
docker_healthcheck_db   | 2024-01-02T23:19:58.494119Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.32) initializing of server in progress as process 354
docker_healthcheck_db   | 2024-01-02T23:19:58.587680Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
docker_healthcheck_db   | 2024-01-02T23:19:58.961840Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
docker_healthcheck_db   | 2024-01-02T23:20:00.556381Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
docker_healthcheck_db   | 2024-01-02 23:20:03+00:00 [Note] [Entrypoint]: Database files initialized
docker_healthcheck_db   | 2024-01-02 23:20:03+00:00 [Note] [Entrypoint]: Starting temporary server
docker_healthcheck_db   | 2024-01-02T23:20:03.934177Z 0 [Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead.
docker_healthcheck_db   | 2024-01-02T23:20:03.938944Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.32) starting as process 414
docker_healthcheck_db   | 2024-01-02T23:20:04.083260Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
docker_healthcheck_db   | 2024-01-02T23:20:04.362314Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
docker_healthcheck_db   | 2024-01-02T23:20:04.950096Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
docker_healthcheck_db   | 2024-01-02T23:20:04.950288Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
docker_healthcheck_db   | 2024-01-02T23:20:04.954519Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
docker_healthcheck_db   | 2024-01-02T23:20:04.998689Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.32'  socket: '/var/run/mysqld/mysqld.sock'  port: 0  MySQL Community Server - GPL.
docker_healthcheck_db   | 2024-01-02T23:20:04.998722Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
docker_healthcheck_db   | 2024-01-02 23:20:05+00:00 [Note] [Entrypoint]: Temporary server started.
docker_healthcheck_db   | '/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
docker_healthcheck_db   | Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
docker_healthcheck_db   | Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it.
docker_healthcheck_db   | Warning: Unable to load '/usr/share/zoneinfo/leapseconds' as time zone. Skipping it.
docker_healthcheck_db   | Warning: Unable to load '/usr/share/zoneinfo/tzdata.zi' as time zone. Skipping it.
docker_healthcheck_db   | Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
docker_healthcheck_db   | Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it.
docker_healthcheck_db   | 2024-01-02 23:20:08+00:00 [Note] [Entrypoint]: Creating database docker_healthcheck
docker_healthcheck_db   | 2024-01-02 23:20:08+00:00 [Note] [Entrypoint]: Creating user docker_healthcheck
docker_healthcheck_db   | 2024-01-02 23:20:08+00:00 [Note] [Entrypoint]: Giving user docker_healthcheck access to schema docker_healthcheck
docker_healthcheck_db   | 
docker_healthcheck_db   | 2024-01-02 23:20:08+00:00 [Note] [Entrypoint]: Stopping temporary server
docker_healthcheck_db   | 2024-01-02T23:20:08.995225Z 14 [System] [MY-013172] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 8.0.32).
docker_healthcheck_web  | Performing system checks...
docker_healthcheck_web  | 
docker_healthcheck_web  | System check identified no issues (0 silenced).
docker_healthcheck_web  | Exception in thread django-main-thread:
docker_healthcheck_web  | Traceback (most recent call last):
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 282, in ensure_connection
docker_healthcheck_web  |     self.connect()
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
docker_healthcheck_web  |     return func(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 263, in connect
docker_healthcheck_web  |     self.connection = self.get_new_connection(conn_params)
docker_healthcheck_web  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
docker_healthcheck_web  |     return func(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 247, in get_new_connection
docker_healthcheck_web  |     connection = Database.connect(**conn_params)
docker_healthcheck_web  |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/MySQLdb/__init__.py", line 123, in Connect
docker_healthcheck_web  |     return Connection(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/MySQLdb/connections.py", line 185, in __init__
docker_healthcheck_web  |     super().__init__(*args, **kwargs2)
docker_healthcheck_web  | MySQLdb.OperationalError: (2002, "Can't connect to MySQL server on 'db' (115)")
docker_healthcheck_web  | 
docker_healthcheck_web  | The above exception was the direct cause of the following exception:
docker_healthcheck_web  | 
docker_healthcheck_web  | Traceback (most recent call last):
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
docker_healthcheck_web  |     self.run()
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/threading.py", line 975, in run
docker_healthcheck_web  |     self._target(*self._args, **self._kwargs)
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/utils/autoreload.py", line 64, in wrapper
docker_healthcheck_web  |     fn(*args, **kwargs)
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/core/management/commands/runserver.py", line 137, in inner_run
docker_healthcheck_web  |     self.check_migrations()
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/core/management/base.py", line 564, in check_migrations
docker_healthcheck_web  |     executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
docker_healthcheck_web  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/migrations/executor.py", line 18, in __init__
docker_healthcheck_web  |     self.loader = MigrationLoader(self.connection)
docker_healthcheck_web  |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/migrations/loader.py", line 58, in __init__
docker_healthcheck_web  |     self.build_graph()
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/migrations/loader.py", line 235, in build_graph
docker_healthcheck_web  |     self.applied_migrations = recorder.applied_migrations()
docker_healthcheck_web  |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/migrations/recorder.py", line 81, in applied_migrations
docker_healthcheck_web  |     if self.has_table():
docker_healthcheck_web  |        ^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/migrations/recorder.py", line 57, in has_table
docker_healthcheck_web  |     with self.connection.cursor() as cursor:
docker_healthcheck_web  |          ^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
docker_healthcheck_web  |     return func(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 323, in cursor
docker_healthcheck_web  |     return self._cursor()
docker_healthcheck_web  |            ^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 299, in _cursor
docker_healthcheck_web  |     self.ensure_connection()
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
docker_healthcheck_web  |     return func(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 281, in ensure_connection
docker_healthcheck_web  |     with self.wrap_database_errors:
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
docker_healthcheck_web  |     raise dj_exc_value.with_traceback(traceback) from exc_value
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 282, in ensure_connection
docker_healthcheck_web  |     self.connect()
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
docker_healthcheck_web  |     return func(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/base/base.py", line 263, in connect
docker_healthcheck_web  |     self.connection = self.get_new_connection(conn_params)
docker_healthcheck_web  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
docker_healthcheck_web  |     return func(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 247, in get_new_connection
docker_healthcheck_web  |     connection = Database.connect(**conn_params)
docker_healthcheck_web  |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/MySQLdb/__init__.py", line 123, in Connect
docker_healthcheck_web  |     return Connection(*args, **kwargs)
docker_healthcheck_web  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_healthcheck_web  |   File "/usr/local/lib/python3.11/site-packages/MySQLdb/connections.py", line 185, in __init__
docker_healthcheck_web  |     super().__init__(*args, **kwargs2)
docker_healthcheck_web  | django.db.utils.OperationalError: (2002, "Can't connect to MySQL server on 'db' (115)")
docker_healthcheck_db   | 2024-01-02T23:20:10.505232Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.32)  MySQL Community Server - GPL.
docker_healthcheck_db   | 2024-01-02 23:20:11+00:00 [Note] [Entrypoint]: Temporary server stopped
docker_healthcheck_db   | 
docker_healthcheck_db   | 2024-01-02 23:20:11+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
docker_healthcheck_db   | 
docker_healthcheck_db   | 2024-01-02T23:20:11.563285Z 0 [Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead.
docker_healthcheck_db   | 2024-01-02T23:20:11.568460Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.32) starting as process 1
docker_healthcheck_db   | 2024-01-02T23:20:11.673006Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
docker_healthcheck_db   | 2024-01-02T23:20:12.004580Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
docker_healthcheck_db   | 2024-01-02T23:20:12.432642Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
docker_healthcheck_db   | 2024-01-02T23:20:12.432818Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
docker_healthcheck_db   | 2024-01-02T23:20:12.437618Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
docker_healthcheck_db   | 2024-01-02T23:20:12.484355Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.32'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.
docker_healthcheck_db   | 2024-01-02T23:20:12.487790Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock

この、最後から2行目のdocker_healthcheck_db | 2024-01-02T23:20:12.484355Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.32' socket: '/var/run/mysqld/mysqld.sockの状態になって初めて接続できることになりそうです。そのため、ここに至るまでにHealthyと判定されたら次に進む = アプリケーション側で失敗する、ということにはなりそうです。

追加でちょっと実験してみた

2つのターミナルを用意して、 - コンテナを立ち上げる - 片方で起動した後、もう片方でコンテナに入りmysqladminでpingを通す ことをしてみました。

tatsuya@MacBook-Pro-3 docker % docker exec -it docker_healthcheck_db bash

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqladmin: connect to server at 'localhost' failed

error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)'

Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqladmin: connect to server at 'localhost' failed

error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)'

Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqladmin: connect to server at 'localhost' failed

error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)'

Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqladmin: connect to server at 'localhost' failed

error: 'Access denied for user 'docker_healthcheck'@'localhost' (using password: YES)'

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqld is alive

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqladmin: connect to server at 'localhost' failed

error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)'

Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqld is alive

bash-4.4# mysqladmin -u docker_healthcheck -pdocker_healthcheck ping

mysqladmin: [Warning] Using a password on the command line interface can be insecure.

mysqld is alive

すると、全てのケースではなく、たまたまですが上記のようなケース(一度mysqld is aliveになっても後で失敗する)もあったので、どういう仕組みかは別ですが、最後の状態にならずともmysqld is aliveになることはありそう。

mysqladminのpingについて

https://dev.mysql.com/doc/refman/8.0/ja/mysqladmin.html

  • ping

サーバーが使用可能かどうかをチェックします。 サーバーが稼働中の場合は mysqladmin のリターンステータスは 0 になり、稼働していない場合は 1 になります。 Access denied のようなエラーの場合でも 0 となります。これは、サーバーは稼働しているが接続を拒否したことを意味しており、サーバーが稼働していない状態とは異なるからです。

とのことで、これ以上の記述がないですがやはり稼働できた = 接続できた とはならならそう。

また、 https://gihyo.jp/dev/serial/01/mysql-road-construction-news/0012

によると、以下の記述がありました。

ただし、残念ながらmysqladmin pingコマンドも(psコマンドよりは便利に使えたとしても⁠)⁠、「⁠mysqldプロセスが起動しており、MySQLプロトコルでしゃべりかけた場合にMySQLプロトコルで応答を返した」以上のことは判定できません。

となると、やっぱりアプリケーションレイヤーからの接続可否のチェックが出来ている訳ではない、ということだと思っています。そのため、Healthyかどうかを判断材料にするのはよろしくないかな、と考えるようになりました。

次はdocker-composeのマニュアルに紹介されているようなものを試してみたいと思います。

参考記事

Docker の healthcheck を初めて使った話

docker-composeでMySQLの起動を待つ

DockerのHEALTHCHECKの動きを理解する