本記事には広告(アフィリエイトリンク)が含まれます。

Ansible入門 ad-hocとplaybook

Linuxエンジニア養成講座 第35回|全36回・フェーズ4「サーバー構築と運用」の8回目(8/9回目)です。
前回: 第34回で Git の基本操作(init / add / commit / diff / log / branch)を習得し、設定ファイルの変更履歴を管理する方法を実践しました。
今回学ぶこと: Ansible の基本を習得し、ad-hoc コマンドと playbook で複数サーバーの構成管理を自動化する方法を実践します。

前回の予告で「ansible-core を導入し、ad-hoc コマンドと簡単な playbook で alma-sub へのリモート操作を体験します。今回 Git で管理した設定ファイルを、複数サーバーに一括で配布する。その自動化の第一歩を踏み出します」とお伝えしました。Git は変更の「記録」を担うツールでした。今回の Ansible は、記録した設定を「実行」するツールです。Git で管理した設定ファイルを playbook に組み込み、複数サーバーに一括で適用する。記録と実行を組み合わせることで、構成管理の信頼性が大きく向上します。

この記事を読み終えると、以下のことができるようになります。

  • 手作業による構成管理の問題点と自動化の必要性を説明できる
  • Ansible のインベントリを作成し、ad-hoc コマンドで管理対象ホストを操作できる
  • playbook を書いて実行できる
  • --check --diff で変更内容を事前確認してから適用する手順を実践できる
  • 冪等性の概念を説明し、2回目の実行で changed=0 になることを確認できる

なぜ自動化が必要か

この講座で35回にわたって手作業でコマンドを打ってきました。ここで一つ考えてみてください。3年後、あなたの担当サーバーが50台になったとき、手は2本のままです。自動化しなければどうなるでしょうか。

時間の限界 ── 仕事は増え続けるが人は増えない

サーバー台数、サービス数、設定項目は増え続けます。運用担当者の人数はそのままです。手作業が増えるほど、「考える仕事」に使える時間が減っていきます。設計の見直し、運用改善、障害の根本原因分析といった、人間にしかできない判断に充てるべき時間を、手作業が食い潰していきます。

自動化の目的は「楽をする」ことではありません。「人間にしかできない判断に集中するため」です。

品質の限界 ── 人間は疲れると間違える

100台のサーバーに同じ設定を入れれば、確率的に数台でミスが起きます。ミスは金曜の夜、疲れた頭で発生します。緊急対応の焦りの中で発生します。playbook は何度実行しても同じ結果を返します。疲れません。焦りません。忘れません。

継続性の限界 ── 属人化を排除する

もし playbook がなく全て手作業だったら、チームで担当者が1人退職したとき何が起きるか、考えてみてください。

手順書を書いていても、書いた人にしか正しく実行できない暗黙知が残ります。「この設定は先にあちらのファイルを変えてから」「この行は環境によって値が違う」といった細かな判断が頭の中にだけ存在します。playbook はそれ自体が実行可能な手順書です。「誰がやっても同じ結果」を保証します。

シェルスクリプトとの違い

第14回第26回でシェルスクリプトを学びました。シェルスクリプトも自動化ツールの一つですが、Ansible の playbook とはアプローチが異なります。

シェルスクリプトは HOW(どうやるか)を記述します。「このコマンドを実行し、次にこのコマンドを実行する」という手順の記録です。対して Ansible の playbook は WHAT(どうあるべきか)を記述します。「このファイルがこの内容で存在すること」「このサービスが起動していること」というあるべき状態の宣言です。

この違いが「冪等性(べきとうせい)」という性質を生みます。冪等性とは、同じ操作を何回実行しても結果が変わらない性質のことです。シェルスクリプトで echo "設定値" >> /etc/config を2回実行すれば設定が二重に追記されます。Ansible の lineinfile モジュールで同じ行を指定すれば、既にその行が存在していれば何もしません。playbook を何回実行しても、結果は常に同じ状態に収束します。

Ansible の全体像

Ansible の構成要素を整理します。

コントロールノード: Ansible を実行する側のサーバーです。今回は alma-main が担います。

管理対象ノード: Ansible で操作される側のサーバーです。今回は alma-sub が担います。

Ansible の大きな特徴はエージェントレス設計です。管理対象ノードに Ansible をインストールする必要がありません。SSH 接続と Python さえあれば動作します。第27回で設定した SSH 鍵認証がそのまま使えます。

なぜエージェントレスなのか。管理対象が100台あるとき、100台に専用エージェントをインストールし、バージョンを揃え、死活監視し、アップデートし続けるコストを想像してみてください。SSH は Linux サーバーに最初から入っています。既存のインフラをそのまま利用するのが Ansible の設計思想です。

主要な用語を整理します。

  • インベントリ: 管理対象ノードの一覧を記述したファイル
  • モジュール: 「ファイルをコピーする」「サービスを起動する」などの個々の操作部品
  • タスク: モジュールに具体的なパラメータを指定した1つの操作
  • プレイブック(playbook): タスクをまとめた YAML ファイル。あるべき状態の定義書
  • ハンドラー: 特定のタスクで変更が発生した場合にだけ実行されるタスク(例: 設定変更後のサービス再起動)
Ansibleのエージェントレス設計。コントロールノード(alma-main)にAnsible・playbook・inventoryと秘密鍵を配置し、SSH鍵認証で複数の管理対象ノードを操作する。管理対象にはAnsibleのインストールが不要で、SSHとPythonがあれば動作する。100台でも同じplaybookで管理できる

Ansible をインストールする

alma-main にのみ ansible-core をインストールします。alma-sub には何もインストールしません。エージェントレスの体験です。

alma-mainで実行

実行コマンド:

$ sudo dnf install -y ansible-core

実行結果(末尾の要約部分):

Installed:
  ansible-core-1:2.14.18-1.el9.x86_64
  python3-cffi-1.14.5-5.el9.x86_64
  python3-cryptography-36.0.1-5.el9_6.x86_64
  python3-packaging-20.9-5.el9.noarch
  python3-ply-3.11-14.el9.noarch
  python3-pycparser-2.20-6.el9.noarch
  python3-pyparsing-2.4.7-9.el9.noarch
  python3-pyyaml-5.4.1-6.el9.x86_64
  python3-resolvelib-0.5.4-5.el9.noarch
  sshpass-1.09-4.el9.x86_64

Complete!

ansible-core と依存パッケージ(Python ライブラリ群)が計10パッケージインストールされます。ansible-core は Ansible の中核部分で、72個の組み込みモジュールが含まれています。フルパッケージの ansible(数千のモジュールを含む)とは別物です。今回の演習には ansible-core で十分です。

バージョンを確認します。

alma-mainで実行

実行コマンド:

$ ansible --version

実行結果:

ansible [core 2.14.18]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/developer/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /home/developer/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.21 (main, Feb 10 2025, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-5)] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

config file = /etc/ansible/ansible.cfg がデフォルトの設定ファイルです。python version = 3.9.21 は alma-main にインストール済みの Python です。Ansible は内部で Python を使ってモジュールを実行します。

インベントリファイルを作成する

管理対象ノードの一覧をインベントリファイルに記述します。まず作業ディレクトリを作成します。

alma-mainで実行

実行コマンド:

$ mkdir ~/ansible-practice && cd ~/ansible-practice

インベントリファイル inventory.ini を作成します。

alma-mainで実行

実行コマンド:

$ vi inventory.ini

以下の内容を記述します。

[servers]
alma-sub ansible_host=10.0.1.3

[servers:vars]
ansible_user=developer
ansible_ssh_private_key_file=/home/developer/.ssh/id_ed25519

各行の意味を説明します。

  • [servers]: グループ名です。複数のホストをまとめて操作するための分類です
  • alma-sub ansible_host=10.0.1.3: ホスト名 alma-sub と、接続先 IP アドレス 10.0.1.3 の対応付けです
  • [servers:vars]: servers グループ内の全ホストに適用される変数です
  • ansible_user=developer: SSH 接続に使うユーザー名です
  • ansible_ssh_private_key_file=...: SSH 接続に使う秘密鍵のパスです。第27回で設定した ed25519 鍵をそのまま指定しています

Ansible はパスワード認証でも動作しますが、実務では鍵認証を使います。Ansible は playbook を実行するたびに管理対象に SSH 接続するため、パスワード認証では毎回パスワードを入力するか、playbook にパスワードを書くことになります。毎回入力するのは自動化の意味がなく、ファイルにパスワードを書くのはセキュリティ上の問題があります。鍵認証であれば、パスワードの入力もファイルへの記載も不要で、安全に自動化できます。

管理対象が数百台規模になった場合の鍵管理についても触れておきます。コントロールノード側は1つの鍵ペアを使い、その公開鍵を全管理対象ノードに配布するのが基本です。鍵を管理対象ごとに分けるのではなく、Ansible 専用の鍵ペアを1つ作成し、全サーバーの authorized_keys にその公開鍵を登録します。

これは「1つの秘密鍵が漏洩すれば全台にアクセスされる」というリスクを伴います。そのため、秘密鍵はコントロールノードから外に持ち出さないこと、コントロールノード自体へのアクセスを厳格に制限すること(第27回で学んだ踏み台構成と同じ考え方です)が鉄則です。大規模な組織では、LDAP や Active Directory と連携した一元的なアカウント管理で鍵の配布を自動化することもあります。

ad-hoc コマンドで動かしてみる

playbook を書く前に、まず ad-hoc コマンドで Ansible の動作を体験します。ad-hoc コマンドは、1回限りの操作をコマンドラインから直接実行する方法です。

ping モジュールで疎通確認する

alma-mainで実行

実行コマンド:

$ cd ~/ansible-practice
$ ansible -i inventory.ini servers -m ping

実行結果:

alma-sub | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

SUCCESS"ping": "pong" が表示されれば成功です。ここで注意が必要なのは、Ansible の ping モジュールは ICMP の ping とは異なるということです。SSH で管理対象ノードに接続し、Python が実行できることを確認しています。ネットワークの疎通確認ではなく「Ansible として操作可能か」の確認です。

command モジュールでコマンドを実行する

alma-mainで実行

実行コマンド:

$ ansible -i inventory.ini servers -m command -a 'hostname'

実行結果:

alma-sub | CHANGED | rc=0 >>
alma-sub

alma-sub 上で hostname コマンドが実行され、結果が返ってきました。CHANGED と表示されていますが、実際にはサーバーの状態は変わっていません。command モジュールは実行結果を判断できないため、常に CHANGED を返します。この点は後ほど冪等性の説明で掘り下げます。

setup モジュールでサーバー情報を取得する

alma-mainで実行

実行コマンド:

$ ansible -i inventory.ini servers -m setup -a 'filter=ansible_hostname'

実行結果:

alma-sub | SUCCESS => {
    "ansible_facts": {
        "ansible_hostname": "alma-sub",
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false
}

setup モジュールは管理対象ノードのシステム情報(ファクト)を収集します。ホスト名、OS、IP アドレス、CPU、メモリなど多数の情報が取得できます。filter で必要な情報だけに絞り込んでいます。

ad-hoc コマンドの構造

ここまで実行した ad-hoc コマンドの構造を整理します。

ansible [対象] -i [インベントリ] -m [モジュール] -a '[引数]'
  • [対象]: 操作するグループ名またはホスト名(例: servers
  • -i [インベントリ]: インベントリファイルのパス(例: inventory.ini
  • -m [モジュール]: 使用するモジュール(例: ping, command, setup
  • -a '[引数]': モジュールに渡す引数(例: 'hostname', 'filter=ansible_hostname'

はじめての Playbook

YAML の基本

playbook は YAML(ヤムル)形式で記述します。最低限のルールを押さえておきます。

  • --- でファイルの開始を示す
  • インデントはスペース2個(タブは使えない)
  • - はリスト(配列)の要素
  • key: value は辞書(キーと値のペア)

インデントがずれるとエラーになります。vi で編集するときは :set expandtab shiftwidth=2 を設定しておくとタブがスペースに変換されます。

site.yml を作成して実行する

alma-mainで実行

実行コマンド:

$ vi ~/ansible-practice/site.yml

以下の内容を記述します。

---
- name: alma-sub の状態を確認する
  hosts: servers
  gather_facts: true

  tasks:
    - name: ホスト名を表示する
      ansible.builtin.debug:
        msg: "このサーバーは {{ ansible_hostname }} です"

    - name: OS情報を表示する
      ansible.builtin.debug:
        msg: "{{ ansible_distribution }} {{ ansible_distribution_version }}"

    - name: /tmp に確認ファイルを作成する
      ansible.builtin.copy:
        content: "Ansible で配置したファイルです\n"
        dest: /tmp/ansible-test.txt
        mode: '0644'

    - name: 確認ファイルの内容を表示する
      ansible.builtin.command: cat /tmp/ansible-test.txt
      register: result
      changed_when: false

    - name: ファイル内容を出力する
      ansible.builtin.debug:
        msg: "{{ result.stdout }}"

各部分を説明します。

  • name:: このプレイ(タスクのまとまり)の名前です。実行時にログに表示されます
  • hosts: servers: インベントリの servers グループを対象にします
  • gather_facts: true: 実行前にサーバー情報を自動収集します。ansible_hostnameansible_distribution はここで取得されます
  • tasks:: タスクのリストです。上から順に実行されます
  • ansible.builtin.debug: メッセージを画面に表示するモジュールです
  • ansible.builtin.copy: ファイルを配置するモジュールです。content: で内容を直接指定しています
  • register: result: コマンドの実行結果を変数 result に格納します
  • changed_when: false: このタスクは状態を変更しないため changed を報告しないよう指示しています

実行します。

alma-mainで実行

実行コマンド:

$ cd ~/ansible-practice
$ ansible-playbook -i inventory.ini site.yml

実行結果:

PLAY [alma-sub の状態を確認する] ***********************************************

TASK [Gathering Facts] *********************************************************
ok: [alma-sub]

TASK [ホスト名を表示する] ******************************************************
ok: [alma-sub] => {
    "msg": "このサーバーは alma-sub です"
}

TASK [OS情報を表示する] ********************************************************
ok: [alma-sub] => {
    "msg": "AlmaLinux 9.6"
}

TASK [/tmp に確認ファイルを作成する] *******************************************
changed: [alma-sub]

TASK [確認ファイルの内容を表示する] ********************************************
ok: [alma-sub]

TASK [ファイル内容を出力する] **************************************************
ok: [alma-sub] => {
    "msg": "Ansible で配置したファイルです"
}

PLAY RECAP *********************************************************************
alma-sub                   : ok=6    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

実行結果の読み方を確認します。

  • PLAY [...]: プレイの開始。name: で指定した名前が表示されます
  • TASK [...]: 各タスクの実行結果。ok は変更なし、changed は変更ありです
  • PLAY RECAP: 実行結果のサマリー。ok=6 は6タスク成功、changed=1 は1タスクで変更が発生したことを示します

冪等性を体験する

同じ playbook をもう1回実行してみてください。

alma-mainで実行

実行コマンド:

$ ansible-playbook -i inventory.ini site.yml

PLAY RECAPchanged=0 になります。/tmp/ansible-test.txt は既に存在し、内容が同じであれば変更は発生しません。これが冪等性です。

シェルスクリプトで同じことをすると違いが見えます。echo "テスト" >> /tmp/test.txt を2回実行すれば、ファイルに2行書き込まれます。Ansible の copy モジュールは「このファイルがこの内容で存在すること」を宣言しているだけなので、既に条件を満たしていれば何もしません。

先ほど ad-hoc コマンドで command モジュールが常に CHANGED を返した理由もここにあります。command モジュールは任意のコマンドを実行するだけで、「あるべき状態」を知りません。そのため冪等性を保証できず、常に「変更した」と報告します。可能な限り copylineinfileservice などの専用モジュールを使うべきです。

実践 Playbook ── サーバーの基本設定を自動化する

ここからは実務に近い playbook を作成します。サーバーの基本設定(ログインメッセージ配置、firewalld の起動確認、sshd_config の変更)を自動化します。

motd ファイルと playbook を作成する

まずログインメッセージのテンプレートファイルを作成します。

alma-mainで実行

実行コマンド:

$ mkdir -p ~/ansible-practice/files

実行コマンド:

$ vi ~/ansible-practice/files/motd

以下の内容を記述します。

==========================================
  このサーバーは Ansible で管理されています
  手動で設定を変更しないでください
==========================================

次に playbook を作成します。

alma-mainで実行

実行コマンド:

$ vi ~/ansible-practice/setup-server.yml

以下の内容を記述します。

---
- name: サーバーの基本設定を行う
  hosts: servers
  become: true

  tasks:
    - name: /etc/motd にログインメッセージを配置する
      ansible.builtin.copy:
        src: files/motd
        dest: /etc/motd
        owner: root
        group: root
        mode: '0644'

    - name: firewalld が起動していることを確認する
      ansible.builtin.service:
        name: firewalld
        state: started
        enabled: true

    - name: sshd_config で PermitRootLogin を no に設定する
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?PermitRootLogin'
        line: 'PermitRootLogin no'
        backup: true
      notify: sshd を再起動する

  handlers:
    - name: sshd を再起動する
      ansible.builtin.service:
        name: sshd
        state: restarted

この playbook のポイントを説明します。

become: true: このプレイ全体を sudo 権限で実行します。/etc 配下のファイルは root 権限が必要なため、この指定がないと Permission denied になります。

copy モジュール: src: でコントロールノード上のファイルパスを指定し、dest: で管理対象ノード上の配置先を指定します。所有者・権限も同時に設定できます。

service モジュール: state: started は「起動していること」、enabled: true は「自動起動が有効であること」を宣言します。既に起動済みなら何もしません。

lineinfile モジュール: ファイル内の特定の行を書き換えます。regexp: で対象行を正規表現で検索し、line: の内容に置き換えます。'^#?PermitRootLogin' は「行頭が PermitRootLogin または #PermitRootLogin で始まる行」にマッチします。backup: true を指定すると変更前のバックアップが自動作成されます。

handler の仕組み

notify: sshd を再起動するhandlers: セクションに注目してください。handler は、notify 元のタスクで変更(changed)が発生した場合にだけ実行されます。sshd_config を変更していなければ sshd の再起動は不要です。毎回無条件に再起動するのは無駄であり、接続中のセッションが切れるリスクもあります。「変更があったときだけ再起動」という動作を handler が実現します。

–check –diff ── 本番適用前の儀式

50台のサーバーに対してこの playbook を実行する場面を想像してください。実行する前に「何が変わるか」を確認したくなりませんか。

--check はドライランです。実際には変更を適用せず、「もし実行したらどうなるか」を表示します。--diff を併用すると、ファイルの変更差分も表示されます。第34回で学んだ git diff と同じ感覚で変更内容を確認できます。

alma-mainで実行

実行コマンド:

$ cd ~/ansible-practice
$ ansible-playbook -i inventory.ini setup-server.yml --check --diff

実行結果:

PLAY [サーバーの基本設定を行う] ************************************************

TASK [Gathering Facts] *********************************************************
ok: [alma-sub]

TASK [/etc/motd にログインメッセージを配置する] ********************************
--- before: /etc/motd
+++ after: /home/developer/ansible-practice/files/motd
@@ -0,0 +1,4 @@
+==========================================
+  このサーバーは Ansible で管理されています
+  手動で設定を変更しないでください
+==========================================

changed: [alma-sub]

TASK [firewalld が起動していることを確認する] **********************************
ok: [alma-sub]

TASK [sshd_config で PermitRootLogin を no に設定する] *************************
--- before: /etc/ssh/sshd_config (content)
+++ after: /etc/ssh/sshd_config (content)
@@ -37,7 +37,7 @@
 # Authentication:

 #LoginGraceTime 2m
-#PermitRootLogin prohibit-password
+PermitRootLogin no
 #StrictModes yes
 #MaxAuthTries 6
 #MaxSessions 10

changed: [alma-sub]

RUNNING HANDLER [sshd を再起動する] ********************************************
changed: [alma-sub]

PLAY RECAP *********************************************************************
alma-sub                   : ok=5    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

--- before+++ after の差分出力で、motd の内容が追加されること、#PermitRootLogin prohibit-passwordPermitRootLogin no に書き換わることがわかります。--check なので実際には変更されていません。この差分を確認してから本番適用する。これが --check --diff の使い方です。

–check –diff は省略しない。これを鉄則にしてください。

本番適用して確認する

差分を確認して問題がなければ、--check --diff を外して実行します。

alma-mainで実行

実行コマンド:

$ ansible-playbook -i inventory.ini setup-server.yml

適用が完了したら、alma-sub で結果を確認します。

alma-mainで実行(alma-sub にリモート確認)

実行コマンド:

$ ansible -i inventory.ini servers -m command -a 'cat /etc/motd'

実行結果:

alma-sub | CHANGED | rc=0 >>
==========================================
  このサーバーは Ansible で管理されています
  手動で設定を変更しないでください
==========================================

実行コマンド:

$ ansible -i inventory.ini servers -m command -a 'grep PermitRootLogin /etc/ssh/sshd_config' --become

実行結果:

alma-sub | CHANGED | rc=0 >>
PermitRootLogin no
# the setting of "PermitRootLogin without-password".

motd が配置され、sshd_config の PermitRootLogin が no に変更されていることを確認できました。

冪等性の確認として、もう1回同じ playbook を実行してみてください。

alma-mainで実行

実行コマンド:

$ ansible-playbook -i inventory.ini setup-server.yml

今度は PLAY RECAPchanged=0 になります。全タスクが「既にあるべき状態になっている」と判断し、何も変更しません。handler も実行されません。これが冪等性です。

ヒヤリハット ── –check なしで本番適用した結果

現場のヒヤリハット

あるエンジニアが sshd_config の PermitRootLogin を変更する playbook を書きました。lineinfileregexp にタイプミスがあり、意図しない行を書き換える内容になっていました。--check --diff を省略してそのまま全本番サーバー(30台)に適用した結果、全台の sshd が設定不正で再起動に失敗。SSH 接続ができなくなり、コンソールから1台ずつ手作業で復旧する事態になりました。

教訓:

  • --check --diff を省略しない。差分を目視確認してから適用する
  • 最初は1台に限定して適用する(--limit alma-sub オプション)。問題がなければ全台に展開する
  • backup: true を指定しておく。設定ファイルの変更前バックアップが自動で作成される

Playbook を Git で管理する

第34回で学んだ Git を早速使います。playbook を Git リポジトリで管理します。

alma-mainで実行

実行コマンド:

$ cd ~/ansible-practice
$ echo '*.retry' > .gitignore
$ git init
$ git add .
$ git commit -m "Ansible playbook の初期バージョンを追加"

実行結果:

[master (root-commit) 1bd6a2a] Ansible playbook の初期バージョンを追加
 5 files changed, 72 insertions(+)

.gitignore*.retry を追加しています。Ansible は playbook の実行に失敗したとき .retry ファイルを自動生成しますが、これは一時ファイルなので Git の管理対象から除外します。

Git(記録)と Ansible(実行)を組み合わせた運用サイクルを整理します。

  • playbook を変更する
  • git diff で変更内容を確認する
  • git commit で変更を記録する
  • ansible-playbook --check --diff で適用結果を事前確認する
  • ansible-playbook で適用する

この運用では、playbook の変更履歴が Git に残り、サーバーへの適用履歴が Ansible のログに残ります。「いつ・何を・なぜ変更し、どのサーバーに適用したか」を追跡できる体制が整います。

やってみよう

以下の課題に取り組んでください。playbook check-ntp.yml を書いて実行し、alma-sub の NTP 同期状態を確認します。

課題:

  • chronyd が起動し、自動起動が有効であることを保証する(service モジュール)
  • chronyc sources の実行結果を取得して表示する(command + register + debug
  • --check --diff で事前確認してから適用する

ヒント: ansible-doc ansible.builtin.service を実行すると、service モジュールの使い方とパラメータの一覧を確認できます。使ったことのないモジュールに出会ったら、まず ansible-doc で調べる習慣をつけてください。

考える時間を取ってから、以下の解答例を確認してください。

解答例

alma-mainで実行

実行コマンド:

$ vi ~/ansible-practice/check-ntp.yml

以下の内容を記述します。

---
- name: NTP 同期状態を確認する
  hosts: servers
  become: true

  tasks:
    - name: chronyd が起動・自動起動であることを保証する
      ansible.builtin.service:
        name: chronyd
        state: started
        enabled: true

    - name: NTP 同期状態を取得する
      ansible.builtin.command: chronyc sources
      register: ntp_result
      changed_when: false

    - name: NTP 同期状態を表示する
      ansible.builtin.debug:
        msg: "{{ ntp_result.stdout_lines }}"

まず --check --diff で事前確認します。

alma-mainで実行

実行コマンド:

$ cd ~/ansible-practice
$ ansible-playbook -i inventory.ini check-ntp.yml --check --diff

chronyd が既に起動済みであれば changed=0 になるはずです。問題がなければ本番適用します。

alma-mainで実行

実行コマンド:

$ ansible-playbook -i inventory.ini check-ntp.yml

実行結果(NTP 同期状態の表示部分):

TASK [NTP 同期状態を表示する] **************************************************
ok: [alma-sub] => {
    "msg": [
        "MS Name/IP address         Stratum Poll Reach LastRx Last sample               ",
        "===============================================================================",
        "^* y.ns.gin.ntt.net              2   6   177    18   +752us[+1623ms] +/-   89ms"
    ]
}

^* が付いている行が現在同期中の NTP サーバーです。chronyd の起動保証と NTP 同期状態の確認を1つの playbook で実行できました。

クリーンアップ: site.yml で作成した /tmp/ansible-test.txt を削除します。

alma-mainで実行

実行コマンド:

$ ansible -i inventory.ini servers -m file -a 'path=/tmp/ansible-test.txt state=absent' --become

Ansible をもっと知りたいときは

Ansible のドキュメントはコマンドラインから参照できます。

alma-mainで実行

実行コマンド:

$ ansible-doc -l

利用可能なモジュールの一覧が表示されます(ansible-core 2.14 では72モジュール)。特定のモジュールの使い方を調べるには以下のように実行します。

alma-mainで実行

実行コマンド:

$ ansible-doc ansible.builtin.copy

モジュールの全パラメータ、使用例、戻り値が確認できます。新しいモジュールを使うときは ansible-doc で確認する習慣をつけてください。man コマンドと同じ感覚です。

今回は触れませんでしたが、Ansible にはさらに多くの機能があります。名前だけ紹介しておきます。

  • ロール: playbook を再利用しやすい単位に分割する仕組み
  • コレクション: モジュールやロールをパッケージとして配布する仕組み
  • Vault: パスワードなどの機密情報を暗号化して管理する仕組み
  • テンプレート(Jinja2): 変数を埋め込んだ設定ファイルを生成する仕組み

playbook の考え方(あるべき状態を宣言し、冪等に適用する)は Ansible 固有のものではありません。Terraform / OpenTofu(クラウドインフラのプロビジョニング)や Kubernetes(コンテナのオーケストレーション)にも共通する設計思想です。今回学んだ「宣言的記述」と「冪等性」の概念は、今後どのツールに触れるときにも役立ちます。

Ansible と Terraform / OpenTofu は「同じことができるツール」と誤解されることがありますが、役割が異なります。Terraform / OpenTofu はクラウド上にVM・ネットワーク・ストレージなどのインフラリソースを作成・破棄するツールです。一方、Ansible は作成済みのサーバー上でパッケージを導入し、設定ファイルを配置し、サービスを起動する構成管理ツールです。「箱を作る」のが Terraform / OpenTofu、「箱の中身を整える」のが Ansible と覚えておくと、現場で混乱しません。

まとめと次回予告

今回は Ansible の基本を学びました。インベントリの作成、ad-hoc コマンドによる操作、playbook の記述と実行、handler による条件付きサービス再起動、そして --check --diff による事前確認を実践しました。冪等性という概念を理解し、2回目の実行で changed=0 になることを体験しました。

次回は第36回「トラブルシューティング(総合演習・最終回)」です。意図的に壊した環境を直す演習を通じて、これまで35回で学んだ知識を総動員します。CPU・メモリ・ディスクI/O・ネットワークの4リソース確認を鉄則として身につけます。

次回に進む前に: alma-main と alma-sub の両方で SP7 スナップショットを取得してください。次回のトラブルシューティング演習で、意図的に環境を壊す前の復元ポイントとして使用します。

ホストPCで実行(PowerShell)

実行コマンド:

PS> Checkpoint-VM -Name alma-main -SnapshotName "SP7_pre-troubleshoot"
PS> Checkpoint-VM -Name alma-sub -SnapshotName "SP7_pre-troubleshoot"
PS> Checkpoint-VM -Name alma-proxy -SnapshotName "SP7_pre-troubleshoot"

理解度チェック

以下の文が正しければ ○、誤りであれば × と答えてください。

問1: Ansible は管理対象ノードにも Ansible をインストールする必要がある。

答え: ×(Ansible はエージェントレス設計です。管理対象ノードには SSH と Python があれば動作します)

問2: playbook を2回実行すると設定が二重に適用される。

答え: ×(冪等性により、既にあるべき状態になっていれば何も変更しません。2回目は changed=0 になります)

問3: --check --diff は実際に変更を適用するオプションである。

答え: ×(--check はドライランです。変更を適用せず「もし実行したらどうなるか」を表示します)

問4: handler は notify 元のタスクで changed が発生した場合にだけ実行される。

答え: ○(変更がなければ handler は実行されません。不要なサービス再起動を防ぎます)

問5: command モジュールは冪等性が保証されないため、可能な限り専用モジュールを使うべきである。

答え: ○(command モジュールは任意のコマンドを実行するだけで「あるべき状態」を知らないため、常に changed を返します)

問6: playbook を Git で管理すると、変更履歴を記録できる。

答え: ○(Git で「いつ・誰が・なぜ変更したか」を記録し、Ansible で「どのサーバーに適用したか」を管理します)

シリーズ一覧

フェーズ1: エンジニアのいろは

フェーズ2: Linux基礎

フェーズ3: ネットワークとインフラ基盤

フェーズ4: サーバー構築と運用