Hello Bob!! hexpm/elixirで(Rclexの)Docker環境を整備する

https://qiita.com/advent-calendar/2021/nervesjp

この記事は [#NervesJP Advent Calender 2021](https://qiita.com/advent-calendar/2021/nervesjp) の~~24日目~~2日目です（大幅に超過してスンマセン:bow:
1日目の記事は @mnishiguchi さんの「[Nerves電子ペーパーでHello world](https://qiita.com/mnishiguchi/items/f709d6c211cf41078f2f)」でした．

# Elixir/Erlangバージョンの組合せ問題

[世俗派関数型言語Elixir](https://qiita.com/kikuyuta/items/afa4c264720eb29d9760)（by @kikuyutaセンセ）は，関数型言語の老舗本舗であるErlang VMの上でイゴいています．ElixirもErlangもそれぞれ活発に開発が進んでいてバージョンアップを繰り返しています．
そうなるとバージョンの組合せは無限に爆発していってお姉さん困っちゃうわけですが，それでは皆さんのプロジェクトではどの組合せを使うのが良さそうでしょうか？？

## ホストに環境構築する場合

とりあえずElixirを使いたい！最新版でええんやでっ！という方は，`brew`なり`apt`なりでさくっと入れてもらったら良いかと思います．

ホストに所定の組合せを構築したい場合は `asdf` がだんぜんオススメです．
詳しい解説は [#NervesJP Advent Calender 2021](https://qiita.com/advent-calendar/2021/nervesjp) 19日目の @torifukukaiou さんによるawesome!!な記事を読むと良いです．

https://qiita.com/torifukukaiou/items/9009191de6873664bb58

## Dockerで環境構築する場合

速攻で使うにはDocker Hubにある Elixir Official Image を使えば良いでしょう．

https://hub.docker.com/_/elixir

サクッと最新版を `docker run` して bash を起動して，いろいろバージョンを調べてみました．

```shell-session
root@b1e975b797c2:/# mix local.hex
Are you sure you want to install "https://repo.hex.pm/installs/1.13.0/hex-1.0.1.ez"? [Yn] Y
* creating root/.mix/archives/hex-1.0.1
root@b1e975b797c2:/# mix hex.info 
Hex:    1.0.1
Elixir: 1.13.1
OTP:    24.2

Built with: Elixir 1.13.0 and OTP 22.3
root@b1e975b797c2:/# uname -a
Linux b1e975b797c2 5.10.76-linuxkit #1 SMP Mon Nov 8 10:21:19 UTC 2021 x86_64 GNU/Linux
root@b1e975b797c2:/# cat /etc/issue
Debian GNU/Linux 11 \n \l

```

Officialなので安心ではありますが，ここで気になるのが，

- 任意の組合せは用意できない．Erlangは固定化されてしまう．
- OSはDebianがデフォ，あと -slim と -alpine はある．でも他は選べないし，OSバージョンも指定できない．

というところです．

無限にある組合せをぜんぶサポートするのは無理ですし，対象プロジェクトによっては特定の組合せでイゴかないこともありえます（とはいっても実際にそんなの体験したことは無いですが:D
対象の開発プロジェクトや依存ライブラリがCIテストの対象としている組合せとか推奨している組合せとかを使っておくのが心理的に安心安全というわけです．

ちなみに，みんな大好き！Nervesの執筆時点な最新版v1.7.13では，

https://hexdocs.pm/nerves/1.7.13/installation.html

- Erlang/OTP 24.1.5
- Elixir 1.12.3-otp-24

に乗っかっておきたいところです．

そしてNerves v1.7.13でのCIテスト対象になっているのは，

https://github.com/nerves-project/nerves/blob/v1.7.13/.circleci/config.yml

を紐解くと，下記の全5バージョンの組合せになっていました．

```config.yml
<snipped...>

jobs:
  build_elixir_1_13_otp_24:
    docker:
      - image: hexpm/elixir:1.13.1-erlang-24.1.7-alpine-3.14.2
<snipped...>

  build_elixir_1_12_otp_24:
    docker:
      - image: hexpm/elixir:1.12.3-erlang-24.1.5-alpine-3.14.2
<snipped...>

  build_elixir_1_11_otp_23:
    docker:
      - image: hexpm/elixir:1.11.4-erlang-23.3.4-alpine-3.13.3
<snipped...>

  build_elixir_1_10_otp_23:
    docker:
      - image: hexpm/elixir:1.10.4-erlang-23.3.4-alpine-3.13.3
<snipped...>

  build_elixir_1_9_otp_22:
    docker:
      - image: hexpm/elixir:1.9.4-erlang-22.3.4.18-alpine-3.13.3

<snipped...>
workflows:
  version: 2
  build_test:
    jobs:
      - build_elixir_1_13_otp_24
      - build_elixir_1_12_otp_24
      - build_elixir_1_11_otp_23
      - build_elixir_1_10_otp_23
      - build_elixir_1_9_otp_22
```

おっ `hexpm/elixir` ってこれなんぞ？？というのが，今回の記事の発端です．
（なお本記事のNerves成分は以上でっす！ :dash: 

# hexpm/elixir！（とは？？

みんな大好きHexの開発チームである`Bob`さんが公開されているものです．

- hexpm/elixir - Docker Image | Docker Hub 

https://hub.docker.com/r/hexpm/elixir

じゃなかった，`Bob`さんってリポジトリ名みたい:D

- hexpm/bob: The Builder 

https://github.com/hexpm/bob#docker-images


現時点の[Docker Tagsのページ](https://hub.docker.com/r/hexpm/elixir/tags?page=4098)は4098までありました．
1ページあたり25タグ表示なので，4097*25+8 = 102,433タグ！！
最新の `1.13.1` で "Filter Tags" しても69ページまであります！（無限に課金してそう,,, :money_mouth: 

ちなみにもちろん [Erlang単体のDocker image](https://hub.docker.com/r/hexpm/erlang) もご用意されています．

## どの組合せが使えるの？？

どのElixir x Erlangの組合せを `docker build` しているのか，リポジトリのコードからは定義されていると思わしき箇所は見つけられませんでした．
README#Docker-images には，

> Docker images for Bob's Elixir and Erlang builds are built periodically. Bob checks for new Elixir and Erlang releases every 15 minutes and builds images for any new versions it discovers. 

15分ごとにリリースチェックしてるぜ！などと申しております :thinking: 
どうもそれぞれのGitHub tags/releasesをほとんどぜんぶビルドしている気がしなくもない,,, 探検隊の情報を求めます．

いちお分かったこととして，アーキテクチャは "amd64" と "arm64" の2種類，OSは "alpine" 2種類 "ubuntu" 5種類 "debian" 6種類 の計13種類を回しているようです．

https://github.com/hexpm/bob/blob/main/lib/bob/job/docker_checker.ex#L1-L30

```elixir:lib/bob/job/docker.ex
defmodule Bob.Job.DockerChecker do
  @erlang_tag_regex ~r"^(.+)-(alpine|ubuntu|debian)-(.+)$"
  @elixir_tag_regex ~r"^(.+)-erlang-(.+)-(alpine|ubuntu|debian)-(.+)$"

  @archs ["amd64", "arm64"]

  # TODO: Automate picking the OS versions

  @builds %{
    "alpine" => [
      "3.14.3",
      "3.15.0"
    ],
    "ubuntu" => [
      "groovy-20210325",
      "focal-20210325",
      "bionic-20210325",
      "xenial-20210114",
      "trusty-20191217"
    ],
    "debian" => [
      "bullseye-20210902",
      "buster-20210902",
      "stretch-20210902",
      "bullseye-20210902-slim",
      "buster-20210902-slim",
      "stretch-20210902-slim"
    ]
  }

<snipped...>
```

要するにどんだけ組合せあるか分かんないんだけど,,, これだけあれば安心ですねっ！！

## イゴかしてみる

最新版と思わしき `1.13.1-erlang-24.2-ubuntu-focal-20210325` のタグを試してみました．
せっかくなので Ubuntu focal (20.04) を指定しています．

```shell-session
$ docker run -it --rm hexpm/elixir:1.13.1-erlang-24.2-ubuntu-focal-20210325 /bin/bash
root@ef3e34f05207:/# mix local.hex
Are you sure you want to install "https://repo.hex.pm/installs/1.13.0/hex-1.0.1.ez"? [Yn] Y
* creating root/.mix/archives/hex-1.0.1
root@ef3e34f05207:/# mix hex.info
Hex:    1.0.1
Elixir: 1.13.1
OTP:    24.2

Built with: Elixir 1.13.0 and OTP 22.3
root@ef3e34f05207:/# uname -a
Linux ef3e34f05207 5.10.76-linuxkit #1 SMP Mon Nov 8 10:21:19 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
root@ef3e34f05207:/# cat /etc/issue
Ubuntu 20.04.2 LTS \n \l

```

かなり嬉しいのが docker image のサイズです．公式のイメージと比較してみましょう．

```shell-session
$ docker images 
REPOSITORY           TAG                                          IMAGE ID       CREATED        SIZE
elixir               1.13.1                                       b032b26101ec   33 hours ago   1.3GB
hexpm/elixir         1.13.1-erlang-24.1.7-ubuntu-focal-20210325   f6e2d9dafd82   9 days ago     183MB

```

1.3GB vs 183MB となりました．**なんと約1/10です！！**
もちろんそのぶん自分勝手に使うにはいろいろ入れないといけないですが，ベースのサイズは小さいほうに限りますよねぇ．


## RclexのDocker環境に使ってみる

Rclexとは，Elixirでロボット向けのROS 2/DDS通信ができるすごいやつです（てきとー
詳しいことはそのうち別の記事で紹介できればです〜

https://github.com/rclex/rclex

https://hexdocs.pm/rclex/readme.html

<font color="purple">$\huge{May\ the\ BEAM\ be\ with\ your\ Robots!!}$</font>
をRclexでやるには，やっぱりElixir/ErlangとROS 2の環境を同時に整えないといけません．どちらもそれなりに大変です．

Alchemist視点でRclexの環境をホスト上（正確にはAzure VM上ですが）に構築してお試しする方法は，またもや @torifukukaiou さんが5日目の記事にまとめてくださっています！！

https://qiita.com/torifukukaiou/items/9802f2f26f15778679d7

ここまでやるのはメンドいですよね〜 あとGitHub Actionsで素敵にCI回したいし．

それと，ROS 2はUbuntuに強く依存していて，さらにROS 2のdistribution(version)に対応したUbuntuのバージョンを揃えないといけません．最新LTSのFoxyであればUbuntu focal (20.04)になります．

てなことで，この hexpm/elixir をベースとしてRclex向けのDocker環境を仕立ててみました．

https://github.com/rclex/rclex_docker

```Dockerfile
### FIXME according to the target version
# base image
FROM hexpm/elixir:1.12.3-erlang-24.1.5-ubuntu-focal-20210325
# Set Ubuntu Codename
ENV UBUNTU_CODENAME focal
# Set ROS_DISTRO environment
ENV ROS_DISTRO foxy

# force error about debconf
ENV DEBIAN_FRONTEND noninteractive

# update sources list
RUN apt-get clean
RUN apt-get update

# install additonal packages
RUN apt-get install -y git sudo build-essential

# install AStyle to format C code (NIFs)
RUN apt-get install -y astyle

### install ROS START
### https://docs.ros.org/en/foxy/Installation/Ubuntu-Install-Debians.html

# Set locale
RUN apt update && apt -y install locales
RUN locale-gen en_US en_US.UTF-8
RUN update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
RUN export LANG=en_US.UTF-8

# Setup Sources
RUN apt update && apt install -y curl gnupg2 lsb-release
RUN curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key  -o /usr/share/keyrings/ros-archive-keyring.gpg

RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu ${UBUNTU_CODENAME} main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null

# Install ROS 2 packages
RUN apt update
RUN apt install -y ros-${ROS_DISTRO}-ros-base

# Install ROS 2 packages
RUN apt install -y python3-colcon-common-extensions

# Setup ROS environment in shell
RUN echo 'source /opt/ros/${ROS_DISTRO}/setup.sh' >> ~/.bashrc

### install ROS END

# cleanup
RUN apt-get -qy autoremove

# install hex and rebar
RUN mix local.hex --force
RUN mix local.rebar --force

# check version
RUN . /opt/ros/${ROS_DISTRO}/setup.sh && env | grep ROS
RUN mix hex.info

```

hexpm/elixir のUbuntu向けな特定バージョンを `BASE` にして，[Installing ROS 2 via Debian Packages](https://docs.ros.org/en/foxy/Installation/Ubuntu-Install-Debians.html)を `RUN` しまくっているだけです（あと詳しくはコメント読んでください:dash:

最初の３構文をいろいろ変えたらいろいろバージョン派生できるので，とっても便利になりました！
こんな感じでROS 2(Ubuntu) x Elixir x Erlangの組合せを計10種類用意できて，RclexのGitHub Actionsに登録しています[^1]．Elixir x ErlangはNervesのバージョン組に乗っかっています．

[^1]: GalacticはRclexでまだ対応できておらず，正確にはGitHub Actionsでまだ使えていません．

1. foxy-ex1.13.1-otp24.1.7
1. foxy-ex1.12.3-otp24.1.5 **[latest]**
1. foxy-ex1.11.4-otp23.3.4
1. dashing-ex1.12.3-otp24.1.5
1. dashing-ex1.11.4-otp23.3.4
1. dashing-ex1.10.4-otp23.3.4
1. dashing-ex1.9.4-otp22.3.4.18
1. galactic-ex1.13.1-otp24.1.7
1. galactic-ex1.12.3-otp24.1.5
1. galactic-ex1.11.4-otp23.3.4

## 余談：ros official image使えよ？？

あんさんROSも Docker Official Image 出してるの知らへんの？いや知ってますとも〜

https://hub.docker.com/_/ros

当初はこれを `BASE` にしてイメージ作っていたんですが，`erlang-solutions_2.0_all.deb`の挙動がおかしかったりイメージがでかくなったりいろいろありましてね,,, 詳しく気になる方はこのあたり見てください．

https://github.com/rclex/rclex_docker/pull/2

https://github.com/rclex/rclex/issues/27#issuecomment-852680008

# おわりに

ということでこの記事では `Bob` さんが素敵に働いてくれている [hexpm/elixir](https://hub.docker.com/r/hexpm/elixir) を紹介しました．
なんといってもベースのイメージが小さいし，OSもある程度の選択肢があるし，Nerves勢だけじゃなくElixir/Erlang勢やPhoenix勢にも意外と知られていない優良情報と言っても過言ではないような気がします（なにごと

それでは皆さん，よいDocker x Elixirライフを〜


---

[#NervesJP Advent Calender 2021](https://qiita.com/advent-calendar/2021/nervesjp) の3日目は @torifukukaiou さんの「[elixir-desktop/desktop のandroidサンプルをイゴかす](https://qiita.com/torifukukaiou/items/5458458e2ec1bcee5152)」です！（でした！！:bow: