【Ansible AWX】インストール

## まとめ

- Ansible AWX をインストールする。
- 面倒臭いため全ての操作を root ユーザで行っています。良くないことはわかっています。ごめんなさい。

## 検証用ホストの調達

AWS にて、デフォルト VPC のデフォルトサブネット内にインスタンス（ホスト名は `awx` とする）を作成する。

## Ansible AWX の導入

公式のインストールガイドに従ってインストールする。
https://github.com/ansible/awx/blob/devel/INSTALL.md

### Docker のインストール

`Docker` と `docker-py` が必要らしい。

まずは `docker-ce` をインストールしようと思ったところ...

```
[root@awx ~]# yum search docker-ce
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-
                   : motd
警告: 一致するものが見つかりません: docker-ce
No matches found
```

yum リポジトリがない模様。

Docker 公式を参考に yum リポジトリに追加する。

https://docs.docker.com/install/linux/docker-ce/centos/#install-using-the-repository

```
[root@awx ~]# yum-config-manager \
> --add-repo https://download.docker.com/linux/centos/docker-ce.repo
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-
                   : motd
adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
grabbing file https://download.docker.com/linux/centos/docker-ce.repo to /etc/yum.repos.d/docker-ce.repo
repo saved to /etc/yum.repos.d/docker-ce.repo
[root@awx ~]#
```

再度 `yum search` で確認したところ、無事追加されたようです。

```
[root@awx ~]# yum search docker-ce
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-
                   : motd
==================== N/S matched: docker-ce ====================
docker-ce.x86_64 : The open-source application container engine
docker-ce-selinux.noarch : SELinux Policies for the open-source
                         : application container engine

  Name and summary matches only, use "search all" for everything.
[root@awx ~]# 
```

ようやくインストール。

```
[root@awx ~]# yum install docker-ce
```

`docker-py` とやらもインストールしておく。

`pip` が入っていなかったので、こいつを入れた上で `pip install docker-py` します。

```
[root@awx ~]# easy_install pip
```

```
[root@awx ~]# pip install docker-py
```

### Ansible のインストール

ついでにこのタイミングで `Ansible` もインストールする（ `Ansible AWX` のインストールに使うので）。

```
[root@awx ~]# pip install ansible
```

### Ansible AWX のインストール

それっぽいディレクトリに公式リポジトリを `git clone` する。

```
[root@awx local]# git clone https://github.com/ansible/awx.git
```

`docker` を起動して、いよいよ `Ansible AWX` のインストールです。


```
[root@awx local]# cd /usr/local/awx/installer
[root@awx installer]# ansible-playbook --inventory inventory install.yml 

...
(省略)
...

TASK [local_docker : Activate AWX Web Container] *********************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Error creating container: unhashable type: 'list'"}
	to retry, use: --limit @/usr/local/awx/installer/install.retry

PLAY RECAP ***********************************************************************************************************************
localhost                  : ok=10   changed=0    unreachable=0    failed=1  
```

まじかよ。



```
unhashable type: 'list'
```

とは、 python の辞書のキーに配列を渡してることに起因しているらしい。

失敗したタスクを確認してみる。

https://github.com/ansible/awx/blob/devel/installer/roles/local_docker/tasks/standalone.yml

```
- name: Activate AWX Web Container
  docker_container:
    name: awx_web
    state: started
    restart_policy: unless-stopped
    image: "{{ awx_web_docker_actual_image }}"
    volumes:
      - "{{ project_data_dir + ':/var/lib/awx/projects:rw' if project_data_dir is defined else [] }}"
      - "{{ ca_trust_dir + ':/etc/pki/ca-trust/source/anchors:ro' if ca_trust_dir is defined else [] }}"
    user: root
    ports:
      - "{{ host_port }}:8052"
    links: "{{ awx_web_container_links|list }}"
    hostname: "{{ awx_web_hostname }}"
    dns_search_domains: "{{ awx_container_search_domains.split(',') if awx_container_search_domains is defined else omit }}"
    dns_servers: "{{ awx_alternate_dns_servers.split(',') if awx_alternate_dns_servers is defined else omit }}"
    env:
      http_proxy: "{{ http_proxy | default('') }}"
      https_proxy: "{{ https_proxy | default('') }}"
      no_proxy: "{{ no_proxy | default('') }}"
      SECRET_KEY: "{{ secret_key }}"
      DATABASE_NAME: "{{ pg_database }}"
      DATABASE_USER: "{{ pg_username }}"
      DATABASE_PASSWORD: "{{ pg_password }}"
      DATABASE_PORT: "{{ pg_port }}"
      DATABASE_HOST: "{{ pg_hostname_actual }}"
      RABBITMQ_USER: "{{ rabbitmq_default_username }}"
      RABBITMQ_PASSWORD: "{{ rabbitmq_default_password }}"
      RABBITMQ_HOST: "rabbitmq"
      RABBITMQ_PORT: "{{ rabbitmq_port }}"
      RABBITMQ_VHOST: "{{ rabbitmq_default_vhost }}"
      MEMCACHED_HOST: "memcached"
      MEMCACHED_PORT: "11211"
      AWX_ADMIN_USER: "{{ default_admin_user|default('admin') }}"
      AWX_ADMIN_PASSWORD: "{{ default_admin_password|default('password') }}"
  register: awx_web_container
```

`docker_container` モジュールで失敗しているようですが、

```
 volumes:
       - "{{ project_data_dir + ':/var/lib/awx/projects:rw' if project_data_dir is defined else [] }}"
       - "{{ ca_trust_dir + ':/etc/pki/ca-trust/source/anchors:ro' if ca_trust_dir is defined else [] }}"
```
の部分が非常に怪しい。

変数が定義されている場合は文字列を、定義されていない場合は空の配列を返すらしい。えぇ...

`-vvv` オプションをつけて再度実行してみましょう。

```
...
（省略）
...
TASK [local_docker : Activate AWX Web Container] *********************************************************************************
task path: /usr/local/awx/installer/roles/local_docker/tasks/standalone.yml:79
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root
<localhost> EXEC /bin/sh -c 'echo ~root && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830 `" && echo ansible-tmp-1539493799.13-47679237691830="` echo /root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830 `" ) && sleep 0'
Using module file /usr/lib/python2.7/site-packages/ansible/modules/cloud/docker/docker_container.py
<localhost> PUT /root/.ansible/tmp/ansible-local-4387s0rrtI/tmpo9Accv TO /root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py
<localhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/ /root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/env python /root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py", line 113, in <module>
    _ansiballz_main()
  File "/root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py", line 105, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py", line 48, in invoke_module
    imp.load_module('__main__', mod, module, MOD_DESC)
  File "/tmp/ansible_docker_container_payload_YPzsaG/__main__.py", line 2177, in <module>
  File "/tmp/ansible_docker_container_payload_YPzsaG/__main__.py", line 2172, in main
  File "/tmp/ansible_docker_container_payload_YPzsaG/__main__.py", line 1772, in __init__
  File "/tmp/ansible_docker_container_payload_YPzsaG/__main__.py", line 1801, in present
  File "/tmp/ansible_docker_container_payload_YPzsaG/__main__.py", line 1245, in has_different_configuration
  File "/tmp/ansible_docker_container_payload_YPzsaG/__main__.py", line 1672, in _get_expected_volumes
TypeError: unhashable type: 'list'

fatal: [localhost]: FAILED! => {
    "changed": false, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py\", line 113, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py\", line 105, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1539493799.13-47679237691830/AnsiballZ_docker_container.py\", line 48, in invoke_module\n    imp.load_module('__main__', mod, module, MOD_DESC)\n  File \"/tmp/ansible_docker_container_payload_YPzsaG/__main__.py\", line 2177, in <module>\n  File \"/tmp/ansible_docker_container_payload_YPzsaG/__main__.py\", line 2172, in main\n  File \"/tmp/ansible_docker_container_payload_YPzsaG/__main__.py\", line 1772, in __init__\n  File \"/tmp/ansible_docker_container_payload_YPzsaG/__main__.py\", line 1801, in present\n  File \"/tmp/ansible_docker_container_payload_YPzsaG/__main__.py\", line 1245, in has_different_configuration\n  File \"/tmp/ansible_docker_container_payload_YPzsaG/__main__.py\", line 1672, in _get_expected_volumes\nTypeError: unhashable type: 'list'\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", 
    "rc": 1
}
```

公式ソースで `_get_expected_volumes` を検索してみたところ、多分[このへん](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/docker/docker_container.py#L1884)が原因です。


`/path/to/awx/installer/inventory` の変数定義をアンコメントする必要があるようです。

```
# AWX project data folder. If you need access to the location where AWX stores the projects
# it manages from the docker host, you can set this to turn it into a volume for the container.

## ★ここをアンコメント★
project_data_dir=/var/lib/awx/projects
## ★ここをアンコメント★

# CA Trust directory. If you need to provide custom CA certificates, supplying
# this variable causes this directory on the host to be bind mounted over
# /etc/pki/ca-trust in the awx_task and awx_web containers.
# NOTE: only obeyed in local_docker install

## ★ここをアンコメント★
ca_trust_dir=/etc/pki/ca-trust
## ★ここをアンコメント★
```

再度 `ansible-playbook` を実行して、今度は成功。

http://xx.xx.xx.xx （ `xx.xx.xx.xx` はグローバルIP）にアクセスしてみると...

![awx.png](https://qiita-image-store.s3.amazonaws.com/0/288168/b8d98c0c-7a76-4357-83b3-02890aacc3da.png)

とりあえずインストールは完了です。