Dockerfileを使わずにAnsibleのPlaybookだけでコンテナイメージを作りたい！

## 概要

コンテナイメージをPodman（ポストDocker?）とAnsible Benderを使って、Dockerfileを使わずに作る方法について記載。
Podmanに関しては事前知識、設定など使えるようになるまでに幾つかのハマりどころがあったので、本題とは異なる内容（ただし知っていたほうが良さそうなこと）もそれなりに書いている。

## 記事の背景

二年ほど前から、興味半分でDockerとAnsibleを使ってコンテナを作り始めた。我流なのでもっと良い方法はあると思うが、以下のような感じでコンテナ構築を行うことが多かった。

* (1) Dockerfileに以下の内容を記載
    * Ansibleをインストール
    * AnsibleのPlaybook関連ファイル一式をコンテナにコピー
    * ``ansible-playbook``のコマンドを書いてイメージ構築
    * コンテナ起動時に実行するエントリーポイントのシェルスクリプト（``entrypoint.sh``などの名前のファイル）をコンテナにコピー
* (2) Dockerイメージの作成
    * ``docker build ...``を実行 
* (3) Dockerコンテナを起動
    * ``docker run ...``を実行
    * エントリーポイントのシェルスクリプトの中で、``ansible-playbook``を実行

ソースコードで表現するとおおよそ以下のイメージ。
（正確さに欠けるのはお許しを）

* Dockerイメージ作成時のソース

```Dockefile:Dockerfile
FROM ubuntu

# Ansibleをインストール
RUN apt -y update && \
    apt -y install ansible

# AnsibleのPlaybook関連ファイル一式をコンテナにコピー
ADD ./playbook /

# ansible-playbookのコマンドを書いてイメージ構築
WORKDIR playbook
RUN ansible-playbook setup.yml

# エントリーポイントのシェルスクリプトをコンテナにコピー
WORKDIR /
ADD ./entrypoint.sh /
```

* コンテナ起動時のソース

```shell:entrypoint.sh
#!/bin/bash
cd playbook
ansible-playbook entrypoint.yml
```

ソースを見るとおわかりの通り、ほとんどAnsibleに関するコマンドしか実行していない。Dockerfileとエントリーポイント用シェルスクリプト``entrypoint.sh``は、Dockerコンテナを作るために不可避なファイルで（言い方は悪いが）仕方なく使っているようにだんだん思えてきた。そして「**Dockerfileもエントリーポイント用シェルスクリプトも使わずに、全てAnsibleだけでDockerコンテナを作れたらいいのに**」という気持ちが強まってきていた。

## Ansible Containerでのつまづき

そんな思いの中、Ansibleに関する書籍を読んでいたところ、「Ansible ContainerでPlaybookのみでコンテナを作成可能」であることを偶然知る。「これこそ探していたツール。早速使ってみよう！」と思ってググりながらセットアップ開始。
ツールが使えるようになるまで２度３度ハマり、ようやく「``ansible-container build``」コマンドでDockerイメージを作れる状態にまで持ってこれた。（Ansible Containerのセットアップ方法を書くとかなり脱線してしまうのでここでは割愛）
が、実際にイメージをビルドしようとすると、一通りPlaybookを実行し終えた後、最後の方で下記のようなエラーが出てきてイメージが作成されない…。

```
docker.errors.APIError: 400 Client Error: Bad Request ("file with no instructions.")
```

これに関して調査すると、海外のQAサイトで「Ansible Containerは非推奨になったみたいだよ」というコメントを見つける。日本語サイトでも下記のような関連記事があり、代わりに「Ansible Bender」を使うほうが良さそうだと判明。

* Ansibleを使ってdockerコンテナを立ち上げる
    * https://note.com/tar28/n/n2479ab960187

前置きが長くなったが、このような経緯を踏まえて、Ansible Benderを使えるようにする手順を以下に示す。

## 手順

* 主に参考にしたサイトは下記のAnsible Benderの公式サイト
    * https://ansible-community.github.io/ansible-bender/build/html/installation.html

公式サイトにも書かれているように、Ansible Benderを動かすためにはPodmanとBuildahが必要になる。そこで、まずはPodmanとBuildahのインストール方法とやっておいた方が良い設定について記載する。Ansible Benderのインストール方法が出てくるのは本節の最後になるが、しばしお付き合いいただきたい。

### (1) Podmanのインストール

Ansible Benderが利用するのはDockerではなく、Podmanと呼ばれる新たなコンテナ構築ツール。PodmanはDockerとほぼ同じ使い方ができて、デーモンレス（サービス起動が不要）かつルートレス（ルート権限が不要）でイメージやコンテナが構築できる。
以下サイトにディストリビューション毎のインストール方法が書かれおり、記載内容に従えばインストール可能。

* Podman Installation Instructions
    * https://podman.io/getting-started/installation

### (2) Buildahのインストール

BuildahもPodmanと同様に、デーモンレスでコンテナイメージを作成できるツール。（詳細をまだ理解できていないので、必要に応じて追記予定）
インストール方法は下記サイトに記載。

* GitHubのBuildahのリポジトリ
    * https://github.com/containers/buildah/blob/master/install.md

### (3) Podmanに関する設定

#### ユーザIDマッピングの設定

Podman（およびAnsible Bender）をユーザ権限でルートレスで使いたい場合は、以下のような「UID mapping」と呼ばれる設定が必要。

```
$ sudo sh -c "printf \"\n$(whoami):100000:65536\n\" >>/etc/subuid"
```

この設定を行うと、例えばPodmanコンテナ内の「rootのユーザID=0」はホストOS（コンテナ外）では100000プラスされて、「コンテナのrootユーザーID=100000」にマッピングされる。したがって、ホストOSのroot権限を持っていなくてもコンテナ操作ができる。

#### Podmanイメージがストレージを大量に使ってしまう問題への対処

Podmanのイメージやコンテナは、ホームディレクトリの``~/.local/share/containers/storage/``以下に格納される。デフォルトだと、イメージを作り直す度にイメージのファイル一式が（差分ではなく素のままで）追加されていき、ストレージを大量に使ってしまう問題がある。（筆者の環境でも、最初はイメージをビルドする度にストレージの残りがどんどん減っていったので焦った…）
この問題を解決するため、ルートレスコンテナ向けのストレージドライバであるFUSE-OverlayFSをインストールして、Podmanの設定ファイルに反映する。

FUSE-OverlayFSは下記サイトからインストール可能

* GitHubのfuse-overlayfsのリポジトリ
    * https://github.com/containers/fuse-overlayfs

上記サイトREADMEで最後の方に記載された下記コマンドで、fuse-overlayfsのバイナリファイルができる。

```
$ buildah bud -v $PWD:/build/fuse-overlayfs -t fuse-overlayfs -f ./Dockerfile.static.ubuntu .
```

インストール後、``~/.config/containers/storage.conf``に以下を記載すればOK。以後はストレージが食いつぶされることは無くなるはず。

```cfg:~/.config/containers/storage.conf
[storage]
driver = "overlay"

[storage.options]
mount_program = "/usr/bin/fuse-overlayfs"
```

* この問題に関する参考サイト
    * [コンテナ仮想、その裏側 〜user namespaceとrootlessコンテナ〜](https://tech.retrieva.jp/entry/2019/06/04/130134#podman%E3%81%AE%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E7%AE%A1%E7%90%86-fuse-overlayfs)

### (4) AnsibleおよびAnsible Benderのインストール

（ようやく本題のAnsible Benderのインストール方法の説明ができる。）

前提として、**Ansible BenderはPython3.6以上でないと動かない**。そこでまずはAnsibleのコントロールノードにPython3.6以上をインストールする必要あり。
続いて、バージョンが3.6以上のPython環境（仮想環境でも非仮想環境でもいい）にAnsibleとAnsible Benderをpip(3)コマンドでイントールする。

```
$ pip3 install ansible
$ pip3 install ansible-bender
```

上の例では、Ansibleをpip3のコマンドでインストールしているが、既にインストールしているAnsibleを使いたい場合は、Playbookのymlファイル（後ほど作成）を格納するディレクトリに``ansible.cfg``を作り、次のような設定を追記すればAnsibleがPython3を参照するようになるはず。

```ini:ansible.cfg
[defaults]
# python3のバイナリが格納されたパスを追加
interpreter_python=/usr/bin/python3
```

これで、Ansible Benderを使う準備が整った

## 動作サンプル

とりあえずは適当に空のディレクトリを作って

```
$ ansible-bender init
```

と入力すると、雛形のファイル``playbook.yml``ができる。
これ以下のように編集すると、CentOS7ベースのhttpdコンテナイメージが作れる。

```yml:playbook.yml
---
- name: Containerized version of httpd
  hosts: all
  vars:
    ansible_bender:
      base_image: centos:7
      target_image:
        cmd: /usr/sbin/httpd -D FOREGROUND
        name: httpd
  tasks:
  - name: install dependencies needed to run project httpd
    yum:
      name: httpd
      state: present
```

ポイントは、playbookのvarsセクションの変数``ansible_bender``。ここに、作成するコンテナに関する情報を書く。``ansible_bender``配下で使用可能な変数一覧は下記に記載されている。

* Anible BenderサイトのConfiguration
    * https://ansible-community.github.io/ansible-bender/build/html/configuration.html

上記のplaybookを下記のようにビルドして、Podmanイメージを作成

```
$ ansible-bender build playbook.yml
22:46:53.683 utils.py          ERROR  Getting image source signatures
22:46:54.479 utils.py          ERROR  Copying blob sha256:75f8…
(筆者の環境では幾つかエラーメッセージが出るが、原因は未解明）

PLAY [Containerized version of httpd] ******************************************

TASK [Gathering Facts] *********************************************************
ok: [httpd-20200814-224645662649-cont]

TASK [install dependencies needed to run project httpd] ************************
changed: [httpd-20200814-224645662649-cont]

PLAY RECAP *********************************************************************
httpd-20200814-224645662649-cont : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Getting image source signatures
Copying blob sha256:613b…
Copying blob sha256:7d45…
Copying config sha256:e05f…
Writing manifest to image destination
Storing signatures
e05f…
Image 'httpd' was built successfully \o/
```

httpdのイメージが出来ていることを確認

```
$ podman images
REPOSITORY       TAG     IMAGE ID      CREATED         SIZE
localhost/httpd  latest  e05f33470bcd  11 minutes ago  332 MB
```

コンテナを起動

```
$ podman run --name httpd -p 10080:80 -dit httpd
```

http://localhost:10080/ でApacheのテストページが表示されることを確認。

## 未解決の問題

イメージを作成する際にDockerfileをAnsible Playbookで書けるようにはなった！
が、コンテナ起動時のエントリーポイントコマンドをターゲットノードからAnsibleで実行する方法はまだわかっていないので、引き続き調査予定。