Ansible - 自動化の手始め

検証環境で検証用VMを作成する際に、毎回OSをインストールして、検証環境用初期設定をするのが手間なので、Ansibleを使用して簡単に初期設定を完了できるようにしたいと思います。
VMはVCenter環境で作成しているので、OSインストール部分はテンプレートを使用して簡便にします。テンプレート化は特に難しいことはないので省略します。

今回は手始めなので、chrony.confファイルを書き換えて、ntp設定を行う簡単なPlaybookを作成します。

Ansibleコントローラの準備

Ansibleを使用するには、まずAnsibleコマンドを実行するサーバ(コントローラサーバ)を構築します。以下の構成で作ります。

  • AlmaLinux 9.6
  • ansible-core 2.14.1
  • python3-3.9.2

Ansibleインストール

まずはansible-coreをインストールし、ansible用ユーザを作成します。Pythonも必要になります。Pythonがインストールされていない場合は別途インストールする必要があります。

[root@ansible-ctl01 ~]# dnf install -y ansible-core

[root@ansible-ctl01 ~]# useradd user01
[root@ansible-ctl01 ~]# passwd user01

これだけでansibleを実行できるようになります。

操作対象サーバとなるクライアントサーバ作成

ansibleクライアント用VMを作成します。VMはテンプレートから作成します。テンプレートの主な設定は以下としています。

  • OSはminimalインストール
  • ネットワークはDHCP
  • rootでSSHログイン可能

クライアント側ではansibleで設定する前に、準備として以下を実行します。

固定IPアドレスの割り当て

VM作成直後はDHCPでIPアドレスを割り当てているので、固定IPに設定します。

[root@alma96 ~]# nmcli c m ens192 ipv4.address 10.0.220.10/16 ipv4.gateway 10.0.0.254 ipv4.dns 10.0.1.23 ipv4.method manual
[root@alma96 ~]# nmcli c up ens192

ホスト名の変更

ホスト名もテンプレート用のホスト名が付いているので変更します。

[root@alma96  ~]# hostnamectl set-hostname ansible-client01

ansible用ユーザの作成

クライアントで作成するユーザは、管理を簡略化する為、コントローラと同じユーザ名にしています。また、ユーザがsudoできるようにwheelグループに追加しておきます。

[root@ansible-client01 ~]# useradd user01
[root@ansible-client01 ~]# passwd user01

[root@ansible-client01 ~]# usermod -aG wheel user01

コントローラ側での準備

AnsibleはSSHを使用してコントローラからクライアントへ通信します。その為、コントローラ側の準備としてクライアントのfingerprintを登録しておく必要があります。
fingerprintを登録しておかないと、初回SSHログイン時にfingrprintを登録するかの確認が行われる為、ansibleの処理が止まってしまいます。
fingerprintの登録はansible用に作成したuser01で実行します。

[user01@ansible-ctl01 ~]$ ssh 10.0.220.10
The authenticity of host '10.0.220.10 (10.0.220.10)' can't be established.
ED25519 key fingerprint is SHA256:Sux22rS5K/48aMJPwRPL9wZ4piguP2Wc3ftlvJHuUrs.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.0.220.10' (ED25519) to the list of known hosts.
user01@10.0.220.10's password:
Last login: Tue Aug 26 10:15:21 2025 from 192.168.1.23

known_hostsにfingerprintが登録されていることが確認できます。

[user01@ansible-ctl01 ~]$ cat .ssh/known_hosts
10.0.220.10 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICw6pIsshUKysnoiheh9/FwTHbP77ujnfp7ZHGByOHg83
10.0.220.10 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0WbncibioQsPLLyPhlQV/2qRMotjfP633KtIHtaWZVtHax+J+2QzLvGuKO342VRGO/10KfMaXDM9+35/LeCXizXlSqfz6vjH0xAlNbfdbHNJ6vtdNHbmU6tLUvvpJP46f9mIHjARfusYCLJ90/2xj/+86LjQji75wuIvFcYInVaE9i6ecAOJjUQX8SPIyAPNzv6HNKc4Xxtj/VI6a6FUwIPS5jKyynhwKIgdHEugjfHUHEBCBS+QQWnM+JSadtu8z+kZWIrbGejP3vsrH+rxXnplpEw+hWwzKxutbWqQqpR7VgwC+M3Y2ShbKeCUOG+FztmnT7bCA3TC6cB88MV7OK7eUJoZjirzD76rfkH+FBt85E6oxDHGXSOBhtOXMu9JiA9FI3JD3mYtmaOXNDriNMNFf/BsuI2x+dw8u+HRd0mhe4dDDSllFCo6pD/RXakDrgRcrzhtF7U+PLbGRWgTFjtHIVKwndoSSXCBTAtNDwPkgoPYDXvWv753phW9COJw9auCk=
10.0.220.10 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAYHWCpAABBBC7zZ057HAOc4dH7UyFf1Q9+V9ZtUgZlHH8JPTBZx9lPxQ3BUbwpSmmJAcf7gonlBOJnan38MrjNONChBlw2yMI

Playbookの作成

では、Playbookを作成していきます。
ユーザのホームディレクトリに「ansible」というディレクトリを作成して、この中にファイルを置いていきます。

inventoryファイル作成

まずは操作対象となるクライアントサーバのリストを作成します。リスト内にansibleで使用するユーザ名とパスワードを記載します。左から順に、ホスト名、IPアドレス、ログインユーザ名、ログインパスワード、sudoパスワードになります。

[user01@ansible-ctl01 ~]$ cat ansible/hosts.ini
ansible-client01 ansible_host=10.0.220.10 ansible_user=user01 ansible_password=userpass ansible_become_password=userpass

Playbook作成

処理内容を記載するPlaybookを作成します。

[user01@ansible-ctl01 ~]$ cat ansible/chrony_setting.yml
---
- name: Configure NTP servers in chrony.conf
  hosts: all
  become: true

  vars:
    ntp_servers:
      - 10.0.1.3

  tasks:
    - name: Replace chrony.conf
      template:
        src: chrony.conf.j2
        dest: /etc/chrony.conf
        mode: 0644
        owner: root
        group: root
      notify: Restart chronyd

  handlers:
    - name: Restart chronyd
      service:
        name: chronyd
        state: restarted

hostsで対象ホストを指定しています。allはすべてを指定することを意味しますが、今回は1台しか用意していないので1台のみが実行対象になります。

becomeは対象サーバ上にてsudoで処理を実行する必要がある場合にtrueにします。

Playbook内でtemplateを使用しています。テンプレートファイルの一部をvarsで指定した値に書き換えてクライアントの/etc/chrony.confに配置します。

handlersではファイル置き換え処理を行った場合に実行する処理を記載しています。今回のPlaybookではchronydサービスの再起動を行っています。handlersの呼び出しは、tasks内のnotifyでnameを指定して呼び出しています。

templateは以下のように、元々のchrony.confのNTPサーバ指定部分を変数に置き換えたものをchrony.conf.j2として用意しておきます。プログラムを書いたことがある人なら、なんとなくどのような処理が行われるか想像できるかと思います。
ansibleで編集したことがわかるようにコメントも入れておきます。

・変更前

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
pool 2.almalinux.pool.ntp.org iburst

# Use NTP servers from DHCP.
sourcedir /run/chrony-dhcp
~ 省略 ~

・変更後

# Ansible-managed chrony.conf

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
#pool 2.almalinux.pool.ntp.org iburst
{% for server in ntp_servers %}
server {{ server }} iburst
{% endfor %}

# Use NTP servers from DHCP.
sourcedir /run/chrony-dhcp
~ 省略 ~

テスト実行

準備が整ったので、テスト実行してみます。
「--check」オプションをつけると、実際には処理が行われず、実行結果を確認できます。

[user01@ansible-ctl01 ~]$ ansible-playbook -i ansible/hosts.ini ansible/chrony_setting.yml --check

PLAY [Configure NTP servers in chrony.conf] *****************************************************************************

TASK [Gathering Facts] **************************************************************************************************
ok: [ansible-client01]

TASK [Replace chrony.conf] **********************************************************************************************
changed: [ansible-client01]

RUNNING HANDLER [Restart chronyd] ***************************************************************************************
changed: [ansible-client01]

PLAY RECAP **************************************************************************************************************
ansible-client01           : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

「changed」となった個所が実際に変更が行われる処理になります。上記の結果から、chrony.confの置き換えとchronydサービスの再起動が行われることがわかります。

実行

では、「--check」オプションを外して実行し、処理が行われることを確認します。

[user01@ansible-ctl01 ~]$ ansible-playbook -i ansible/hosts.ini ansible/chrony_setting.yml

PLAY [Configure NTP servers in chrony.conf] *****************************************************************************

TASK [Gathering Facts] **************************************************************************************************
ok: [ansible-client01]

TASK [Replace chrony.conf] **********************************************************************************************
changed: [ansible-client01]

RUNNING HANDLER [Restart chronyd] ***************************************************************************************
changed: [ansible-client01]

PLAY RECAP **************************************************************************************************************
ansible-client01           : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

テスト実行時と同じ結果になりました。正常に処理されたようです。
クライアント側で確認してみます。

[user01@ansible-client01 ~]$ head -n 10 /etc/chrony.conf
# Ansible-managed chrony.conf

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
#pool 2.almalinux.pool.ntp.org iburst
server 10.0.1.3 iburst

# Use NTP servers from DHCP.
sourcedir /run/chrony-dhcp

[user01@ansible-client01 ~]$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 10.0.1.3                     3   6    37    18  -2640ns[ -771us] +/- 4609us

NTPサーバの指定がPlaybookのvarsで指定したIPになり、chronydサービス再起動によって時刻同期が行われるようになっていることが確認できます。

冪等性の確認

ansibleは冪等性が保たれる※1はずなので、もう一度実行して変更が行われないことを確認します。

※1:冪等性はPlaybook内の処理内容によっては保たれない場合もあります。

[user01@ansible-ctl01 ~]$ ansible-playbook -i ansible/hosts.ini ansible/chrony_setting.yml

PLAY [Configure NTP servers in chrony.conf] *****************************************************************************

TASK [Gathering Facts] **************************************************************************************************
ok: [ansible-client01]

TASK [Replace chrony.conf] **********************************************************************************************
ok: [ansible-client01]

PLAY RECAP **************************************************************************************************************
ansible-client01           : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

2回目は「changed」がなく、変更が行われていないことがわかります。
TASK [Replace chrony.conf]が実行されなかったことで、初回実行時には処理が行われていたRUNNING HANDLER [Restart chronyd]の処理が呼び出されていないことがわかります。

Playbookを変更

Playbookを変更して実行し、もう一度変更処理が実行されることを確認します。Playbookの中のvarsで、NTPサーバのIPを指定していますが、複数指定することも可能なので、nictのNTPサーバを追加して実行してみます。
yamlではキーの各要素の先頭に「-」をつけて列挙するとリスト型として認識されます。

  vars:
    ntp_servers:
      - 10.0.1.3
      - ntp.nict.jp

上記のようにPlaybookを変更して、実行します。

[user01@ansible-ctl01 ~]$ ansible-playbook -i ansible/hosts.ini ansible/chrony_setting.yml

PLAY [Configure NTP servers in chrony.conf] *****************************************************************************

TASK [Gathering Facts] **************************************************************************************************
ok: [ansible-client01]

TASK [Replace chrony.conf] **********************************************************************************************
changed: [ansible-client01]

RUNNING HANDLER [Restart chronyd] ***************************************************************************************
changed: [ansible-client01]

PLAY RECAP **************************************************************************************************************
ansible-client01           : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

編集前のクライアントの状態はIP一つのみの指定でしたが、varsにもう一つNTPサーバを追加したので、現状とPlaybookが指示する状態が異なります。その為、処理が行われました。

クライアントで確認してみます。

[user01@ansible-client01 ~]$ head -n 10 /etc/chrony.conf
# Ansible-managed chrony.conf

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
#pool 2.almalinux.pool.ntp.org iburst
server 10.0.1.3 iburst
server ntp.nict.jp iburst

# Use NTP servers from DHCP.
sourcedir /run/chrony-dhcp
[user01@ansible-client01 ~]$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 10.0.1.3                     3   6    77     8   -101us[ -204us] +/- 5583us
^? ntp-b2.nict.go.jp             0   7     0     -     +0ns[   +0ns] +/-    0ns

二つ目のNTPサーバも正しくchrony.confに追加されました。
※弊社環境ではNTP通信が直接外部に出ていけないので、nictのNTPサーバと同期できていない状態です。

まとめ

confファイルを置き換えてサービスを再起動するという基本的な処理を自動化できました。
ただし、今回のPlaybookはパスワードを平文で記載していたり、SSHパスワード接続を使用していたりとセキュリティ的によろしくないので、このまま使用することはできません。パスワード暗号化や鍵認証を用いた方法に変更する必要があります。

参考

お問い合わせ

弊社では様々なサービスを取り扱っております。
詳細はサービス一覧からご覧ください。

お気軽にお問い合わせください。応対時間 9:30-17:30 [ 土・日・祝日除く ]

お問い合わせ