Rubyと筋肉とギターとわたし

筋トレが仕事です

【Elixir】docker compose構成のelixirとpostgresqlが接続できなかった話

あけましておめでとうございますもてぃ。

今年一発目の記事はElixirです。

僕はdockerが大好きで愛してやまないので、Elixir * PostgreSQL構成のdockerリポジトリを作りました。

github.com

その際はまった内容を備忘録として残しておきたいと思います。

環境

docker-compose.ymlの構成

こんな感じ。

version: '3'

services:
  postgres:
    build: ./postgres
    image: ecto-database
    container_name: ecto-database
    environment:
      POSTGRES_HOST_AUTH_METHOD: 'trust'
      TZ: 'Asia/Tokyo'
    volumes:
      - ./postgres/init:/docker-entrypoint-initdb.d
      - ecto-data:/var/lib/postgresql/data
    ports:
      - '5555:5432'
    restart: always
  app:
    build: .
    image: ecto-app
    container_name: ecto-app
    volumes:
      - .:/app
    tty: true
    stdin_open: true
    restart: always
    depends_on:
      - postgres

volumes:
  ecto-data:
    driver: local

ライブラリにはectopostgrexを使用してます。

ecto.create

app containerに対してmix ecto.createを実行しています。

# docker exec -it ecto-app mix ecto.createでもおk
% docker compose exec app mix ecto.create
Compiling 3 files (.ex)
Generated ecto_app app

05:34:55.399 [error] GenServer #PID<0.284.0> terminating
** (DBConnection.ConnectionError) tcp connect (postgres:5555): connection refused - :econnrefused
    (db_connection 2.4.3) lib/db_connection/connection.ex:100: DBConnection.Connection.connect/2
    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol

05:34:55.407 [error] GenServer #PID<0.290.0> terminating
** (DBConnection.ConnectionError) tcp connect (postgres:5555): connection refused - :econnrefused
    (db_connection 2.4.3) lib/db_connection/connection.ex:100: DBConnection.Connection.connect/2
    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol
** (Mix) The database for EctoApp.Repo couldn't be created: killed

いろいろ調べてみたら「postgresqlサーバーが立ち上がってない」だのいろいろ書かれてましたが、docker compose upで立ち上げたpostgresql コンテナは問題なく立ち上がってました。

docker-compose.ymlでentripointも設定して権限もしっかり付与しているので、ecto_appユーザーが接続できてないってこともないです。

念のため確認

% docker compose exec postgres psql -U ecto_app
psql (14.1)
Type "help" for help.

ecto_app=# \du
                                   List of roles
 Role name |                         Attributes                         | Member of
-----------+------------------------------------------------------------+-----------
 ecto_app  | Superuser, Create role, Create DB                          | {}
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS | {}

ecto_app=#

ちゃんと権限もありましたね。

configの見直し

接続設定はconfig/config.exsに書かれてます。

import Config

config :ecto_app, EctoApp.Repo,
  database: "ecto_app",
  username: "ecto_app",
  password: "ecto_app_psql",
  hostname: "localhost",
  port: "5555"

config :ecto_app, ecto_repos: [EctoApp.Repo]

if Mix.env() == :dev do
  config :mix_test_watch,
    tasks: [
      "credo"
    ]
end

もともとpostgresqlをローカルにいれていない自分は、DBが必要になったときに毎回postgresql用のdocker-compose.ymlを作っていて、そのtemplateを毎度使っていました。

その設定をそのまま上のconfig.exsに反映していたのがまずかったのです…。

解決策

docker-composeで立ち上げる際のポート、ホストはdocker network上で生成されるものにおき替える必要があります。

docker-composeをみるとdatabaseのホストはpostgres、ポートはdocker network上では5432を開放しています。なのでその通りにconfig.exsを変更してあげる。

import Config

config :ecto_app, EctoApp.Repo,
  database: "ecto_app",
  username: "ecto_app",
  password: "ecto_app_psql",
  hostname: "postgres",
  port: "5432"

config :ecto_app, ecto_repos: [EctoApp.Repo]

if Mix.env() == :dev do
  config :mix_test_watch,
    tasks: [
      "credo"
    ]
end

再度ecto.createしてみると…

% docker compose exec app mix ecto.create
Compiling 3 files (.ex)
Generated ecto_app app
The database for EctoApp.Repo has already been created

なんかすでに作られてましたが、うまくコマンド実行できました。

終わり

postgresqlとelixirの簡単なdocker compose構成が出来上がりました。

これでphoenixにも応用できるはずなので、phoenix tutorialをやる際はちょっとこのリポジトリをいじって圧倒的環境構築したいとおもいます。

Elixir/Phoenix極めるぞー!

(今年の抱負はまた別の記事で)