22
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Docker上でElixirのPhoenixとPostgreSQLを使ってみた

Last updated at Posted at 2019-08-21

Docker上でElixirのPhoenixとPostgreSQLを使ってみた

Phoenixの勉強をしたくて、手始めにDBからデータをとってきてテーブルで表示するというのをやってみたのでそれの備忘録兼Docker用いた日本語の記事が少ないような気がしたので、記事を投稿しようと思いました。

思ったより長めになってしまったので忙しい人は、こちらからソースコードを各自cloneしてください。

phoenix-postgres

1. Dockerfileとdocker-compose.yml

とりあえず、今回のファイル群の中身です。説明も後にしますが、興味ない人はコピペだけでもいいと思います。

  • Dockerfile

    FROM elixir
    
    WORKDIR /workspace
    
    RUN apt-get -y update &&\
        mix local.hex --force && \
        mix archive.install hex phx_new --force
    
  • docker-compose.yml

    version: '3'
    
    services:
      PostgreSQL:
        image: postgres
        container_name: postgres
        ports:
          - 5432:5432
        environment:
          POSTGRES_USER: root
          POSTGRES_PASSWORD: root
          POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
        hostname: postgres
        restart: always
        user: root
    
      elixir:
        build: .
        ports:
          - 4000:4000
        container_name: elixir
        volumes:
          - .:/workspace
        depends_on:
          - PostgreSQL
        command: /bin/bash
        tty: true
        stdin_open: true
    

まずはDockerfileから解説していきます。

  • Dockerfile

    Dockerfileに関してはいたってシンプルです。

    From命令でelixirイメージをpullして使用します。

    WORKDIR命令では、Docker上でカレントディレクトリとして使用したいディレクトリを指定しています。デフォルトで指定したディレクトリが存在しない場合、新規に作成してくれます。

    次に、RUN命令ですがとりあえず今後何かしらのコマンドを使用するかもしれないので apt-get -y updateだけは実行しています。他に何かしら入れたい場合は、以下のように書けばいいでしょう。

    RUN apt-get -y update && apt-get install -y \
    	curl \
    	zip && \
    	mix local.hex --force && \
        mix archive.install hex phx_new --force
    

    \は改行を表すもので可読性を高めるために用いられます。&&はコマンドの区切りに用いられます。

    さて次に、mix local.hex --forcemix archive.install hex phx_new --forceに関してですが、これはPhoenixをインストールするために記述しており、またオプションの--forceつけています。Dockerの性質上、インストール時のyes/noを実行中に入力できないためこのオプションを付けて実行しています。

  • docker-compose.yml

    docker-composeはDBを用いるため少し複雑です。まずこのdocker-composeはversion: '3'とあるようにバージョンが3です。他の記事と合わせてこの記事をみるとき、片方がバージョン2を用いていた場合挙動がおかしくなったりエラーが発生する可能性があるので注意してください。

    さてservicesでは複数のコンテナを管理することが可能です。今回の場合ではelixirのコンテナとpostgresのコンテナを管理しています。ではそれぞれのコンテナについてみていきます。

    • PostgreSQL

      PostgreSQLというのはサービス名です。コンテナの名前ではないことに注意してください。コンテナの名前はcontainer_nameに示されている通りpostgresです。

      imageでは使用するイメージを設定します。今回の場合ではpostgresです。

      container_nameは先ほど説明した通りこのサービスのコンテナ名です。

      portsでは通信するポートを指定します。文法としては

      ports:
      	- ホスト側(自分のPC本体)のポート番号:コンテナ側のポート番号
      	- 以下例
      	- 4000:4000
      	- 8080:3000
      

      となります。

      environmentではpostgresの環境設定を行います。今回の場合では、user名とpassword、文字コードの指定をしています。

      hostnameではその名の通りホスト名を設定しています。

      restartでは何らかの問題でこのコンテナが落ちた場合に再接続させるためにalwaysを設定します。alwaysを設定することにより、明示的に docker stop コンテナ名 とされない限り、終了ステータスに関係なく常に再起動が行われます。

      userではuserを設定します。

    • elixir

      portscontainer_nameに関しては先ほど説明したので省きます。

      buildではimageとは違いDockerfileを用いてコンテナを作成したいときに用います。

      build: .の時はdokcer-composeファイルと同階層にDockerfileを作成する必要がありますが以下のようにすればdocker-composeファイルから見た相対パスでDockerfileを指定できます。

      build:
      	context:./hoge
      		
      

      volumesの働きはホスト環境のディレクトリとコンテナのディレクトリを紐づけて共有するといったものである。文法は以下のような感じ。

      volumes:
      	- ホストのディレクトリの相対パス若しくは絶対パス(ほとんどの場合相対パス):コンテナのディレクトリの絶対パス
      	- 以下例
      	- ./hoge:/workspace
      	- ./foo/run.sh:/woorkspace/run.sh
      

      ディレクトリを共有するときは共有したいコンテナの場所の親ディレクトリを、

      ファイルを共有したいときは共有したいコンテナの場所に同じ名前のファイルを指定するといった感じで書きます。

      depends_onの働きは、サービスの起動順序を決めます。ここではPostgreSQLサービスが立ち上がったのちにelixirサービスが立ち上がります。

      commandではコンテナ起動時に、指定されたコマンドを実行します。

      ttystdin_openはコンテナを永続化(起動しっぱなし)にするために設定します。

2. PhoenixとPostgreSQLを用いた簡単なwebアプリケーション

2.1 Phoenixプロジェクトの作成

やっと本題です。サブタイルにはWebアプリケーションと言っていますが、実際はDBに格納されたデータを只表示するだけなので、アプリケーション性は皆無です。

では早速、コマンドプロントを開いて以下のコマンドを実行してください。

cd /docker-composeの入ったディレクトリ
docker-compose up -d --build
docker attach elixir

docker-compose up -d --build実行後は色々な処理が発生するので、それが終わるのを待ってから、docker attach elixirを実行してください。

この時点でelixirコンテナ内にログインできているのを確認してから以下のコマンドを実行してください。

mix phx.new postgres_sample --no-webpack
cd postgres_sample

途中でyes/noを聞いてくるのですべてyを選択してください。

postgres_sampleディレクトリに移動していることを確認したのちに、ディレクトリの階層が以下のようになっていることを確認してください。

root@c77dbed21542:/workspace/postgres_sample# ls -al
total 18
drwxrwxrwx 2 root root 4096 Aug 21 05:30 .
drwxrwxrwx 2 root root 4096 Aug 21 05:31 ..
-rwxr-xr-x 1 root root  159 Aug 21 05:30 .formatter.exs
-rwxr-xr-x 1 root root  739 Aug 21 05:30 .gitignore
-rwxr-xr-x 1 root root  672 Aug 21 05:30 README.md
drwxrwxrwx 2 root root    0 Aug 21 05:30 config
drwxrwxrwx 2 root root    0 Aug 21 05:30 lib
-rwxr-xr-x 1 root root 1671 Aug 21 05:30 mix.exs
drwxrwxrwx 2 root root    0 Aug 21 05:30 priv
drwxrwxrwx 2 root root    0 Aug 21 05:30 test

確認出来たら、config/dev.exsをエディタで開いてください。

config :postgres_sample, PostgresSample.Repo,
  username: "postgres",
  password: "postgres",
  database: "postgres_sample_dev",
  hostname: "localhost",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

初期ではこのようになっているのでこれを以下のように変えます。

config :postgres_sample, PostgresSample.Repo,
  username: "root",
  password: "root",
  database: "postgres_sample_dev",
  hostname: "postgres",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

変更出来たら、コマンドプロントに戻って、以下を実行してください。

mix ecto.create
iex -S mix phx.server

もし、mix ecto.create実行時にエラーが出た場合は、mix deps.getコマンドを実行してください。

さて、iex -S mix phx.serverを実行したら、localhost:4000に接続してください。

すると、以下のような画面が表示されると思います。

1566369635326.png

これが表示されていればとりあえず、Phoenixが最低限動いていることが確認できます。

2.2 PostgreSQLのDBの構築

次は、PostgreSQLでDBを構築します。

まずはコマンドプロントで今、elixirが動いているので新しいウィンドウでコマンドプロントを立ち上げるか、ctrl+p,q(ctrlを押しながらp,qの順番に押す)を実行すればコンテナを止めることなくホストの環境に戻ることができます。

ではホストの環境で、docker exec -it postgres psql -U rootと実行してください。

すると以下のように表示されるかと思います。

psql (11.5 (Debian 11.5-1.pgdg90+1))
Type "help" for help.

root=#

まずは、今現在どのようなデータベースが存在するかを確認するために、\lと入力してください。すると以下のように表示されるはずです。

                                  List of databases
        Name         | Owner | Encoding |  Collate   |   Ctype    | Access privileges
---------------------+-------+----------+------------+------------+-------------------
 postgres            | root  | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres_sample_dev | root  | UTF8     | en_US.utf8 | en_US.utf8 |
 root                | root  | UTF8     | en_US.utf8 | en_US.utf8 |
 template0           | root  | UTF8     | en_US.utf8 | en_US.utf8 | =c/root          +
                     |       |          |            |            | root=CTc/root
 template1           | root  | UTF8     | en_US.utf8 | en_US.utf8 | =c/root          +
                     |       |          |            |            | root=CTc/root
(5 rows)

次に早速データベースを構築していきます。まずは以下を実行してください。

create database elixir;

\lコマンドでしっかりと作成されていることを確認します。

                                  List of databases
        Name         | Owner | Encoding |  Collate   |   Ctype    | Access privileges
---------------------+-------+----------+------------+------------+-------------------
 elixir              | root  | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres            | root  | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres_sample_dev | root  | UTF8     | en_US.utf8 | en_US.utf8 |
 root                | root  | UTF8     | en_US.utf8 | en_US.utf8 |
 template0           | root  | UTF8     | en_US.utf8 | en_US.utf8 | =c/root          +
                     |       |          |            |            | root=CTc/root
 template1           | root  | UTF8     | en_US.utf8 | en_US.utf8 | =c/root          +
                     |       |          |            |            | root=CTc/root
(6 rows)

一番上にelixirとあるのが確認できます。

次に\c elixirで、先ほど作ったelixirデータベースに接続します。

接続すると先ほどまで、root=#となっていたところが、elixir=#となっていることが確認できます。

確認出来たら、テーブルの作成に移ります。今回は主キーなどの設定は行わず、ごくごく簡単なテーブルを作成します。

以下のコマンドでテーブルを作りましょう。

create table fruits_datas(id integer, name varchar(255), value integer, region varchar(255));

果物を管理するテーブルを今回は作成しました。それぞれのintegervarcharなどの意味は各自で確認してください。

では、テーブルが正しく作成されているか\z\d fruits_datasで確認します。前者はテーブル自体の存在確認で、後者はテーブルの定義を確認できます。
結果として、以下のようになると思います。

elixir=# \z
                                Access privileges
 Schema |     Name     | Type  | Access privileges | Column privileges | Policies
--------+--------------+-------+-------------------+-------------------+----------
 public | fruits_datas | table |                   |                   |
(1 row)

elixir=# \d fruits_datas
                   Table "public.fruits_datas"
 Column |          Type          | Collation | Nullable | Default
--------+------------------------+-----------+----------+---------
 id     | integer                |           |          |
 name   | character varying(255) |           |          |
 value  | integer                |           |          |
 region | character varying(255) |           |          |

こうなっていれば正しくテーブルは作成されています。

では、実際にこのテーブルに対してデータを与えていきます。以下のコマンドを実行して下さい。

insert into fruits_datas values(1, 'みかん', 80, '日本'), (2, 'りんご', 120, 'アメリカ'), (3, 'バナナ', 150, 'インド'), (2, 'りんご', 90, '中国'), (3, 'バナナ', 120, 'ブラジル'); 

これが正しく、実行されていれば以下のようになるはずです。

elixir=# select * from fruits_datas;
 id |  name  | value |  region
----+--------+-------+----------
  1 | みかん |    80 | 日本
  2 | りんご |   120 | アメリカ
  3 | バナナ |   150 | インド
  2 | りんご |    90 | 中国
  3 | バナナ |   120 | ブラジル
(5 rows)

このようになっていれば今回のdbでの作業は終わりなので\qを実行するかctrl+p,qしてください。

2.3 PhoenixとDBの紐づけ

次は、dbとPhoenixを紐づけていきます。とりあえず再度docker attach elixirでコンテナにログインし、postgres_sampleディレクトリに移動しておいてください。

と言っても、先ほどctrl+p,qで終了した人は、attachしてもなかなかコマンドプロントが表示されないと思いますが、Enterキーなどを打ってもらうと

nil
iex(2)>

のように表示されると思うので、ctrl+cを2回押してもらうと、作業可能状態になります。

では、postgres_sampleディレクトリ下に/lib/util/db.exを作成しましょう。

そして、db.exを以下のように編集します。

PostgresSample.Repoとなっているところはプロジェクト作成時のmix phx.new postgres_sample --no-webpackのpostgres_sampleの部分に合わせてください。わからなければ、postgres_sample\config\config.exsやpostgres_sample\config\dev.exsにそれっぽいのが書いているのでそれをコピペしましょう。

defmodule Db do
  def query( sql ) when sql != "" do
    { :ok, result } = Ecto.Adapters.SQL.query( PostgresSample.Repo, sql, [] )
    result
  end
  def columns_rows( result ) do
    result
    |> rows
    |> Enum.map( fn row -> Enum.into( List.zip( [ columns( result ), row ] ), %{} ) end )
  end
  def rows( %{ rows: rows } = _result ), do: rows
  def columns( %{ columns: columns } = _result ), do: columns
end

次に、postgres_sample以下にlib\postgres_sample_web\templates\page\index.html.eexを作成して以下のように編集します。

<%
result = Db.query( "select * from fruits_datas" )
data = result |> Db.columns_rows
%>
<table border="1">
<tr>
  <th>ID</th>
  <th>名前</th>
  <th>価格</th>
  <th>生産国</th>
</tr>
<%= for record <- data do %>
<tr>
    <td><%= record[ "id" ] %></td>
    <td><%= record[ "name" ] %></td>
    <td><%= record[ "value" ] %></td>
    <td><%= record[ "region" ] %></td>
</tr>
<% end %>
</table>

次に、config/dev.exsのdatabaseの部分をpostgres_sample_devからelixirに変更します。

config :postgres_sample, PostgresSample.Repo,
  username: "root",
  password: "root",
  database: "elixir",
  hostname: "postgres",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

では、動くかどうか確認しましょう。

iex -S mix phx.serverを実行してください。

実行したらlocalhost:4000に接続してください。

すると成功していれば以下のような画面が表示されるはずです。

1566375102305.png

さきほどDBで作成したデータがきちんと表示されていますね。

以上で基本的なことは終了です。お疲れさまでした。

終わりに

これから、自分もこの知識を生かし次のステップに進もうと思います。またある程度知識がつけば記事にしたいと思っています。長々とありがとうございました。

参考文献

Excelから関数型言語マスター3回目:WebにDBデータ表示【PostgreSQL or MySQL編】

22
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?