Check! mysqlのDockerコンテナにアクセスすると ER_NOT_SUPPORTED_AUTH_MODE エラーが出てしまうときの対処

こんにちは、 @dz_ こと大平かづみです。

Prologue
----

掲題の通り、 [mysqlのオフィシャルイメージ](https://hub.docker.com/_/mysql)を使ったコンテナにアクセスしようとすると `ER_NOT_SUPPORTED_AUTH_MODE` というエラーが発生して困ったので解決方法を書き留めます。

### 環境

一部省略していますが、このようなコンテナ構成です。apiコンテナからdbコンテナにアクセスします。apiコンテナは Node.js(TypeScript) で構成していますが、検索してみると php でも同様の事象が起こっているようなので言語には依存しないと考えられます。

```yaml:docker-compose.yaml
version: "3.7"
services:
  api:
    build:
      context: ./<somewhere>
      dockerfile: Dockerfile
    container_name: api
    links:
      - db
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
  db:
    image: mysql:8
    container_name: db
    volumes:
      - db-data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}

volumes:
  db-data:
    external:
      name: db-data
```

エラー内容
----
これは [TypeORM](http://typeorm.io/) を利用して mysql にアクセスしようとした際のエラーメッセージです。前述したように、言語やライブラリに関連なく php などでも同様のエラーメッセージが出力されるようです。

```
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
```

解決方法
----

調べてみたところ、Authentication plugin に `mysql_native_password` を指定すれば、パスワードによるログインが許可されるようです。おそらく、セキュリティの観点ではデフォルトから変えないほうがよさそうですが、開発段階ではいったん緩めてしまおうと思います。

さて、mysqlイメージを用いたコンテナにこの `mysql_native_password` を設定するには？ドキュメントによると、 _docker-compose` では `command` に `--default-authentication-plugin=mysql_native_password` を指定すればよいようです。

- 参考: [... via docker stack deploy or docker-compose | mysql - Docker Hub](https://hub.docker.com/_/mysql#-via-docker-stack-deploy-or-docker-compose)

```yaml:docker-compose.yaml
  db:
    image: mysql:8
    container_name: db
    volumes:
      - db-data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    command: --default-authentication-plugin=mysql_native_password # <- 追加
```

が、私の環境では効かず、設定を確認したところ、下記のように `caching_sha2_password` があたっていました。どうやら、このオプションを設定する前に作ったユーザーに対してはすでにデフォルトの設定が適用されていたようです。

```
mysql> SELECT Host, User, plugin from mysql.user;
+-----------+------------------+-----------------------+
| Host      | User             | plugin                |
+-----------+------------------+-----------------------+
| %         | you              | caching_sha2_password |
| %         | root             | caching_sha2_password |
| localhost | mysql.infoschema | caching_sha2_password |
| localhost | mysql.session    | caching_sha2_password |
| localhost | mysql.sys        | caching_sha2_password |
| localhost | root             | caching_sha2_password |
+-----------+------------------+-----------------------+
6 rows in set (0.00 sec)
```

そこで、データを一度リセット（私の場合は volume を削除・新規作成）してからコンテナ群を起動すると、ユーザーに `mysql_native_password` が設定されました！ :raised_hands: 

```
mysql> SELECT Host, User, plugin from mysql.user;
+-----------+------------------+-----------------------+
| Host      | User             | plugin                |
+-----------+------------------+-----------------------+
| %         | you              | mysql_native_password |
| %         | root             | mysql_native_password |
| localhost | mysql.infoschema | caching_sha2_password |
| localhost | mysql.session    | caching_sha2_password |
| localhost | mysql.sys        | caching_sha2_password |
| localhost | root             | mysql_native_password |
+-----------+------------------+-----------------------+
6 rows in set (0.01 sec)
```

ちなみに、手動でこのプラグインを変更するには、下記の操作でできます。

- 参考: https://github.com/mysqljs/mysql/issues/1507#issuecomment-385224467

```bash
docker exec -it db mysql -u root -p
Enter password:
mysql> ALTER USER 'you'@'%' IDENTIFIED WITH mysql_native_password BY 'your-password';
```

Epilogue
----

ということで、これで作業再開できます～