Ansible入門②：dnf, command,file等のモジュールを実行する。

## 1. 概要

この記事では、Ansibleの基本的なモジュールの使い方を学びます。Ansibleモジュールとは、特定のタスクを実行するためのコードの単位です。モジュールはPlaybookの中で呼び出され、ターゲットホストで特定の操作を行います。

今回は以下のモジュールについて説明します。

- **dnf**：パッケージ管理モジュール- パッケージのインストール、更新、削除
- **service**：サービス管理モジュール - システムサービスの起動、停止、有効化、無効化
- **command**：コマンド実行モジュール - リモートホスト上でコマンドを実行
- **debug**：デバッグ情報表示モジュール - 変数の内容や情報をプレイブック実行中に表示
- **service_facts**：サービス情報収集モジュール - システム上のサービスの状態や設定を収集
- **file**：ファイル・ディレクトリ管理モジュール - ファイルやディレクトリの作成、削除、権限変更

Ansibleを使ってnginxをインストールし、設定、確認するプロセスを通じてこれらのモジュールの使い方を学ぶのが目的です。

## 2. 環境構築

こちらからCfnテンプレートをローカルに保存してください
https://github.com/super-masaya/ansible-gakusyuu/blob/main/ansible_gakusyuu.yaml

※保存したファイルの161-162行目を削除して、自身で作成したキーペアのpemファイルの内容を張り付けてください。

※このキーは公開せず、安全に管理してください。

```
-----BEGIN RSA PRIVATE KEY-----
# Insert the actual private key content here
-----END RSA PRIVATE KEY-----
```

環境構築については、前回の記事「[Ansible入門①：環境準備編](https://qiita.com/supermasaya11/items/8f9155965ad8c969ff35)」の「2. 環境構築手順」から「3. Ansible Masterサーバのセットアップ」を参照してください


※ロンドンリージョン(eu-west-2)で実行してください（テンプレートはロンドンリージョン用に作成されています）

※今回のCfnテンプレートはansibleを実行するための事前準備は整えられているので、ansibleインストール等の作業は不要です。

## 3. 実践手順

ルートに切り替えます
```bash
sudo su -
```

ansibleユーザに切り替えます
```bash
su - ansible
```

全ターゲットへ疎通確認できるか確認します
```bash
ansible -i ~/ansible/inventory.ini all_targets -m ping
```

出力結果
```bash
ansible-dev-target1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
ansible-test-target2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
ansible-test-target1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
ansible-dev-target2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
[ansible@ip-10-0-1-100 ~]$
```

## 4. プレイブックとは

プレイブック（Playbook）は、Ansibleにおける自動化の中心的な概念です。YAMLで記述され、以下の特徴があります。

- **宣言型の設定ファイル**：実行したいタスクを順番に記述します
- **複数のホストに対する一連の処理**：一度の実行で複数サーバーに同じ設定を適用できます
- **冪等性（べきとうせい）**：同じプレイブックを複数回実行しても結果が変わらない性質を持ちます
- **構成管理のコード化**：インフラの状態をコードとして管理できます

プレイブックは「プレイ」と呼ばれる単位で構成されます。

1. **name**: プレイに説明的な名前を付与
2. **hosts**：対象となるホストグループの指定
3. **tasks**：実行するタスクのリスト
4. **become**：権限昇格の設定（rootユーザーなど）

## 5. プレイブック作成

プレイブックを作成します
```bash
vi install_nginx.yaml
```

この内容を張り付けてください

※実際にどのモジュールを使うべきか探す場合には公式マニュアルから使用できるモジュールを参照してください。
[Ansible documentation](https://docs.ansible.com/)
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html

```yaml
# Playbook: Setup nginx on dev_servers and test_servers host groups

- name: Setup nginx server on myserver list (host group)
  hosts:
    - dev_servers
    - test_servers
  become: yes
  
  tasks:
    # Package management module: Install the latest version of nginx
    - name: Install the latest version of nginx
      ansible.builtin.dnf:
        name: nginx
        state: latest
  
    # Service management module: Start the nginx service
    - name: Start nginx service
      ansible.builtin.service:
        name: nginx
        state: started
  
    # Service management module: Enable nginx service to start at boot
    - name: Enable nginx service
      ansible.builtin.service:
        name: nginx
        enabled: yes
  
    # Command execution module: Get nginx version information
    - name: Get nginx Installed version
      ansible.builtin.command: nginx -v
      register: nginx_version
      changed_when: false
  
    # Debug module: Display the retrieved nginx version
    - name: Display nginx version
      ansible.builtin.debug:
        var: nginx_version.stderr
  
    # Fact gathering module: Get service status information
    - name: Get status of nginx service
      ansible.builtin.service_facts:
  
    # Debug module: Display nginx service status
    - name: Display nginx service status
      ansible.builtin.debug:
        msg: "Nginx service status: {{ ansible_facts.services['nginx.service'].state }}"
```

貼り付けたら`:wq`で保存して下さい

## 6. プレイブック実行

プレイブックを実行していきます
```bash
ansible-playbook -i ansible/inventory.ini install_nginx.yaml
```

出力結果
```bash
PLAY [Setup nginx server on myserver list (host group)] **********************************

TASK [Gathering Facts] *******************************************************************
ok: [ansible-dev-target1]
ok: [ansible-test-target2]
ok: [ansible-test-target1]
ok: [ansible-dev-target2]

TASK [Install the latest version of nginx] ***********************************************
changed: [ansible-dev-target1]
changed: [ansible-test-target2]
changed: [ansible-test-target1]
changed: [ansible-dev-target2]

TASK [Start nginx service] ***************************************************************
changed: [ansible-dev-target1]
changed: [ansible-test-target2]
changed: [ansible-test-target1]
changed: [ansible-dev-target2]

TASK [Enable nginx service] **************************************************************
changed: [ansible-dev-target1]
changed: [ansible-test-target2]
changed: [ansible-test-target1]
changed: [ansible-dev-target2]

TASK [Get nginx Installed version] *******************************************************
ok: [ansible-dev-target1]
ok: [ansible-test-target2]
ok: [ansible-test-target1]
ok: [ansible-dev-target2]

TASK [Display nginx version] *************************************************************
ok: [ansible-dev-target1] => {
    "nginx_version.stderr": "nginx version: nginx/1.24.0"
}
ok: [ansible-dev-target2] => {
    "nginx_version.stderr": "nginx version: nginx/1.24.0"
}
ok: [ansible-test-target1] => {
    "nginx_version.stderr": "nginx version: nginx/1.24.0"
}
ok: [ansible-test-target2] => {
    "nginx_version.stderr": "nginx version: nginx/1.24.0"
}

TASK [Get status of nginx service] *******************************************************
ok: [ansible-dev-target1]
ok: [ansible-test-target2]
ok: [ansible-dev-target2]
ok: [ansible-test-target1]

TASK [Display nginx service status] ******************************************************
ok: [ansible-dev-target1] => {
    "msg": "Nginx service status: running"
}
ok: [ansible-dev-target2] => {
    "msg": "Nginx service status: running"
}
ok: [ansible-test-target1] => {
    "msg": "Nginx service status: running"
}
ok: [ansible-test-target2] => {
    "msg": "Nginx service status: running"
}

PLAY RECAP *******************************************************************************
ansible-dev-target1        : ok=8    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-dev-target2        : ok=8    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-test-target1       : ok=8    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-test-target2       : ok=8    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

## 7. PLAY RECAPの説明

PLAY RECAPは、Ansibleのプレイブック実行の最終的な結果を示す重要な出力です。

- **ok=8**: 8つのタスクが正常に実行されました（変更あり・なしを含む）
- **changed=3**: 3つのタスクがターゲットホストで実際に変更を行いました（nginxのインストール、サービスの起動、サービスの有効化）
- **unreachable=0**: 接続できなかったホストはありませんでした
- **failed=0**: 失敗したタスクはありませんでした
- **skipped=0**: スキップされたタスクはありませんでした
- **rescued=0**: エラーが発生しましたが、rescue機能で救済されたタスクはありませんでした
- **ignored=0**: 無視されたエラーはありませんでした

この出力から、すべてのターゲットホストで同じ数のタスクが実行され、すべて成功したことがわかります。また、各ホストで3つのタスク（パッケージのインストール、サービスの起動、サービスの有効化）で実際に変更が行われたことがわかります。

### 7.1 詳細な情報を取得するオプション

以下のオプションを追加することでより詳細な情報を取得できます

```
-v: 基本的な出力
-vv: より詳細な出力
-vvv: デバッグレベルの出力（SSHの接続情報なども含む）
-vvvv: 最も詳細な出力（モジュールのソースコードも含む）
```

例
```bash
ansible-playbook -i ansible/inventory.ini install_nginx.yaml -v
```

## 8. fileモジュールでディレクトリとファイルを作成

fileモジュールを追加していきます
```bash
vi install_nginx.yaml
```

以下の内容を追記してください
```yaml
    # File management module: Create directory
    - name: Create directory
      ansible.builtin.file:
        path: /home/ansible/filemodule
        state: directory
  
    # File management module: Create empty file
    - name: Create file
      ansible.builtin.file:
        path: /home/ansible/filemodule/filemodule.txt
        state: touch
```

追加したら`:wq`で保存してください。

```bash
ansible-playbook -i ansible/inventory.ini install_nginx.yaml -v
```

出力結果（抜粋）
```bash
Using /home/ansible/.ansible.cfg as config file

PLAY [Setup nginx server on myserver list (host group)] **********************************

TASK [Gathering Facts] *******************************************************************
ok: [ansible-test-target2]
ok: [ansible-dev-target1]
ok: [ansible-test-target1]
ok: [ansible-dev-target2]

TASK [Install the latest version of nginx] ***********************************************
ok: [ansible-dev-target1] => {"changed": false, "msg": "Nothing to do", "rc": 0, "results": []}
ok: [ansible-test-target2] => {"changed": false, "msg": "Nothing to do", "rc": 0, "results": []}
ok: [ansible-test-target1] => {"changed": false, "msg": "Nothing to do", "rc": 0, "results": []}
ok: [ansible-dev-target2] => {"changed": false, "msg": "Nothing to do", "rc": 0, "results": []}

~情報が多いので抜粋~

TASK [Create directory] ******************************************************************
changed: [ansible-dev-target1] => {"changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/home/ansible/filemodule", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 6, "state": "directory", "uid": 0}
changed: [ansible-test-target2] => {"changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/home/ansible/filemodule", "secontext": "unconfined_u:object_r:user_home_t:s0", "size":6, "state": "directory", "uid": 0}
changed: [ansible-test-target1] => {"changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/home/ansible/filemodule", "secontext": "unconfined_u:object_r:user_home_t:s0", "size":6, "state": "directory", "uid": 0}
changed: [ansible-dev-target2] => {"changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/home/ansible/filemodule", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 6, "state": "directory", "uid": 0}

TASK [Create file] ***********************************************************************
changed: [ansible-dev-target1] => {"changed": true, "dest": "/home/ansible/filemodule/filemodule.txt", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 0}
changed: [ansible-test-target1] => {"changed": true, "dest": "/home/ansible/filemodule/filemodule.txt", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 0}
changed: [ansible-dev-target2] => {"changed": true, "dest": "/home/ansible/filemodule/filemodule.txt", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 0}
changed: [ansible-test-target2] => {"changed": true, "dest": "/home/ansible/filemodule/filemodule.txt", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 0}

PLAY RECAP *******************************************************************************
ansible-dev-target1        : ok=10   changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-dev-target2        : ok=10   changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-test-target1       : ok=10   changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-test-target2       : ok=10   changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

## 9. PLAY RECAPの説明(2回目)

2回目のプレイブック実行の結果を示すPLAY RECAPからは、以下の情報が読み取れます。

- **ok=10**: 前回より2つ増えて10個のタスクが正常に実行されました（fileモジュールの2つのタスクが追加された）
- **changed=2**: 今回は2つのタスクだけが変更を行いました（ディレクトリとファイルの作成）
- **unreachable=0**: 接続できなかったホストはありませんでした
- **failed=0**: 失敗したタスクはありませんでした

前回すでにnginxのインストールや設定が完了しているため、それらのタスクは`changed=false`（変更なし）となり、実行されましたが変更は行われませんでした。これはAnsibleの冪等性（べきとうせい）の特性を示しています。同じタスクを何度実行しても、必要な時だけ変更が行われ、すでに望ましい状態の場合は変更されません。

今回の実行では、新たに追加したfileモジュールのタスク（ディレクトリとファイルの作成）のみが実際に変更を行ったことがわかります。

## 10. 結果確認

ターゲットに設定が反映されたかターゲット1にssh接続して確認します。

```bash
    ssh -i .ssh/ansible-test.pem ec2-user@10.0.1.101
```

出力結果（`yes`でエンターしてください）
```bash
The authenticity of host '10.0.1.101 (10.0.1.101)' can't be established.
ED25519 key fingerprint is SHA256:lL53BrIeU36+awe1wusKxHlXnPu9eimpZPNDlxuSLQY.
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.1.101' (ED25519) to the list of known hosts.
```


nginxの稼働状況確認
```bash
systemctl status nginx
```

出力結果
```bash
● nginx.service - The nginx HTTP and reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: disabled)
     Active: active (running) since Sun 2025-06-01 09:30:38 UTC; 31min ago
 Invocation: 4ea9000a2516492fbe40d819ffb03af8
   Main PID: 2217 (nginx)
      Tasks: 3 (limit: 5139)
     Memory: 2.9M (peak: 3M)
        CPU: 38ms
     CGroup: /system.slice/nginx.service
             ├─2217 "nginx: master process /usr/sbin/nginx"
             ├─2218 "nginx: worker process"
             └─2220 "nginx: worker process"

Jun 01 09:30:38 ip-10-0-1-101.eu-west-2.compute.internal systemd[1]: Starting nginx.service - The nginx HTTP and reverse proxy server...
Jun 01 09:30:38 ip-10-0-1-101.eu-west-2.compute.internal nginx[2214]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Jun 01 09:30:38 ip-10-0-1-101.eu-west-2.compute.internal nginx[2214]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Jun 01 09:30:38 ip-10-0-1-101.eu-west-2.compute.internal systemd[1]: Started nginx.service - The nginx HTTP and reverse proxy server.
```

fileモジュールで作成したファイルの確認
```bash
ls -la /home/ansible/filemodule
```

出力結果
```bash
total 0
drwxr-xr-x. 2 root root 28 Jun  1 09:56 .
drwxr-xr-x. 3 root root 24 Jun  1 09:56 ..
-rw-r--r--. 1 root root  0 Jun  1 09:56 filemodule.txt
```

nginxが正常に起動してることと、ディレクトリとファイルが作成されていることが確認できました！

## 11. 後片付け(リソースの削除)

検証が終わったら、AWS リソースを削除してコストが発生しないようにしましょう。
「Ansible入門①：環境準備編」(https://qiita.com/supermasaya11/items/8f9155965ad8c969ff35) の「6. CloudFormationテンプレートで作成したリソースの削除手順」を参照して、作成したリソースを削除してください。


## 12. まとめ

今回はplaybookを実行して、ターゲットのサーバにnginxのインストールやディレクトリ、ファイルの作成をして少し実践的なハンズオンを実施しました。

まだまだAnsible入門記事を投稿していくので是非ご覧になってください:relaxed: