Help us understand the problem. What is going on with this article?

PostgreSQL(Docker)にRails(Docker)が接続できなくなったから調べてみた。(could not translate host name "db" to address: Name or service not known)

この記事で解決したい課題

今まで「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を動かそうとしてますと。

docker-compose.yml
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
config/database.yml
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'] %>

hostusernamepasswordを追記するだけのはず。これで今までうまくいっていた。

でもこうなる。

$ 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.ymlpasswordに定義してあげないといけないです。

docker-compose.yml
  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
config/database.yml
  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が全てのアクセスを認証レスで許可してしまうので非推奨となっていますが、ローカル開発中はこれでも問題ない気がします。

docker-compose.yml
  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.ymlservicesの名前(今回はdbweb)になるはずです。

config/database.ymlhostにも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を遡ってみると...

https://github.com/docker-library/docs/commit/2e59fc9a6aee9125eac1a29283205333da4760fa#diff-febe75211617514f6ecb81d53ec56250R133

- 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で検索しても引っかからないし。
ENTRYPOINTdocker-entrypoint.shを実行しているみたいなのでそちらを見てみる。

https://github.com/docker-library/postgres/blob/master/12/alpine/docker-entrypoint.sh#L109

あ、さっきコンテナのログに出てたエラーメッセージだ!

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_METHODtrust以外の場合、このメッセージを出力してexit 1でPostgreSQLの起動の前に終了するようになっているようです。

こちらもCommitを辿ってみると、

https://github.com/docker-library/postgres/commit/42ce7437ee68150ee29f5272428aa4fc657dc6dc#diff-40178ea00ee6cba1430d5ce8cca2afaaL106

で見つけることができました。(2020年1月10日にマージされているみたいです。)
このコミットまではPOSTGRES_PASSWORDがなくてもWarningを出力するだけでPostgreSQL自体は起動できていたみたいです。

まとめ

そんなこんなでDB作成時点で少しつまづいちゃいました。
自作でないイメージやライブラリを利用するときは、ちゃんと改版も追えるようになっておくと慌てなくてすむなという学びを得ました。

at-946
Product managerを名乗っている。昨日の自分に教えてあげるつもりで自分が困ったこと、つまずいたことを書き記す。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした