docker mysqlでハマってしまった件
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
データベースって本番環境では、クラウドデータベースを使うことが多く、また、テスト用にもテスト環境のDBに繋いでいました。ローカルテスト環境にdbが必要になったので、mysqlをdockerでテストしようと思ったら、上手く動かなくてハマってしまったことを共有したいと思ったので、ブログに残したいと思います。
mysqlで何がしたかったかとハマった内容
ローカルでmysqlをdockerで用意し、テスト用のデータを流し込んでテストに使おうとしてました。
最終的には問題なくできたのですが、以下でハマってしまいました。
- mysqlのinitializeでcsvファイルをロードしようとしたらロードできなかった
- mysqlにロードしたレコードでnullを期待してたレコードがnullでなく、ブランク(空白)になった
- gitlabのCIで実行しようとしたらmysqlにcsvがロードできてなかった
ここから先でそれぞれ詳しく見ていきたいと思います。
mysqlのinitializeでcsvファイルをロードしようとしたらロードできなかった
csvファイルをimportするのに、以下のコマンドを使用しました。
LOAD DATA LOCAL INFILE '[fileName]' INTO TABLE [tableName] FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ([column...]);
すると、
ERROR 3948 (42000): Loading local data is disabled; this must be enabled on both the client and server sides
こんな感じのエラーが、、、なんだこれ?
エラーメッセージを見る限り、clientとserver両方でlocal dataのloadを有効化する必要がありそう。
そこで、ネットを調べたら以下が出てきました。
クライアント側の設定
mysql -u [userName] -p --enable-local-infile
サーバー側の設定
SET GLOBAL local_infile=on;
とするといけるらしい。
試しに、やってみたら、、、でけた!(て〜てってれ〜)
とりあえずできたけど、、、テストの際に毎回やる?
「テストで使うのに毎回これをやるのも大変だし、ロード済みのイメージを作るなんて面倒だし無駄だしどうにかできないかな?」
さらに調べてみたら、mysqlのdockerは起動時にスクリプトが動かせるとのこと。
/docker-entrypoint-initdb.d配下にあるファイルが実行される。ファイルの頭に数字をつけるとその順番で実行してくれるとのことでした。
- テーブルの作成
- csvのimport
という形で、スクリプトを用意しました。
これで、docker compose upしたところ、また最初のエラーが出てしまいました。
それは当然で、enable-local-infileをスクリプトで実行するってどうやるんだろう?と悩みました。
ここでまた調べると、/etc/mysql/conf.d/my.cnfを書けば、設定を入れられるとのことでした。
[mysqld] local-infile=1 [mysql] local-infile=1
この設定を入れることで、コンテナ起動時にcsvファイルのロードが無事にできました。
mysqlにロードしたレコードでnullを期待してたレコードがnullでなく、ブランク(空白)になった
こんどはテストの内容の話なのですが、途中で特定のカラムがnullだった場合の処理をテストしようとなりました。
コードを書いて、いざテストを動かすと、エラーになりました。
nullが取れると思ったのでnullチェックをしたところ、nullでないとの判定になっていました。
あれ?と思ってmysqlでクエリかけて調べてみたら、確かにブランクが入っていて、nullじゃなかったのでした。
調べるとcsvインポートはデフォルトではカラムに値が設定されていない場合はブランク(数値の場合は0)が入っていたため失敗していました。
とりあえず、nullにしたいのが数行だけだったので、手っ取り早く、nullにしたいレコードだけinsert文で対応しました。そこで、調べたところ、nullifを使えばnullが入るとのこと。
LOAD DATA LOCAL INFILE '[fileName]' INTO TABLE [tableName] FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' SET column1=nullif(@column1,""),(column2=nullif(@column2,"")...;
ただし、SET文なので、カラムごとにどのカラムか指定しなければならないなど、少し形を変える必要があったので今回は試しませんでした。
gitlabのCIで実行しようとしたらmysqlにcsvがロードできてなかった
最終的に無事にローカルで全てのテストが完了したので、gitlabにpushして、レビューを受けることになります。
マージリクエストを処理するのにテストを通す必要があったのでgitlabのCIでテストを動かすようにしました。
ローカルでテストが全てPASSしたため、とくに気にせずにpushしました。
すると、testがエラーしました。なぜだ、、、ローカル環境ではPASSしてるのに、、、
悩みながら、デバッグを仕込み、どうやらmysqlにレコードがインポートされてないことがわかりました。
dockerがcsvやスクリプト、コンフィグファイルがマウントできてないのか。
全部正常に認識できていそうでしたが、ログから以下のWarningを発見しました。
mysql: [Warning] World-writable config file '/etc/mysql/conf.d/my.cnf' is ignored.
cnfがignored、、、!?
なぜ!?
調べたところ、mysql8.X以降ではmy.cnfは誰でも読み書きできる権限があるとignoreされるらしい!?
知らないよそんなの、、、
git cloneで持ってくると777になったりするらしいのでそのせいでテストがエラーしてました。
なので、苦肉の策ですが、コンテナ起動前にファイルの権限を書き換えるスクリプトを仕込んで対応しました。
最後に
普段使ってこなかったので、色々ハマってしまいました。
日頃からmysqlをdockerで使っている皆様にはあたりまえのことだったかもしれませんが
たま〜に使おうとすると色々ハマってしまうポイントはまだまだありそうなので、今後もナレッジを貯めていければと思います。