ansible-benderを使ってNuxt.jsのコンテナイメージをビルドしてみた

# 背景
以前AnsibleのDockerモジュールを活用してDockerコンテナのイメージビルドからコンテナ作成までをAnsible上で行いました。[Ansibleを使ってNuxt.jsのDockerコンテナを構築してみた](https://qiita.com/Yuhta/items/e9c8aa4e95b6d39e6323)
その時はAnsible-Containerというコンテナ管理に便利なツールがあることを紹介しましたが、2020年現在は開発終了されています。ただし、その後ansible-benderというAnsible Containerの機能を一部引き継いだツールが新しく登場しました。
今回はansible-benderを使ってコンテナイメージをビルドしていきたいと思います。
# ansible-benderについて
ansible-benderはAnsible Playbookを使ってコンテナイメージをビルドすることができるツールです。(https://github.com/ansible-community/ansible-bender)
Dockerモジュールでは、Dockerfileの宛先パスをPlaybookで指定してコンテナイメージのビルドを行いましたが、ansible-benderでは直接Playbook内にコンテナイメージを記述することでコンテナイメージのビルドが可能となります。pipパッケージからインストールすることが可能です。
`pip install ansible-bender`
# 必須要件
ansible-benderを動かすには予めPodmanとBuildahをインストールする必要があります。
こちらのツールは[前回の記事](https://qiita.com/Yuhta/private/c2597bef7a5eb3bb6c53)で紹介していますので、初めてツールの存在を知った方はそちらの記事もご参照ください。
またコンテナイメージをビルドする際、Pythonのインストールされたコンテナベースイメージが必要です。(Python2系3系問わない)
事前にPythonを含む最小コンテナイメージを用意しておくと後々便利です。参考記事[ansible-bender でコンテナイメージを作成する](https://qiita.com/tsuchm/items/90a49b827130f80f5e46)

```Dockerfile
FROM debian:buster
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y python3 && apt-get clean
```

# コンテナイメージのビルド
ansible-benderの準備ができましたら、コンテナイメージのビルドを行っていきます。
ここでは、前回の[記事](https://qiita.com/Yuhta/items/e9c8aa4e95b6d39e6323)で構築したNuxt.jsのコンテナイメージをansible-benderでビルドできるか検証していきます。
###### ベースコンテナイメージの取得

```dockerfile:Dockerfile
FROM node:latest  
WORKDIR /app  
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y python3 && apt-get clean
```

Debianコンテナをベースイメージにするよりもnodeイメージをベースにした方が後々のコンテナイメージビルドが楽になりますので、nodeイメージにPythonをインストールしてベースイメージを取得しました。続いてディレクトリ構成について紹介します。

```
├── Prototype  
│   └── app/(Nuxt.jsのひな型)    
└── build_nuxtimage.yml
```

前回[記事](https://qiita.com/Yuhta/items/e9c8aa4e95b6d39e6323)を踏襲していますが、変更点としてはDockerfileがなくなっております。コンテナイメージの作成を`build_nuxtimage.yml`で作成するので、不要となります。
比較用に前回のDockerfileの中身も載せて置きます。

```dockerfile:Dockerfile
FROM node:latest
ENV NUXT_HOST=0.0.0.0
ENV NUXT_TELEMETRY_DISABLED=1
WORKDIR /app

COPY ./app/package.json ./app/yarn.lock ./
RUN yarn install

COPY ./app .

CMD ["yarn", "run", "dev"]
```

これをansible-benderのPlaybookに直しますと次のようになります。

```yaml:build_nuxtimage.yml
---  
- name: Build Nuxt.js image  
  hosts: all  
  gather_facts: false  
  vars:  
    ansible_python_interpreter: /usr/bin/python3  
    ansible_bender:  
      base_image: "localhost/node-python3"  
      ansible_extra_args: "-vvv"  

      target_image:  
        name: yuta28/bender-test  
        working_dir: /app  
        environment:  
          NUXT_HOST: 0.0.0.0  
        labels:  
          built-by: '{{ ansible_user }}'  
        cmd: "yarn run dev"  

  tasks:  
  - name: Copy Prototype parts  
    copy:  
      src: "{{ item }}"  
      dest: /app/  
    with_items:  
      - "{{ playbook_dir }}/Prototype/app/package.json"  
      - "{{ playbook_dir }}/Prototype/app/yarn.lock"  

  - name: Yarn install  
    command: "yarn install"  

  - name: Archive app directory  
    delegate_to: localhost  
    archive:  
      path: "{{ playbook_dir }}/Prototype/app"  
      dest: "{{ playbook_dir }}/Prototype/app.tar.gz"  
      format: gz  

  - name: Unpacks app.tar.gz  
    unarchive:  
      src: "{{ playbook_dir }}/Prototype/app.tar.gz"  
      dest: /
```

Dockerfile内のRUN命令箇所は、commandモジュール、CMD命令はtarget_image内のcmd変数に変換してPlaybookでコンテナのイメージビルドのPlaybookを作成しました。
基本的にはTaskにDockerfileの命令文を記載しておりますが、Nuxt.jsのひな型プロジェクトのコピーはansilbeのcopy/synchronizeで実行しますと、ファイルの量が多すぎるため処理が完了できませんでした。
代替案としてひな型プロジェクトのファイル群をarchiveモジュールで圧縮して、コンテナ内で展開するように変更しております。このPlaybookをansible-benderコマンドで実行します。
`ansible-bender build build_nuxtimage.yml`

```
$ ansible-bender build build_nuxtimage.yml   
ansible-playbook 2.9.13  
  config file = /tmp/ab_x4m8plk/ansible.cfg  
  configured module search path = ['/home/ec2-user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']  
  ansible python module location = /usr/lib/python3.6/site-packages/ansible  
  executable location = /usr/bin/ansible-playbook  
  python version = 3.6.8 (default, Dec  5 2019, 15:45:45) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]  
Using /tmp/ab_x4m8plk/ansible.cfg as config file  
host_list declined parsing /tmp/ab_x4m8plk/inventory as it did not pass its verify_file() method  
script declined parsing /tmp/ab_x4m8plk/inventory as it did not pass its verify_file() method  
auto declined parsing /tmp/ab_x4m8plk/inventory as it did not pass its verify_file() method  
Parsed /tmp/ab_x4m8plk/inventory inventory source with ini plugin  

PLAYBOOK: .build_nuxtimage-20200913-071938404664-rjqqvxkjpc.yaml *********************************************************************************************  
1 plays in .build_nuxtimage-20200913-071938404664-rjqqvxkjpc.yaml  

PLAY [Build Nuxt.js image] ***********************************************************************************************************************************  
META: ran handlers  

TASK [Copy Prototype parts] **********************************************************************************************************************************  
task path: /home/ec2-user/ansible-bender/simple-test/.build_nuxtimage-20200913-071938404664-rjqqvxkjpc.yaml:5  
<yuta28-bender-test-20200913-071934769597-cont> RUN [b'buildah', b'mount', b'--', b'yuta28-bender-test-20200913-071934769597-cont']  
<yuta28-bender-test-20200913-071934769597-cont> RUN [b'buildah', b'run', b'--', b'yuta28-bender-test-20200913-071934769597-cont', b'/bin/sh', b'-c', b'( umask 77 && mkdir -p "` echo /tmp `"&& mkdir "` echo /tmp/ansible-tmp-1599981579.9843893-85539-240769061862493 `" && echo ansible-tmp-1599981579.9843893-85539-240769061862493="` echo /tmp/ansible-tmp-1599981579.9843893-85539-240769061862493 `" ) && sleep 0']  
Using module file /usr/lib/python3.6/site-packages/ansible/modules/files/stat.py  
<yuta28-bender-test-20200913-071934769597-cont> PUT /home/ec2-user/.ansible/tmp/ansible-local-85531c92iy6sw/tmptlan88hf TO /tmp/ansible-tmp-1599981579.9843893-85539-240769061862493/AnsiballZ_stat.py  
<yuta28-bender-test-20200913-071934769597-cont> RUN [b'buildah', b'run', b'--', b'yuta28-bender-test-20200913-071934769597-cont', b'/bin/sh', b'-c', b'chmod u+x /tmp/ansible-tmp-1599981579.9843893-85539-240769061862493/ /tmp/ansible-tmp-1599981579.9843893-85539-240769061862493/AnsiballZ_stat.py && sleep 0']  
<yuta28-bender-test-20200913-071934769597-cont> RUN [b'buildah', b'run', b'--', b'yuta28-bender-test-20200913-071934769597-cont', b'/bin/sh', b'-c', b'/usr/bin/python3 /tmp/ansible-tmp-1599981579.9843893-85539-240769061862493/AnsiballZ_stat.py && sleep 0'] 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
META: ran handlers  
META: ran handlers  

PLAY RECAP ***************************************************************************************************************************************************  
yuta28-bender-test-20200913-071934769597-cont : ok=4    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0     

Getting image source signatures  
Copying blob sha256:b323b70996e4f6d603c331669ac44cf6234a2e22002d3686e9c88398c6911c25  
Copying blob sha256:e8847c2734e1bec913fbb00dbd638e9bff6c42f699bbeb897728e234cf1a6e72  
Copying blob sha256:a4c504f73441d711e6e71e5c177a772efe4d500c2d97b2a1358ea65edc98e8d5  
Copying blob sha256:ef5de533cb53783465669a60fc9b0b1c782d319c5c8991fe9868f3be8be8e58b  
Copying blob sha256:cbe6bbd0c86ff46f87950e109e891445a085c9bbf5af737b0ae3871d782e5335  
Copying blob sha256:174e334f3f463272eeba0021b8b2c54a8891abc0c74201bbd98e87e8c00524e2  
Copying blob sha256:e404bec46f40991019fbf239256326c48afb7672768755740a6ba862d431e5c6  
Copying blob sha256:58b4b808347b3cdc40003e905f03059f37ae8cf169e91ecf25250e475e27728f  
Copying blob sha256:24a8e30559a7f9f31238761563cc4ab907863039cbc3778449ca8c55f50ecc4a  
Copying blob sha256:9b6e6b48454d80e68f9709e8b82abfe7bd0d059464b9c1796d462601ea59182f  
Copying blob sha256:9f9869f02922117d69ea1b18239a9d55ac8e6d4ca578fc7ef226d20c3e7424f2  
Copying config sha256:ddcc7b1e45f6483f9df7f0d19a17969aa27f92f2e6f77962d3c87c29f32c8186  
Writing manifest to image destination  
Storing signatures  
ddcc7b1e45f6483f9df7f0d19a17969aa27f92f2e6f77962d3c87c29f32c8186  
Image 'yuta28/bender-test' was built successfully \o/

$ podman images  
REPOSITORY                     TAG      IMAGE ID       CREATED          SIZE  
localhost/yuta28/bender-test   latest   ddcc7b1e45f6   32 minutes ago   1.41 GB  
localhost/node-python3         latest   79003b61771b   3 hours ago      990 MB
```

コンテナイメージ`localhost/yuta28/bender-test`が作成されましたので、コンテナを起動します。

```
$ podman run --rm -it -p 8080:3000 localhost/yuta28/bender-test:latest  
yarn run v1.22.5  
$ nuxt  

 WARN  mode option is deprecated. You can safely remove it from nuxt.config                                                                                                          07:59:19  


   ╭────────────────────────────────────────╮  
   │                                        │  
   │   Nuxt.js @ v2.14.5                    │  
   │                                        │  
   │   ▸ Environment: development           │  
   │   ▸ Rendering:   server-side           │  
   │   ▸ Target:      server                │  
   │                                        │  
   │   Listening: http://10.0.2.100:3000/   │  
   │                                        │  
   ╰────────────────────────────────────────╯  

ℹ Preparing project for development                                                                                                                                                  07:59:21  
ℹ Initial build may take a while                                                                                                                                                     07:59:21  
✔ Builder initialized                                                                                                                                                                07:59:21  
✔ Nuxt files generated                                                                                                                                                               07:59:21  

✔ Client  
  Compiled successfully in 8.21s  

✔ Server  
  Compiled successfully in 6.60s  

ℹ Waiting for file changes                                                                                                                                                           07:59:30  
ℹ Memory usage: 212 MB (RSS: 320 MB)                                                                                                                                                 07:59:30  
ℹ Listening on: http://10.0.2.100:3000/
```

別ターミナル画面から`podman ps`コマンドでコンテナ起動を確認します。

```
$ podman ps  
CONTAINER ID  IMAGE                                COMMAND       CREATED         STATUS             PORTS                   NAMES  
fe83f066139c  localhost/yuta28/bender-test:latest  yarn run dev  12 minutes ago  Up 12 minutes ago  0.0.0.0:8080->3000/tcp  strange_moore
```
![9fb09283f27cc2f4bc21d7e1cf223b8a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648568/46f6c4c2-6e20-9b58-29ce-0b527af1699a.png)
コンテナが起動でき、ブラウザからもNuxt.jsのサンプルページにアクセスすることが確認できました。
# 課題
DockerfileからPlaybookへ変更してコンテナイメージビルドを行いましたが、Dockerfileでは出来たのにPlaybookではできなかったことがいくつかありました。
①telemetryによるデータ収集問い合わせの無効化設定を埋め込めない
Dockerfileでは環境変数としてこのような設定を行っていました。
`NUXT_TELEMETRY_DISABLED=1`
こちらですが、Nuxtの統計情報の収集問い合わせを無視する設定です。デフォルトでは有効化されており、最初にコンテナを起動すると以下のメッセージが出てきます。

```
yarn run v1.22.5  
$ nuxt  

ℹ NuxtJS collects completely anonymous data about usage.                                                                                                      11:21:11  
  This will help us improving Nuxt developer experience over the time.  
  Read more on https://git.io/nuxt-telemetry  

? Are you interested in participation? Yes ← Yes/Noで選択
```

起動のたびに聞かれるのが煩わしいので設定で無効化しようと、変数をPlaybookに組み込みましたが、以下のエラーが出て失敗しました。

```
$ ansible-bender build simple-test/build_nuxtimage.yml   
There was an error during execution: variable /target_image/environment/NUXT_TELEMETRY_DISABLED is set to 1, which is not of type string  
```

詳細な原因が分からないですが、環境変数がString型でないからエラーになっているかと思われます。`NUXT_TELEMETRY_DISABLED`はBoolean型ですが、Boolean型の環境変数をPlaybookに加える方法がわかりませんでした。
こちらの対処法ですが、Nuxt.jsの公式リファレンスで紹介されていた方法で`app/nuxt.config.js`に設定を追記しました。

```javascript:nuxt.config.js
export default {  
~~~~~末尾追加~~~~~  
  telemetry: false  
}
```
②noneイメージが溜まり続ける
Playbook実行直後にイメージリストを調べると以下のように中間層であるnoneイメージがたくさん溜まってきます。

```
$ podman images  
REPOSITORY                     TAG        IMAGE ID       CREATED       SIZE  
localhost/yuta28/bender-test   latest     3b568629853d   2 hours ago   1.41 GB  
<none>                         <none>     30f38e724bd8   2 hours ago   1.41 GB  
<none>                         <none>     2c3401135802   2 hours ago   1.41 GB  
<none>                         <none>     ab36ebfbcb82   2 hours ago   1.41 GB  
<none>                         <none>     0c15be92f176   2 hours ago   992 MB  
<none>                         <none>     0f4f0464e1e9   2 hours ago   992 MB  
<none>                         <none>     f19ac9e8da69   2 hours ago   991 MB  
localhost/node-python3         latest     73bb42b858aa   5 days ago    991 MB  
localhost/debian-python3       buster     af956b4f0450   8 days ago    177 MB
```

サイズも1GBと大きく溜まり続けるとローカルマシンに負担がかかるので作成後に手動で`podman image prune`で削除していますが、Dockerfileのビルド時には出てこなかった事象でPlaybook内でどのような設定を加えたらいいのかわかりませんでした。
# 所感
上の課題にも挙げられているようにDockerfileからビルドする時と比較しますといくつか問題点が生じ、コンテナイメージのビルドもAnsibleのDockerモジュールから実行した方が体感的には早く感じました。Dockerfileを書く必要がなく、Playbook内だけでコンテナイメージビルドを完結すことができるのはメリットだと思いましたが、やっぱりDockerfileを書いてそこからビルド指定したほうが良い気がしてきました。
そもそもAnsibleでコンテナ管理を行うメリットがあまり思い浮かばず、docker-composeやKubernetesでコンテナ管理を行った方がトレンドにも乗っており、実務で使うには少し心もとない感じがしました。
ansible-benderでコンテナ管理を行うメリットについてわかる方がいましたらコメント欄にてご指摘お願いいたします。