関数型言語は何度も挫折してきましたが、Elixirは若干その敷居を下げてくれた気がします。(モナドとか出てこないし)
今回はDockerのelixirイメージを使って開発環境を作成しますが、若干手を加えてElixir特有のノード間通信(コンテナ間通信)を簡単に試せる環境にします。
今回作成するのは以下のような環境です。
早速作りましょう!
elixir docker環境の変更
dockerにあるelixirイメージを走らせるだけであれば
$ docker run -it --rm elixir bash
ですみます。
ですが、これだといくつか不便な点があります。
- userがrootになっている
- ソースコードを置く場所がマウントされていない
- コンテナ間のネットワーク設定がない
- host名がわかりやすくない (後でelixir同士を通信させるときにわかりにくい)
ということで、elixir向けdockerfileを作り、起動コマンドもすこし変更します。
elixir 向け docker file
elixir_dockerfileとして作成。
FROM elixir
LABEL maintainer="hassyheisaku"
ENV USER_NAME heisaku
ENV UID 1000
RUN useradd -m -u ${UID} ${USER_NAME}
ENV HOME /home/${USER_NAME}
ENV TZ Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR ${HOME}
USER ${USER_NAME}
ユーザとしてheisaku
を追加したのがメインポイントです。
ビルドしておきます。
$ docker build -t myelixir -f elixir_dockerfile .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myelixir latest 5597fe573818 20 hours ago 1.3GB
elixir latest 6fac7ad41d86 2 weeks ago 1.3GB
:
もともとのelixirイメージが結構大きいですね。。
docker networkの作成
dockerのコンテナ同士をつなぐためにdocker networkを作成しておきます。これによるブリッジ接続でコンテナ同士がつながります。
$ docker network create elixirnet
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
bf3721881a89 elixirnet bridge local
:
起動コマンド
Volumeのマウント、docker networkへのアタッチ、ホスト名指定を行います。
$ docker run -it -v /home/heisaku/elixir:/work -h hoge --network elixirnet --rm myelixir bash
#host名hogeの場合
-v volumeのマウント(ホスト側:コンテナ側)
-h host名指定
–network 作成したdocker networkを指定
Elixirコンテナ間接続
コンテナ1 Hoge
#ホスト名 hoge をつける
$ docker run -it -v /home/heisaku/elixir:/work -h hoge --network elixirnet --rm elixirheisaku bash
#ここからコンテナ内
heisaku@hoge:~$ hostname -i
172.18.0.2
heisaku@hoge:~$ iex --sname hoge --cookie hogecookie
# cookieで両方のコンテナのiexに同じ文字列を与える {#a6}
iex(hoge@hoge)1>
コンテナ2 Fuga
#ホスト名 fuga をつける
$ docker run -it -v /home/heisaku/elixir:/work -h fuga --network elixirnet --rm elixirheisaku bash
#ここからコンテナ内
heisaku@fuga:~$ hostname -i
172.18.0.3
heisaku@fuga:~$ iex --sname fuga --cookie hogecookie
#コンテナ1 への接続
iex(fuga@fuga)1> Node.connect :"hoge@hoge"
true
iex(fuga@fuga)2> Node.list
[:hoge@hoge]
iex(fuga@fuga)3>
接続できた!
再びコンテナ1 Hoge
コンテナ2でiex起動・接続後
iex(hoge@hoge)1> Node.list
[:fuga@fuga]
#↑↑↑↑コンテナ2から接続があったのでコンテナ1でも認識できている
#関数で相手側を呼び出せているか確認
iex(hoge@hoge)2> func = fn -> IO.inspect Node.self end
#Function<45.65746770/0 in :erl_eval.expr/5>
iex(hoge@hoge)4> Node.spawn(:"hoge@hoge", func)
:hoge@hoge
#PID<0.120.0>
iex(hoge@hoge)3> Node.spawn(:"fuga@fuga", func)
:fuga@fuga
#PID<11237.122.0>
動きました!
まとめ
Elixirのように分散環境を意識したプログラミング言語の場合は、このようにコンテナで環境作成するとはかどりますね。
オチは?