ラベル Ansible の投稿を表示しています。 すべての投稿を表示
ラベル Ansible の投稿を表示しています。 すべての投稿を表示
2024年10月19日土曜日

Ansible core 2.17にしたらRHEL 8系でdnfモジュール実行できなくなった話

先日、Ansibleコントロールノードを10.4.0 (Ansible core 2.17)にバージョンアップした。

RHEL 8系のOSに対してdnfモジュールを用いて操作をしようとしたところ、以下のようなエラーが発生し実行に失敗してしまった。

TASK [Update gitlab packages] *******************************************************************************************************************************************************************
{
  "changed": false,
  "module_stderr": "Shared connection to 192.168.11.26 closed.\r\n",
  "module_stdout": "Traceback (most recent call last):\r\n  File \"<stdin>\", 
  line 12, in <module>\r\n  File \"<frozen importlib._bootstrap>\", 
  line 971, in _find_and_load\r\n  File \"<frozen importlib._bootstrap>\", 
  line 951, in _find_and_load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", 
  line 894, in _find_spec\r\n  File \"<frozen importlib._bootstrap_external>\", 
  line 1157, in find_spec\r\n  File \"<frozen importlib._bootstrap_external>\", 
  line 1131, in _get_spec\r\n  File \"<frozen importlib._bootstrap_external>\", 
  line 1112, in _legacy_get_spec\r\n  File \"<frozen importlib._bootstrap>\", 
  line 441, in spec_from_loader\r\n  File \"<frozen importlib._bootstrap_external>\", 
  line 544, in spec_from_file_location\r\n  File \"/tmp/ansible_ansible.legacy.dnf_payload_25y3u3vr/ansible_ansible.legacy.dnf_payload.zip/ansible/module_utils/basic.py\", line 5\r\nSyntaxError: future feature annotations is not defined\r\n",
  "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
  "rc": 1
}

RHEL 8のOSにPython 3.12を個別にdnfでインストールして、ansible_python_interpreterでPythonのパスを指定しても、dnfモジュールはうまく動作してくれないので、いろいろ調べたところ、どうやらこれは、管理ノード側のPythonが古い(Python 3.7未満)ことによる「仕様」であり解決できないようだ。

以下に本情報が記載されている、GitHubのIssueを日本語訳したものを引用する。

ansible-core 2.17 は、ターゲット実行に Python 3.7 以降のみをサポートしています。それより低いバージョンの Python を使用しているシステムはサポートされていません。これらのシステムでは、より新しいバージョンの Python をインストールできる可能性がありますが、パッケージ マネージャーなどのシステム タスク用のパッケージが不足しているため、すべてのモジュールが機能しない可能性があります。
そのコメントに従って、よりフレンドリーなエラーのために機能を人為的に制限することは、長期的には価値がないと判断しました。
SyntaxError: future feature annotations is not defined #82068

残念ながら、自宅はまだまだRHEL 8系OSが現役なので、この仕様は非常に困る。本事象の回避のため、AnsibleコントロールノードをAnsible 10.x (Ansible core 2.17) → Ansible 9.x (Ansible core 2.16) にダウングレードすることにした。

Ansibleダウングレード手順

1. インストール可能なAnsibleのバージョン確認

まずは、インストール可能なAnsibleのバージョンを確認する。存在しないバージョンとして0を指定することで、エラー表示にてインストール可能なバージョンが表示される。

# python3 -m pip install --upgrade ansible=='0'
ERROR: Ignored the following yanked versions: 9.0.0, 9.5.0, 9.6.0, 10.0.0
ERROR: Could not find a version that satisfies the requirement ansible==0 (from versions: 1.0, 1.1, 1.2, 1.2.1, 1.2.2, 1.2.3, 1.3.0, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.5, 1.5.1, 1.5.2, 1.5.3, 1.5.4, 1.5.5, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.7, 1.7.1, 1.7.2, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.9.0.1, 1.9.1, 1.9.2, 1.9.3, 1.9.4, 1.9.5, 1.9.6, 2.0.0.0, 2.0.0.1, 2.0.0.2, 2.0.1.0, 2.0.2.0, 2.1.0.0, 2.1.1.0, 2.1.2.0, 2.1.3.0, 2.1.4.0, 2.1.5.0, 2.1.6.0, 2.2.0.0, 2.2.1.0, 2.2.2.0, 2.2.3.0, 2.3.0.0, 2.3.1.0, 2.3.2.0, 2.3.3.0, 2.4.0.0, 2.4.1.0, 2.4.2.0, 2.4.3.0, 2.4.4.0, 2.4.5.0, 2.4.6.0, 2.5.0a1, 2.5.0b1, 2.5.0b2, 2.5.0rc1, 2.5.0rc2, 2.5.0rc3, 2.5.0, 2.5.1, 2.5.2, 2.5.3, 2.5.4, 2.5.5, 2.5.6, 2.5.7, 2.5.8, 2.5.9, 2.5.10, 2.5.11, 2.5.12, 2.5.13, 2.5.14, 2.5.15, 2.6.0a1, 2.6.0a2, 2.6.0rc1, 2.6.0rc2, 2.6.0rc3, 2.6.0rc4, 2.6.0rc5, 2.6.0, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.6.6, 2.6.7, 2.6.8, 2.6.9, 2.6.10, 2.6.11, 2.6.12, 2.6.13, 2.6.14, 2.6.15, 2.6.16, 2.6.17, 2.6.18, 2.6.19, 2.6.20, 2.7.0.dev0, 2.7.0a1, 2.7.0b1, 2.7.0rc1, 2.7.0rc2, 2.7.0rc3, 2.7.0rc4, 2.7.0, 2.7.1, 2.7.2, 2.7.3, 2.7.4, 2.7.5, 2.7.6, 2.7.7, 2.7.8, 2.7.9, 2.7.10, 2.7.11, 2.7.12, 2.7.13, 2.7.14, 2.7.15, 2.7.16, 2.7.17, 2.7.18, 2.8.0a1, 2.8.0b1, 2.8.0rc1, 2.8.0rc2, 2.8.0rc3, 2.8.0, 2.8.1, 2.8.2, 2.8.3, 2.8.4, 2.8.5, 2.8.6, 2.8.7, 2.8.8, 2.8.9, 2.8.10, 2.8.11, 2.8.12, 2.8.13, 2.8.14, 2.8.15, 2.8.16rc1, 2.8.16, 2.8.17rc1, 2.8.17, 2.8.18rc1, 2.8.18, 2.8.19rc1, 2.8.19, 2.8.20rc1, 2.8.20, 2.9.0b1, 2.9.0rc1, 2.9.0rc2, 2.9.0rc3, 2.9.0rc4, 2.9.0rc5, 2.9.0, 2.9.1, 2.9.2, 2.9.3, 2.9.4, 2.9.5, 2.9.6, 2.9.7, 2.9.8, 2.9.9, 2.9.10, 2.9.11, 2.9.12, 2.9.13, 2.9.14rc1, 2.9.14, 2.9.15rc1, 2.9.15, 2.9.16rc1, 2.9.16, 2.9.17rc1, 2.9.17, 2.9.18rc1, 2.9.18, 2.9.19rc1, 2.9.19, 2.9.20rc1, 2.9.20, 2.9.21rc1, 2.9.21, 2.9.22rc1, 2.9.22, 2.9.23rc1, 2.9.23, 2.9.24rc1, 2.9.24, 2.9.25rc1, 2.9.25, 2.9.26rc1, 2.9.26, 2.9.27rc1, 2.9.27, 2.10.0a1, 2.10.0a2, 2.10.0a3, 2.10.0a4, 2.10.0a5, 2.10.0a6, 2.10.0a7, 2.10.0a8, 2.10.0a9, 2.10.0b1, 2.10.0b2, 2.10.0rc1, 2.10.0, 2.10.1, 2.10.2, 2.10.3, 2.10.4, 2.10.5, 2.10.6, 2.10.7, 3.0.0b1, 3.0.0rc1, 3.0.0, 3.1.0, 3.2.0, 3.3.0, 3.4.0, 4.0.0a1, 4.0.0a2, 4.0.0a3, 4.0.0a4, 4.0.0b1, 4.0.0b2, 4.0.0rc1, 4.0.0, 4.1.0, 4.2.0, 4.3.0, 4.4.0, 4.5.0, 4.6.0, 4.7.0, 4.8.0, 4.9.0, 4.10.0, 5.0.0a1, 5.0.0a2, 5.0.0a3, 5.0.0b1, 5.0.0b2, 5.0.0rc1, 5.0.1, 5.1.0, 5.2.0, 5.3.0, 5.4.0, 5.5.0, 5.6.0, 5.7.0, 5.7.1, 5.8.0, 5.9.0, 5.10.0, 6.0.0a1, 6.0.0a2, 6.0.0a3, 6.0.0b1, 6.0.0b2, 6.0.0rc1, 6.0.0, 6.1.0, 6.2.0, 6.3.0, 6.4.0, 6.5.0, 6.6.0, 6.7.0, 7.0.0a1, 7.0.0a2, 7.0.0b1, 7.0.0rc1, 7.0.0, 7.1.0, 7.2.0, 7.3.0, 7.4.0, 7.5.0, 7.6.0, 7.7.0, 8.0.0a1, 8.0.0a2, 8.0.0a3, 8.0.0b1, 8.0.0rc1, 8.0.0, 8.1.0, 8.2.0, 8.3.0, 8.4.0, 8.5.0, 8.6.0, 8.6.1, 8.7.0, 9.0.0a1, 9.0.0a2, 9.0.0a3, 9.0.0b1, 9.0.0rc1, 9.0.1, 9.1.0, 9.2.0, 9.3.0, 9.4.0, 9.5.1, 9.6.1, 9.7.0, 9.8.0, 9.9.0, 9.10.0, 9.11.0, 10.0.0a1, 10.0.0a2, 10.0.0a3, 10.0.0b1, 10.0.0rc1, 10.0.1, 10.1.0, 10.2.0, 10.3.0, 10.4.0, 10.5.0, 11.0.0a1)
ERROR: No matching distribution found for ansible==0

2. Ansible 9.11.0をインストール

Ansible 9.xの最新バージョンとなるAnsible 9.11.0をインストールする。

# python3 -m pip install ansible=='9.11.0'

Ansibleのバージョンを確認するとAnsible core 2.16.12になっていることがわかる。

# ansible --version
ansible [core 2.16.12]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.12/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.12.3 (main, Jul  2 2024, 16:34:01) [GCC 8.5.0 20210514 (Red Hat 8.5.0-22)] (/usr/bin/python3)
  jinja version = 3.1.4
  libyaml = True

3. 動作確認

あらためて、dnfモジュールを含めたPlaybookを実行すると、以下の通り成功した。

TASK [Update gitlab packages] **************************************************************************************************************************************************************************************
changed: [t1026gitl]

以上で、Ansibleのダウングレード手順は完了となる。今後は、RHEL 8系のサーバーを徐々にRHEL 9系に移行し、Ansibleの最新バージョンが利用できるよう環境を整えていきたい。

2024年9月22日日曜日

RHEL系のOSに導入したAnsibleをバージョンアップする手順

自宅ではAnsibleを用いて各種作業の自動化を進めている。Ansibleのインストール手順は以下に記載している。

上記は、2022年7月にインストールした記事であり、Ansibleのバージョンが6.0.0とかなり古くなっていた。

Ansibleのバージョン情報は、以下URLから確認することができ、Ansible core 2.13に該当するAnsible 6.xはすでにUnmaintained (end of life)となっている。

本記事では、RHEL系のOSに導入したAnsibleをバージョンアップする手順を記載する。

環境

コントロールノードとなるOSは、AlmaLinuxを用いる。Red Hat系のディストリビューションであるRHELやRocky Linuxなどでも同様の手順で作業ができるだろう。

  • OS : AlmaLinux 8.5
  • Python : 3.8 → 3.9
  • Ansible : 6.0.0 → 8.1.0
また、以下バージョンにおいても同様の手順で実施できることを確認している。
  • OS : AlmaLinux 8.10
  • Python : 3.9 → 3.12
  • Ansible : 8.3.0 → 10.4.0

Ansibleバージョンアップ手順

1. pipを最新バージョンに更新

Ansibleインストール前に、pip自体を最新化する。

# python3 -m pip install --upgrade pip

2. インストール可能なAnsibleのバージョンを確認

pipを使ってインストール可能なバージョンを確認してみる。pip install [インストールモジュール名]=='0'のように存在しないバージョンを記載すると、インストール可能なバージョンの一覧が表示される。

以下の通り、Python 3.8の場合は、Ansible 6.xまでしか表示されない。そのため、以降の手順でPython 3.9のインストールを行うことにする。

# python3 -m pip install --upgrade ansible=='0'
~(中略)~
ansible== (from versions: (中略) 6.0.0, 6.1.0, 6.2.0,
6.3.0, 6.4.0, 6.5.0, 6.6.0, 6.7.0)
ERROR: No matching distribution found for ansible==0

3. Pythonをインストール

Ansibleを利用する場合、Pythonも対応するバージョンを使用する必要がある。例えば、最近のバージョンでは以下のバージョン制約がある。

Ansible Python
10.x 3.10-3.12
9.x 3.10-3.12
8.x 3.9-3.11
7.x 3.9-3.11
6.x 3.8-3.10

Ansibleのバージョン8.1.0の場合は、Python 3.9及びpipをインストールする。

# dnf install python39-pip -y

4. 通常使用するPythonの入れ替え

通常利用するPythonをPython変更するため、alternativesを使って、以下の通り設定する。以下はPython 3.6をPython 3.9に変更する手順となる。

# ls -l /usr/bin/python3*
lrwxrwxrwx 1 root root   25  7月  1  2022 /usr/bin/python3 -> /etc/alternatives/python3
lrwxrwxrwx 1 root root   31 10月 10  2021 /usr/bin/python3.6 -> /usr/libexec/platform-python3.6
lrwxrwxrwx 1 root root   32 10月 10  2021 /usr/bin/python3.6m -> /usr/libexec/platform-python3.6m
-rwxr-xr-x 1 root root 7760  4月 21  2022 /usr/bin/python3.8
-rwxr-xr-x 1 root root 7768  4月  4 01:41 /usr/bin/python3.9

# alternatives --list
libnssckbi.so.x86_64    auto    /usr/lib64/pkcs11/p11-kit-trust.so
python                  auto    /usr/libexec/no-python
ifup                    auto    /usr/libexec/nm-ifup
cifs-idmap-plugin       auto    /usr/lib64/cifs-utils/cifs_idmap_sss.so
python3                 manual  /usr/bin/python3.8
libwbclient.so.0.15-64  auto    /usr/lib64/samba/wbclient/libwbclient.so.0.15

# alternatives --set python3 /usr/bin/python3.9

# alternatives --list
libnssckbi.so.x86_64    auto    /usr/lib64/pkcs11/p11-kit-trust.so
python                  auto    /usr/libexec/no-python
ifup                    auto    /usr/libexec/nm-ifup
cifs-idmap-plugin       auto    /usr/lib64/cifs-utils/cifs_idmap_sss.so
python3                 manual  /usr/bin/python3.9
libwbclient.so.0.15-64  auto    /usr/lib64/samba/wbclient/libwbclient.so.0.15

5. 再度、インストール可能なAnsibleのバージョンを確認

再度、pipを最新化したのち、インストール可能なAnsibleのバージョンを確認する。今度は、8.xのバージョンがインストール可能となっていることがわかる。

# python3 -m pip install --upgrade pip

# python3 -m pip install --upgrade ansible=='0'
~(中略)~
ansible== (from versions: (中略) 6.0.0, 6.1.0, 6.2.0,
6.3.0, 6.4.0, 6.5.0, 6.6.0, 6.7.0, 7.0.0a1, 7.0.0a2,
7.0.0b1, 7.0.0rc1, 7.0.0, 7.1.0, 7.2.0, 7.3.0, 7.4.0,
7.5.0, 7.6.0, 7.7.0, 8.0.0a1, 8.0.0a2, 8.0.0a3,
8.0.0b1, 8.0.0rc1, 8.0.0, 8.1.0)
ERROR: No matching distribution found for ansible==0

6. Ansibleバージョンアップ

Pythonをインストールしたら、インストール対象のAnsibleのバージョン(今回は8.1.0)を指定して、以下の通りAnsibleをインストールする。

# python3 -m pip install ansible=='8.1.0'

問題なくインストールされたことを以下コマンドで確認する。Ansible 8.1.0がインストールされ、ansibleコマンドの表示バージョンがansible core 2.15となっていることがわかる。

# pip3 list
Package             Version
------------------- -------
ansible             8.1.0
ansible-core        2.15.1
cffi                1.15.1
cryptography        41.0.1
importlib-resources 5.0.7
Jinja2              3.1.2
MarkupSafe          2.1.3
packaging           23.1
pip                 20.2.4
pycparser           2.21
PyYAML              6.0
resolvelib          1.0.1
setuptools          50.3.2

# ansible --version
ansible [core 2.15.1]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.9.16 (main, Apr  3 2023, 12:39:37) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18)] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

7. Ansible関連のPythonモジュールを追加

最後に、Ansibleを実行する際に必要となるPythonモジュールをインストールする。

私の環境の場合は以下をインストールした。

モジュール 用途
ansible-lint Ansible Lint(構文チェックツール)本体。
jmespath Ansible Lintの構文チェックで利用されるモジュール。
pywinrm Windows操作用モジュール。
pyvmomi vSphere操作用モジュール。
zabbix-api Zabbix操作用モジュール。
passlib パスワードハッシュ用モジュール。
# python3 -m pip install ansible-lint jmespath
# python3 -m pip install pywinrm pyvmomi zabbix-api passlib

以上で、RHEL系のOSに導入したAnsibleのバージョンアップ手順は完了となる。

Ansible 8.1.0へバージョンアップ後も、ほとんどのPlaybookはそのまま実行させることができたが、Zabbix操作のPlaybookは接続時の指定方法が変更となっており、そのままでは実行できなかった。Ansibleを用いたZabbixの操作方法の最新手順は、以下別記事に記載する。

また、Ansible core 2.17以降は、ターゲット側にPython 3.7以降のインストールが必須となっているため注意すること(それ以前はPython 2.7の実行が可能だった)。

更新履歴

  • 2023/7/1 新規作成
  • 2024/9/22 汎用的に使用できるよう、記載を修正
2024年6月22日土曜日

Zabbix 7.0をAnsibleで操作するためのcommunity.zabbixモジュール最新化手順

2024年6月4日にリリースされたZabbix 7.0をAnsibleで操作しようとしたところ、以下のようなInvalid paramsのエラーで失敗した。

failed: [t1023zabi] (item={'name': 't3051kube', 'groups': 'Linux servers', 'type': 'agent', 'ip': '192.168.1.1', 
'templates': ['Linux by Zabbix agent']}) => {"ansible_loop_var": "item", "changed": false, "item": {"groups": "Linux servers", 
"ip": "192.168.1.1", "name": "t3051kube", "templates": ["Linux by Zabbix agent"], "type": "agent"}, 
"msg": "connection error occurred: REST API returned {'code': -32602, 'message': 'Invalid params.', 
'data': 'Invalid parameter \"/1/groups/1\": unexpected parameter \"name\".'} 
when sending {\"jsonrpc\": \"2.0\", \"method\": \"host.create\", \"id\": \"706afb46-c4e4-461b-83e9-6e798396b800\", \"params\": {\"host\": \"t3051kube\", 
\"interfaces\": [{\"type\": 1, \"main\": 1, \"useip\": 1, \"ip\": \"192.168.1.1\", \"details\": {}, \"dns\": \"\", \"port\": \"10050\"}], 
\"groups\": [{\"groupid\": \"2\", \"name\": \"Linux servers\", \"flags\": \"0\", 
\"uuid\": \"dc579cd7a1a34222933f24f52a68bcd8\"}], \"status\": 0}, 
\"auth\": \"7922aa3xxxx556615xxxx0344xxxx2fea\"}"}

原因はAnsibleのcommunity.zabbixモジュールが古く、Zabbix 7.0にサポートしていなかったことが原因だった。本記事では、Zabbix 7.0をAnsibleで操作するために必要となるcommunity.zabbixモジュールの最新化手順を記載する。

環境

今回手順確認を行った環境の情報は以下の通り。

  • OS : AlmaLinux 8.5
  • Ansible : 8.1.0 (Ansible core 2.15.3)
  • community.zabbix : 2.1.0 → 3.0.0

community.zabbixモジュールの最新化手順

1. Ansible Galaxyにてモジュールを確認

community.zabbixモジュールは以下URLからバージョン情報を確認することができる。

また、変更内容はGitHubのChangelogから直接確認する。

以下の通り、v3.0.0においてZabbix 7.0で各種モジュールが動作するよう改修がされていることが確認できる。

zabbix_discovery_rule, zabbix_group_events_info, zabbix_host, zabbix_host_events_info, 
zabbix_proxy, zabbix_proxy_info modules updated to work wih Zabbix 7.0

2. 現在のバージョン確認

現在インストールされているcommunity.zabbixモジュールのバージョンを確認する。

# ansible-galaxy collection list community.zabbix
実行結果------------------------------
# /usr/local/lib/python3.9/site-packages/ansible_collections
Collection       Version
---------------- -------
community.zabbix 2.1.0  

3. Ansible Galaxyより最新版モジュールをインストール

以下コマンドで最新版モジュールをインストールする。なお、すでにモジュールがインストールされている場合インストールが実行されない (All requested collections are already installed.と表示されインストールが実施されない) ため、--forceオプションまたはバージョンを指定することでインストールを続行するようにしている。

# ansible-galaxy collection install community.zabbix --force
または
# ansible-galaxy collection install community.zabbix:3.0.0

4. インストール後のバージョン確認

インストール後のcommunity.zabbixモジュールのバージョンを確認する。

# ansible-galaxy collection list community.zabbix
実行結果------------------------------
# /root/.ansible/collections/ansible_collections
Collection       Version
---------------- -------
community.zabbix 3.0.0  

# /usr/local/lib/python3.9/site-packages/ansible_collections
Collection       Version
---------------- -------
community.zabbix 2.1.0  

5. Ansibleを実行

最後にZabbix 7.0に対してPlaybookを実行する。以下は、Zabbix 7.0に対して、community.zabbix.zabbix_hostモジュールを用いて監視対象ホストを登録した際の実行結果となり、問題なく実行できていることがわかる。
# ansible-playbook -i hosts -l t1023zabi zabbix/set_zabbix_host.yml

[root@t1025alma test]# ansible-playbook -i hosts -l t1023zabi zabbix/set_zabbix_host.yml

PLAY [Set zabbix host] **************************************************************************************************************************************************************

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

TASK [Create host] ******************************************************************************************************************************************************************
changed: [t1023zabi] => (item={'name': 't3051kube', 'groups': 'Linux servers', 'type': 'agent', 'ip': '192.168.1.1', 'templates': ['Linux by Zabbix agent']})

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

2024年2月24日土曜日

Ansibleでタスク単位でタイムアウトを設定する

Ansibleでタスクを実行する際に、30秒以上応答が返ってこない場合、タイムアウトでエラーとなり処理が中断されてしまう。その際に、以下のようなエラーが表示される。

2024-02-10 13:06:29,478 p=2302695 u=root n=ansible | 
persistent connection idle timeout triggered, timeout value is 30 secs.
See the timeout setting options in the Network Debug and Troubleshooting Guide.

Ansibleのタイムアウトはデフォルトで30秒と設定されているが、タスクによっては時間を要するものがあり、タイムアウトを長くしたい場合がある。

本記事では、Ansibleでタスク単位でタイムアウトを設定する方法を記載する。

環境

今回の設定を確認した環境は以下の通り。

  • OS : AlmaLinux release 8.5
  • Ansible : ansible [core 2.15.3]

タスク単位でタイムアウトを設定する方法

タスク単位でタイムアウトを設定する場合は、タスクにvars設定を追加し、ansible_command_timeoutを設定する。

以下例はVyOSのファイアウォール設定のタスクとなるが、ルール数が多い場合、30秒では設定は完了しないことがあるため、120秒に時間を延ばしている。

# ファイアウォールルール設定
- name: Set firewall rules
  vyos.vyos.vyos_firewall_rules:
    config:
      - afi: ipv4
        rule_sets:
          - name: OUTSIDE-IN-RULE
            default_action: drop
            rules: "{{ vyos_firewall_rule_outside }}"
          - name: INSIDE-IN-RULE
            default_action: drop
            rules: "{{ vyos_firewall_rule_inside }}"
    state: replaced
  vars:
    ansible_command_timeout: 120

【参考】デフォルトのタイムアウト時間を変更する方法

ansible.cfgで以下を記載することで、デフォルトのタイムアウト時間を変更することができる。

[persistent_connection]  
command_timeout = 30

以上で、Ansibleでタスク単位でタイムアウトを設定する方法は完了となる。

参考

ネットワークデバッグおよびトラブルシューティングガイド - Ansible Documentation

2023年10月7日土曜日

Ansible実行時の「Encryption using the Python crypt module is deprecated.」の警告メッセージを消す方法

ある日Ansibleを実行していると、以下の警告メッセージが表示された。

[DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated.
The Python crypt module is deprecated and will be removed from Python 3.13.
Install the passlib library for continued encryption functionality.
This feature will be removed in version 2.17.
Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.

本記事では、Ansibleの「Encryption using the Python crypt module is deprecated.」の警告メッセージを消す方法を記載する。

環境

  • OS : AlmaLinux 8.5
  • Ansible : 8.1.0 (ansible [core 2.15.1])
  • Python : 3.9.16

原因

以下2点つの条件が重なった際に警告メッセージが表示されるようだ。

  1. Playbookでpassword_hashといったパスワードをハッシュ化する構文を使用している。
    • password: "{{ 'P@ssw0rd!' | password_hash('sha512') }}"
  2. Pythonライブラリであるpasslibがインストールされていない。

解消方法

警告メッセージにも記載されている通り、Pythonライブラリであるpasslibをインストールすれば問題ない。

# python3 -m pip install passlib
WARNING: Running pip install with root privileges is generally not a good idea. Try `python3 -m pip install --user` instead.
Collecting passlib
  Downloading passlib-1.7.4-py2.py3-none-any.whl (525 kB)
     |████████████████████████████████| 525 kB 13.8 MB/s 
Installing collected packages: passlib
Successfully installed passlib-1.7.4

簡単ではあるが、以上で完了となる。

2023年8月5日土曜日

Ansible Lintの「よくあるエラー」を解消する

先日、Ansibleを6.0.0から8.1.0にバージョンアップした。

その際にAnsible Lintもバージョンアップされ、今まではエラーとならなかったPlaybookにおいて新たにエラーが出力されるようになった。

本記事では、今回私が遭遇したAnsible Lintの「よくあるエラー」を解消するための方法を記載する。

環境

  • Ansible : 2.15.1
  • Ansible Lint : 6.17.2

Ansible Lintのよくあるエラー

yaml[octal-values]: Forbidden implicit octal value “0644”

https://ansible.readthedocs.io/projects/lint/rules/yaml/

modeのパーミッションをダブルクォートで囲む。

誤った記載

mode: 0644

正しい記載

mode: "0644"

no-changed-when: Commands should not change things if nothing needs doing.

https://ansible.readthedocs.io/projects/lint/rules/no-changed-when/

commandshellモジュールを使う際は、changed_whenにて変更有無の条件を定義する。例えば、以下の例ではコマンド実行結果が0の場合はchangedのステータスに設定する。

正しい記載

    - name: Install go command
      ansible.builtin.command:
        cmd: "{{ item }}"
        chdir: "{{ cri_dockerd.dir_git }}"
      become: true
      environment: "{{ proxy_env }}"
      register: result_cmd              # <- add
      changed_when: result_cmd.rc == 0  # <- add
      loop:
        - "wget https://storage.googleapis.com/golang/getgo/installer_linux"
        - "chmod +x ./installer_linux"
        - "./installer_linux"
      when: result.rc != 0

no-free-form: Avoid using free-form when calling module actions. (ansible.builtin.service)

https://ansible.readthedocs.io/projects/lint/rules/no-free-form/

1行で書くのではなく、きちんとYAMLで書く。

誤った記載

    - name: Enable service
      ansible.builtin.service: name={{ item }} enabled=yes state=started
      loop: "{{ service_enable_list }}"
      become: true

正しい記載

    - name: Enable service
      ansible.builtin.service:
        name: "{{ item }}"
        enabled: true
        state: started
      loop: "{{ service_enable_list }}"
      become: true

run-once[task]: Using run_once may behave differently if strategy is set to free.

https://ansible.readthedocs.io/projects/lint/rules/run-once/

Ansibleの実行戦略(strategy)がfreeの場合、run_onceの指定はお勧めできない(想定通りに動作しない可能性がある)、というもの。freeの場合は、各タスクにおいてすべてのホストの実行の終了を待たずに、次のタスクが実行されるため。

なお、デフォルトはlinearとなり、各タスクのすべてのホストの実行が終了してから、次のタスクに遷移する。

解決策はrun_onceを使わなければよいが、使いたい場合もあるので、その場合は以下の通りタスクのname# noqa: run-once[task]のコメントを付与すると、Ansible Lintがエラーを表示しなくなる。

    - name: Remove local repository # noqa: run-once[task]
      ansible.builtin.file:
        path: "{{ dir_output_config_dest }}"
        state: absent
      delegate_to: localhost
      run_once: true

jinja[invalid]: You need to install “jmespath” prior to running json_query filter

https://ansible.readthedocs.io/projects/lint/rules/jinja/

jmespathがインストールされていないと出力されるエラー。pipにてjmespathをインストールする。

# python3 -m pip install jmespath
WARNING: Running pip install with root privileges is generally not a good idea. Try `python3 -m pip install --user` instead.
Collecting jmespath
  Using cached jmespath-1.0.1-py3-none-any.whl (20 kB)
Installing collected packages: jmespath
Successfully installed jmespath-1.0.1

fqcn[canonical]: You should use canonical module name ansible.windows.win_file instead of ansible.builtin.win_file.

モジュール名が誤っている。ansible.builtin.win_fileではなくansible.windows.win_fileのように正しいモジュール名を記載する。

誤った記載

    - name: Remove windows sciprt & config file
      ansible.builtin.win_file:
        path: "{{ dir_output_config_src }}"
        state: absent

正しい記載

    - name: Remove windows sciprt & config file
      ansible.windows.win_file:
        path: "{{ dir_output_config_src }}"
        state: absent

name[casing]: All names should start with an uppercase letter.

nameの最初の文字は大文字にする。

誤った記載

- name: create test vm from snapshot
  gather_facts: true
  hosts: vmware_servers

正しい記載

- name: Create test vm from snapshot
  gather_facts: true
  hosts: vmware_servers

args[module]: Unsupported parameters for (basic.py) module: login_password, login_user, server_url. Supported parameters include: exact_match, host_inventory, host_ip, host_name, http_login_password, http_login_user, remove_duplicate. (warning)

サポートされてないモジュールのパラメータ指定を行った際にエラーとなる。今回であれば、Zabbixモジュールの記載方法誤り(バージョンアップによって記載方法が変わったため)。Zabbixモジュールの記載については、以下別記事を参照。

yaml[truthy]: Truthy value should be one of [false, true]

yes/noではなくtrue/falseで記載を統一する。

誤った記載

- name: Reboot
ansible.builtin.reboot:
async: 1
poll: 0
changed_when: no
become: yes

正しい記載

- name: Reboot
ansible.builtin.reboot:
async: 1
poll: 0
changed_when: false
become: true

以上。

2023年7月29日土曜日

Ansible Lintの最後に約2分の待ち時間が発生する問題

先日、Ansibleを6.0.0から8.1.0へバージョンアップした。その際に、Ansible Lintのバージョンも新しくなった(6.3.0 -> 6.17.2)。

バージョンアップしたところ、Ansible Lintの処理が想定以上に長期化してしまう問題が発生した。具体的には、Ansible Lintの処理完了後の最後のメッセージ表示がされるまでに、約2分の待ち時間が発生するようになってしまった。

$ ansible-lint

~(中略)~

WARNING  /usr/local/lib/python3.9/site-packages/jmespath/lexer.py:169 PendingDeprecationWarning deprecated string literal syntax
-- ★この間で2分ほど待機がある --
Passed: 0 failure(s), 0 warning(s) on 57 files. Last profile that met the validation criteria was 'production'.

本記事では、この事象を解決する方法を記載する。

環境

環境は以下の通り。

  • OS : AlmaLinux 8.5
  • Ansible : 8.1.0 [core 2.15.1]
  • Ansible Lint : 6.17.2

Ansible Lintのバージョンは以下コマンドで確認している。

$ ansible-lint --version
ansible-lint 6.17.2 using ansible-core:2.15.1 ansible-compat:4.1.2 ruamel-yaml:None ruamel-yaml-clib:None

解決方法

結論から言うと、以下の通り--offlineオプションを付与することで解決できる。

$ ansible-lint --offline

調査内容詳細

ここからは参考情報となるので、興味のある方のみ確認いただきたい。

今回の事象調査のため、ansible-lintコマンドに-vvオプションを付与したうえで詳細な実行結果を確認した。すると、Ansible Lintで時間を要するパターンにおいては以下のログが出力されており、インターネット接続のタイムアウト待ちの時間によって処理が長期化していることを確認した。

DEBUG    Unable to fetch latest version from https://api.github.com/repos/ansible/ansible-lint/releases/latest due to: <urlopen error [Errno 110] Connection timed out>

どうやら、GitHubに最新の情報取得を行おうと試みているようだ。そこで、ansible-lintコマンドのヘルプから--offlineオプションがあることを確認し、付与することで上記動作が発生しないようにした。

以下に、参考情報として--offlineオプションの有無における実行結果を記載する。

--offlineオプションなし

$ ansible-lint -vv

~(中略)~

Read documentation for instructions on how to ignore specific rule violations.

DEBUG    Determined rule-profile order: {'internal-error': (0, 'min'), 'load-failure': (1, 'min'), 'parser-error': (2, 'min'), 'syntax-check': (3, 'min'), 'command-instead-of-module': (4, 'basic'), 'command-instead-of-shell': (5, 'basic'), 'deprecated-bare-vars': (6, 'basic'), 'deprecated-local-action': (7, 'basic'), 'deprecated-module': (8, 'basic'), 'inline-env-var': (9, 'basic'), 'key-order': (10, 'basic'), 'literal-compare': (11, 'basic'), 'jinja': (12, 'basic'), 'no-free-form': (13, 'basic'), 'no-jinja-when': (14, 'basic'), 'no-tabs': (15, 'basic'), 'partial-become': (16, 'basic'), 'playbook-extension': (17, 'basic'), 'role-name': (18, 'basic'), 'schema': (19, 'basic'), 'name': (20, 'basic'), 'var-naming': (21, 'basic'), 'yaml': (22, 'basic'), 'name': (23, 'moderate'), 'name': (24, 'moderate'), 'name': (25, 'moderate'), 'spell-var-name': (26, 'moderate'), 'avoid-implicit': (27, 'safety'), 'latest': (28, 'safety'), 'package-latest': (29, 'safety'), 'risky-file-permissions': (30, 'safety'), 'risky-octal': (31, 'safety'), 'risky-shell-pipe': (32, 'safety'), 'galaxy': (33, 'shared'), 'ignore-errors': (34, 'shared'), 'layout': (35, 'shared'), 'meta-incorrect': (36, 'shared'), 'meta-no-tags': (37, 'shared'), 'meta-video-links': (38, 'shared'), 'meta-version': (39, 'shared'), 'meta-runtime': (40, 'shared'), 'no-changed-when': (41, 'shared'), 'no-changelog': (42, 'shared'), 'no-handler': (43, 'shared'), 'no-relative-paths': (44, 'shared'), 'max-block-depth': (45, 'shared'), 'max-tasks': (46, 'shared'), 'unsafe-loop': (47, 'shared'), 'avoid-dot-notation': (48, 'production'), 'sanity': (49, 'production'), 'fqcn': (50, 'production'), 'import-task-no-when': (51, 'production'), 'meta-no-dependencies': (52, 'production'), 'single-entry-point': (53, 'production'), 'use-loop': (54, 'production')}
                Rule Violation Summary
 count tag               profile rule associated tags
    12 yaml[empty-lines] basic   formatting, yaml
    23 yaml[indentation] basic   formatting, yaml

DEBUG    Unable to fetch latest version from https://api.github.com/repos/ansible/ansible-lint/releases/latest due to: <urlopen error [Errno 110] Connection timed out>
Failed: 35 failure(s), 0 warning(s) on 130 files. Last profile that met the validation criteria was 'min'.

--offlineオプションあり

$ ansible-lint --offline -vv

~(中略)~

Read documentation for instructions on how to ignore specific rule violations.

DEBUG    Determined rule-profile order: {'internal-error': (0, 'min'), 'load-failure': (1, 'min'), 'parser-error': (2, 'min'), 'syntax-check': (3, 'min'), 'command-instead-of-module': (4, 'basic'), 'command-instead-of-shell': (5, 'basic'), 'deprecated-bare-vars': (6, 'basic'), 'deprecated-local-action': (7, 'basic'), 'deprecated-module': (8, 'basic'), 'inline-env-var': (9, 'basic'), 'key-order': (10, 'basic'), 'literal-compare': (11, 'basic'), 'jinja': (12, 'basic'), 'no-free-form': (13, 'basic'), 'no-jinja-when': (14, 'basic'), 'no-tabs': (15, 'basic'), 'partial-become': (16, 'basic'), 'playbook-extension': (17, 'basic'), 'role-name': (18, 'basic'), 'schema': (19, 'basic'), 'name': (20, 'basic'), 'var-naming': (21, 'basic'), 'yaml': (22, 'basic'), 'name': (23, 'moderate'), 'name': (24, 'moderate'), 'name': (25, 'moderate'), 'spell-var-name': (26, 'moderate'), 'avoid-implicit': (27, 'safety'), 'latest': (28, 'safety'), 'package-latest': (29, 'safety'), 'risky-file-permissions': (30, 'safety'), 'risky-octal': (31, 'safety'), 'risky-shell-pipe': (32, 'safety'), 'galaxy': (33, 'shared'), 'ignore-errors': (34, 'shared'), 'layout': (35, 'shared'), 'meta-incorrect': (36, 'shared'), 'meta-no-tags': (37, 'shared'), 'meta-video-links': (38, 'shared'), 'meta-version': (39, 'shared'), 'meta-runtime': (40, 'shared'), 'no-changed-when': (41, 'shared'), 'no-changelog': (42, 'shared'), 'no-handler': (43, 'shared'), 'no-relative-paths': (44, 'shared'), 'max-block-depth': (45, 'shared'), 'max-tasks': (46, 'shared'), 'unsafe-loop': (47, 'shared'), 'avoid-dot-notation': (48, 'production'), 'sanity': (49, 'production'), 'fqcn': (50, 'production'), 'import-task-no-when': (51, 'production'), 'meta-no-dependencies': (52, 'production'), 'single-entry-point': (53, 'production'), 'use-loop': (54, 'production')}
                Rule Violation Summary
 count tag               profile rule associated tags
    12 yaml[empty-lines] basic   formatting, yaml
    23 yaml[indentation] basic   formatting, yaml

Failed: 35 failure(s), 0 warning(s) on 130 files. Last profile that met the validation criteria was 'min'.

以上。

2023年6月25日日曜日

AnsibleでZabbixを操作する (Ansible 8.1 [core 2.15]対応)

自宅ではAnsibleを導入してから、以下OSやソフトウェアに対して作業のコード化や自動化を行ってきた。導入手順などは以下記事を参照。

OS/ソフトウェア URL
Linux AnsibleでLinuxサーバの初期設定と単体テストを行う
Windows Server AnsibleからWindows Serverに接続するための事前作業
vSphere ESXi AnsibleでESXi上に仮想マシンを構築する

今回は、Ansibleを使ってZabbixを操作するための手順を記載する。Zabbixはcommunity.zabbixというモジュールで操作可能となる。

環境

今回手順を確認した環境は以下の通り。

  • コントロールノード
    • OS : AlmaLinux 8.5
    • Ansible : 8.1.0 [core 2.15.1]
  • Zabbix : 6.0.17

AnsibleでZabbixを操作する場合は、事前にzabbix-apiのPythonパッケージをインストールする必要があるため、pipコマンドを使って以下の通りインストールしておこう。

# pip3 install zabbix-api

Zabbixのメンテナンス期間を作成するPlaybook

community.zabbixは、Zabbix設定のための各種モジュールが提供されている。設定したい内容に応じて、以下URLより必要なモジュールを探して利用しよう。

今回は、シンプルにZabbixの「メンテナンス期間」を作成するだけのPlaybookを作成した。

以下にPlaybookの内容を説明する。

Playbook説明

変数 (vars)

Ansibleを使ってZabbixを操作する場合、ansible_useransible_passwordではなくZabbixのログインユーザ情報とパスワードの指定が常に必要となる。それ以外の設定値の変数は、使用するZabbixモジュールに応じて追加が必要となる。

変数 設定値
ansible_user Zabbixのログインユーザを指定。今回はAdminを指定する。
ansible_httpapi_pass Zabbixのログインユーザのパスワードを指定する。
ansible_network_os community.zabbix.zabbix
ansible_connection httpapi
ansible_httpapi_port 今回はZabbixサーバはHTTPアクセスによる構成としていることから、80を指定する。
ansible_zabbix_url_path Zabbixサーバにアクセスする際のURLのパスを記載する。具体的には、http://[ZabbixサーバのIPアドレス]/[パス][パス]に記載の文字列を指定すればよく、通常はzabbixとなるはずだ。
ansible_host ZabbixサーバのIPアドレスを指定する。
zabbix_maintenance.name 「メンテナンス期間」に使用する名前を指定する。
zabbix_maintenance.host_name 「メンテナンス期間」で指定する監視対象ホストを指定する。
zabbix_maintenance.minutes 「メンテナンス期間」の期間を指定する。今回は「30分」を指定する。

タスク (tasks)

今回のPlaybookのタスクは、以下1個のみとなる。

タスク モジュール 説明
メンテナンス期間作成 zabbix_maintenance メンテナンス期間を作成する。「有効期間の開始日時」はタスク実行時間となり、「有効期間の終了日時」はminutesに設定した時間経過後となるため、今回は30分後となる。例えば、Ansibleを5:00に実行した場合は、5:30がメンテナンス期間の終了日時となる。

Playbook

以上をふまえ、Playbook全体は以下となる。

set_zabbix_maintenance.yml

---
- name: Set zabbix maintenance
  gather_facts: true
  hosts: zabbix_servers

  vars:
    ansible_user: Admin
    ansible_httpapi_pass: zabbix
    ansible_network_os: community.zabbix.zabbix
    ansible_connection: httpapi
    ansible_httpapi_port: 80
    ansible_zabbix_url_path: zabbix
    ansible_host: 192.168.11.24

    zabbix_maintenance:
      name: Test maintenance
      host_name: Zabbix server
      minutes: 30

  tasks:
    - name: Enable maintenance
      community.zabbix.zabbix_maintenance:
        name: "{{ zabbix_maintenance.name }}"
        host_name: "{{ zabbix_maintenance.host_name }}"
        minutes: "{{ zabbix_maintenance.minutes }}"
        collect_data: true
        state: present

実行結果

Playbookの実行結果は以下の通り。

# ansible-playbook -i hosts zabbix/set_zabbix_maintenance.yml 

PLAY [Get zabbix info] *********************************************************************************************

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

TASK [Enable maintenance] ******************************************************************************************
changed: [t1024cent]

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

Zabbixの管理画面からも、メンテナンス期間が作成されたことが確認できた。22:40にAnsibleを実行したため、メンテナンス期間の終了日時は30分後の23:10となっていることがわかる。

以上で、Ansibleを使ってZabbixを操作するための手順は完了となる。

更新履歴

  • 2023/4/22 新規作成
  • 2023/6/25 Ansibleバージョンを6.0.0 -> 8.1.0へバージョンアップしたことに伴い記載を更新

2023年6月24日土曜日

AnsibleでKubernetesを操作する

自宅ではAnsibleを導入してから、以下OSやソフトウェアに対して作業のコード化や自動化を行ってきた。導入手順などは以下記事を参照。

OS/ソフトウェア URL
Linux AnsibleでLinuxサーバの初期設定と単体テストを行う
Windows Server AnsibleからWindows Serverに接続するための事前作業
vSphere ESXi AnsibleでESXi上に仮想マシンを構築する

今回は、Ansibleを使ってKubernetesを操作する手順を記載する。Kubernetesは、Ansibleに標準で導入されているkubernetes.coreというモジュールで操作可能となる。

# ansible-galaxy collection list

Collection                    Version
----------------------------- -------
~(中略)~
kubernetes.core               2.3.1  
~(以下略)~

環境

以下に今回構築する各種ソフトウェアのバージョンを記載する。

  • Ansible: 2.13.1
  • Kubernetes: v1.27.1

Ansibleコントロールノード構築やLinuxサーバの事前準備の手順は、以下記事を参照いただきたい。

Kubernetesコントロールプレーンに対する準備

AnsibleでKubernetesを操作する際は、コントロールプレーンを経由してKubernetes APIにアクセスする。その際に、コントロールプレーンのノードに対して追加のPythonモジュールが必要となる。

1. pipをインストール

Dockerを利用する際はPythonパッケージのインストールが必要となるため、pipをインストールしておく。

# dnf install python3-pip

2. pipを用いてPythonモジュールをインストール

pipを用いて、以下の通りkubernetesのPythonモジュールをインストールする。

# python3 -m pip install kubernetes

3. Ansible実行ユーザーにKubernetesクラスタの環境設定ファイルを配置

Kubernetesのコントロールプレーン構築時に、Kubernetes APIへアクセスするために環境設定ファイルをホームディレクトリに配置する作業をするが、Ansible実行ユーザーに対しても同様に実施が必要となる。

私の環境ではansibleuserとなるため、以下の通り配置作業を実施した。

# su - ansibleuser
> mkdir -p $HOME/.kube
> sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
> sudo chown $(id -u):$(id -g) $HOME/.kube/config

実際にAnsibleでKubernetesを操作してみる

動作確認として、AnsibleでKubernetesにNamespaceを作成してみよう。Kubernetesのリソース操作は、kubernetes.core.k8sというモジュールで実現できる。

以下のシンプルなPlaybookを実行する。

---
- name: Create K8s namespace
  gather_facts: true
  hosts: t3051kube

  vars:
    ansible_user: ansibleuser

  tasks:
    # Namespace作成
    - name: Create namespace
      kubernetes.core.k8s:
        name: test-namespace
        api_version: v1
        kind: Namespace
        state: present

実行結果は以下の通り。

# ansible-playbook -i hosts k8s/create_k8s_namespace.yml 

PLAY [Create K8s namespace] ****************************************************************************************

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

TASK [Create namespace] ********************************************************************************************
changed: [t3051kube]

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

kubectlコマンドで確認すると、以下の通りNamespaceが作成されている。

# kubectl get namespace
NAME              STATUS   AGE
default           Active   18d
kube-flannel      Active   18d
kube-node-lease   Active   18d
kube-public       Active   18d
kube-system       Active   18d
mynamespace       Active   18d
test-namespace    Active   2m5s ★Ansibleで作成されたNamespace

以上で、Ansibleを使ってKubernetesを操作する手順は完了となる。今回はNamespaceの作成を例として記載したが、kubernetes.core.k8sモジュールを使うことで、kubectlコマンドを用いて行う各種リソースの作成や、マニフェストファイルからのリソースなども実現することができる。

2023年6月3日土曜日

Windows用Zabbix Agent 2をmsiexecコマンドを使ってサイレントインストールする

先日、Windows用Zabbix Agent 2をMSIインストーラのウィザードに従ってGUIでインストールする手順を記事にした。

そこまで多くの設定が必要ではないとはいえ、GUIで設定することも手間となる。幸いMSIインストーラはmsiexecというコマンドを使えば、ウィザードを使わずにサイレントインストールをさせることができる。

本記事では、Windows用Zabbix Agent 2をmsiexecコマンドを使ってサイレントインストールする手順を記載する。また、Ansibleを使ってZabbix Agentを自動インストールするPlaybookを作ってみたので紹介する。

環境

  • Zabbix Server : 6.0.17
  • 監視対象OS : Windows Server 2022
  • Zabbix Agent 2バージョン : 6.0.17

Zabbix Agentサイレントインストール手順

1. Zabbix Agent 2のダウンロード

Zabbix Agent 2は以下URLからダウンロードできる。

今回は、以下の通り、Zabbix 6.0用のエージェントを指定する。

項目 設定値
OS DISTRIBUTION Windows
OS VERSION Any
HARDWARE amd64
ZABBIX VERSION 6.0 LTS
ENCRYPTION OpenSSL
PACKAGING MSI

図11

指定すると、従来のZabbix AgentとZabbix Agent 2の二種類が表示されるので、Zabbix Agent 2のMSIインストーラをダウンロードする。

2. コマンドプロンプトを「管理者として実行」で起動

コマンドプロンプトを「管理者として実行」で起動する。

3. msiexecコマンドにてインストールを実行

以下の通り、実行する。コマンドプロンプトでは行末に^を付与するとコマンドの改行ができる。今回は見やすくするため改行を入れているが、一行で記述しても当然実行できる。

msiexec /l*v log.txt^
        /i zabbix_agent2-x.x.x-windows-amd64-openssl.msi /qn^
        HOSTNAME=[Zabbixに登録したホスト名]^
        SERVER=[Zabbix ServerのIPアドレス]^
        SERVERACTIVE=[Zabbix ServerのIPアドレス]

以下実行例となる。サイレントインストールとなるため、実行結果は何も表示されない。

C:\work> msiexec /l*v log.txt^
/i zabbix_agent2-6.0.17-windows-amd64-openssl.msi /qn^
HOSTNAME=test-server^
SERVER=192.168.1.1^
SERVERACTIVE=192.168.1.1

インストールが問題なくされているか不安な場合は、「コントロールパネル」→「プログラムと機能」にZabbix Agentが存在することを確認しよう。

AnsibleでZabbix Agentを自動インストールするPlaybook

Playbookの説明は以下の通り。Zabbix Agentがインストールされているかどうかチェックしたのち、MSIインストーラをコピーしてサイレントインストールする、シンプルなPlaybookとなる。

タスク名 モジュール 説明
Check zabbix agent installation status win_shell PowerShellでGet-WmiObject Win32_Productを実行し、Zabbix Agnetのインストール状況を確認する。
Copy zabbix agent file win_copy Zabbix Agnetがインストールされていない場合、Zabbix AgentのMSIインストーラファイルをコピーする。
Install zabbix agent win_command msiexecにてZabbix Agentをサイレントインストールする。

実際のPlaybookを以下に記載する。

---
- name: Install windows zabbix agent
  gather_facts: true
  hosts: windows_servers

  vars:
    ansible_user: ansibleuser
    ansible_password: XXXXXXXX
    ansible_port: 5986
    ansible_connection: winrm
    ansible_winrm_server_cert_validation: ignore

    zabbix_agent:
      server: 192.168.1.1
      name: zabbix_agent2-6.0.17-windows-amd64-openssl.msi
      logname: log_install_zabbix_agent.log

  tasks:
    # インストール確認
    - name: Check zabbix agent installation status
      ansible.windows.win_shell: |
        Get-WmiObject Win32_Product | Where-Object { $_.Name -like "Zabbix Agent*" }
      changed_when: false
      register: result_check

    # ファイルコピー
    - name: Copy zabbix agent file
      ansible.windows.win_copy:
        src: /ansible_mnt/
        dest: C:\Temp
      when: result_check.stdout == ""

    # インストール実行
    - name: Install zabbix agent
      ansible.windows.win_command: |
        C:\Windows\System32\msiexec.exe /l*v C:\Temp\{{ zabbix_agent.logname }} /i C:\Temp\{{ zabbix_agent.name }} /qn^
          HOSTNAME={{ inventory_hostname }} SERVER={{ zabbix_agent.server }} SERVERACTIVE={{ zabbix_agent.server }}
      changed_when: true
      register: result
      when: result_check.stdout == ""

以上で、Windows用Zabbix Agent 2をmsiexecコマンドを使ってサイレントインストールする手順は完了となる。

参考

2023年4月29日土曜日

AnsibleでWindows Updateを操作する

自宅ではAnsibleを導入してから、各種自動化を進めている。

今回は、Ansibleを使ってWindows Updateを操作するための手順を記載する。

環境

今回手順を確認した環境は以下の通り。

  • コントロールノード
    • OS : AlmaLinux 8.5
    • Ansible : ansible [core 2.13.1]
  • 操作対象サーバ
    • OS : Windows Server 2016

cronを操作するPlaybook

AnsibleにおけるWindows Updateの操作は、ansible.windows.win_updatesモジュールにて行う。

AnsibleからWindows Serverに接続するための事前作業

Ansibleを使ってWindows OSを操作する場合、事前にpywinrmのインストールや、WinRMの有効化などの準備作業が必要となる。必要な手順は、過去の記事でまとめてあるので参照いただきたい。

以下にPlaybookの内容を説明する。

Playbook説明

変数 (vars)

変数 設定値
ansible_user 操作対象サーバに接続する際に使用するユーザ名を指定。
ansible_password 操作対象サーバに接続する際に使用するユーザ名のパスワードを指定。
ansible_port WinRMの5986ポートを指定。
ansible_connection winrmを指定。
ansible_winrm_server_cert_validation ignoreを指定し、WinRM接続時の証明書検証を無視する。

タスク及びハンドラー (tasks / handlers)

今回のPlaybookのタスクは以下となる。主役はansible.windows.win_updatesとなり、Windows Update後の再起動をハンドラーで実行している。

OS再起動は、ansible.windows.win_updatesにおいても処理はさせることは可能となる(rebootreboot_timeoutのパラメータで設定できる)。もっとシンプルに作りたい場合は、そちらを利用してもよいだろう。

タスク モジュール 説明
Windowsアップデート ansible.windows.win_updates Windows Updateを行う。category_namesで対象とする更新プログラムのカテゴリを選択する。インストールする場合は、state: installedを設定し、インストールせず確認だけしたい場合は、state: searchedを指定する。
結果表示 ansible.builtin.debug Windows Updateの結果を表示する。これによって、適用されたHotfixのKB番号などを確認できる。
再起動 ansible.windows.win_reboot Windows Updateが実行された場合、OSの再起動を行う。
再起動後の待機 ansible.builtin.wait_for_connection OS再起動後に、Ansibleから接続可能となるまで待機する。

Playbook

以上をふまえ、Playbook全体は以下となる。カテゴリはSecurityUpdatesのみとした。

update_windows_server.yml

---
- name: Setup windows server
  gather_facts: true
  hosts: windows_servers

  vars:
    ansible_user: ansibleuser
    ansible_password: XXXXXXXX
    ansible_port: 5986
    ansible_connection: winrm
    ansible_winrm_server_cert_validation: ignore

  tasks:
    # Windowsアップデート
    - name: Update windows
      ansible.windows.win_updates:
        category_names:
          - SecurityUpdates
          # - CriticalUpdates
          # - UpdateRollups
        state: installed
      register: result
      notify:
        - Reboot
        - Wait for reboot

    # 結果表示
    - name: Show result
      ansible.builtin.debug:
        msg: "{{ result }}"

  handlers:
    # 再起動
    - name: Reboot
      ansible.windows.win_reboot:
      when: result.reboot_required

    # 再起動後の待機
    - name: Wait for reboot
      ansible.builtin.wait_for_connection:
        delay: 5
        timeout: 120

実行結果

Playbookの実行結果は以下の通り。結果として、「2023-04 x64 ベース システム用 Windows Server 2016 の累積更新プログラム (KB5025228)」が適用されていることがわかる。

# ansible-playbook -i hosts -l win2016 windows/update_windows_server.yml 

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

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

TASK [Update windows] *******************************************************************************************************************************************************************************************************
changed: [win2016]

TASK [Show result] **********************************************************************************************************************************************************************************************************
ok: [win2016] => {
    "msg": {
        "changed": true,
        "failed": false,
        "failed_update_count": 0,
        "filtered_updates": {
            "481f5459-70d6-456f-82ff-670f3b1e328a": {
                "categories": [
                    "Definition Updates",
                    "Microsoft Defender Antivirus"
                ],
                "downloaded": false,
                "filtered_reason": "category_names",
                "filtered_reasons": [
                    "category_names"
                ],
                "id": "481f5459-70d6-456f-82ff-670f3b1e328a",
                "installed": false,
                "kb": [
                    "2267602"
                ],
                "title": "Microsoft Defender Antivirus のセキュリティ インテリジェンス更新プログラム - KB2267602 (バージョン 1.387.1907.0)"
            },
            "fc5ae207-e126-4617-a7a5-8e2b05f690ef": {
                "categories": [
                    "Update Rollups",
                    "Windows Server 2016",
                    "Windows Server 2019"
                ],
                "downloaded": false,
                "filtered_reason": "category_names",
                "filtered_reasons": [
                    "category_names"
                ],
                "id": "fc5ae207-e126-4617-a7a5-8e2b05f690ef",
                "installed": false,
                "kb": [
                    "890830"
                ],
                "title": "悪意のあるソフトウェアの削除ツール x64 - v5.112 (KB890830)"
            }
        },
        "found_update_count": 1,
        "installed_update_count": 1,
        "reboot_required": true,
        "updates": {
            "97ab5eba-74ba-4d85-8a5e-28db67733520": {
                "categories": [
                    "Security Updates",
                    "Windows Server 2016"
                ],
                "downloaded": true,
                "id": "97ab5eba-74ba-4d85-8a5e-28db67733520",
                "installed": true,
                "kb": [
                    "5025228"
                ],
                "title": "2023-04 x64 ベース システム用 Windows Server 2016 の累積更新プログラム (KB5025228)"
            }
        }
    }
}

RUNNING HANDLER [Reboot] ****************************************************************************************************************************************************************************************************
changed: [win2016]

RUNNING HANDLER [Wait for reboot] *******************************************************************************************************************************************************************************************
ok: [win2016]

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

再起動後、OS上でも正常に更新プログラムが適用されていることが確認できた。

以上で、Ansibleを使ってWindows Updateを操作するための手順は完了となる。

2023年4月2日日曜日

Ansibleの設定ファイル「ansible.cfg」概要

Ansibleでいくつか実行時の動作を変更することができる。例えば、Playbookを実行する際に、標準出力だけでなくテキストファイルにもログを出力させたりすることができる。

本記事では、Ansibleの設定ファイル「ansible.cfg」について設定方法と設定内容を記載する。

ansible.cfgについて

ansible.cfgの説明は公式のマニュアルにも記載されている。

ansible.cfgは複数の配置場所を選択することができる。以下に配置されているファイルについて上から順に検索され、最初に見つかったansible.cfgの設定のみが反映され、他のファイルの設定は無視される。

  • 環境変数ANSIBLE_CONFIGで指定したパス
  • カレントディレクトリのansible.cfg
  • ホームディレクトリの~/.ansible.cfg
  • /etc配下の/etc/ansible/ansible.cfg

ansible.cfgの生成

ansible.cfgはテキストファイルなので手書きで作成してもよいが、コマンドで初期ファイルを作成することもできる。ただし、1000行以上となる長いファイルとなるため可読性は低いので、どのようなオプションがあるか一覧として表示させる際に利用するなどするとよい。

実行コマンドは以下の通り。

# ansible-config init --disabled -t all > ansible.cfg
# ls -l
合計 56
-rw-r--r-- 1 root root 54413  3月 21 11:21 ansible.cfg

ansible.cfgの設定項目

個人的によく使う設定項目

ansible.cfgの設定値は非常に多くあるが、その中でも個人的によく使う設定値を以下に記載する。

設定項目 説明
display_args_to_stdout Playbookのタスク実行時に、設定したパラメータを表示する。実際の表示例は後述する。
forks 複数のホストに対する処理の同時実行数。デフォルト5台と少なめになっているので、この値を増加させることで処理の高速化が期待できる。
host_key_checking SSH接続時の警告メッセージを無視する設定。例えば、今までSSH接続したことがないホストにSSH接続すると、本当に接続して問題ないか確認する警告メッセージが表示されてしまうが、その警告が出た場合でも接続を続行することができる。
log_path Ansibleの実行ログを出力するログのパスをファイル名まで含め指定する。
nocolor 通常、Ansibleの実行結果は、okが緑、changedが黄色、failedが赤と色付けされて表示がされる。この色付けを無効化したい場合はTrueで設定する。
vault_password_file Ansible Vaultが読み込むパスワードファイルを指定する。

display_args_to_stdout設定時の表示

display_args_to_stdoutを設定しない場合(デフォルト)と設定した場合の表示の違いを以下に記載する。設定した場合は、各タスクの行において設定しているパラメータが表示されていることがわかる。

display_args_to_stdout未設定時(デフォルト)

TASK [Install yum-utils] ****************************************************************************************************************************************************************************************************
ok: [t1051kube]

TASK [Check existence of docker-ce.repo] ************************************************************************************************************************************************************************************
ok: [t1051kube]

display_args_to_stdout設定時

TASK [Install yum-utils name=yum-utils, disablerepo=dvd*, state=present] ****************************************************************************************************************************************************
ok: [t1051kube]

TASK [Check existence of docker-ce.repo _raw_params=ls -l "/etc/yum.repos.d/docker-ce.repo"] ********************************************************************************************************************************
ok: [t1051kube]

ansible.cfg設定例

最後に、ansible.cfg設定例を記載する。display_args_to_stdoutは通常は無効で利用しており、必要な場合のみ有効にすることから、通常はコメントアウトして利用している。

[defaults]
# display_args_to_stdout = True
forks = 20
host_key_checking = False
log_path = /var/log/ansible/ansible_test.log
# nocolor = True
vault_password_file = .vault_pass

Ansibleの設定ファイル「ansible.cfg」に関する説明は以上となる。

2023年1月14日土曜日

AnsibleでVyOSを操作する

自宅ではAnsibleを導入してから、以下OSやソフトウェアに対して作業のコード化や自動化を行ってきた。導入手順などは以下記事を参照。

OS/ソフトウェア URL
Linux AnsibleでLinuxサーバの初期設定と単体テストを行う
Windows Server AnsibleからWindows Serverに接続するための事前作業
vSphere ESXi AnsibleでESXi上に仮想マシンを構築する

今回は、Ansibleを使って仮想ルーターOSであるVyOSを操作する手順を記載する。VyOSは、Ansibleに標準で導入されているvyos.vyosというモジュールで操作可能となる。

環境

今回手順を確認した環境は以下の通り。

  • コントロールノード
    • OS : AlmaLinux 8.5
    • Ansible : ansible [core 2.13.1]
  • VyOS : 1.4

VyOSの各種設定を行うPlaybook

今回のPlaybookでは、VyOSに対して各種設定を行い、設定後にコンフィグをファイルとして保存する。

以下にPlaybookの内容を説明する。

Playbook説明

変数 (vars)

Ansibleを使ってVyOSを操作する際の変数は、以下の通り設定する。VyOSはCisco機器のようなansible_become_methodの設定値は不要となる。

変数 設定値
ansible_user 任意のSSH接続用ユーザを設定。今回はvyosで設定。
ansible_password 任意のSSH接続用ユーザのパスワードを設定。
ansible_connection network_cli
ansible_network_os vyos
vyos_interface インタフェースのdescriptionやIPアドレスを設定。
vyos_ntp_server 時刻同期先のNTPサーバを設定。
vyos_snmp SNMPに必要となるコミュニティ名やTrapターゲットを設定する。なお、TrapソースのIPアドレス指定する際に、vyos_interfaceの変数からIPアドレスを取得している。
vyos_route スタティックルートを設定。

タスク (tasks)

Playbookのタスクは以下の通り。主にvyos.vyosモジュールで個別に用意されている設定モジュールを使って設定可能なものをタスクとして作成した。これ以外の設定に関しては、vyos.vyos.vyos_configを使って、VyOSのコマンドを直接流して設定することができる(当然その際には冪等性には注意すること)。

タスク モジュール 説明
事前config保存(save) & configファイル取得 vyos.vyos.vyos_config VyOSのconfigを取得する。取得内容はshow configuration commandsの内容となる。また、configが保存されていないことを想定して、save: trueを設定する。
タイムゾーン設定 vyos.vyos.vyos_config タイムゾーンを設定する。タイムゾーンを設定するモジュールはないため、直接設定コマンドを実行する。
ホスト名設定 vyos.vyos.vyos_hostname ホスト名を設定する。
インタフェース設定 vyos.vyos.vyos_interfaces インタフェースのdescriptionを設定する。本来はIPアドレスもこのモジュールで設定できるが、次のタスクでIPv6を無効化する際に、IPアドレスの再設定が必要になることから、本タスクでは設定は省略している。
インタフェース設定 (IPアドレス設定 & IPv6 無効化) vyos.vyos.vyos_config IPアドレスとIPv6の無効化設定を直接コマンドにて設定する。
SSH & Console設定 vyos.vyos.vyos_config SSHとConsoleの設定を直接設定コマンドにて設定する
NTP設定 vyos.vyos.vyos_ntp_global NTPによる時刻同期先の設定を行う。
SNMP設定 vyos.vyos.vyos_snmp_server SNMPの設定を行う。SNMP Trapのターゲット設定は本モジュールでは動作に失敗したため、次のタスクにて直接コマンドにて設定する。
SNMP設定 (Trap target) vyos.vyos.vyos_config SNMP Trapのターゲット設定を直接コマンドにて設定する。
スタティックルート設定 vyos.vyos.vyos_static_routes スタティックルートを設定する。
事後config保存(save) & configファイル取得 vyos.vyos.vyos_config VyOSのconfigを取得する。
ファイル差分確認 ansible.builtin.command Linuxのdiffコマンドにて、設定前後のconfig比較を行う。
ファイル差分表示 ansible.builtin.debug diffコマンドの比較結果を表示する。

Playbook

以上をふまえ、Playbook全体は以下となる。

set_vyos_global_settings.yml

---
- name: Set vyos global settings
  gather_facts: true
  hosts: vyos

  vars:
    ansible_user: vyos
    ansible_password: 'P@ssw0rd!'
    ansible_connection: network_cli
    ansible_network_os: vyos

    vyos_interface:
      - name: eth0
        description: OUTSIDE
        address: 192.168.3.4
        prefix: 24
      - name: eth1
        description: INSIDE
        address: 192.168.1.4
        prefix: 24

    vyos_ntp_server:
      - server: 192.168.3.2

    vyos_snmp:
      community: public
      authorization_type: ro
      trap_source: '{{ (vyos_interface | selectattr("name", "==", "eth1") | first).address }}'
      trap_target: 192.168.1.24
      trap_community: public

    vyos_route:
      dest: 0.0.0.0/0
      next_hop: 192.168.3.254

  tasks:
    # 事前config保存(save) & configファイル取得
    - name: Get configuration command
      vyos.vyos.vyos_config:
        backup: true
        backup_options:
          dir_path: "{{ lookup('env', 'PWD') }}/vyos"
          filename: "{{ inventory_hostname }}_conf_before.log"
        save: true

    # タイムゾーン設定
    - name: Set timezone
      vyos.vyos.vyos_config:
        lines:
          - set system time-zone 'Asia/Tokyo'

    # ホスト名設定
    - name: Set hostname
      vyos.vyos.vyos_hostname:
        config:
          hostname: "{{ inventory_hostname }}"
        state: replaced

    # インタフェース設定
    - name: Set interfaces
      vyos.vyos.vyos_interfaces:
        config:
          - name: "{{ item.name }}"
            description: "{{ item.description }}"
        state: replaced
      loop: "{{ vyos_interface }}"

    # インタフェース設定 (IPアドレス設定 & IPv6 無効化)
    - name: Set interfaces ipv6 address
      vyos.vyos.vyos_config:
        lines:
          - set interfaces ethernet {{ item.name }} address {{ item.address }}/{{ item.prefix }}
          - set interfaces ethernet {{ item.name }} ipv6 address no-default-link-local
      loop: "{{ vyos_interface }}"

    # SSH & Console設定
    - name: Set SSH and consle
      vyos.vyos.vyos_config:
        lines:
          - set service ssh listen-address '{{ (vyos_interface | selectattr("name", "==", "eth1") | first).address }}'
          - delete system console

    # NTP設定
    - name: Set ntp server
      vyos.vyos.vyos_ntp_global:
        config:
          servers: "{{ vyos_ntp_server }}"
        state: replaced

    # SNMP設定
    # Trap targetは「requires a community to be set!」のエラーで失敗する
    # 設定をすべて置き換えをしないよう、state: mergedを指定
    - name: Set snmp
      vyos.vyos.vyos_snmp_server:
        config:
          communities:
            - name: "{{ vyos_snmp.community }}"
              authorization_type: "{{ vyos_snmp.authorization_type }}"
          trap_source: "{{ vyos_snmp.trap_source }}"
        state: merged

    # SNMP設定 (Trap target)
    - name: Set snmp trap target
      vyos.vyos.vyos_config:
        lines:
          - set service snmp trap-target {{ vyos_snmp.trap_target }} community {{ vyos_snmp.trap_community }}

    # スタティックルート設定
    - name: Set static route
      vyos.vyos.vyos_static_routes:
        config:
          - address_families:
              - afi: ipv4
                routes:
                  - dest: "{{ vyos_route.dest }}"
                    next_hops:
                      - forward_router_address: "{{ vyos_route.next_hop }}"
        state: replaced

    # 事後config保存(save) & configファイル取得
    - name: Get configuration command
      vyos.vyos.vyos_config:
        backup: true
        backup_options:
          dir_path: "{{ lookup('env', 'PWD') }}/vyos"
          filename: "{{ inventory_hostname }}_conf_after.log"
        save: true

    # ファイル差分確認
    # 差分があった場合も処理を継続するため、failed_when: falseを付与
    - name: Diff configuration
      ansible.builtin.command: |
        diff "vyos/{{ inventory_hostname }}_conf_before.log" "vyos/{{ inventory_hostname }}_conf_after.log"
      register: result
      changed_when: false
      delegate_to: localhost
      failed_when: false

    # ファイル差分表示
    - name: Show configuration differences
      ansible.builtin.debug:
        msg: "{{ result.stdout_lines }}"

実行結果

Playbookの実行結果は以下の通り。事前と事後でconfig比較を行い、設定差分がわかるようになっている。

# ansible-playbook -i hosts -l t3034vyos vyos/set_vyos_global_settings_sample.yml 

PLAY [Set vyos global settings] ********************************************************************************************************************************************

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

TASK [Get configuration command] *******************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set timezone] ********************************************************************************************************************************************************
[WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device
changed: [t3034vyos]

TASK [Set hostname] ********************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set interfaces] ******************************************************************************************************************************************************
changed: [t3034vyos] => (item={'name': 'eth0', 'description': 'OUTSIDE', 'address': '192.168.3.34', 'prefix': 24})
changed: [t3034vyos] => (item={'name': 'eth1', 'description': 'INSIDE', 'address': '192.168.1.34', 'prefix': 24})

TASK [Set interfaces ipv6 address] *****************************************************************************************************************************************
changed: [t3034vyos] => (item={'name': 'eth0', 'description': 'OUTSIDE', 'address': '192.168.3.34', 'prefix': 24})
changed: [t3034vyos] => (item={'name': 'eth1', 'description': 'INSIDE', 'address': '192.168.1.34', 'prefix': 24})

TASK [Set SSH and consle] **************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set ntp server] ******************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set snmp] ************************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set snmp trap target] ************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set static route] ****************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Get configuration command] *******************************************************************************************************************************************
changed: [t3034vyos]

TASK [Diff configuration] **************************************************************************************************************************************************
ok: [t3034vyos -> localhost]

TASK [Show configuration differences] **************************************************************************************************************************************
ok: [t3034vyos] => {
    "msg": [
        "0a1,2",
        "> set interfaces ethernet eth0 address '192.168.3.4/24'",
        "> set interfaces ethernet eth0 description 'OUTSIDE'",
        "1a4",
        "> set interfaces ethernet eth0 ipv6 address no-default-link-local",
        "3a7",
        "> set interfaces ethernet eth1 description 'INSIDE'",
        "4a9",
        "> set interfaces ethernet eth1 ipv6 address no-default-link-local",
        "6c11,15",
        "< set service ssh",
        "---",
        "> set protocols static route 0.0.0.0/0 next-hop 192.168.3.254",
        "> set service snmp community public authorization 'ro'",
        "> set service snmp trap-source '192.168.1.4'",
        "> set service snmp trap-target 192.168.1.24 community 'public'",
        "> set service ssh listen-address '192.168.1.4'",
        "15,16c24",
        "< set system console device ttyS0 speed '115200'",
        "< set system host-name 'vyos'",
        "---",
        "> set system host-name 't3034vyos'",
        "18,20c26",
        "< set system ntp server time1.vyos.net",
        "< set system ntp server time2.vyos.net",
        "< set system ntp server time3.vyos.net",
        "---",
        "> set system ntp server 192.168.3.2",
        "22c28,29",
        "< set system syslog global facility protocols level 'debug'",
        "\\ ファイル末尾に改行がありません",
        "---",
        "> set system syslog global facility protocols level 'debug'",
        "> set system time-zone 'Asia/Tokyo'",
        "\\ ファイル末尾に改行がありません"
    ]
}

PLAY RECAP *****************************************************************************************************************************************************************
t3034vyos                  : ok=14   changed=11   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

VyOSのconfigファイルも以下の通り出力される。

vyos/t3034vyos_conf_after.log

set interfaces ethernet eth0 address '192.168.3.4/24'
set interfaces ethernet eth0 description 'OUTSIDE'
set interfaces ethernet eth0 hw-id '00:0c:29:b8:da:49'
set interfaces ethernet eth0 ipv6 address no-default-link-local

~(以下略)~

以上で、Ansibleを使って仮想ルーターOSであるVyOSを操作する手順は完了となる。

2022年12月17日土曜日

AnsibleでCisco Business 250 (CBS250)を操作する

自宅ではAnsibleを導入してから、以下OSやソフトウェアに対して作業のコード化や自動化を行ってきた。導入手順などは以下記事を参照。

OS/ソフトウェア URL
Linux AnsibleでLinuxサーバの初期設定と単体テストを行う
Windows Server AnsibleからWindows Serverに接続するための事前作業
vSphere ESXi AnsibleでESXi上に仮想マシンを構築する

今回は、Ansibleを使って物理スイッチであるCisco Business 250 (CBS250)を操作する手順を記載する。Cisco Business 250 (CBS250)は、Ansibleに標準で導入されているcommunity.ciscosmbというモジュールで操作可能となる。

注意事項

私の環境では何度かコマンド実行すると、ときおりcommand timeout triggered, timeout value is 30 secs.のエラーでコマンド実行に失敗する事象が発生した。タイムアウト時間を延ばしてもうまくいかないが、しらばく時間を空けると成功するようになることまではわかっているが、解決には至らなかった。

環境

今回手順を確認した環境は以下の通り。

  • コントロールノード
    • OS : AlmaLinux 8.5
    • Ansible : ansible [core 2.13.1]
    • jqコマンドを使うため、あらかじめパッケージをインストールしておく
  • スイッチ
    • 機種 : Cisco Business 250 (CBS250-8T-E-2G)
    • ファームウェアバージョン : 3.2.0.84

事前準備として、あらかじめCBS250に対して以下を実施しておく。

  • SSH接続できるよう設定済みであること
  • SSH用の一般ユーザを作成済みであること
  • enableコマンド実行時のパスワードを設定済みであること

Cisco Business 250のconfigを取得するPlaybook

今回のPlaybookでは、show startup-configを実行して取得したコンフィグをファイルとして保存する。

以下にPlaybookの内容を説明する。

Playbook説明

変数 (vars)

Ansibleを使ってネットワーク機器を操作する際の変数は、以下の通り設定する。ポイントは、ansible_network_oscommunity.ciscosmb.ciscosmbと設定することだ。それ以外は、Cisco IOSなどを操作する際と大きく設定は変わらない。

変数 設定値
ansible_user 任意のSSH接続用ユーザを設定。今回はciscoで設定。
ansible_password 任意のSSH接続用ユーザのパスワードを設定。
ansible_connection network_cli
ansible_network_os community.ciscosmb.ciscosmb
ansible_become_method enable
ansible_become_password enableコマンド実行時のパスワードを設定。

タスク (tasks)

Playbookのタスクは以下の通り。

タスク モジュール 説明
Exec command (show startup-config) community.ciscosmb.command show startup-configを実行した結果を変数に格納する。
Output file cbs250 configuration ansible.builtin.copy コンフィグが保存された変数の内容をファイルに出力する。出力結果はJSON形式になる。
Convert file cbs250 configuration ansible.builtin.shell JSON形式の出力結果をRAW形式に変換する。この処理はAnsibleのshellモジュールを用いてjqコマンドを用いてファイルを整形することで実現した。
Remove temp file ansible.builtin.file 整形前のJSON形式のファイルを削除する。

Playbook

以上をふまえ、Playbook全体は以下となる。

get_cbs250_config.yml

---
- name: Get cisco business 250 configuration
  gather_facts: true
  hosts: cbs250

  vars:
    ansible_user: cisco
    ansible_password: 'P@ssw0rd'
    ansible_connection: network_cli
    ansible_network_os: community.ciscosmb.ciscosmb
    ansible_become_method: enable
    ansible_become_password: 'P@ssw0rd'

    file_cbs250_config: "{{ lookup('env', 'PWD') }}/cbs250/{{ inventory_hostname }}_conf.log"
    file_cbs250_config_tmp: "{{ lookup('env', 'PWD') }}/cbs250/{{ inventory_hostname }}_conf_tmp.log"

  tasks:
    # config取得
    - name: Exec command (show startup-config)
      community.ciscosmb.command:
        commands: show startup-config
      register: result
      become: true

    # configをファイル出力
    - name: Output file cbs250 configuration
      ansible.builtin.copy:
        content: "{{ result.stdout_lines }}"
        dest: "{{ file_cbs250_config_tmp }}"
        mode: '0644'
      delegate_to: localhost

    # JSON形式からRAW形式に変換
    - name: Convert file cbs250 configuration
      ansible.builtin.shell: |
        set -o pipefail && cat "{{ file_cbs250_config_tmp }}" | jq -r .[][] > "{{ file_cbs250_config }}"
      changed_when: false
      delegate_to: localhost

    # 一時ファイルの削除
    - name: Remove temp file
      ansible.builtin.file:
        path: "{{ file_cbs250_config_tmp }}"
        state: absent

実行結果

Playbookの実行結果は以下の通り。

# ansible-playbook -i hosts get_cbs250_config.yml 

PLAY [Get cisco business 250 configuration] *************************************************************************

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

TASK [Exec command (show startup-config)] ***************************************************************************
ok: [cbs250]

TASK [Output file cbs250 configuration] *****************************************************************************
changed: [cbs250 -> localhost]

TASK [Convert file cbs250 configuration] ****************************************************************************
ok: [cbs250 -> localhost]

TASK [Remove temp file] *********************************************************************************************
changed: [cbs250]

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

CBS250のconfigファイルも以下の通り出力される。

cbs250/cbs250_conf.log

config-file-header
cbs250
v3.2.0.84 / RCBS3.2_950_377_134
CLI v1.0
file SSD indicator encrypted
@
ssd-control-start
ssd config
ssd file passphrase control unrestricted
no ssd file integrity control

~(以下略)~

以上で、Ansibleを使って物理スイッチであるCisco Business 250 (CBS250)を操作する手順は完了となる。

2022年12月10日土曜日

Ansibleを使ってDcokerのコンテナ環境をを操作する

今まで本ブログでは、Ansibleを使ってLinuxやWindows Serverの操作を実施してきた。Ansibleは他にも様々なモジュールが標準で含まれている。

Ansibleに含まれるモジュールは、以下コマンドで確認することができる。

# ansible-galaxy collection list
Collection                    Version
----------------------------- -------
amazon.aws                    3.5.0
ansible.netcommon             3.1.3
ansible.posix                 1.4.0
ansible.utils                 2.7.0
ansible.windows               1.12.0
arista.eos                    5.0.1

~(中略)~

community.docker              2.7.1

~(中略)~

vmware.vmware_rest            2.2.0
vultr.cloud                   1.3.0
vyos.vyos                     3.0.1
wti.remote                    1.0.4

今回は、上記に記載されているcommunity.dockerモジュールを使うことでDockerのコンテナ環境を操作することができる。今回は、Ansibleを使ってDockerのコンテナの環境を操作し、コンテナの停止、ビルド、起動を行うための手順を記載する。

環境

今回手順を確認した環境のOSは以下の通り。

  • コントロールノード
    • OS : AlmaLinux 8.5
    • Ansible : ansible [core 2.13.1]
  • DockerホストOS
    • OS : CentOS Stream release 8
    • Docker : docker-ce-20.10.14

Ansibleコントロールノード構築やLinuxサーバの事前準備の手順は、以下記事を参照いただきたい。

また、Docker上の操作対象のコンテナは以下3つとなる。各コンテナは、あらかじめDockerfileや必要な設定ファイルを準備しておく。必要な設定ファイルについては、それぞれ過去記事を参照いただきたい。

Dockerホストに対する事前準備

AnsibleでDockerを操作する場合、Dcokerホストに対して事前準備作業が必要となる。

1. pipをインストール

Dockerを利用する際はPythonパッケージのインストールが必要となるため、pipをインストールしておく。

# dnf install python3-pip

2. pipを用いてPythonパッケージをインストール

pipを用いて、以下の通りPythonパッケージをインストールする。

# python3 -m pip install docker docker-compose

もしこのモジュールをインストールしていない場合は、Playbook実行時に以下のようにPythonのモジュールが不足するといったエラーが発生する。

fatal: [dockerhost]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "can_talk_to_docker": false, "changed": false, "msg": "Failed to import the required Python library (Docker SDK for Python: docker above 5.0.0 (Python >= 3.6) or docker before 5.0.0 (Python 2.7) or docker-py (Python 2.6)) on t3027dock's Python /usr/libexec/platform-python. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter, for example via `pip install docker` (Python >= 3.6) or `pip install docker==4.4.4` (Python 2.7) or `pip install docker-py` (Python 2.6). The error was: No module named 'docker'"}
fatal: [dockerhost]: FAILED! => {"changed": false, "msg": "Unable to load docker-compose. Try `pip install docker-compose`. Error: Traceback (most recent call last):\n  File \"/tmp/ansible_community.docker.docker_compose_payload_uyzpmc__/ansible_community.docker.docker_compose_payload.zip/ansible_collections/community/docker/plugins/modules/docker_compose.py\", line 497, in <module>\nModuleNotFoundError: No module named 'compose'\n"}

なお、Pythonパッケージについて、dockerまたはdocker-pyのどちらかをインストールすればよいらしい。ただし、両方のインストールはNGであると明確にAnsibleのマニュアルに記載されているので注意しよう。

docker-py モジュールと docker python モジュールは、同じ名前空間を使用するため、
両方インストールすると、インストールが破損します。両方のパッケージをアンインストールし、
docker-py または docker python モジュールのいずれかを再インストールしてください。
Python 2.6 のサポートが必要でない場合は、
docker モジュールをインストールすることが推奨されます。
いずれかのモジュールをアンインストールするだけでは、
もう一方のモジュールが壊れた状態になる可能性があることに注意してください。

3. Ansible実行ユーザをdockerグループに所属させる

Ansible実行時にAnsible実行ユーザがDockerにアクセスできる権限が必要となる。

# usermod -aG docker ansibleuser
# id ansibleuser
uid=2001(ansibleuser) gid=2001(ansibleuser) groups=2001(ansibleuser),990(docker)

以上でDockerホストに対する事前作業は完了となる。実際にPythonを実行してみよう。

Docker操作用Playbook

1. Playbook説明

今回のPlaybookの実行フローは以下の通り。

各タスクのモジュールと処理の説明を以下表に記載する。

タスク名 モジュール 説明
Copy Dockerfile copy 各コンテナ用のDockerfile及び必要な設定ファイルをディレクトリごとコピーする。
Copy Dockerfile template template 各コンテナ用のDockerfileについて再配置する(変数で指定したベースイメージに設定したファイルを配置する)。
Build docker image docker_image Dockerfileを用いてイメージをビルドする。
Copy docker-compose.yml template template docker-compose.ymlをコピーする。
Down container docker_compose 現在稼働しているコンテナを停止&削除(Down)する。
Up container docker_compose 新しいイメージからコンテナを作成&起動(Up)する。

実際のPlaybookは以下となる。

---
- name: Rebuild docker container
  gather_facts: false
  hosts: docker_servers

  vars:
    ansible_user: ansibleuser

    docker_config:
      image: almalinux
      tag: 8.7

    docker_container:
      - name: "{{ docker_config.image }}-postfix"
        path: container-postfix
      - name: "{{ docker_config.image }}-unbound"
        path: container-unbound
      - name: "{{ docker_config.image }}-squid"
        path: container-squid

    docker_file_path: /tmp/docker

  tasks:
    # Dockerfileコピー
    - name: Copy Dockerfile
      ansible.builtin.copy:
        src: "templates/{{ item.path }}"
        dest: "/tmp/"
        mode: 0644
      loop: "{{ docker_container }}"

    # Dockerfileテンプレートコピー
    - name: Copy Dockerfile template
      ansible.builtin.template:
        src: "templates/{{ item.path }}/Dockerfile"
        dest: "/tmp/{{ item.path }}/"
        mode: 0644
      loop: "{{ docker_container }}"

    # イメージ構築
    - name: Build docker image
      community.docker.docker_image:
        build:
          path: "/tmp/{{ item.path }}"
        source: build
        name: "{{ item.name }}"
        tag: "{{ docker_config.tag }}"
        state: present
      loop: "{{ docker_container }}"

    # docker-compose.ymlテンプレートコピー
    - name: Copy docker-compose.yml template
      ansible.builtin.template:
        src: "templates/compose/docker-compose.yml"
        dest: "/root/compose/"
        mode: 0644
      become: true

    # コンテナ停止&削除
    - name: Down container
      community.docker.docker_compose:
        project_src: /root/compose
        build: false
        # stopped: true # 停止のみ実行する場合
        state: absent
      become: true

    # コンテナ起動
    - name: Up container
      community.docker.docker_compose:
        project_src: /root/compose
        build: false
        state: present
      become: true

2. Playbook実行

# ansible-playbook -i hosts -l dockerhost docker/rebuild_docker_container.yml

PLAY [Rebuild docker container] **********************************************************************************

TASK [Copy Dockerfile] *******************************************************************************************
changed: [dockerhost] => (item={'name': 'almalinux-postfix', 'path': 'container-postfix'})
changed: [dockerhost] => (item={'name': 'almalinux-unbound', 'path': 'container-unbound'})
changed: [dockerhost] => (item={'name': 'almalinux-squid', 'path': 'container-squid'})

TASK [Copy Dockerfile template] **********************************************************************************
changed: [dockerhost] => (item={'name': 'almalinux-postfix', 'path': 'container-postfix'})
changed: [dockerhost] => (item={'name': 'almalinux-unbound', 'path': 'container-unbound'})
changed: [dockerhost] => (item={'name': 'almalinux-squid', 'path': 'container-squid'})

TASK [Build docker image] ****************************************************************************************
ok: [dockerhost] => (item={'name': 'almalinux-postfix', 'path': 'container-postfix'})
ok: [dockerhost] => (item={'name': 'almalinux-unbound', 'path': 'container-unbound'})
ok: [dockerhost] => (item={'name': 'almalinux-squid', 'path': 'container-squid'})

TASK [Copy docker-compose.yml template] **************************************************************************
changed: [dockerhost]

TASK [Down container] ********************************************************************************************
ok: [dockerhost]

TASK [Up container] **********************************************************************************************
changed: [dockerhost]

PLAY RECAP *******************************************************************************************************
dockerhost                 : ok=6    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

3. 結果確認

Playbook実行後のDcokerのコンテナイメージとコンテナの状態を比較してみよう。

まずは実行前の状態を以下に示す。イメージはAlmaLinux 8.6のみとなっており、それぞれのコンテナのタグも8.6になっていることがわかる。

# docker images
REPOSITORY          TAG       IMAGE ID       CREATED         SIZE
almalinux-unbound   8.6       fd58943ebaca   2 months ago    243MB
almalinux-squid     8.6       f6e9116750e0   2 months ago    298MB
almalinux-postfix   8.6       0ca7abe54260   2 months ago    281MB
almalinux           8.6       90516e8d258d   6 months ago    189MB

# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                                              NAMES
df0976682bfe   almalinux-squid:8.6     "/usr/sbin/squid -N …"   49 minutes ago   Up 49 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp                                          almalinux-squid
553c80481ac3   almalinux-unbound:8.6   "/usr/sbin/unbound -d"   49 minutes ago   Up 49 minutes   0.0.0.0:10053->53/tcp, 0.0.0.0:10053->53/udp, :::10053->53/tcp, :::10053->53/udp   almalinux-unbound
303103b4abe8   almalinux-postfix:8.6   "/startup.sh"            49 minutes ago   Up 49 minutes   0.0.0.0:25->25/tcp, :::25->25/tcp                                                  almalinux-postfix

次に、Playbook実行後の状態を以下に示す。8.6のイメージに加えて、新たに8.7のイメージが作成されていることがわかる。また、コンテナもタグが8.7に更新されていることが確認できる。

# docker images
REPOSITORY          TAG       IMAGE ID       CREATED          SIZE
almalinux-squid     8.7       7fe03e3252a7   2 seconds ago    292MB
almalinux-unbound   8.7       bed4cd545c1f   17 seconds ago   240MB
almalinux-postfix   8.7       261591afe46c   18 minutes ago   275MB
almalinux           8.7       39f63d416992   3 days ago       190MB
almalinux-unbound   8.6       fd58943ebaca   2 months ago     243MB
almalinux-squid     8.6       f6e9116750e0   2 months ago     298MB
almalinux-postfix   8.6       0ca7abe54260   2 months ago     281MB
almalinux           8.6       90516e8d258d   6 months ago     189MB

# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                                              NAMES
ec700661b92c   almalinux-unbound:8.7   "/usr/sbin/unbound -d"   25 seconds ago   Up 25 seconds   0.0.0.0:10053->53/tcp, 0.0.0.0:10053->53/udp, :::10053->53/tcp, :::10053->53/udp   almalinux-unbound
1d769e4c9ee6   almalinux-squid:8.7     "/usr/sbin/squid -N …"   25 seconds ago   Up 25 seconds   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp                                          almalinux-squid
9ff38aa8b594   almalinux-postfix:8.7   "/startup.sh"            25 seconds ago   Up 25 seconds   0.0.0.0:25->25/tcp, :::25->25/tcp                                                  almalinux-postfix

以上で、AnsibleにてDockerコンテナを操作する手順は完了となる。

参考

人気の投稿