MySQL8では5系から比べると大幅にパフォーマンスが改善されたり、Window関数が使えるようになるなど様々な改善がされています。
ただ、デフォルトの認証方式がcaching_sha2_password
になったため、この認証方式に対応していないRailsからはデフォルト設定のまま接続することができなくなりました。
何も対応せずにRailsからDB接続しようとすると下記のようなエラーが発生します。
Mysql2::Error::ConnectionError: Plugin caching_sha2_password could not be loaded: /usr/lib/x86_64-linux-gnu/mariadb19/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory
これを解消するにはMySQLの認証方式を以前のmysql_native_password
に変更すれば良いです。
開発環境はdocker-composeを使っている場合は下記のようにcommandに--default-authentication-plugin=mysql_native_password
を記載すれば変更できます。
オプションについてはMySQLの公式ページをご覧ください。
db:
image: mysql:8.0.23
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
volumes:
- ./tmp/mysql:/var/lib/mysql
ここまではググれば関連記事が大量に出てきたのですが、GitHub ActionsでRailsとMySQL8を接続させる場合には同様のやり方では上手くいかず、ググってもピンポイントな記事が見つからなかったのでこの記事を書くことにしました。
GitHub Actionsではどうなるのか?
GitHub ActionsでRSpecを動かす場合を考えます。
docker-composeと同じノリで書くと下記のようになります。
リファレンスを見ると、command
はなくて代わりにoptions
というものがあるのでそちらで指定してみます。
name: CI
on: pull_request
jobs:
rspec:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
RAILS_ENV: test
DB_HOST: 127.0.0.1
DB_PORT: 33060
services:
db:
image: mysql:8.0.23
options: --default-authentication-plugin=mysql_native_password
ports:
- 33060:3306
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
BIND-ADDRESS: 0.0.0.0
steps:
- uses: actions/checkout@v2.3.4
- name: Set up Ruby
uses: ruby/setup-ruby@v1.64.1
with:
ruby-version: 3.0.0
bundler-cache: true
- name: bundle install
run: |
gem install bundler
bundle install --jobs 4 --retry 3 --path vendor/bundle
- name: migration
run: |
bundle exec rails db:create
bundle exec rails db:test:prepare
- name: run rspec
run: bundle exec rspec
これを実行すると下記のエラーが発生します。
/usr/bin/docker create --name 79005cee16264952b54833428bec9736_mysql8023_27d475 --label 442333 --network github_network_cc57110ece5a47fe9679c23b82763cab --network-alias db -p 33060:3306 --default-authentication-plugin=mysql_native_password -e "MYSQL_USER=root" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -e "BIND-ADDRESS=0.0.0.0" -e GITHUB_ACTIONS=true -e CI=true mysql:8.0.23
unknown flag: --default-authentication-plugin
See 'docker create --help'.
Error: Exit code 125 returned from process: file name '/usr/bin/docker', arguments 'create --name 79005cee16264952b54833428bec9736_mysql8023_27d475 --label 442333 --network github_network_cc57110ece5a47fe9679c23b82763cab --network-alias db -p 33060:3306 --default-authentication-plugin=mysql_native_password -e "MYSQL_USER=root" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -e "BIND-ADDRESS=0.0.0.0" -e GITHUB_ACTIONS=true -e CI=true mysql:8.0.23'.
unknown flag: --default-authentication-plugin
と言っています。--default-authentication-plugin
がわからないみたいですね。
エラーメッセージの直前に実行されたコマンドがあるので見てみると、--default-authentication-plugin
がmysqlのオプションではなく、dockerのオプションに指定されていることがわかります(リファレンスにもきちんとDockerのオプションと書いてあります)。
dockerコマンドに対して指定してしまっているので、unknownと言われているんですね。
改めてリファレンスを読んでもimage(mysql)に対してオプションを指定する方法は現時点ではないようです。
では、どうするか?
実はMySQLにはもう1つ設定値を外部から指定する方法があります。
MySQLの設定ファイルを使う
MySQLの設定ファイルmy.conf
を見てみると下記のように外部ファイルを読み込む記載があります。
# Custom config should go here
!includedir /etc/mysql/conf.d/
そしてGithub Actionsのリファレンスをみると、serviceにvolumesを指定することでファイルを共有できそうです。
これらの仕組みを使います。
まず追加する設定ファイルを用意します。
my.cnfファイルの書き方は下記をご覧ください。
https://dev.mysql.com/doc/refman/5.6/ja/option-files.html
[mysqld]
default_authentication_plugin=mysql_native_password
次にvolumesを指定します。
(host側もmysql/conf.d
にしようとしたのですが、Github Actionsではhost側に/
が含まれていると正しく実行できなかったので下記のような1階層のディレクトリにしました)
services:
db:
image: mysql:8.0.23
- options: --default-authentication-plugin=mysql_native_password
+ volumes:
+ - mysqlconf.d:/etc/mysql/conf.d
ports:
- 33060:3306
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
BIND-ADDRESS: 0.0.0.0
これらの修正を行うと先ほど失敗していたMySQL Serviceも生成され、DB接続もできるようになりました。
...
Starting db service container
...
/usr/bin/docker create --name c32ec17009124eab8461d555fc75a8e5_mysql8023_2e2c19 --label 442333 --network github_network_d9a9770fae8e43fcb9fc76e256a37c24 --network-alias db -p 33060:3306 -e "MYSQL_USER=root" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -e "BIND-ADDRESS=0.0.0.0" -e GITHUB_ACTIONS=true -e CI=true -v "mysqlconf.d":"/etc/mysql/conf.d" mysql:8.0.23
...
Run bundle exec rails db:create
bundle exec rails db:create
bundle exec rails db:test:prepare
shell: /bin/bash -e {0}
env:
RAILS_ENV: test
DB_HOST: 127.0.0.1
DB_PORT: 33060
Created database 'app_test'
...