RailsでHerokuのPostgreSQLからConohaのMariaDBデータベースサーバーに移行する方法

RailsでHerokuのデータベースであるPostgreSQL( Heroku Postgres)からConohaのMariaDBデータベースサーバーに移行する方法です。

ソングストーリー(https://www.songstory.me/)のデータベース数が1万行近くになり、移行が必要になりました。

今回はConoHaのMariaDB(月500円税込み)に移行したいと思います。
理由は、検討した中で一番安いからです。
データベースサーバーを1つ持っていれば違うサービスでも使えます。

基本料金月額500円/10GB。10GBあればテキストデータなら十分すぎると言えるでしょう。

(画像データや動画データは通常はオブジェクトストレージという専用のサーバーに入れます)

ということで、まず必要なのはバックアップです。

Heroku PostgresにHeidiSQLというデータベース接続ツールでアクセスします。
「HeidiSQL」
https://forest.watch.impress.co.jp/library/software/heidisql/

これは使った中では一番使いやすい無料のデータベース管理ソフトです。
ごく稀に固まりますがそこはご愛嬌。

さて、今回は
PostgreSQLからMySQL(MariaDB)への移行なのですが、ここで注意が必要です。

「SQL形式のデータでのエクスポート、インポートでのデータ移行」は、基本使えないと思ったほうが良いです。
データベースの形式が違うので、同じSQL仲間なのですが、うまくインポートが出来ません。

そこで、どうするかというと、データ形式を共通のものにします。

 

そうです。CSV形式にして、データをエクスポート。
その後、インポートします。

一度今年の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にインポートしてみましょう。

インポート設定。最初の行を無視は「1」に設定。

インポート設定。最初の行を無視は「1」に設定。

これも同様にHeidiSQLでインポートします。
「最初の行を無視」は「1」に設定しましょう。
エクスポート時に設定した、カラム名が出力されている最初の1行目のデータは無視されます。

この状態で

rails s

を行い、正しく動作しているか確認します。

今度はHeroku上からデータベースをマイグレーションします。

テーブルを選択して、メニューの「ツール」>「CSVファイルのインポート」で、先ほどのファイルを選択します。

データを入れたら、PostgreSQLではSQL文で、プライマリキーを最大値にセットする必要があったのですが、MariaDBでは必要ないようです。
一応、PostgreSQLでidのプライマリキーを最大値にセットする方法のツイートを貼っておきます。

さて、いよいよ大詰めです。

・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ファイルのインポート」で、エクスポートしたファイルを選択し、インポートを行います。

以上で、データベースの移行が完了しました。

・感想

ハマりポイントは、やはり本番環境で『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を頂けると大変助かります。