INODEVLOG

WEBアプリ開発・事業開発・ビジネスモデル・読書の情報などをお届けしています。

MacのRails5開発でPostgreSQL導入とエラー解決法

f:id:inodev:20170929014845j:plain

久しぶりの更新です。

暫く体壊してリアル死にかけてましたが、最近徐々に仕事にも復帰し始めました。

色々考えてたこともあるので機会があればまた別途まとめようかなと思います。


今回は久々に技術記事が書きたくなったので、リハビリがてらPostgreSQLについてです。

DB周りの設定は初心者にとってとっつきづらいイメージがあるかと思います。

しかし、開発PJに途中で参加した場合や本番環境等の都合からローカルのDBもPostgreSQLに変更したい場合、すでにRuby on Railsのプロジェクトがあり config/database.yml を見ながら設定する必要に迫られるでしょう。

そんなときにスムーズに導入できるよう実際の流れとその際に出そうなエラーについてまとめておきます。

開発をストレス無く始めるヒントとして活用してもらえれば幸いです!

簡単なPostgreSQLの導入手順

前提

Rails5で config/database.yml が下記のように設定されているとします。

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5
  username: username
  password: password
  host: db

development:
  <<: *default
  database: db_development

test:
  <<: *default
  database: db_test

また、Gemfileには pg が導入されているとします。

導入手順一覧

導入の詳細はこのへんを参考にしてもらえれば良いと思うので、最初に簡単な流れだけ記載します。

MacのRailsアプリでPostgreSQLを使う方法 - Qiita

(1)brew install postgresql で導入 (2)initdb /usr/local/var/postgres -E utf8 でDB情報の保存先(データベースクラスタ)を作成 (3)postgres -D /usr/local/var/postgres でPostgreSQLの起動 ※ bundle install 等が実行可能になるはず (4)createuser username でロール作成 (5)psql -d postgres とかでDB接続して ALTER ROLE username WITH Superuser; でスーパーユーザー化 ※ rake db:create 等が実行可能になるはず

ちなみにPGDATAの環境変数を指定していた場合は、PostgreSQL起動時の-Dオプションを省略可能です。 1〜5の手順が終わったら使いやすいようにツール周りも含めてカスタマイズもしていければいいですね。

では早速各種エラーメッセージ、及びその解決手順などをまとめていきます。

Case1. initdb時にデータベースクラスタが既にある場合

「(2)initdb /usr/local/var/postgres -E utf8 でDB情報の保存先(データベースクラスタ)を作成」の時点で、 過去にPostgreSQLを使っていた場合下記のようなエラーが出ることがあります。

$ initdb /usr/local/var/postgres -E utf8
~中略~
initdb: directory "/usr/local/var/postgres" exists but is not empty
If you want to create a new database system, either remove or empty
the directory "/usr/local/var/postgres" or run initdb
with an argument other than "/usr/local/var/postgres".
$

PostgreSQLを入れ直して過去のやつはいらないなら全部消して再度実行するのが良いかと。

rm -rf /usr/local/var/postgres

$ initdb /usr/local/var/postgres -E utf8
The files belonging to this database system will be owned by user "inodev".
This user must also own the server process.

The database cluster will be initialized with locale "ja_JP.UTF-8".
initdb: could not find suitable text search configuration for locale "ja_JP.UTF-8"
The default text search configuration will be set to "simple".

Data page checksums are disabled.

creating directory /usr/local/var/postgres ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

~中略~

Success. You can now start the database server using:

    pg_ctl -D /usr/local/var/postgres -l logfile start

$

Success.のメッセージが確認できればOKですね!

Case2. Portがすでに使われていて起動しない場合

ポスグレを起動させようとしたら下記のようなエラーが出ました。

$ postgres -D /usr/local/var/postgres
LOG:  could not bind IPv6 socket: Address already in use
HINT:  Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
LOG:  could not bind IPv6 socket: Address already in use
HINT:  Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
LOG:  could not bind IPv4 socket: Address already in use
HINT:  Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
WARNING:  could not create listen socket for "localhost"
FATAL:  could not create any TCP/IP sockets
LOG:  database system is shut down

あれ?まだPort 5432使うプロセスは起動させてないはず…。 lsofコマンドを-iオプションで使用して、該当ポートを使用しているプロセスの表示を試みてみる。

$ lsof -i:5432
$

やはりなにもでない(:3」∠) 自分が起動したものではない可能性もあるのでsudoを付けて再度実行してみる。 (ちなみに読み取り権限がないファイルは見れないらしいのでlsofは管理者権限で実行するのが良い)

lsofコマンドでポートを使用しているプロセスを確認するメモ - tweeeetyのぶろぐ的めも

lsofコマンドで表示できるのは、このコマンドを実行するユーザーに対して 読み取り権限が設定されるファイルに限ります

下記が実行結果です。

$ sudo lsof -i:5432
COMMAND  PID     USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
postgres  99 postgres    4u  IPv6 0xa841aac6690d54bb      0t0  TCP *:postgresql (LISTEN)
postgres  99 postgres    5u  IPv4 0xa841aac669c4154b      0t0  TCP *:postgresql (LISTEN)
$

おまえかーーー!というわけで、こいつを殺せば良さそうだけれどそもそもなぜ勝手に起動してるのか? プロジェクトによってはMySQL等も使ったりもするから、PostgreSQLがいつのまにか勝手にポート専有してるのウザすぎます…w

Mac にインストールした PostgreSQL の自動起動を停止する方法 | Ari's Blog

このへんを見るとどうやらMacのLaunchDaemonに登録されているため勝手に起動しているようです。 上記デーモンの自動起動の設定ファイルを確認して、次のコマンドで削除したらプロセスも無事に消えました。

$ sudo launchctl unload -w /Library/LaunchDaemons/com.edb.launchd.postgresql-9.3.plist
$ sudo lsof -i:5432
$

過去にPostgreSQLをbrew経由で入れたことがあるので、今回入れ直したのが原因かもしれません。 brew経由でポスグレ入れると勝手に設定されるのかな…。 設定した記憶がない(:3」∠)

取り敢えず再度Portを確認したらデーモンも落ちてたのでPostgreSQLを再度起動させてみると、

$ postgres -D /usr/local/var/postgres
LOG:  database system was shut down at 2017-09-28 00:33:21 JST
LOG:  MultiXact member wraparound protections are now enabled
LOG:  database system is ready to accept connections
LOG:  autovacuum launcher started

DB起動のログが出たのでOKですね。

今回のような特殊な状況でなければ、普通にps -ef等のコマンドでプロセス番号見てkillで落としてあげれば良いでしょう。

Case3. Railsで指定しているホスト名が認識できない場合

例えば rails db:create でdatabase.ymlからDBを作成しようとした時等に下記のようなエラーが出ていた場合、

$ rails db:create
could not translate host name "db" to address: nodename nor servname provided, or not known
Couldn't create database for {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "username"=>"username", "password"=>"password", "host"=>"db", "database"=>"db_development"}
rails aborted!
PG::ConnectionBad: could not translate host name "db" to address: nodename nor servname provided, or not known
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:create
(See full trace by running task with --trace)
$

名前解決をローカルIPアドレスで出来るように設定してあげれば良いです。 名前解決(リゾルバ)の仕組みを知りたい人は下記を見るといいかも。

http://www.turbolinux.com/products/server/11s/user_guide/x1246.html

具体的にやることは /etc/hosts を編集して、見つからないと怒られているdbなどのホスト名を127.0.0.1に加えてあげればOKです。

sudo vim /etc/hosts

#before
127.0.0.1       localhost

#after
127.0.0.1       localhost db

Case4. Railsで指定しているロール(DBのユーザー)がない場合

同じく rails db:create 等で下記のようなエラーが出ていた場合、

$ rails db:create
FATAL:  role "username" does not exist
Couldn't create database for {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "username"=>"username", "password"=>"password", "host"=>"db", "database"=>"db_development"}
rails aborted!
ActiveRecord::NoDatabaseError: FATAL:  role "username" does not exist
bin/rails:4:in `require'
bin/rails:4:in `<main>'
PG::ConnectionBad: FATAL:  role "username" does not exist
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:create
(See full trace by running task with --trace)
$

単純に「(4)createuser username でロール作成」をしていないので実行できずにエラーになってます。 現在存在するロールを確認しましょう。

$ psql -q -c'select * from pg_user' postgres
 usename | usesysid | usecreatedb | usesuper | userepl | usebypassrls |  passwd  | valuntil | useconfig
---------+----------+-------------+----------+---------+--------------+----------+----------+-----------
 inodev  |       10 | t           | t        | t       | t            | ******** |          |
(1 row)

$

config/database.yml を見て必要なロールを下記コマンドで作成します。

$ createuser username
$

下記のようにロールが増えていればOKですね!

$ psql -q -c'select * from pg_user' postgres
  usename  | usesysid | usecreatedb | usesuper | userepl | usebypassrls |  passwd  | valuntil | useconfig
-----------+----------+-------------+----------+---------+--------------+----------+----------+-----------
 inodev    |       10 | t           | t        | t       | t            | ******** |          |
 username |    16384 | f           | f        | f       | f            | ******** |          |
(2 rows)

$

Case5. PostgreSQLのRoleにusecreatedb権限がない場合

ロールをデフォルトの状態で作成しただけだと権限がないので、 rails db:create でロールがないというエラー内容からDBが作れなかったというエラーになります。

$ rails db:create
PG::InsufficientPrivilege: ERROR:  permission denied to create database
: CREATE DATABASE "db_development" ENCODING = 'unicode'
Couldn't create database for {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "username"=>"username", "password"=>"password", "host"=>"db", "database"=>"db_development"}
rails aborted!
ActiveRecord::StatementInvalid: PG::InsufficientPrivilege: ERROR:  permission denied to create database
: CREATE DATABASE "db_development" ENCODING = 'unicode'
bin/rails:4:in `require'
bin/rails:4:in `<main>'
PG::InsufficientPrivilege: ERROR:  permission denied to create database
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:create
(See full trace by running task with --trace)
$

これは普通にDB操作してユーザ作って権限を与えてあげます。 rails db:create などでDB作成などもろもろの作業をrails(rake)コマンド経由で行いたいので、 作ったロールにALTERでスーパーユーザーの権限を付与してあげましょう。

既存のテーブルを確認して、

$ psql -l
                               List of databases
   Name    | Owner  | Encoding |   Collate   |    Ctype    | Access privileges
-----------+--------+----------+-------------+-------------+-------------------
 postgres  | inodev | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 |
 template0 | inodev | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/inodev        +
           |        |          |             |             | inodev=CTc/inodev
 template1 | inodev | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/inodev        +
           |        |          |             |             | inodev=CTc/inodev
(3 rows)

$

取り敢えず、初期データベースとして存在するpostgresにアクセスして、 ALTER ROLE username WITH Superuser;のSQLを実行します。

$ psql -d postgres
psql (9.6.5)
Type "help" for help.

postgres=# ALTER ROLE username WITH Superuser;

もう一度ロールの権限を確認してusesuperが"f"から"t"になっていればOKですね!

$ psql -q -c'select * from pg_user' postgres
  usename  | usesysid | usecreatedb | usesuper | userepl | usebypassrls |  passwd  | valuntil | useconfig
-----------+----------+-------------+----------+---------+--------------+----------+----------+-----------
 inodev    |       10 | t           | t        | t       | t            | ******** |          |
username |    16384 | f           | t        | f       | f            | ******** |          |
(2 rows)

ロールの権限が付与できたら最後にRailsの情報をもとにDB作成する rake db:create を実行する。

$ rails db:create
Created database 'db_development'
Created database 'db_test'
$

エラーが出ないでDB作成が確認できれば、rake db:migrateでマイグレーションも実行して万事OK!

まとめ

ローカル開発環境(Mac)でPostgreSQLやMySQLを使用するのはとっつきづらい印象がありますが、DBに依存するカラムを使用する場合などもあるので使わざるをえない場合もあります。

また出来るだけ普段から本番に近い環境を維持したほうがエラーも早期発見できる場合が増えるかもしれません。

MacにDBを導入することになったときに、この記事などを参照して焦らず使えるようにしてもらえれば幸いです。