🐈

Relearning Ansible 2025

に公開

2017年に RedHat の Ansible を使用して以来 Ansible を使っていませんでしたが、今年2025年の Ansible に追いつこうとした作業メモです。用語がふさわしくなかったり、開発者から見ると中途半端な調査で断定的な物言いをしている場合もありますが、利用者としての理解と立ち上がりの早さを重視しています。致命的な個所は修正しますのでご指摘ください。

セットアップ

以前は RedHat のリポジトリから取得できる rpm で環境を構築していました。今回は WLS 上の Ubuntu を使用します。

Installing and upgrading Ansible with pipx の記事に従います。

pipx

pip とは異なり、コマンド実行用の venv をユーザの環境にセットアップして、OSの環境を変更せずに特定のコマンドを必要な venv 環境で実行できるようにセットアップするツールです。が、pipx 本体のインストールは root 権限での環境変更になっています。Install pipx の記載に従います。

sudo apt update
sudo apt install pipx
pipx ensurepath

最後のコマンドで ~/.profile が変更され、~/.local/binPATH に含まれるようになります。

Ansible

pipx を使ってインストールします。

$ pipx install --include-deps ansible

~/.local/bin/ にコマンドが増えます。 --include-deps オプションは依存先パッケージのコマンドも bin に追加するための指定で、実際に使用する ansibleansible-playbook のコマンドは実は依存先の ansible-core に存在するコマンドです。このオプションを指定しなくても、ansible等のコマンドは ~/.local/share/pipx/venvs/ansible/bin に存在しますが、このオプションによって ~/.local/bin/ に適切なシンボリックリンクを作成してくれます。

なお ansible の依存関係は以下のようになっていました。

ansible==11.5.0
└── ansible-core [required: ~=2.18.5, installed: 2.18.5]
    ├── Jinja2 [required: >=3.0.0, installed: 3.1.6]
    │   └── MarkupSafe [required: >=2.0, installed: 3.0.2]
    ├── PyYAML [required: >=5.1, installed: 6.0.2]
    ├── cryptography [required: Any, installed: 44.0.2]
    │   └── cffi [required: >=1.12, installed: 1.17.1]
    │       └── pycparser [required: Any, installed: 2.22]
    ├── packaging [required: Any, installed: 25.0]
    └── resolvelib [required: >=0.5.3,<1.1.0, installed: 1.0.1]

ここまでで作成した仮想環境 ansible に モジュール argcomplete を追加し、コマンドラインで ansible 関係のコマンドのオプションにもTAB補完が効くようにします。

$ pipx inject --include-apps ansible argcomplete

--include-apps オプションにより追加で3つのコマンドが有効になりますがこれが OS (/usr/bin) に既に存在している旨の警告が出ます。PATH の優先順は ~/.local/bin の方が高いので、ansible しか使わない前提であれば特に問題は発生しないと思われます。

エディタは VS Code を使うことになると思います。Ansible 拡張を使用したくなると思うのでこの実行に必要な ansible-lint を追加します。

$ pipx inject --include-apps ansible ansible-lint

ここまでで有効になったコマンドを確認します。

$ pipx list

これ以外にモジュールが必要になるケースなど、 pipx が作成した仮想環境内で pip を実行する必要がある場合、pipx inject <仮想環境名> ではなく pipx runpip <仮想環境名> を実行するようです。

インベントリがない状態で ansible のコマンドが動作することを ping モジュールを使用して確かめます。インベントリがない場合、 localhost の指定のみが有効となるようです。また、 ping モジュールは ICMP の ping ではなく、ansible での接続(ssh等)が行えるかのテストなので、接続先がない今の段階では接続方法に local を指定します。

$ ansible -c local -m ping localhost

エディタの準備

前述のとおり VS Code を前提とします。おそらく RedHat の Ansible 拡張をインストールするでしょう。
Ansible Extention の設定 にあるとおり、

  • ./playbooks ディレクトリの下の YAML ファイル
  • .ansible.yml または .ansible.yaml の(二重)拡張子がついたファイル
  • site.yml や site.yaml のような Ansible で認識される特別な名前のファイル
  • *playbook*.yml や *playbook*.yaml のように名前に playbook を含むファイル

は ansible の playbook の書き方に準じた強調表示や補完が期待できるようになります。これ以外のファイルにも対応が必要なら settings.json で割り当てて設定します。

インベントリの作成

デフォルトのインベントリは /etc/ansible/hosts ですが、ansible.cfg を作成してデフォルトを変更します。

ansible.cfg
[defaults]
inventory = ./inventory

ファイルの配置を以下のようにします。

$ tree .
.
├── ansible.cfg
├── inventory
│   ├── linux
│   ├── ontap
│   └── windows
└── playbooks
    └── init.yaml

インベントリの各ファイルは OS の種類ごとにグループを指定します。また、フォーマットは
Assigning a variable to one machine: host variables
にあるホスト変数の設定の見やすさから INI ファイルのフォーマットを採用します。

linux
[linux]
10.0.0.10
ontap
[ontap]
10.0.0.20
windows
[windows]
10.0.0.30

ansible コマンドでグループの設定が正しいことを確認します。ここでは --list-hosts を指定して対象となるホストの確認だけを行います。-m オプションでモジュールを指定せず、--list-hosts だけを指定すると対象ホストの確認ができます。

$ ansible --list-hosts linux
  hosts (1):
    10.0.0.10
$ ansible --list-hosts ontap
  hosts (1):
    10.0.0.20
$ ansible --list-hosts windows
  hosts (1):
    10.0.0.30

接続用の設定

OS の種類ごとにAnsibleのグループを分けたのは、接続の方式が OS の種類ごとに異なることを考慮したものです。グループに属するホストに共通の変数を設定するには group_vars ディレクトリを作成し、グループ名と同じファイル名の yml ファイルを作成します。

$ tree .
.
├── ansible.cfg
├── inventory
│   ├── group_vars
│   │   ├── linux.yml   (追加)
│   │   ├── ontap.yml   (追加)
│   │   └── windows.yml (追加)
│   ├── linux
│   ├── ontap
│   └── windows
└── playbooks
    ├── test_linut.yaml
    ├── test_ontap.yml
    └── test_win.yml

接続設定:linux

最終的に ssh であっても root ユーザのパスワード認証は無効にすることと思います。可能な限りこの環境のままセットアップが可能になるべきなので、ansible での操作も一般ユーザでログインしたあと、su または sudo を行うことになります。一般ユーザでの接続時に鍵を使わずパスワード認証を行うためには、 sshpass または paramiko モジュールの追加が必要なようです。

$ sudo apt install sshpass

または

$ pipx runpip ansible install paramiko

また、susudo とでは root ユーザの PATH の内容が異なる可能性を考慮してください。 PATH の内容は接続先が Ubuntu なのか RedHat なのか、または他のディストリビューションなのかで事情が変わると思います。
過去に Web で見つけたプレイブックを参考にしたところ、PATH の違いから shell モジュールで起動するコマンドの動作が異なり、想定外の結果となった経験があります。
一般的な話をすると、パスの参照先は OS のディストリビューションに元から存在するファイルだけを参照し、参照先の数は少ないほうが危険は少ないといえるはずです(shell モジュールを使わなくて済めばそれに越したことはありません)。

inventory/group_vars/linux.yml
ansible_user: username
ansible_password: user_password
ansible_become_user: root
ansible_become_password: root_password
ansible_become: true
ansible_become_method: su

公開鍵を配置するには authorized_key モジュールが使用できます。プレイブックの作成や実施結果の確認のために ssh 接続する機会は少ないくないので配置しておくと捗るでしょう。

ansible -m authorized_key -a "user=username key='$(cat ~/.ssh/id_ed25519.pub)'" linux

接続設定:ontap

ONTAP を操作する REST API は Python のモジュール netapp-lib によって発行されます。pipx で ansible をインストールしたときこのモジュールも仮想環境中にあり、REST API の呼び出し元もコントローラー(ansible コマンドを実行するマシン)自身になります。このため、ontapを操作するタスクでは delegate_to: localhost の指定が必要です。
また、プレイブックには gather_facts: false の指定をしておかないと ssh 接続を試行して失敗します。

変数設定は以下のとおり。

inventory/group_vars/ontap.yml
netapp_hostname: "10.0.0.20"
netapp_username: "admin"
netapp_password: "password"

プレイブックのサンプルは以下のとおり。

playbooks/test_ontap.yml
- name: Test
  hosts: ontap
  gather_facts: false
  become: false
  tasks:
    - name: Test ontap_info
      delegate_to: localhost
      netapp.ontap.na_ontap_info:
        hostname: "{{ netapp_hostname }}"
        username: "{{ netapp_username }}"
        password: "{{ netapp_password }}"
        https: true
        validate_certs: false
      register: ontap_info

    - name: Display ONTAP Info
      ansible.builtin.debug:
        var: ontap_info

接続設定:windows

接続方式には winrmpsrpssh が選べます。手元の環境で使用できるのが winrm だったのでこれを指定します。[1]

また、接続方式を決める根拠として Ansible で設定を変更する前の Windows の状態をどこまでセットアップしておくかも考えなければいけません。ホスト名の変更とドメインへの参加が大きな分岐点と思われますが、ここでは

  • ホスト名は手作業で変更する
    IPアドレスと合わせて手作業での変更

  • ドメインへの参加は任意
    参加済みでも、これから参加してもよい

という状態を想定し、ローカル管理者での NTML 認証とします[2]。変数設定は以下のようになります。

inventory/group_vars/windows.yml
ansible_connection: winrm
ansible_user: administrator
ansible_password: "{{ windows_password }}"
ansible_winrm_transport: ntlm
ansible_port: 5985

Windows用には Powershell で使用できる Desired State Configuration という仕組みがあり(Windows Server 2012 R2 以降)、Ansible では win_dsc タスクで同等の呼び出しが可能らしいです。他の win_ タスクを使うか win_dsc タスクを使うかは、どちらで必要な結果を得られるか・どちらに慣れているか・どちらかに避けるべきバグがあるか、を考慮しつつ妥当と思われるほうを使ってよいという状態のようです。

Windows のタスクの例

playbooks/test_win.yml
---
- name: Get Windows Hostname
  hosts: windows
  tasks:
    - name: Get the hostname
      ansible.windows.win_shell: !
        hostname
      register: hostname_result

    - name: Display the hostname
      ansible.builtin.debug:
        var: hostname_result.stdout

Vault: パスワード等の隠匿

パスワードが記載されたファイルは暗号化しておくべきでしょう。

$ ansible-vault encrypt inventory/group_vars/linux.yml

パスワードの入力(2回)が求められると思います。
編集するときには encrypt の代わりに edit を指定するとエディタが開きます。

参照するときにはパスワードを入力するオプションをつけてあげる必要があります。

$ ansible-playbook playbooks/test-linux.yml --ask-vault-pass

さすがに少々煩雑なので、暗号化するときに使用したパスワードをファイルに保存して自動的に参照するようにします。

ansible.cfg
[defaults]
vault_password_file = vault_pass.txt  (追加)
inventory = ./inventory

valult_pass.txt.gitignore に追加し、valut 用パスワードを記入しておきます。

このファイルは絶対にコミットしてしまわれないようにしてください。
パーミッションも 600 に変更しておくべきでしょう。

このあと

個々のタスクはオンラインドキュメントを探しながら適切なものを見つけて適用していけばいいのですが、

  • 何を基準にホストのグループ分けをするか
  • 何をロールにして、何をプレイブックにするか
  • プレイブックでグループを指定するかホストを指定するか

は、マニュアルやベストプラクティスを見てもそれが必ずしも自分の直面しているサーバ群の規模や複雑さの度合いに適合するとは限りません。サーバが5台のときと、30台を超えたり可変だったりする場合には要求される自動化の具合が異なっているでしょう。

それと同時に、Ansibleで設定したから間違いない、というのは間違いです。設定変更が期待した動作を引き起こすかは Ansible とは違った観点で確認する必要があり、この確認作業を自動化することも重要な要素だと考えます。

設定変更の自動化と同じくらい、もしくはそれ以上に、テストの自動化にも力を注ぐべきでしょう。

脚注
  1. いまからやり直すのであれば ssh にしておいて、覚えなければならないことが多少なりとも減ることを期待したいところです。その場合は Windows SSH を参照。 ↩︎

  2. この場合、リモートリソースへのアクセスができないことが考えられます。 ↩︎

Discussion