dockerfileとdocker-composeでAnsible実行環境構築

# はじめに
色んな方が公開してくださっているdocker-compose.ymlの内容を修正して使用させてもらっているが、自分でDockerfile、docker-compose.ymlの内容を最初から作成したことがありませんでした。そのためDockerの勉強として試行錯誤してAnsibleの実行環境を用意したので備忘として記載します。

# 目標
- Dockerfile, docker-compose.ymlを自分で最初から作成する。勉強目的なので、そこまで内容は凝らない
- Dockerで作成するのはAnsible実行環境とする（自分の知識の中でちょうどいいのがこれだった）
- 実行時の手間を削減するためAnsible実行時はコンテナ内に入らず`docker exec`で実行できるようにする

# ディレクトリ構成

```
.
`-- ansible
    |-- Dockerfile
    |-- ansible_dir
    |   |-- conf
    |   |   |-- ansible.cfg
    |   |   |-- ansible.cfg.org
    |   |   `-- hosts
    |   `-- playbook
    |       `-- ping.yml
    `-- docker-compose.yml
```

# Dockerfile
```Dockerfile:Dockerfile
FROM alpine
RUN apk --update-cache --no-cache add py3-pip openssh sshpass \
 && pip3 install ansible
```
OSは軽量コンテナイメージのAlpine Linux。
Ansibleはpipでインストールする。そのためpipのインストール、Ansible実行時に表示されたエラーメッセージを元に`openssh`と`sshpass`をインストール。
ちなみに`--no-cache`オプション付与の有無で2MB程度の差が出た。
`--update-cache`は`apk update`を行うオプション[らしい](https://blog.kasei-san.com/entry/2020/08/25/084430)。裏取りしてないので本当か不明。

https://blog.kasei-san.com/entry/2020/08/25/084430


# docker-compose.yml

```yml:docker-compose.yml
version: '3'

services:
  ansible:
    build:
      context: .
      dockerfile: Dockerfile
    image: ansible:latest
    container_name: ansible
    environment:
      - ANSIBLE_CONFIG=/opt/conf/ansible.cfg
    volumes:
      - ./ansible_dir:/opt/
    working_dir: /opt/playbook
    command: tail -f /dev/null
```
環境変数`ANSIBLE_CONFIG`でansible.cfgのパスを指定し、ansible.cfgを編集することでパラメータを設定。
ansible.cfgやインベントリファイル（hosts）、playbookを都度コンテナに入って編集するのは面倒なので、ホストの`ansible_dir`配下に格納して置き、バインドマウントでコンテナに入らず修正できるようにしておく。
`working_dir`をplaybook格納先にしておくことで、`./ansible_dir/playbook/`配下で`ansible-playbook`を実行しようとした際に、playbook指定が楽になる。
`command`はコンテナが停止してしまうことの抑止。Dockerfileに記載してもいいかも。

# Ansible実行例
## テストファイル
### インベントリファイル
```:hosts
[test]
192.168.1.8

[all:vars]
ansible_user=<user name>
ansible_password=<password>
```

### Ansible設定
```:ansible.cfg
[defaults]
inventory      = /opt/conf/hosts
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
```
毎回インベントリファイル指定は面倒なので指定しておく。
初回アクセス時やサーバ作り直ししたとき、フィンガープリント関係でエラーになるのが面倒なので以下を参考にsshのオプション設定。

https://qiita.com/taka379sy/items/331a294d67e02e18d68d#%E5%9B%9E%E9%81%BF%E6%96%B9%E6%B3%95

`ControlMaster`、`ControlPersist`は有効にしておくとAnsibleの処理速度が速くなるらしいので参考にしたオプションをそのまま流用。

https://blog.mosuke.tech/entry/2015/12/01/181304/

### テスト用playbook
あくまでテスト用なのでpingモジュールだけ。
```yml:ping.yml
- hosts: all
  gather_facts: true

  tasks:
    - name: ping test
      ping:
```

## テスト
`ansible`と`ansible-playbook`コマンド両方を実行。どちらもピンポンできること確認できました。

```bash

[root@docker ansible]# docker exec -it ansible ansible 192.168.1.8 -m ping
192.168.1.8 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

[root@docker playbook]# docker exec -it ansible ansible-playbook -l 192.168.1.8 ping.yml

PLAY [all] **************************************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [192.168.1.8]

TASK [ping test] ********************************************************************************************************************************************
ok: [192.168.1.8]

PLAY RECAP **************************************************************************************************************************************************
192.168.1.8                : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

# おわりに
簡単なものですが、Dockerfileとdocker-compose.ymlファイルの作成が出来ました。
今回Alpine LinuxでAnsibleのコンテナイメージを作成してサイズは457MBだったのですが、以下のリポジトリを見ると367MBまで縮小できるようですね。pipでインストールしたので、pip周りのオプションの見直しやコンパイルしてインストールする方法などをすれば小さくできるのでしょうか。

https://hub.docker.com/r/alpine/ansible

そこまでコンテナイメージのダイエットに今興味があるわけではないので、いったん今回はこれで終了にします。