2019-11-22に更新

プロキシ環境下で Ansible AWX をインストールする Playbook を作る

読了目安:11分

構成管理ツール (いわゆる Infrastructure as Code(IaC)) の中でも比較的易しいと言われる Ansible。
その Ansible の運用を省力化してくれるツールとして Ansible Tower というものがあり、そのコミュニティ管理版 (OSS版) が AWX だ。

その AWX がインストールされたサーバを作成する Playbook を作ろうとしたら意外と手間取ったので、そのメモ。

なお、インストール先は CentOS 7 である。
AWX のインストールは、 公式ビルド済み docker イメージを Docker Compose を使ってコンテナ化する方法 を使った。

11/21: 追記

docker-compose の PyPI (pip) パッケージが 1.25.0 に更新後、 CentOS 7 (正確には、 Python 2 系を使っている環境) で docker-compose のインストールや実行がうまくいかなくなった。
(commit:719a1b0 で) subprocess32 に依存するようになり、 gcc 等のビルド環境のインストールが別途必要になったためだ。

それに加え、たとえ gcc をインストールしても、依存パッケージの python2 対応が不十分なようで、 docker-compose 実行時にエラーになってしまう。

docker/compose#7030 の Issue には挙がっているが、修正されるかどうか不明なため、一つ前の 1.24.1 を使うことで回避するように、コードを修正した。

python2 ツラい。。。
しかし、 2020年にサポート切れになった後も、 CentOS 7 が生きているしばらくの間はこの辛みと付き合わなければならないようだ。
ツラい。。。

追記ここまで

最終的な Playbook

まず、最終的に作成した playbook を掲載する。

AWX を docker-compose と 公式ビルド済みコンテナ を使ってインストールする Ansible Playbook

---
- hosts: all
  vars:
    venv_dir: /tmp/venv-awx
    awx_repo: https://github.com/ansible/awx.git
    awx_repo_dir: /tmp/awx
    awx_version: 9.0.1
    postgres_data_dir: /tmp/pgdocker
    #http_proxy: http://proxy.example.com
    #https_proxy: http://proxy.example.com
    #no_proxy: localhost,127.0.0.1,company.example.com
    envs:
      http_proxy: "{{ http_proxy | default(ansible_env.http_proxy | default('')) }}"
      https_proxy: "{{ https_proxy | default(ansible_env.https_proxy | default('')) }}"
      no_proxy: "{{ no_proxy | default(ansible_env.no_proxy | default('localhost,127.0.0.1')) }}"
  tasks:
    - environment: "{{envs}}"
      block:
        - name: update pre-setting packages
          become: yes
          yum:
            name:
              - epel-release
              - yum-utils
            state: latest

        - name: add docker-ce repo
          become: yes
          shell: "yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo"
          args:
            chdir: /etc/yum.repos.d
            creates: docker-ce.repo

        - name: remove conflict packages
          become: yes
          yum:
            name:
              # https://docs.docker.com/install/linux/docker-ce/centos/#uninstall-old-versions
              - docker
              - docker-engine
              - docker-compose
            state: absent

        - name: ensure packages
          become: yes
          yum:
            name:
              - git
              - python-pip
              - python-virtualenv
              - docker-ce
            state: latest
          register: result_pagkages

        # to abort "Cannot uninstall 'requests'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall." error
        # using virtualenv to avoid the conflict of python packages between yum and pip when installing docker-compose
        #  -> https://github.com/docker/compose/issues/5883
        # 
        # to abort "Unable to load docker-compose. Try `pip install docker-compose`." error
        # https://www.uramiraikan.net/Works/entry-3362.html
        # 
        # create virtualenv with --system-site-packages option
        # to make use of any libraries installed in the system’s Python
        # https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#running-in-a-virtualenv
        # 
        # to avoid docker/compose#7030 issue, fix docker-compose package version to 1.24.1
        # https://github.com/docker/compose/issues/7030
        - name: ensure pip packages
          pip:
            name:
              - ansible
              - docker-compose==1.24.1
            state: present
            virtualenv: "{{ venv_dir }}"
            virtualenv_site_packages: yes

        - block:
            - name: create docker proxy settings directory
              become: yes
              file:
                path: /etc/systemd/system/docker.service.d
                state: directory
                owner: root
                group: root
                mode: u=rwx,g=rx,o=rx
            - name: create docker proxy settings
              become: yes
              copy:
                dest: /etc/systemd/system/docker.service.d/http-proxy.conf
                content: |
                  [Service]
                  Environment="HTTP_PROXY={{ envs.http_proxy }}" "HTTPS_PROXY={{ envs.https_proxy }}" "NO_PROXY={{ envs.no_proxy }}"
              register: result_proxy
          when: envs.http_proxy or envs.https_proxy

        - block:
            - name: get the current groups
              command: groups
              changed_when: false
              register: original_groups

            - name: ensure the connecting user is member of docker group
              become: yes
              user:
                name: "{{ ansible_env.USER }}"
                groups: docker
                append: yes
              register: result_user

            # reset ssh connection for refletction the changed groups
            # (`meta: reset_connection` occurs an error on current ansible version)
            # https://stackoverflow.com/a/28213378
            - name: kill open ssh sessions for force reconnection
              shell: sleep 1; pkill -u {{ ansible_env.USER }} sshd
              async: 3
              poll: 2
              when: result_user is changed
          when: ansible_env.USER != 'root'

        - name: enable and restart docker service
          become: yes
          systemd:
            name: docker
            state: restarted
            enabled: yes
            daemon_reload: yes
          when: result_pagkages is changed or result_user is changed or result_proxy is changed

        - name: git clone awx
          git:
            repo: '{{awx_repo}}'
            dest: '{{awx_repo_dir}}'
            version: '{{awx_version}}'
            clone: yes
            force: yes

        - name: record playbook options
          set_fact:
            install_awx_playbook_opotions: >-
              -e postgres_data_dir={{ postgres_data_dir }}
              {% if envs.http_proxy or envs.https_proxy %}
              -e http_proxy={{ envs.http_proxy }} -e https_proxy={{ envs.https_proxy }} -e no_proxy={{ envs.no_proxy }}
              {% endif %}

        - name: execute awx playbook
          shell: "source {{ venv_dir+'/bin/activate' | quote }}; ansible-playbook -i inventory install.yml {{ install_awx_playbook_opotions }}"
          args:
            chdir: '{{awx_repo_dir}}/installer'
          tags:
            - inner-playbook

        - name: ensure the connecting user is not member of docker group
          become: yes
          user:
            name: "{{ ansible_env.USER }}"
            groups: "{{ original_groups.stdout.split(' ') | join(',') }}"
            append: no
          when: result_user is changed
          tags:
            - revert-docker-group

How to use

Directory

$ ansible-playbook -i 'username@hostname,' install_awx.yml

Uses inventory file

Create the following inventory file:

hostname

[all:vars]
ansible_ssh_user=username

#http_proxy=http://proxy:3128
#https_proxy=http://proxy:3128
#no_proxy=mycorp.org

and run below

$ ansible-playbook -i inventory install_awx.yml

解説

一番大きなポイントは、 Python の Virtualenv を作成し、そこに pip 版 docker-compose を入れて動かしていることだ。

docker-compose Python 依存パッケージの競合

AWX を docker-compose でインストールするには、 docker-compose Python モジュールのインストールが必要だ。
しかし、 OS のパッケージマネージャ (yum) でインストールしているモジュールと、 上記 docker-compose Python module で、一部の依存パッケージが競合していたりすると、 pip で global な環境に docker-compose pip モジュールをインストールしようとした時に、

"Cannot uninstall 'requests'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall." 

などというようなエラーが発生してしまうことがある。

このため、 Virtualenv 環境を作成して、そこに docker-compose をインストールする形としている。

yum でインストールしたモジュールへの依存

一方、 逆に パッケージマネージャ (yum) でインストールしているモジュールそのものに依存している場合、 単に Virtualenv を作成しただけだと、モジュール不足で動かなくなることがある。
このため、 Ansible の pip モジュール を使うときは、 virtualenv_site_packages: yes を設定している。

"source {{ venv_dir+'/bin/activate' | quote }}; ansible-playbook -i inventory install.yml {{ install_awx_playbook_opotions }}"

最後に playbook 内で AWX のインストール playbook を実行する際は、上記のように Virtualenv を activate した状態で実行している。

docker グループへの追加

ssh でログインしたユーザが docker コンテナの操作を行えるようにするため、 一時的に docker グループへ追加している。

グループを変更しても、 ホストマシンにログインし直さないと、設定が反映されず docker を操作することができない。
本来であれば、 Ansible の meta モジュールの reset_connection のアクションで ssh でのサインインが自動的にやり直されるはずなのだが、何故かうまく動かない。

このため、 ホストPC の sshd デーモンを非同期で Kill させることで、強制的に ssh の再接続を行っている。

プロキシの解決

プロキシの設定は、以下の両方が必要だ。

  • docker イメージをダウンロードするための、コンテナホストへの設定
  • docker コンテナ内への設定

playbook 実行時のオプションとして ansible-playbook -i 'username@hostname,' -e http_proxy=http://proxy.example.com -e http_proxy=http://proxy.example.com install_awx.yml のように設定できるほか、
その設定を省略した場合でも、 コンテナホストマシンの http_proxy 環境変数などが設定されていれば、それを使うようにしている。

参考

更新履歴

  • 2019-10-23: 初版
  • 2019-11-22: CentOS 7 と docker-compose==1.25.0 の組み合わせで発生する不具合を回避。 AWX のバージョンを 9.0.1 に更新。
Originally published at aquasoftware.net
ツイッターでシェア
みんなに共有、忘れないようにメモ

advanceboy

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント