この記事で解決したい課題
今まで「Quickstart: Compose and Rails | Docker Documentation」を参考にRails on Docker開発をしてきたのですが、
2020.05.10現在同じようにrails new
を進めているとrails db:create
で「could not translate host name "db" to address: Name or service not known」とエラーが起きた。
ん?今までできていたのになんでだろう?ということで調査をしてみました。
課題の詳細
上のサイトを参考にRuby2.7.1のイメージでRails6.0.3を動かそうとしてますと。
version: '3'
services:
db:
image: postgres:12.2-alpine
volumes:
- ./tmp/db:/var/lib/postgresql/data
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/app
ports:
- "3000:3000"
depends_on:
- db
default: &default
adapter: postgresql
encoding: unicode
host: db
username: postgres
password:
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: app_development
test:
<<: *default
database: app_test
production:
<<: *default
database: app_production
username: app
password: <%= ENV['APP_DATABASE_PASSWORD'] %>
host
、username
、password
を追記するだけのはず。これで今までうまくいっていた。
でもこうなる。
$ docker-compose --rm run web rails db:create
Creating network "sample_default" with the default driver
Creating sample_db_1 ... done
could not translate host name "db" to address: Name or service not known
Couldn't create 'app_development' database. Please check your configuration.
rails aborted!
PG::ConnectionBad: could not translate host name "db" to address: Name or service not known
/usr/local/bundle/gems/pg-1.2.3/lib/pg.rb:58:in `initialize'
/usr/local/bundle/gems/pg-1.2.3/lib/pg.rb:58:in `new'
/usr/local/bundle/gems/pg-1.2.3/lib/pg.rb:58:in `connect'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/postgresql_adapter.rb:46:in `postgresql_connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/abstract/connection_pool.rb:887:in `new_connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/abstract/connection_pool.rb:931:in `checkout_new_connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/abstract/connection_pool.rb:910:in `try_to_checkout_new_connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/abstract/connection_pool.rb:871:in `acquire_connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/abstract/connection_pool.rb:593:in `checkout'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/abstract/connection_pool.rb:437:in `connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_adapters/abstract/connection_pool.rb:1119:in `retrieve_connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_handling.rb:221:in `retrieve_connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/connection_handling.rb:189:in `connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/postgresql_database_tasks.rb:12:in `connection'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/postgresql_database_tasks.rb:21:in `create'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:126:in `create'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:185:in `block in create_current'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:479:in `block (2 levels) in each_current_configuration'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:476:in `each'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:476:in `block in each_current_configuration'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:475:in `each'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:475:in `each_current_configuration'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/tasks/database_tasks.rb:184:in `create_current'
/usr/local/bundle/gems/activerecord-6.0.3/lib/active_record/railties/databases.rake:39:in `block (2 levels) in <main>'
一体どうした。
課題の解決
解決1
ひとつめの結論としては、postgresコンテナに環境変数POSTGRES_PASSWORD
を設定しないといけないです。
そして、その値をconfig/database.yml
のpassword
に定義してあげないといけないです。
version: '3'
services:
db:
image: postgres:12.2-alpine
volumes:
- ./tmp/db:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_PASSWORD=password
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/app
ports:
- "3000:3000"
depends_on:
- db
default: &default
adapter: postgresql
encoding: unicode
host: db
username: postgres
- password:
+ password: password
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: app_development
test:
<<: *default
database: app_test
production:
<<: *default
database: app_production
username: app
password: <%= ENV['APP_DATABASE_PASSWORD'] %>
dbコンテナを起動させたままの場合は一度downしてdb:create
してください。
解決2
二つ目の結論としては、POSTGRES_HOST_AUTH_METHOD=trust
の環境変数を与えてあげます。
PostgreSQLが全てのアクセスを認証レスで許可してしまうので非推奨となっていますが、ローカル開発中はこれでも問題ない気がします。
version: '3'
services:
db:
image: postgres:12.2-alpine
volumes:
- ./tmp/db:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_HOST_AUTH_METHOD=trust
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/app
ports:
- "3000:3000"
depends_on:
- db
こちらの場合はconfig/database.yml
は変更しなくてOKです。
dbコンテナを起動させている場合は一度downしてからdb:create
してください。
調査の軌跡
ここからは、解決に向けた調査の軌跡を記録しておきます。お時間がある方はお付き合いください!
まずは目の前に表示されたエラーメッセージを凝視します。
ホスト名が違う?
could not translate host name "db" to address: Name or service not known
Couldn't create 'app_development' database. Please check your configuration.
rails aborted!
PG::ConnectionBad: could not translate host name "db" to address: Name or service not known
db
という名前のホストが見つからないと言ってます。そのせいでコネクションできない、設定を見直せって言ってます。
今、Docker Composeを使ってwebコンテナとdbコンテナを立ち上げています。
この2つのコンテナは同じdocker-compose.yml
で定義しているので同じDockerネットワーク内に存在します。
そして、ホスト名はdocker-compose.yml
のservices
の名前(今回はdb
とweb
)になるはずです。
config/database.yml
のhost
にもdb
と記述していますので、どうやらこの辺りのタイポとかではなさそうです。
コンテナちゃんと動いてたのかな...?
なんだかよくわからない。どこがいけないのかもよくわからない。
とりあえずコンテナのステータスでも確認してみます。
$ docker-compose ps -a
Name Command State Ports
----------------------------------------------------------------------------------------
sample_db_1 docker-entrypoint.sh postgres Exit 1
sample_web_run_1 rails db:create Exit 1 111111111
あれ、なんか違和感。webコンテナはrunでコマンド実行が終わったから停止しているのはわかるだけど、なんでdbコンテナまで停止してるんだっけ...depends_on
で起動させられてるから起動しっぱなしになるはず...
気になったのでコンテナのログを確認。
$ docker-compose logs
Attaching to sample_db_1
db_1 | Error: Database is uninitialized and superuser password is not specified.
db_1 | You must specify POSTGRES_PASSWORD to a non-empty value for the
db_1 | superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".
db_1 |
db_1 | You may also use "POSTGRES_HOST_AUTH_METHOD=trust" to allow all
db_1 | connections without a password. This is *not* recommended.
db_1 |
db_1 | See PostgreSQL documentation about "trust":
db_1 | https://www.postgresql.org/docs/current/auth-trust.html
なんかDBコンテナでエラー出てた!多分こいつだ!
エラーメッセージを読むと
- スーパーユーザーのパスワードが定義されていないよ!
POSTGRES_PASSWORD
の環境変数に値を入れて! - 非推奨だけど、
POSTGRES_HOST_AUTH_METHOD=trust
の環境変数を使えばパスワードなしでも使えるよ!
ということらしい。
ここまでくれば、結論に書いた対処に行き着く。
READMEにそんなこと書いてたっけなぁ...
前はPOSTGRES_PASSWORD
を環境変数に与えなくてもできた。
DockerHubのDescriptionを読んでみる。
POSTGRES_PASSWORD
This environment variable is required for you to use the PostgreSQL image. It must not be empty or undefined. This environment variable sets the superuser password for PostgreSQL. The default superuser is defined by the POSTGRES_USER environment variable.
空文字や定義なしはダメだ、って書いてある。
いや、前できたんだよ。絶対。
このドキュメンテーションはGitHubで管理されている(長すぎるらしくてDockerHubのDescriptionに収まらないらしく、全文はGitHub見てってなってた。)
いつかまではこれ定義なしでもいけたんじゃないだろうか?Commitを遡ってみると...
- This environment variable is normally required for you to use the PostgreSQL image. This environment variable sets the superuser password for PostgreSQL. The default superuser is defined by the `POSTGRES_USER` environment variable.
+ This environment variable is required for you to use the PostgreSQL image. It must not be empty or undefined. This environment variable sets the superuser password for PostgreSQL. The default superuser is defined by the `POSTGRES_USER` environment variable.
いた!やっぱりPOSTGRES_PASSWORD
の存在性が途中でMUSTになっている。
じゃあどっかでDockerイメージ変わっているのか
ということでDockerfileとかも読んでみる。
今回利用したpostgres:12.2-alpine
のソースコードはこちら。
Dockerfileは特に関係なさそう。passwordで検索しても引っかからないし。
ENTRYPOINT
でdocker-entrypoint.shを実行しているみたいなのでそちらを見てみる。
あ、さっきコンテナのログに出てたエラーメッセージだ!
...
if [ -z "$POSTGRES_PASSWORD" ] && [ 'trust' != "$POSTGRES_HOST_AUTH_METHOD" ]; then
# The - option suppresses leading tabs but *not* spaces. :)
cat >&2 <<-'EOE'
Error: Database is uninitialized and superuser password is not specified.
You must specify POSTGRES_PASSWORD to a non-empty value for the
superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".
You may also use "POSTGRES_HOST_AUTH_METHOD=trust" to allow all
connections without a password. This is *not* recommended.
See PostgreSQL documentation about "trust":
https://www.postgresql.org/docs/current/auth-trust.html
EOE
exit 1
fi
...
環境変数POSTGRES_PASSWORD
の文字列が0(参考: Bash の if 文(test文)のオプションを整理してみた - Qiita)、かつ、環境変数POSTGRES_HOST_AUTH_METHOD
がtrust
以外の場合、このメッセージを出力してexit 1
でPostgreSQLの起動の前に終了するようになっているようです。
こちらもCommitを辿ってみると、
で見つけることができました。(2020年1月10日にマージされているみたいです。)
このコミットまではPOSTGRES_PASSWORD
がなくてもWarningを出力するだけでPostgreSQL自体は起動できていたみたいです。
まとめ
そんなこんなでDB作成時点で少しつまづいちゃいました。
自作でないイメージやライブラリを利用するときは、ちゃんと改版も追えるようになっておくと慌てなくてすむなという学びを得ました。