2022年12月3日土曜日

オフライン環境のPythonの仮想環境にAnsibleコントロールノードを構築する

Ansibleはエージェントレスでさまざまな機器の設定を行ことができる非常に強力なツールである。通常、Ansibleコントロールノードを構築する際には、pipを用いてインターネット環境よりインストールする必要があり、インターネットに接続できない環境での利用が難しいという問題がある。
※このようなインターネットと物理的に接続をさせない環境をエアギャップ環境と呼ぶ。

このような環境の場合は、別のインターネットに接続できる環境であらかじめダウンロードしたファイルを持ち込み適用することが一般的である。セキュリティパッチのファイルやウィルス対策ソフトのパターンファイルなども同様の手法で適用することが多い。

AnsibleはPythonをベースに動作するソフトウェアであるため、Pythonの機能を利用することで、エアギャップ環境でのインストールは可能である。

今回は、事前にインターネットに接続できる環境にてAnsibleコントロールノードに必要となるPythonパッケージをダウンロードしておき、それをオフライン環境のLinuxサーバに配置し、Pythonの仮想環境を用いてインストールする方法を紹介する。

以下に作業の概要図を記載する。

環境

今回の動作検証はAlmaLinux 8に対して実施した。ただし、Red Hat系のディストリビューションであるCentOSやRocky Linuxなどでも同様の手順で実施できるだろう。

  • OS : AlmaLinux 8.6
  • Python : 3.8.12
  • Ansible : ansible [core 2.13.5]

AnsibleのPythonパッケージをダウンロード

1. Python 3.8をインストール

AlmaLinux 8.xなどのRed Hat 8.x系のOSでは、Pythonをインストールしても通常3.6がインストールされる。Python 3.6の場合は、Ansible 2.11までであれば動作させることはできるが以下問題がある。

  • Ansible実行時に毎回[DEPRECATION WARNING]の警告メッセージが表示される
  • Ansible 2.11は2022年11月でメンテナンス終了となる

そのため、今回はPython 3.8もインストールし、Ansible 2.13を利用できるようにする。Python 3.8はdnfを使ってインストールできる。

# dnf install python38 -y
メタデータの期限切れの最終確認: 4:28:17 時間前の 2022年10月29日 17時49分52秒 に 実施しました。
依存関係が解決しました。
================================================================================
 パッケージ        Arch   バージョン                            Repo      サイズ
================================================================================
インストール:
 python38          x86_64 3.8.12-1.module_el8.6.0+2778+cd494b30 appstream  78 k
依存関係のインストール:
 python38-libs     x86_64 3.8.12-1.module_el8.6.0+2778+cd494b30 appstream 8.3 M
 python38-pip-wheel
                   noarch 19.3.1-5.module_el8.6.0+2778+cd494b30 appstream 1.0 M
 python38-setuptools-wheel
                   noarch 41.6.0-5.module_el8.6.0+2778+cd494b30 appstream 303 k
弱い依存関係のインストール:
 python38-pip      noarch 19.3.1-5.module_el8.6.0+2778+cd494b30 appstream 1.8 M
 python38-setuptools
                   noarch 41.6.0-5.module_el8.6.0+2778+cd494b30 appstream 667 k
モジュールストリームの有効化中:
 python38                 3.8

~(中略)~

インストール済み:
  python38-3.8.12-1.module_el8.6.0+2778+cd494b30.x86_64
  python38-libs-3.8.12-1.module_el8.6.0+2778+cd494b30.x86_64
  python38-pip-19.3.1-5.module_el8.6.0+2778+cd494b30.noarch
  python38-pip-wheel-19.3.1-5.module_el8.6.0+2778+cd494b30.noarch
  python38-setuptools-41.6.0-5.module_el8.6.0+2778+cd494b30.noarch
  python38-setuptools-wheel-41.6.0-5.module_el8.6.0+2778+cd494b30.noarch

完了しました!

2. pipをバージョンアップ

ダウンロードやインストール作業で失敗することがあるため、pipを最新版にしておく。

# pip-3.8 install -U pip
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip-3.8 install --user` instead.
Collecting pip
  Downloading https://files.pythonhosted.org/packages/47/ef/8b5470b5b94b36231ed9c0bde90caa71c0d4322d4a15f009b2b7f4287fe0/pip-22.3-py3-none-any.whl (2.1MB)
     |????????????????????????????????| 2.1MB 10.9MB/s
Installing collected packages: pip
Successfully installed pip-22.3

3. Ansibleコントロールノード構築用のPythonパッケージをダウンロード

ダウンロードしたファイルを保管するディレクトリを以下の通り作成する。

# mkdir ansible-pack

Pythonパッケージはpip downloadコマンドを実行することでダウンロードできる。今回は以下3点のパッケージをダウンロードする。

  • pip
  • ansible
  • pywinrm

以下に実行結果を記載する。

pip

# pip-3.8 download -d ./ansible-pack pip
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Collecting pip
  Using cached pip-22.3-py3-none-any.whl (2.1 MB)
Saved ./ansible-pack/pip-22.3-py3-none-any.whl
Successfully downloaded pip

ansible

# pip-3.8 download -d ./ansible-pack ansible
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Collecting ansible
  Downloading ansible-6.5.0-py3-none-any.whl (44.9 MB)
     qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq 44.9/44.9 MB 30.7 MB/s eta 0:00:00
Collecting ansible-core~=2.13.5
  Downloading ansible_core-2.13.5-py3-none-any.whl (2.1 MB)
     qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq 2.1/2.1 MB 28.6 MB/s eta 0:00:00

~(中略)~

Saved ./ansible-pack/pycparser-2.21-py2.py3-none-any.whl
Successfully downloaded ansible ansible-core jinja2 PyYAML resolvelib cryptography packaging cffi MarkupSafe pyparsing pycparser

pywinrm

# pip-3.8 download -d ./ansible-pack pywinrm
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Collecting pywinrm
  Using cached pywinrm-0.4.3-py2.py3-none-any.whl (44 kB)

~(中略)~

Saved ./ansible-pack/urllib3-1.26.12-py2.py3-none-any.whl
Successfully downloaded pywinrm requests requests-ntlm six xmltodict certifi charset-normalizer cryptography idna ntlm-auth urllib3 cffi pycparser

ダウンロードしたファイルは合計53MBとなった。

# du -h ansible-pack/
53M     ansible-pack/

4. ダウンロードしたPythonパッケージを圧縮して持ち出し

持ち出ししやすいようtarを使ってパッケージを圧縮する。

# tar zcvf ansible-pack.tgz ansible-pack/
ansible-pack/
ansible-pack/pip-22.3-py3-none-any.whl
ansible-pack/ansible-6.5.0-py3-none-any.whl
ansible-pack/ansible_core-2.13.5-py3-none-any.whl
ansible-pack/Jinja2-3.1.2-py3-none-any.whl
ansible-pack/PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl
ansible-pack/resolvelib-0.8.1-py2.py3-none-any.whl
ansible-pack/cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl
ansible-pack/packaging-21.3-py3-none-any.whl
ansible-pack/cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
ansible-pack/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
ansible-pack/pyparsing-3.0.9-py3-none-any.whl
ansible-pack/pycparser-2.21-py2.py3-none-any.whl
ansible-pack/pywinrm-0.4.3-py2.py3-none-any.whl
ansible-pack/requests-2.28.1-py3-none-any.whl
ansible-pack/requests_ntlm-1.1.0-py2.py3-none-any.whl
ansible-pack/six-1.16.0-py2.py3-none-any.whl
ansible-pack/xmltodict-0.13.0-py2.py3-none-any.whl
ansible-pack/certifi-2022.9.24-py3-none-any.whl
ansible-pack/charset_normalizer-2.1.1-py3-none-any.whl
ansible-pack/idna-3.4-py3-none-any.whl
ansible-pack/ntlm_auth-1.5.0-py2.py3-none-any.whl
ansible-pack/urllib3-1.26.12-py2.py3-none-any.whl

圧縮したファイルを任意の手法で持ち出しを行い、オフライン環境のLinuxサーバに配置しておこう。

ダウンロードしたPythonパッケージをPythonの仮想環境にインストール

続けて、ダウンロードしたPythonパッケージをPythonの仮想環境にインストールする手順を説明する。

1. 前提パッケージ確認

Ansible 2.13を動作させるためには、最低限以下2つのパッケージインストールが必要となる。

  • Python 3.8
  • sshpass

Python 3.8は前述の通り、Ansible の動作条件として必要となる。sshpassは、AnsibleからLinuxなどの機器に対してパスワード認証方式を用いてSSH接続する際に必要となる。

# dnf install --disablerepo=dvd* python38 -y
# dnf install --disablerepo=dvd* sshpass -y

2. ダウンロードしたファイルを展開

インターネット環境からダウンロードしたファイルを展開する。

# tar zxvf ansible-pack.tgz
ansible-pack/
ansible-pack/pip-22.3-py3-none-any.whl
ansible-pack/ansible-6.5.0-py3-none-any.whl
ansible-pack/ansible_core-2.13.5-py3-none-any.whl
ansible-pack/Jinja2-3.1.2-py3-none-any.whl
ansible-pack/PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl
ansible-pack/resolvelib-0.8.1-py2.py3-none-any.whl
ansible-pack/cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl
ansible-pack/packaging-21.3-py3-none-any.whl
ansible-pack/cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
ansible-pack/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
ansible-pack/pyparsing-3.0.9-py3-none-any.whl
ansible-pack/pycparser-2.21-py2.py3-none-any.whl
ansible-pack/pywinrm-0.4.3-py2.py3-none-any.whl
ansible-pack/requests-2.28.1-py3-none-any.whl
ansible-pack/requests_ntlm-1.1.0-py2.py3-none-any.whl
ansible-pack/six-1.16.0-py2.py3-none-any.whl
ansible-pack/xmltodict-0.13.0-py2.py3-none-any.whl
ansible-pack/certifi-2022.9.24-py3-none-any.whl
ansible-pack/charset_normalizer-2.1.1-py3-none-any.whl
ansible-pack/idna-3.4-py3-none-any.whl
ansible-pack/ntlm_auth-1.5.0-py2.py3-none-any.whl
ansible-pack/urllib3-1.26.12-py2.py3-none-any.whl

3. Pythonの仮想環境を作成

Pythonの仮想環境は、python -m venv [インストールディレクトリ名]とすることですぐに作成できる。

今回はディレクトリ名を.venvとし、Python 3.8を利用する必要があるので、python3.8コマンドを使って以下通りコマンドを実行した。

# python3.8 -m venv .venv
# ls -l .venv/
合計 4
drwxr-xr-x 2 root root 210 10月 29 22:15 bin
drwxr-xr-x 2 root root   6 10月 29 22:15 include
drwxr-xr-x 3 root root  23 10月 29 22:15 lib
lrwxrwxrwx 1 root root   3 10月 29 22:15 lib64 -> lib
-rw-r--r-- 1 root root  70 10月 29 22:15 pyvenv.cfg

4. Pythonの仮想環境を有効化

Pythonの仮想環境を有効化する際には、source [インストールディレクトリ]/bin/activateを実行する。実行すると、プロンプトに(.venv)が表示される。

[root@locahost ~]# source .venv/bin/activate
(.venv) [root@locahost ~]#

以降はPythonの仮想環境での作業については、(.venv)#でプロンプトを表記する。

5. Pythonの仮想環境にPythonパッケージをインストール

Pythonの仮想環境にて、ダウンロードしておいた以下のPythonパッケージをインストールしていく。

  • pip
  • ansible
  • pywinrm

通常のPythonパッケージと同様、pip installでインストールするが、以下オプションを付与する点に注意する。

オプション 説明
--no-index インターネット環境のパッケージインデックス(PyPI; Python Package Index)の検索を行わない。
--find-links=[パッケージディレクトリ] パッケージを検索するローカルディレクトリを指定する。

pip

(.venv)# pip install --no-index --find-links=./ansible-pack/ -U pip
Looking in links: ./ansible-pack/
Processing ./ansible-pack/pip-22.3-py3-none-any.whl
Installing collected packages: pip
  Found existing installation: pip 19.3.1
    Uninstalling pip-19.3.1:
      Successfully uninstalled pip-19.3.1
Successfully installed pip-22.3

ansible

(.venv)# pip install --no-index --find-links=./ansible-pack/ ansible
Looking in links: ./ansible-pack/
Processing ./ansible-pack/ansible-6.5.0-py3-none-any.whl
Processing ./ansible-pack/ansible_core-2.13.5-py3-none-any.whl
Processing ./ansible-pack/resolvelib-0.8.1-py2.py3-none-any.whl
Processing ./ansible-pack/cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl
Processing ./ansible-pack/PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Processing ./ansible-pack/Jinja2-3.1.2-py3-none-any.whl
Processing ./ansible-pack/packaging-21.3-py3-none-any.whl
Processing ./ansible-pack/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Processing ./ansible-pack/cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Processing ./ansible-pack/pyparsing-3.0.9-py3-none-any.whl
Processing ./ansible-pack/pycparser-2.21-py2.py3-none-any.whl
Installing collected packages: resolvelib, PyYAML, pyparsing, pycparser, MarkupSafe, packaging, jinja2, cffi, cryptography, ansible-core, ansible
Successfully installed MarkupSafe-2.1.1 PyYAML-6.0 ansible-6.5.0 ansible-core-2.13.5 cffi-1.15.1 cryptography-38.0.1 jinja2-3.1.2 packaging-21.3 pycparser-2.21 pyparsing-3.0.9 resolvelib-0.8.1

pywinrm

(.venv)# pip install --no-index --find-links=./ansible-pack/ pywinrm
Looking in links: ./ansible-pack/
Processing ./ansible-pack/pywinrm-0.4.3-py2.py3-none-any.whl
Processing ./ansible-pack/requests_ntlm-1.1.0-py2.py3-none-any.whl
Processing ./ansible-pack/six-1.16.0-py2.py3-none-any.whl
Processing ./ansible-pack/requests-2.28.1-py3-none-any.whl
Processing ./ansible-pack/xmltodict-0.13.0-py2.py3-none-any.whl
Processing ./ansible-pack/urllib3-1.26.12-py2.py3-none-any.whl
Processing ./ansible-pack/idna-3.4-py3-none-any.whl
Processing ./ansible-pack/certifi-2022.9.24-py3-none-any.whl
Processing ./ansible-pack/charset_normalizer-2.1.1-py3-none-any.whl
Processing ./ansible-pack/ntlm_auth-1.5.0-py2.py3-none-any.whl
Requirement already satisfied: cryptography>=1.3 in ./.venv/lib/python3.8/site-packages (from requests-ntlm>=1.1.0->pywinrm) (38.0.1)
Requirement already satisfied: cffi>=1.12 in ./.venv/lib/python3.8/site-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->pywinrm) (1.15.1)
Requirement already satisfied: pycparser in ./.venv/lib/python3.8/site-packages (from cffi>=1.12->cryptography>=1.3->requests-ntlm>=1.1.0->pywinrm) (2.21)
Installing collected packages: xmltodict, urllib3, six, ntlm-auth, idna, charset-normalizer, certifi, requests, requests-ntlm, pywinrm
Successfully installed certifi-2022.9.24 charset-normalizer-2.1.1 idna-3.4 ntlm-auth-1.5.0 pywinrm-0.4.3 requests-2.28.1 requests-ntlm-1.1.0 six-1.16.0 urllib3-1.26.12 xmltodict-0.13.0

6. Pythonパッケージインストール後の確認

インストール後、pip listでパッケージが表示されることを確認しておこう。

(.venv)# pip list
Package            Version
------------------ ---------
ansible            6.5.0
ansible-core       2.13.5
certifi            2022.9.24
cffi               1.15.1
charset-normalizer 2.1.1
cryptography       38.0.1
idna               3.4
Jinja2             3.1.2
MarkupSafe         2.1.1
ntlm-auth          1.5.0
packaging          21.3
pip                22.3
pycparser          2.21
pyparsing          3.0.9
pywinrm            0.4.3
PyYAML             6.0
requests           2.28.1
requests-ntlm      1.1.0
resolvelib         0.8.1
setuptools         41.6.0
six                1.16.0
urllib3            1.26.12
xmltodict          0.13.0

Ansibleも問題なく2.13.5がインストールできていることが確認できる。

(.venv)# ansible --version
ansible [core 2.13.5]
  config file = /root/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /root/.venv/lib64/python3.8/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /root/.venv/bin/ansible
  python version = 3.8.12 (default, Apr 21 2022, 07:55:08) [GCC 8.5.0 20210514 (Red Hat 8.5.0-10)]
  jinja version = 3.1.2
  libyaml = True

なお、Pythonの仮想環境の場合、ansibleansible-playbookの配置場所は、インストールディレクトリは以下の/binとなる。環境変数でパスが設定されているため、ここに配置されたコマンドは特にPythonの仮想環境であることを意識することなくコマンド実行することができる。

(.venv)# which ansible
/root/.venv/bin/ansible
(.venv)# which ansible-playbook
/root/.venv/bin/ansible-playbook

Pythonの仮想環境にてAnsibleコントロールノードから処理を実行

1. ansibleコマンドでpingモジュールを実行

それでは実際にAnsibleを実行してみよう。まずはLinuxサーバに対して定番のpingモジュールを実行してみる。

Ansible実行前にインベントリファイルとしてhostsというファイル名でファイルを作成しておこう。

hosts

(.venv)# cat hosts
[linux_servers]
t1193alma ansible_host=192.168.11.193

[windows_servers]
t1194w219 ansible_host=192.168.11.194

[windows_servers:vars]
ansible_user=ansibleuser
ansible_password=[ansibleuserのパスワード]
ansible_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore

ansibleコマンドにてpingモジュールを実行してみると、問題なく"ping": "pong"と表示された。

(.venv)# ansible -i hosts -u ansibleuser -m ping t1193alma --ask-pass
SSH password:
t1193alma | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

2. ansibleコマンドでwin_pingモジュールを実行

同様に、win_pingモジュールを実行してみると、こちらも問題なく"ping": "pong"と表示された。

(.venv)# ansible -i hosts -m win_ping t1194w219
t1194w219 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

3. ansible-playbookコマンドでPlaybookを実行

最後にansible-playbookでPlaybookを実行してみよう。Playbookの内容としては、以下のようなwin_pingするだけの簡単なものを作成した。

ping_windows_server.yml

# cat ping_windows_server.yml
---
- name: Setup windows server
  gather_facts: false
  hosts: windows_servers

  tasks:
    - name: Ping windows server
      ansible.windows.win_ping:

    - name: Debug
      ansible.builtin.debug:
        msg: "{{ inventory_hostname }}"

実行結果は以下の通り。問題なく処理が実行できた。

(.venv)# ansible-playbook -i hosts -l t1194w219 ping_windows_server.yml

PLAY [Setup windows server] ****************************************************

TASK [Ping windows server] *****************************************************
ok: [t1194w219]

TASK [Debug] *******************************************************************
ok: [t1194w219] => {
    "msg": "t1194w219"
}

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

Pythonの仮想環境のAnsibleコントロールノードをアンインストール

1. Pythonの仮想環境を無効化

Pythonの仮想環境を無効化する場合は、deactivateコマンドを用いる。実行すると、プロンプトが通常の状態に戻る。

(.venv) [root@localhost ~]# deactivate
[root@localhost ~]#

2. Pythonの仮想環境をアンインストール

Pythonの仮想環境のアンインストールは、単純に作成したディレクトリ(今回の場合であれば.venv)を削除すればよい。

# rm -rf .venv/

以上で、オフライン環境のPythonの仮想環境にAnsibleコントロールノードを構築する手順は完了となる。

Pythonの仮想環境を用いることで、既存の環境への影響を極力抑えることができ、不要となればディレクトリを削除するだけで済む。そのため、一時的にAnsibleで処理を実行したい時などに利用することができる。

参考

0 件のコメント:

コメントを投稿

人気の投稿