🐳

Docker DBコンテナからの「SQLSTATE[HY000] [2002] Connection refused 」

2022/10/12に公開

今日の1.5時間を費やした、クソデカため息案件です。

前提要件

  • Docker Engine + Docker Compose を使用
  • いち Compose に対し、WebコンテナとDBコンテナを用意
  • Webコンテナには Laravel をインストール、DBコンテナには MariaDB をインストール

このエラーが出た時に見るべきところ

  1. DB_USER が作成されているか
  2. DB_HOST がコンテナ名になっているかどうか
  3. DB_PORT がゲスト側のポートで設定されているか
  4. Laravel の config キャッシュが残っていないか

1. DB_USER が作成されているか

これはデータベース・クライアント(Sequel Pro など)か何かで確認してください。もしくは、Webが繋がるならば、Adminer等でもOK。
Laravel の .env に設定されたユーザー情報でそもそも入れるかどうかの確認ですね。

MariaDB を利用する場合 MARIADB_DATABASEMARIADB_USER など、コンテナを作成するにあたって必要なオプションがありますので、最低限の設定を満たさない場合には、当然作成されず、エラーになります。

2. DB_HOST がコンテナ名になっているかどうか

これは良く他文献でも言われているものですね。

よくある誤りは、DB_HOST に対して localhost127.0.0.1 とかを設定しているのが挙げられますが、ローカルホストでDBコンテナを認識できるのはホスト側(Mac OS / Windows など、Docker Engine が動いているマシンOS)であって、ゲスト側(コンテナの中)の場合にローカルホストを指定すると、自コンテナを認識してしまうことになります。
当然、自コンテナにDBコンテナへのルーティングは定義されてないので繋がらない(Connection refused)になります。

docker-compose.yml

...

  database:
    container_name: abcd1234 # ここの名前を使う
    hostname: abcd.database.local
    image: mariadb

...

.env

...

DB_CONNECTION=mysql
DB_HOST=abcd1234 # container_name を入れる
DB_PORT=3306
DB_DATABASE=db
DB_USERNAME=user
DB_PASSWORD=secret

...

慣れていないと陥りがちなスタックポイントなので要注意。

3. DB_PORT がゲスト側のポートで設定されているか

これもよくあるものだと思います。

例えば、いちComposeネットワークに対して、複数のDBコンテナが立ち上がっているとき、ホストからそれぞれのゲストを認識してアクセスをするためのポート番号は分けて定義するはずです。(Aコンテナは 3306 、Bコンテナは 13306 、など)

この時の定義はあくまでも「ホストからゲストを認識するためのポート」なので、コンテナ間(ゲスト間)を認識するものではありません。

先ほどの例で指定した 13306 -> 3306 のマッピングをしているのであれば、WebコンテナからDBコンテナにアクセスする場合には、内部的に使われている 3306 番を指定しないと、認識がされずエラーになりますので、ここも要注意。

docker-compose.yml

...

    ports:
      - target: 3306 # ゲスト は 3306 にマッピングする
        published: 13306 # ホスト to ゲスト は 13306 から入る
        protocol: tcp
        mode: host

...

.env

...

DB_CONNECTION=mysql
DB_HOST=abcd1234
DB_PORT=3306 # ゲスト側ではホストのINポートを認識はしないので 3306 で通す
DB_DATABASE=db
DB_USERNAME=user
DB_PASSWORD=secret

...

4. Laravel の config キャッシュが残っていないか

これは逐一やっておいてください。プロビジョニング工程では、致し方なしです。

環境変数をガリガリと変えていく作業や、初めて開発環境を構築する時に発生するものと思いますので、Laravelやキャッシュコントロールが行き渡っている環境での作業では、何は無くとも、都度疑ってみてください。

僕も、以下のコマンドで治った!みたいなこともあったので、お試しあれ。

$ php artisan cache:clear
$ php artisan config:clear
$ php artisan route:clear
$ php artisan view:clear

Discussion