BETA

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

投稿日:2019-10-23
最終更新:2019-11-22

構成管理ツール (いわゆる 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 '[email protected],' 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 '[email protected],' -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 に更新。
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@advanceboy の技術ブログ 本サイトは https://aquasoftware.net/blog/ こちら。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう