RailsでHerokuのデータベースであるPostgreSQL( Heroku Postgres)からConohaのMariaDBデータベースサーバーに移行する方法です。
ソングストーリー(https://www.songstory.me/)のデータベース数が1万行近くになり、移行が必要になりました。
今回はConoHaのMariaDB(月550円税込み)に移行したいと思います。
理由は、検討した中で一番安いからです。
データベースサーバーを1つ持っていれば違うサービスでも使えます。
基本料金月額500円/10GB。10GBあればテキストデータなら十分すぎると言えるでしょう。
(画像データや動画データは通常はオブジェクトストレージという専用のサーバーに入れます)
ということで、まず必要なのはバックアップです。
Heroku PostgresにHeidiSQLというデータベース接続ツールでアクセスします。
「HeidiSQL」
https://forest.watch.impress.co.jp/library/software/heidisql/
これは使った中では一番使いやすい無料のデータベース管理ソフトです。
ごく稀に固まりますがそこはご愛嬌。
さて、今回は
PostgreSQLからMySQL(MariaDB)への移行なのですが、ここで注意が必要です。
「SQL形式のデータでのエクスポート、インポートでのデータ移行」は、基本使えないと思ったほうが良いです。
データベースの形式が違うので、同じSQL仲間なのですが、うまくインポートが出来ません。
そこで、どうするかというと、データ形式を共通のものにします。
HeidiSQLでCSV形式でエクスポートする。
—-
HeidiSQLでのcsvエクスポート、インポート https://t.co/8YNl6CE3jw #Qiita— りーず Leez(そーめる) (@so_meru) October 29, 2020
そうです。CSV形式にして、データをエクスポート。
その後、インポートします。
出来た。PostgreSQLからMySQLへのデータ移行。
HeidiSQLで「グリッド行のエクスポート」。
データを表示してすべて選択状態で書き出す。
文字コードはUTF-8。改行コードは「Windows形式の改行」。カラムを選択は2つある下のほう(すべて)。
出力形式はExcel CSV。カラム名は含めない。 pic.twitter.com/5PxZvMh50F— りーず Leez(そーめる) (@so_meru) October 29, 2020
一度今年の10月終わりごろに上記のように、テストでやったことがあったので今回もその方法を使います。上のやり方とはチェックボタンが少し違うので、下で説明します。
その前に、まずはデータベースが無いといけないですね。ということで、ConoHaを契約。
データベースサーバーを契約します。
DBサーバー – ConoHa
https://www.conoha.jp/database/
ConoHaのデータベースはMariaDBを採用しています。MariaDBはMySQLから派生した新世代のリレーショナルデータベースです。MySQLとの高い互換性を持ちながら、高いパフォーマンスと頑健性に優れています。スケーラブルなMariaDBを利用することで、開発に専念することができます。
(引用)
ざっくり、ほぼMySQLと同じと考えて大丈夫なようです。クライアントもphpMyAdminがつかえますし。僕はHeidiSQL使います。
また、これの凄いところはスケーラビリティ(拡張性)があるところです。容量10GBを超えたら月額300円でディスク容量を5GBずつ追加することが出来るらしいです。
さて、契約の「OK」ボタンを押すとすぐにサーバーが稼働し始めます。(クレジットカードを登録している場合)
まだ実際のデータベースが存在しないので、
①サイト用のデータベースを1つ作成します。
②さらにユーザーも1つ作成します。これでアクセスする訳ですね。
ConoHa上で行います。
これで、データベース名、ユーザー名、パスワードなどが設定できました。パスワードは忘れないようにしましょう。
あとは、作ったユーザーが「接続したいデータベース」をConoHa上で設定します。
(これは見れば分かると思います。選択するだけです)
以上でデータベースの設定は完了です。
・HerokuのRailsからどのようにデータベースに接続するのか?環境変数を使おう。
HerokuのRailsからデータベースに接続する方法です。
結論から言うと、環境変数を使って読み込ませます。ファイルにパスワードは書きません。
Railsのdatabase.ymlの
production:
の部分にデータベース名、ユーザー名、パスワードをそこに書いても良いのですが、
それだとファイルが誤って流出した時にパスワード等が漏れてしまうというリスクがあります。
そこで、Herokuのコマンドラインで環境変数を設定し、database.ymlにはその環境変数名を書くだけ(Herokuが自動で読み取ってくれる)にします。
具体的には
database.ymlには
database: <%= ENV[‘DATABASE_NAME’] %>
username: <%= ENV[‘DATABASE_USERNAME’] %>
password: <%= ENV[‘DATABASE_PASSWORD’] %>
このように記載し、あとはHeroku上でコマンドを打ちます。
【herokuコマンド】環境変数
https://qiita.com/chihiro/items/b8a3ef54716842dd115a
Herokuへのデプロイ方法【Heroku+Rails+MySQL】
https://qiita.com/murakami-mm/items/9587e21fc0ed57c803d0
$ heroku config:add DB_NAME='<データベース名>’
$ heroku config:add DB_USERNAME='<ユーザー名>’
$ heroku config:add DB_PASSWORD='<パスワード>’
$ heroku config:add DB_HOSTNAME='<ホスト名>’
$ heroku config:add DB_PORT=’3306′
$ heroku config:add DATABASE_URL=’mysql2://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true’>※ RailsのGemfileで’mysql2’を使用しているので、DATABASE_URLはmysql2://で始める必要がある。(引用)
とあります。
DATABASE_URLという変数にはすべての情報が入っているので、デフォルトの場合はHerokuでは
production:
<<: *default
url: <%= ENV['DATABASE_URL'] %>
という記載のみでデータベースに接続出来てしまいます。
https://railsguides.jp/configuring.html#%E6%8E%A5%E7%B6%9A%E8%A8%AD%E5%AE%9A
『config/database.ymlファイルがあり、
環境変数ENV[‘DATABASE_URL’]が設定されていない場合は、
config/database.ymlファイルを使ってデータベース接続が行われます。』引用:Railsガイド
今回は
Herokuへのデプロイ方法【Heroku+Rails+MySQL】
https://qiita.com/murakami-mm/items/9587e21fc0ed57c803d0
と同じ方法で行います。
・データベース名
・ユーザー名
・パスワード
・ホスト名
・ポート番号
・DATABASE_URL
の環境変数を用意します。
『DATABASE_URLは、一応Heroku Postgresの設定を残しておきたいので変えないで、それ以外の変数を設定して動かせば良いかな…』と、思っていたのですが、どうもHerokuのデータベースを変更する際に、Heroku Postgresは消去しなければいけないようです。なので、DATABASE_URLも書き換えです。
まずはConoHaのデータベース通りに環境変数に設定します。
・現在のHerokuの環境変数をheroku configで確認して、変更しよう。
環境変数がどのように設定されているかを
コマンドライン
heroku config
で確認します。
DATABASE_URLはHeroku Postgresのものがまだあるはずです。
次に、コマンドラインで
heroku config:add CONOHA_DB_NAME='データベース名'
heroku config:add CONOHA_DB_USERNAME='ユーザー名'
heroku config:add CONOHA_DB_PASSWORD='パスワード'
heroku config:add CONOHA_DB_HOSTNAME='ホスト名'
heroku config:add CONOHA_DB_PORT='3306'
heroku config:add DATABASE_URL='mysql2://ユーザー名:パスワード@ホスト名/データベース名?reconnect=true'
このような感じで設定していきましょう。
ConoHaのデータベース(MariaDB)の画面を見ながら進めて下さい。ホスト名は「グローバル」のほうからコピペします。
ConoHaのデータベースサーバー一覧画面
https://manage.conoha.jp/Service/Index
ちなみに間違えた場合、消去するには
heroku config:unset ENV_NAME
のように、変数名を指定します。
あとはdababase.ymlのproduction:の部分を
production:
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
database: <%= ENV['CONOHA_DB_NAME'] %>
username: <%= ENV['CONOHA_DB_USERNAME'] %>
password: <%= ENV['CONOHA_DB_PASSWORD'] %>
host: <%= ENV['CONOHA_DB_HOSTNAME'] %>
port: <%= ENV['CONOHA_DB_PORT'] %>
このような感じで書き換えましょう。
DATABASE_URLはHerokuが認識するために必要だと思っています。
というかDATABASE_URLを設定していれば、これ一行でいけるかもしれません(未確認)。
ローカルのデータベースにも繋げられるように、
development:
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
database: アプリ名_development
username: ユーザー名
password: パスワード
host: localhost
port: 3306
このように設定します。test:も同じです。
・MariaDB(MySQL用)のGemを入れて、PosgtreSQLのGemはコメントアウトする。
また、MariaDBを使うために、mysql2というgemを使うので、
Gemfileに
gem 'mysql2'
を追記して、
gem 'pg'
の部分はもう使わないのでコメントアウトします。
そうしたら
bundle install
します。
・まずはローカル環境でデータベースを構築する
では実際にプロダクション環境を上記のようにしてConoHaのデータベースに設定し、ローカルのデータベースの設定も完了したら、今度はデータベースを作成します。
最初はローカルでデータベースが動くか検証してみましょう。
まずはローカルPCのMariaDBを立ち上げておきます。
(MariaDBはローカルPCにインストールしてユーザーも作成しておいて下さい。MySQLでも大丈夫ですが、僕はMariaDBも別途インストールしました。)
Windows10の検索窓で「MariaDB」と打てば、立ち上げるためのコマンドアプリが出るはずです。(パスワードが必要)
その後、
rails db:create
でデータベースを作成。
rails db:migrate
で、データベースにテーブルを作成します。
これが動いてdevelopment環境、test環境のデータベースが作成され、
development環境でのデータベースをHeidiSQLでローカルで接続して確認してみましょう。
・Herokuのデータベース(PostgreSQL)から引き継ぐデータをエクスポートする
データベースが作成されたという確認ができたら、ローカル環境に実際にデータを入れてみましょう。MariaDBは起動させておいて下さい。
データのエクスポートとインポートはHeidiSQLで行います。
まずは必要なデータのエクスポートからです。
HerokuのPostgresqlに接続し、
エクスポートしたいテーブルを選択します。
そうしたら、メニューから、「ツール」>「グリッド行のエクスポート」
を行います。
設定は以下の画像の通りです。
「カラム名を含める」にはチェックを入れます。カラム名を含めないでインポートしようとするとエラーが出ることがあったので、この設定にします。
形式はExcel CSV、カラムを選択は、下のボタンのほう(すべて)。
オプションは、「カラム名を含める」にチェックをして、「Remove linebreaks from contents」はチェックをしないでおきました。
NULL valueは図にある通りです。
(注意点)
エクスポート時に注意する点は、データは必ずidが小さい順に並べておいてエクスポートするようにしましょう。idを大きい順にしてエクスポートすると、インポートでエラーが出ます。
・ローカル環境のデータベース(MariaDB)にデータを入れてみる
上手く行ったら、これをローカルのMariaDBにインポートしてみましょう。
これも同様にHeidiSQLでインポートします。
「最初の行を無視」は「1」に設定しましょう。
エクスポート時に設定した、カラム名が出力されている最初の1行目のデータは無視されます。
この状態で
rails s
を行い、正しく動作しているか確認します。
今度はHeroku上からデータベースをマイグレーションします。
テーブルを選択して、メニューの「ツール」>「CSVファイルのインポート」で、先ほどのファイルを選択します。
データを入れたら、PostgreSQLではSQL文で、プライマリキーを最大値にセットする必要があったのですが、MariaDBでは必要ないようです。
一応、PostgreSQLでidのプライマリキーを最大値にセットする方法のツイートを貼っておきます。
助かった…
songstory-app::DATABASE=> SELECT setval(‘videos_id_seq’, coalesce((SELECT MAX(id)+1 FROM videos), 1), FALSE);
setvalこれで動いた。
—-
“PG::Error: ERROR: duplicate key value violates unique constraint”と出てレコード作成できない時の対処法 https://t.co/L6Iwf9fuyf— りーず Leez(そーめる) (@so_meru) October 30, 2020
さて、いよいよ大詰めです。
・Herokuプロダクション環境へのデプロイとデータベースのマイグレーションを行おう。
今度はいよいよプロダクション環境です。
上記のアプリケーションの設定の変更をHerokuにデプロイしましょう。
僕の場合、デプロイしてデータベースをマイグレーションしようとしたところ、エラーが出たので、そのタイミングでHeroku Postgresを削除しました。
Heroku Postgresが無駄に残っていてもHerokuが困るので、このような仕様になっているのかも(?)しれません。
ちょっと分かりませんが、とにかくデータをエクスポートしてからデータベースを消しました。
まずはデプロイです。
git add .
でファイルをステージに上げます。
そして
git commit -m 'Changed Database to MariaDB'
このように、コミットします。 -m 以下はコメントなので、お好きなコメントを入れましょう。
そのあと
git push heroku master
でプッシュしてデプロイ。
そうしたら、
heroku run rake db:create
でデータベースを作成します。
なぜか heroku run rails db:createでエラーになりました(?)。rakeにしておきましょう。
heroku run rake db:migrate
で、マイグレーションを行います。
・最後に、ConoHaのデータベースにデータをHeidiSQLでインポートする。
あとは、HeidiSQLで必要なデータテーブルにデータを入れましょう。
ローカルと同様の操作です。
テーブルを選択して、メニューの「ツール」>「CSVファイルのインポート」で、エクスポートしたファイルを選択し、インポートを行います。
以上で、データベースの移行が完了しました。
これで、ConoHaのデータベースサーバーにデータが入り、railsのアプリケーションもきちんとそのデータベースに接続して動いている状態になりました。
・感想
ハマりポイントは、やはり本番環境で『PostgreSQLが見つかりません』というようなエラーが出たことでした。
rake aborted!
LoadError: Error loading the ‘postgresql’ Active Record adapter. Missing a gem it depends on? pg is not part of the bundle. Add it to your Gemfile.
こんな感じのエラーです。
これはHeroku Postgres(データベース)をHerokuの設定画面で削除し、今回の記事で書いた通りに環境変数DATABASE_URLにMariaDBのものを設定することで解決しました。
RailsのデータベースをMySQL(MariaDB)設定にした時点でHeroku Postgresは消去しなければいけない可能性があるので、それを見越してデータベースをCSV形式でバックアップしておくのが良いかと思います。
※なにか間違った記載がありましたら(@so_meru)までDMを頂けると大変助かります。