はじめに
Dockerでローカル環境に構築したMySQLコンテナへ、ホストマシン上のアプリから接続しようとした際に以下のような権限エラーになりました。MySQLへ接続時のホストはlocalhost
を指定しています。
(1130, "Host '172.18.0.1' is not allowed to connect to this MySQL server")
コンテナ起動時にユーザ作成・権限の設定を行うことで上記エラーを解消することができたので、本記事はその備忘録です。
環境
- Windows11
- docker desktop(WSL使用)
- DockerHubのMySQL公式イメージ(mysql/mysql-server:8.0)を使用。(mysql 8.0.32)
結論(解消方法)
MySQLコンテナの/docker-entrypoint-initdb.d
フォルダにDockerネットワークのゲートウェイ(上記エラーの環境では'172.18.0.1')からの接続を許可する初期化クエリを追加します。
MySQL公式のコンテナイメージでは、/docker-entrypoint-initdb.d
配下に配置されたクエリがコンテナ初回起動時に実行されます。
以下、docker composeでコンテナ起動する際の例です。
フォルダ構成
.
├ docker-compose.yml
└ mysql-init # 任意の名称
└ init.sql # 任意の名称
docker-compose.yml
volumes:
にてmysql-initフォルダをMySQLコンテナの/docker-entrypoint-initdb.d
へバインドマウントしています。
services:
mysql:
image: mysql/mysql-server:8.0
container_name: mysql-container
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: test_db
volumes:
- ./mysql-init:/docker-entrypoint-initdb.d # bindマウントでコンテナへ初期化クエリを配置
- mysql:/var/lib/mysql
volumes:
mysql:
初期化クエリ
コンテナの初回起動時に以下クエリが実行されます。
以下は全ホストを対象としたユーザー'root'@'%'
を作成して、全権限を付与しています。
必要に応じて、ホスト、権限を絞ってください。
CREATE USER 'root'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
コンテナ起動
docker compose up -d
ホストマシン上のアプリから接続
以下pythonの例。
import pymysql
conn = pymysql.connect(
host="localhost",
port=3306,
user="root",
password="password",
database="test_db",
)
コンテナ停止
ボリュームをマウントして既に起動している場合は、ボリュームを削除して再度コンテナを立ち上げないと、初期化クエリが実行されないので注意ください。
docker compose down -v # ボリュームごと削除
エラーの原因
dockerでコンテナを構築した場合、dockerネットワーク上にコンテナが作成され、dockerネットワークのゲートウェイを経由してコンテナへアクセスされます。
このdockerネットワークのゲートウェイをホストとしたMySQLのユーザーが作成されていないことが原因です。
dockerネットワーク
エラー文からもわかるように、アプリからlocalhost(127.0.0.1)に対して接続しているにもかかわらず、ホストが'172.18.0.1' になっています。
これは、dockerでコンテナを構築した場合、dockerネットワーク上にコンテナが作成され、dockerネットワークのゲートウェイを経由してコンテナへアクセスされるためです。
ゲートウェイのIPは、上記エラー発生時の環境では'172.18.0.1'に指定されていました。
dockerネットワークに関しては、以下記事がわかりやすいです。
ゲートウェイのIPの確認方法
docker inspect {コンテナID}
を実行します。コンテナIDはdocker ps
コマンドで確認できます。
長いので必要な個所のみ抽出していますが、"Gateway": "172.18.0.1"
となっているのがわかります。
[
{
...
"NetworkSettings": {
...
"Networks": {
"{dockerネットワーク名}": {
...
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
...
}
}
}
}
]
dockerネットワーク名の箇所は、ネットワークを指定せず起動した場合、docker composeでは{フォルダ名}_default
のようなネットワーク名で自動作成されます。docker runで起動した場合は、デフォルトで用意されているbridge
ネットワークが使用されます。
docker network ls
でネットワークの一覧確認、docker network inspect {ネットワークID}
でネットワークの詳細確認も可能です。
MySQLのユーザー、権限
デフォルトでは、以下のようにホストをlocalhost
とするユーザーのみ存在しています。
そのためdockerネットワークのゲートウェイのIPからの接続は許可されていません。
# docker exec でコンテナに乗り込み
PS > docker exec -it {コンテナID} sh
# mysqlへ接続
sh-4.4# mysql -u root -p
Enter password:
# ユーザーリスト表示
mysql> SELECT host, user FROM mysql.user;
+-----------+------------------+
| host | user |
+-----------+------------------+
| localhost | healthchecker |
| localhost | mysql.infoschema |
| localhost | mysql.session |
| localhost | mysql.sys |
| localhost | root |
+-----------+------------------+
一方で、上記初期化クエリを設定した場合は、以下のように全てのホスト(%
)に対するユーザーroot
が作成されています。
mysql> SELECT host, user FROM mysql.user;
+-----------+------------------+
| host | user |
+-----------+------------------+
| % | root |
| localhost | healthchecker |
| localhost | mysql.infoschema |
| localhost | mysql.session |
| localhost | mysql.sys |
| localhost | root |
+-----------+------------------+
ユーザーに設定されている権限は、show grants for 'ユーザー名'@'ホスト名'
で確認できます。
参考記事