Ansible Event-DrivenとPerformance Co-Pilot(PCP)ではじめるファイルシステム管理

こんにちは。Red Hatの呉と申します。普段はAnsible Automation Platform製品のテクニカルサポートエンジニアをしております。

今回は題名の通り、Ansible Event-DrivenとPerformance Co-Pilot(PCP)を使ったファイルシステム管理について紹介したいと思います。

Linuxサーバーを使っていたら、いつのまにかファイルシステムがいっぱいになっていたという状況は誰しも一度は経験したことがあるかと思います(私だけでしょうか笑)。 検証の最中にサーバーがフリーズしたりして面倒ですし、それだけならまだしも本番環境で発生したら、面倒では済まない事もあるかと思います。 そこで、ファイルシステムがいっぱいになるのを未然に防ぐ方法は無いかと思い今回の方法を思いつきました。

Performance Co-Pilot(PCP)の設定

Performance Co-Pilot (PCP)は、システムレベルのパフォーマンス測定を監視、視覚化、保存、および分析するためのオープンソースソフトウェアです。RHEL9以降、PCPではWebhookを利用できるようになりました。これにより、「システムがあるパフォーマンスルールに該当する状況になったら、Webhookを発行する」という事ができるようになりました。

まずは参考までに、監視対象のLinuxサーバーにPCPをセットアップする手順を記載します。せっかくなのでAnsibleでセットアップします。

  1. インベントリファイルを作成

まずはインベントリファイルを作成します。IPアドレスやsudoパスワード等は管理対象サーバーのログイン情報に合わせて適宜変更してください。 参考:インベントリファイルを構築する方法

inventory.yaml

---
all:
  children:
    pcp_servers:
      hosts:
        192.168.57.10:
          ansible_become_pass: password
        192.168.57.11:
          ansible_become_pass: password


2. Playbookの作成

次に、以下のコードを使用してプレイブックを作成します。CHANGEMEの部分は、ルールブックを実行するサーバーのIPアドレスもしくはFQDNに置き換えてください。

pre_setup.yaml

---
- hosts: pcp_servers
  become: true
  gather_facts: no
  vars:
    webhook_endpoint: "http://CHANGEME:5000/endpoint"

  tasks:
    - name: Install pcp packages on the managed nodes
      ansible.builtin.dnf:
        name: "{{ item }}"
        state: latest
      loop:
        - pcp

    - name: Install additional packages for stress test and analyze performance
      ansible.builtin.dnf:
        name: "{{ item }}"
        state: latest
      loop:
        - sysstat
        - stress-ng

    - name: Start and enable pmcd service
      ansible.builtin.systemd_service:
        name: pmcd
        state: started
        enabled: true

    - name: Start and enable pmcd service
      ansible.builtin.systemd_service:
        name: pmie
        state: started
        enabled: true

    - name: Start and enable pmcd service
      ansible.builtin.systemd_service:
        name: pmlogger
        state: started
        enabled: true

    - name: Configure global webhook_action as "yes"
      command: "pmieconf -f /var/lib/pcp/config/pmie/config.default modify global webhook_action yes"

    - name: Configure webhook endpoint
      command: "pmieconf -f /var/lib/pcp/config/pmie/config.default modify global webhook_endpoint {{ webhook_endpoint }}"
      loop: "{{ groups['pcp_servers'] }}"
      notify: Restart pmie

  handlers:
    - name: Restart pmie
      ansible.builtin.systemd_service:
        name: pmie
        state: restarted
        enabled: true


3. プレイブックの実行

以下のコマンドでプレイブックを実行してください:

$ ansible-playbook -i inventory.yaml pre_setup.yaml

以上で、PCPサービスを実行するための初期セットアップが完了しました。

Ansible Rulebookの実行環境を用意する

ルールブックを実行するために、まずはルールブックの実行に必要となる環境を用意する必要があります。ルールブックの実行環境としては以下2パターンが考えられます。
1. ルールブックの実行環境を含んだコンテナをダウンロードして、コンテナで実行する。
2. ansible-rulebookおよびJavaなど、必要なパッケージをサーバーにインストールする。

今回は簡単に実行環境を用意したいので、コンテナを利用する方法を記載したいと思います。2をご希望の場合は、公式サイトをご参考ください。

ansible.readthedocs.io

それでは実行環境を構築していきます。

まずは、Red Hat quay.io registryレジストリーから配布されているansible-rulebookコンテナイメージをダウンロードします。

$ podman pull quay.io/ansible/ansible-rulebook

次に、以下のルールブックを用意します。各ルールの解説については、ファイル内のコメントをご参考ください。

pcpEventRules.yaml

---
- name: Listen for RHEL Performance Co-Pilot events
  hosts: all
  sources:
    - ansible.eda.webhook:
        host: 0.0.0.0
        port: 5000

  rules:
    - name: Respond to PMIE rule filesys.filling
      #ファイルシステムが100%フルの場合、Ansibleは管理対象ノードにログインできません。
      #このため、ファイルシステムが100%でない場合のみこのルールを起動します。
      condition: "event.payload.pcp.pmie.message is not match('100%',ignorecase=true) and event.payload.pcp.pmie.rule == 'File system is filling up'" 
      action:
        run_playbook:
          name: /work/filesys.filling.yaml
    
    # 100%フルの場合は、監視対象ノードにログインせず、ファイルシステムがいっぱいになっていることを通知するだけにします。
    #通知用プレイブックの実装は今回はスキップします。
    #- name: Launch PMIE rule filesys.filling when the managed node file system is 100% full
    # condition: "event.payload.pcp.pmie.message is match('100%',ignorecase=true) and event.payload.pcp.pmie.rule == 'File system is filling up'" 
    #  action:
    #    run_playbook:
    #      name: playbooks/manage_filesystems/notify_filesystem_usage.yaml

    #デバッグ用
    - name: Display any contents of event.payload variable
      condition: event.payload is defined
      action:
        debug:
          msg: "Received: {{ event.payload }}"

ちなみに、PCPが送ってくるWebhookの中身はこんな感じになっています。

"pcp": {
    "pmie": {
        "rule": "File system is filling up",
        "hostname": "192.168.122.182",
        "message": "99%used[/dev/mapper/rhel-root]@192.168.122.182"
        }
    }
}

ルールブックが起動するプレイブックも実装しましょう。なお、このプレイブックでは実装例として「/tmpディレクトリ配下で7日間利用が無かったファイルを削除する」というタスクを実装していますが、もしサイズの大きなファイルやディレクトリがあらかじめわかっている場合はそれらをピンポイントで削除するといったタスクなども実装すると役に立つかと思います。ここはそれぞれご利用の環境や運用状況に合わせて実装してください :)

filesys.filling.yaml

---
- name: EDA response to PMIE rule file.filling
  hosts: "{{ ansible_eda.event.payload.pcp.pmie.hostname }}" #Webhookを送信してきたホストに対してプレイブックを実行したいので、受信したWebhookからホスト名を取り出します。
  become: true
  gather_facts: false  #<---管理対象ノードは現在パフォーマンスが低下しているため、不要な負荷をかけるタスクは避けます。
  tasks: 
    - name: Display ansible_eda.event.payload.pcp.pmie.message variable
      debug:
        msg: "ansible_eda.event.payload.pcp.pmie.message value: {{ ansible_eda.event.payload.pcp.pmie.message }}"
   
    - name: Display ansible_eda.event.payload.pcp.pmie.rule variable
      debug:
        msg: "ansible_eda.event.payload.pcp.pmie.rule value: {{ ansible_eda.event.payload.pcp.pmie.rule }}"

    #7日間利用がなかったファイルを見つける
    - name: Find files unused for 7 days
      command: find /tmp -type f -atime +7
      register: unused_files

    #7日間利用がなかったファイルを削除する
    - name: Delete unused files for 7 days
      ansible.builtin.file:
        path: "{{ unused_files.stdout }}"
        state: absent
      register: deleted_files

    #削除したファイルを表示する
    - name: Showing the deleted files
      debug: var=deleted_files

    #削除されたファイルをチャットツールに送信するタスク。これはCiscoのWebexに送信しています。実装はお好みで。
    #- name: Cisco Webex Teams - Text Message to a Room
    # community.general.cisco_webex:
    #    recipient_type: roomId
    #   recipient_id: "{{ room_id }}"
    #    msg_type: markdown
    #   personal_token: "{{ token }}"
    #    msg: "{{ lookup('ansible.builtin.template', './templates/chatMsg.j2') }}"

次に、コンテナを起動します。以下の例では、quay.io/ansible/ansible-rulebook イメージを使用してコンテナを起動し、プロジェクトディレクトリをマウントしてポート5000をリッスンします。/home/vagrant/work/project を実際のプロジェクトディレクトリに置き換えてください。

$ sudo podman run -p 5000:5000  -v /home/vagrant/work/project:/work:z quay.io/ansible/ansible-rulebook ansible-rulebook --rulebook /work/pcpEventRules.yaml -i /work/inventory.yaml --verbose

監視対象ノードでターミナルを開き、curl コマンドを実行して、リクエストがAnsible Event-Drivenサーバーに到達するか確認してください。これにより、PCPからのWebhookがEvent-Drivenサーバーに到達できるか確認できます。IPアドレス192.168.122.11は実際のAnsible Event-Drivenサーバーのホストアドレスに置き換えてください。

$ curl -H 'Content-Type: application/json' -d "{\"message\": \"Ansible is super cool\"}" 192.168.122.11:5000

リクエストが実行中のAnsible Event-Drivenサーバーに到達すると、次のようなログ出力が表示されます:

** 2024-12-19 06:45:46.221739 [debug] ******************************************
Received: {'message': 'Ansible is super cool'}
********************************************************************************

動作テスト

それでは、管理対象サーバーに巨大なファイルを作成して、動作テストをしてみましょう。 ルールブックを起動したまま、以下のようなコマンドを管理対象サーバーで実行します。
たとえば、5GBのダミーファイルを作成する場合:

dd if=/dev/zero of=/tmp/testfile bs=1M count=5120

ファイルサイズを調整しながら、使用率が 95% 程度になるまでファイルを追加してください。
ファイル使用率の確認コマンド:

df -h

筆者の監視対象ノードでは、こんな感じでファイルシステムをいっぱいにしました。この状態でしばらく待ってみましょう。

$ df -h
Filesystem             Size  Used Avail Use% Mounted on
devtmpfs               4.0M     0  4.0M   0% /dev
tmpfs                  3.8G     0  3.8G   0% /dev/shm
tmpfs                  1.5G  9.4M  1.5G   1% /run
/dev/mapper/rhel-root   35G   34G  1.4G  97% /    #<---here
/dev/vda1             1014M  397M  618M  40% /boot
tmpfs                  768M   36K  768M   1% /run/user/976
tmpfs                  768M  100K  768M   1% /run/user/1000

筆者の環境では3分ほど待つと、先ほど作成した、ファイルを削除するプレイブックfile.filling.yamlが実行されるのを確認できました。

なお、うまく再現できない場合や待っていられない場合は、PCPが送信するWebhookの中身を監視対象ノードからCurlで飛ばしてテストする手段もあります。
例えばこちらのように実行できます。endpointのIPは、ルールブックを実行中のサーバーのものに置き換えてください。

$ curl -X POST http://192.168.122.11:5000/endpoint -H "Content-Type: application/json" -d '{
           "pcp": {
             "pmie": {
               "rule": "File system is filling up",
               "hostname": "192.168.122.183",
               "message": "99%used[/dev/mapper/rhel-root]@192.168.122.183"
             }
           }
         }'

まとめ

いかがでしたでしょうか。PCPにはファイルシステムの監視ルール「filling.filesystem」の他にも、現時点で32個ほどルールがありますので、この仕組みはシステムのパフォーマンス監視に役立てると思います。
※PCPのルールについてはpmieconf rulesコマンドでご確認できます。

このクリスマスや年末年始を平和に過ごすための1つの手段として、ぜひご検討ください。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。