久しぶりの更新です。
暫く体壊してリアル死にかけてましたが、最近徐々に仕事にも復帰し始めました。
色々考えてたこともあるので機会があればまた別途まとめようかなと思います。
今回は久々に技術記事が書きたくなったので、リハビリがてらPostgreSQLについてです。
DB周りの設定は初心者にとってとっつきづらいイメージがあるかと思います。
しかし、開発PJに途中で参加した場合や本番環境等の都合からローカルのDBもPostgreSQLに変更したい場合、すでにRuby on Railsのプロジェクトがあり config/database.yml
を見ながら設定する必要に迫られるでしょう。
そんなときにスムーズに導入できるよう実際の流れとその際に出そうなエラーについてまとめておきます。
開発をストレス無く始めるヒントとして活用してもらえれば幸いです!
- 簡単なPostgreSQLの導入手順
- Case1. initdb時にデータベースクラスタが既にある場合
- Case2. Portがすでに使われていて起動しない場合
- Case3. Railsで指定しているホスト名が認識できない場合
- Case4. Railsで指定しているロール(DBのユーザー)がない場合
- Case5. PostgreSQLのRoleにusecreatedb権限がない場合
- まとめ
簡単な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 CREATEDB;
もう一度ロールの権限を確認して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 | t | 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を導入することになったときに、この記事などを参照して焦らず使えるようにしてもらえれば幸いです。