【Docker】MySQL8.0系を使う時に発生する問題について

新しいプロジェクトでは、過去に同じような構成をDockerで作っていればDockerfileを流用しています。

さて、
今回は、最新のMySQLイメージを使う際に発生する問題について記載していきます。

イントロダクション

この記事で得られること

  • MySQLの古いバージョンから、8.0系に移行した際に生じる問題についてわかる。
  • 久しぶりにMySQLのイメージを使ったらエラーが起きた時の対処方法がわかる

環境

ツール

種類 バージョン
Docker 18.03.1

ライブラリ

種類 バージョン
laravel 5.5

状況

Laravelでアプリケーションを作っています。
Dockerで使うイメージは、ApacheとMySQLそれぞれの最新イメージを使用しています。  

MySQLのDockerfileは下記です。

FROM mysql

COPY ./docker-entrypoint-initdb.d/ ./docker-entrypoint-initdb.d/
COPY ./conf.d/ /etc/mysql/conf.d/

RUN chmod 644 /etc/mysql/conf.d/*

問題

php ansible migrateを実行すると次のエラーが発生する。

The server requested authentication method unknown to the client [caching_sha2_password]
(サーバーがクライアントに知られていない認証方法を要求しました[caching_sha2_password])

数日前は問題なく実行できていたのですが、MySQLのイメージを作り直してからエラーが起こるようになりました。 blog.websandbag.com

原因

MySQL8.0からセキュリティが強化されたようです。
MySQL :: MySQL 8.0 Release Notes :: Changes in MySQL 8.0.4 (2018-01-23, Release Candidate)

パスワードの生成方法が選択できるようになっており、ユーザー追加時に指定する必要があるようです。
設定は、default_authentication_pluginに次のどちらかを設定することになります。

  • 従来のmysql_native_password
  • ハッシュ化をするcaching_sha2_password

これが、2018年1月の事で、私が最初にMySQLのイメージを作ったのが2017年でした。

特にバージョンの指定はしていませんので、常に最新のバージョンを取ってくるようになっています。 MySQLイメージのキャッシュが残っており、プロジェクトは変わっても参照しているイメージは同じものだったと考えられます。

FROM mysql

参考

SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client · Issue #1392 · laradock/laradock · GitHub

解決方法

解決方法は2種類あります。

MySQLのバージョンを固定する

確実に動作するバージョンに固定します。
実際サーバーがDocker運用ではなかったり、サーバーを更新する権限がないのであれば、バージョンを固定するのが無難です。

FROM mysql:5
# 省略

MySQLのバージョンに合わせて更新する

MySQL8の仕様に合わせます。

公式の説明よるとハッシュ化方式(caching_sha2_password)が、デフォルトで設定されているようです。

For the server, the default value of the default_authentication_plugin system variable changes from mysql_native_password to caching_sha2_password.

本記事は、従来の方式(mysql_native_password)で説明します。

Step 1. コンテナに同期するmysqlのコンフィグファイルに設定を追加

default_authentication_pluginを指定します。

私の構成では、プロジェクトディレクトリ配下にconf.dというディレクトリを作り、コンテナのconf.dに同期しています。
その中にmy.confという、カスタマイズ用のコンフィグファイルを追加していますので、次のように指定します。

conf.d/my.conf

[mysqld]
# 省略
default_authentication_plugin = mysql_native_password #<--追加

Step 2. ユーザーを追加する処理を変更

プラグインの指定が必要になるので、従来の方法とは少し変わるようです。 そのためユーザーの追加処理に変更を加えます。

前述したconfの件と同様、docker-entrypoint-initdb.dというディレクトリで同期しています。
その中に、初期構築時で流し込むSQLファイルを置いています。
その記述は前のバージョンのものなので、次のように調整します。

CREATE USER '<ユーザー名>'@'localhost' IDENTIFIED WITH mysql_native_password BY <password>;
CREATE USER '<ユーザー名>'@'%' IDENTIFIED WITH mysql_native_password BY <password>;
GRANT ALL PRIVILEGES ON <指定するDB>.* TO '<ユーザー名>'@'localhost' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON <指定するDB>.* TO '<ユーザー名>'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES ;

Step 3. コンテナリビルド

コンテナをリビルドします。
もし、退避したいデータがあれば実行する前に移動して置いてください。

$ docker-compose --no-cache

Step 4. マイグレートを実行

コンテナができたら、マイグレートを実行して完了です。

Tips

LaravelのMySQL8.0サポート状況について

投稿日時点での話です。
設定した状態で、docker-compose upを実行すると、次のエラーが発生します。

Variable 'sql_mode' can't be set to the value of 'NO_AUTO_CREATE_USER'
(変数 'sql_mode'は 'NO_AUTO_CREATE_USER'の値に設定できません)

これは、LaravelのMySQLの最新版をサポートしていないためのようです。
今後アップデートされるようですが、状況によってはMySQLのバージョンを固定する方法を取る必要があるかもしれません。

stackoverflow.com

そのため、今回は最終的にバージョンを固定しました