構成管理ツール入門|Ansibleでサーバーの状態をそろえる

構成管理ツール入門|Ansibleでサーバーの状態をそろえる

IaC でサーバーやネットワークを作れるようになっても、そのサーバーの中に何を入れ、どう設定するかは別の仕事として残ります。

パッケージの版や設定ファイルの 1 行は、手で直していくうちにサーバーごとに少しずつずれていきます。

このずれを、書いておいた「あるべき状態」へ何度でもそろえ直すための道具が、Ansible に代表される構成管理ツールです。

何度流しても結果が変わらない冪等性が分かると、プレイブックを手順書ではなく状態の記述として読める理由にも説明がつきます。

私も DevOps を学んでいる途中なので、Ansible の公式ドキュメントと Red Hat の資料を足場に、手元で小さく動かして確かめながら整理しました。

構成管理は、作ったサーバーの中身を受け持つ

Red Hat は構成管理を、IT システムとその構成要素を望ましい状態に保つためのプロセスと説明しています。

一度正しく設定して終わりにせず、ライフサイクルを通して期待どおりに動き続けるようにするのが目的です。

定義の原文は What is configuration management?(Red Hat) で読めます。

What is configuration management

同じ IaC の仲間でも、Terraform とは受け持つ範囲が違います。

Red Hat の比較記事は、Terraform はインフラを作ることに、Ansible はその中で動くものを設定することに特化していると整理しています。

Terraform がサーバーやネットワークを用意し、そのあと Ansible がソフトウェアの導入やセキュリティの設定を引き継ぐ分担です。

稼働を始めたあとのパッチ当てや設定変更も、Ansible の受け持ちとされていました。

ただし境界は厳密ではなく、Ansible で環境を作ることも、Terraform で簡単な設定スクリプトを流すこともできると同じ記事は書いています。

比較の全文は Ansible vs. Terraform(Red Hat) にあります。

Ansible vs. Terraform

Terraform の側から見た、インフラを宣言的に書いて差分を確かめてから反映する流れは、IaC 入門の記事で扱いました。

IaC入門|インフラをコードで管理する理由

Ansible の設計の原則として公式ドキュメントが最初に挙げているのは、管理される側に追加のソフトウェア(エージェント)を入れない作りです。

接続には SSH と OS の既存の認証情報を使い、プレイブックと呼ぶ YAML のファイルに対象の望ましい状態を書いていきます。

原則の一覧は Introduction to Ansible(Ansible Community Documentation) にまとまっています。

Introduction to Ansible — Ansible Community Documentation

冪等性があるから、何度でも流せる

同じ原則の一覧には、もう一つの柱として冪等性が並んでいます。

Ansible の用語集は、1 回実行した結果と、間に何もせず何度も繰り返した結果がまったく同じになる操作を冪等と定義しています。

プレイブックの解説ページによると、多くのモジュールは望む状態がすでに実現しているかを先に確かめ、実現していれば何もせずに終わります。

同じページには、すべてのプレイブックやモジュールがそう振る舞うわけではないという注意も書かれていました。

説明の元は Ansible playbooks(Ansible Community Documentation) の「Desired state and idempotency」の節です。

Ansible playbooks — Ansible Community Documentation

2 回目が changed=0 になるか

手元の検証は、WSL2 の Ubuntu 26.04 に作った一時的な Python の仮想環境(Python 3.14.4)に ansible-core 2.21.4 を入れて行いました。

接続先は自分自身にしていて、SSH で別のサーバーへ流す操作は実行していません。

管理対象の一覧であるインベントリ(inventory.ini)は、次の 2 行です。

[local]
localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python3

Python の場所を固定しているのは、自動の検出に任せると警告が出たためです。

プレイブック(site.yml)には、ディレクトリと設定ファイルがどうなっているべきかを書きました。

- name: 設定ファイルをそろえる
  hosts: local
  gather_facts: false
  vars:
    base: "{{ playbook_dir }}/demo"
  tasks:
    - name: 設定用のディレクトリがある
      ansible.builtin.file:
        path: "{{ base }}/conf"
        state: directory
        mode: "0755"

    - name: 設定ファイルの中身がこうなっている
      ansible.builtin.copy:
        dest: "{{ base }}/conf/app.conf"
        content: |
          port=8080
          log_level=info
        mode: "0644"

file モジュールには「このディレクトリがある」状態を、copy モジュールには「ファイルの中身はこれ」という状態を渡しているだけです。

同じプレイブックを 2 回続けて流すと、最後の集計は次のようになりました。

$ ansible-playbook -i inventory.ini site.yml   # 1 回目
...
localhost                  : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ ansible-playbook -i inventory.ini site.yml   # 2 回目
...
TASK [設定用のディレクトリがある] ***...
ok: [localhost]

TASK [設定ファイルの中身がこうなっている] ***...
ok: [localhost]
...
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

1 回目は 2 つのタスクがどちらも changed になり、ディレクトリとファイルが作られました。

2 回目は、すでに望む状態になっていたので何も変えずに終わっています。

2 回目に changed=0 が並ぶことが、プレイブックが手順ではなく状態を書けている目印になります。

流す前に差分を見る

次に、誰かがサーバーへ直接入って設定を変えた場面を、手でファイルを書き換えて再現しました。

app.conf の log_level を debug に変えてから、--check と --diff を付けて流した結果です。

$ sed -i "s/log_level=info/log_level=debug/" demo/conf/app.conf
$ ansible-playbook -i inventory.ini site.yml --check --diff
...
TASK [設定ファイルの中身がこうなっている] ***...
--- before: .../demo/conf/app.conf
+++ after: .../demo/conf/app.conf
@@ -1,2 +1,2 @@
 port=8080
-log_level=debug
+log_level=info

changed: [localhost]
...
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

check mode では対象に変更を加えずに流れ、対応しているモジュールが「加えるはずだった変更」を報告します。

diff mode は変更の前と後を並べて見せる機能で、2 つを組み合わせると、ずれの中身を見てから反映するかどうかを決められます。

実際、この時点のファイルは debug のままで、--check を外して流し直したときに初めて info へ戻りました。

Red Hat の比較記事によると、Ansible は Terraform のような状態ファイルを持たず、変更する前に対象のシステムへ直接問い合わせます。

冪等なタスクを繰り返し流すことで、ずれが大きくなる前に抑える考え方です。

公式ドキュメントは、check mode に対応していないモジュールは何も報告せず、何もしないとも書いています。

またdiff の出力には機密情報が出ることがあるので、タスクごとに diff: false で止められるようになっています。

どちらの注意も Validating tasks: check mode and diff mode(Ansible Community Documentation) に書かれています。

Validating tasks: check mode and diff mode — Ansible Community Documentation

押し込むか、取りに来させるか

Ansible の既定は、操作する側からサーバーへ設定を押し込むプッシュ型です。

用語集は、プッシュ型ならサーバーからの問い合わせを待たずに、いつどのサーバーと通信するかを細かく制御できると説明しています。

逆向きの仕組みとして、ansible-pull というコマンドも用意されています。

各サーバーが cron で Git から設定を取り出し、自分自身に対してプレイブックを流す仕組みです。

公式ドキュメントはこれを、Ansible のプッシュ型をプル型へ反転させるもので、大規模な展開にも定期的な修復にも役立つと説明しています。

コマンドの説明は ansible-pull(Ansible Community Documentation) のページです。

ansible-pull — Ansible Community Documentation

管理される側にエージェントを常駐させる Puppet では、各サーバーのエージェントが定期的に設定を当て直します。

Puppet のドキュメントによると、エージェントのサービスは既定で 30 分ごとに設定の適用を実行します。

間隔を延ばせば、プライマリサーバー 1 台でより多くのエージェントを受け持てるとも書かれていました。

設定の手順は Run Puppet agent as a service(Puppet) に載っています。

NO IMAGE

このように仕様を定期的に当て直す運用を、Kief Morris 氏は 2013 年の記事で構成の同期(Configuration Synchronization)と呼びました。

道具の外でサーバーに加えた変更は、次の同期のときに中央で決めた構成へ戻されます。

記事の本文は Configuration Synchronization(martinfowler.com) で読めます。

Configuration Synchronization

GitOps 入門の記事では、同じ向きの違いをデプロイの文脈で比べ、本番の側が Git を取りに来て合わせ続ける運用を扱いました。

GitOps入門|Gitを「本番の正解」の置き場にする運用

取り入れるときの落とし穴

冪等性は Ansible が自動で保証してくれるものではなく、書き方しだいで簡単に崩れます。

シェルのコマンドは毎回 changed になる

シェルで 1 行追記するタスクと、lineinfile モジュールで「この 1 行がある」と書いたタスクを並べて、2 回流しました。

- name: 追記のしかたを比べる
  hosts: local
  gather_facts: false
  tasks:
    - name: シェルで 1 行追記する
      ansible.builtin.shell: echo "port=8080" >> "{{ playbook_dir }}/demo/shell.conf"

    - name: この 1 行がファイルにある
      ansible.builtin.lineinfile:
        path: "{{ playbook_dir }}/demo/line.conf"
        line: port=8080
        create: true
        mode: "0644"

2 回目の結果と、2 つのファイルの中身は次のとおりです。

TASK [シェルで 1 行追記する] ***...
changed: [localhost]

TASK [この 1 行がファイルにある] ***...
ok: [localhost]
...
$ cat demo/shell.conf
port=8080
port=8080
$ cat demo/line.conf
port=8080

シェルのコマンドは「もう済んでいるか」を Ansible が判断できないので、流すたびに実行され、行が 2 つに増えました。

2 回目にも changed が出るタスクは、冪等になっていない疑いがあります。

専用のモジュールがあればそちらを使い、どうしてもコマンドを流すなら、command モジュールの creates のように「やる必要があるか」の条件を添えます。

creates に指定したファイルがすでにあれば、そのタスクは実行されません。

check mode でも、creates か removes を付けたときだけ変更の有無を予測でき、付けていなければタスクは飛ばされます。

この挙動は ansible.builtin.command module(Ansible Community Documentation) の Notes に書かれています。

ansible.builtin.command module – Execute commands on targets — Ansible Community Documentation

書いていない部分はずれていく

構成管理ツールがそろえるのは、プレイブックに書いた部分だけです。

先ほどの Morris 氏の記事は、サーバーのすべてを道具で管理するのは現実的でないと指摘しています。

管理する要素ごとに、レシピを書き、テストし、保守する手間がかかるからです。

実際には、チームは自分たちに関係の深い一部に注意を集中し、残りは OS の既定のままにしているとも書いていました。

ふだんはそれで回りますが、管理の外にある部分が問題を起こすと、原因を追いにくくなります。

サーバーが長く動くほど、ほかのサーバーとの差は広がっていきます。

同じ記事は、この問題がサーバーを短い寿命で作り直す運用や、稼働中は一切変更しないイミュータブルサーバーへ人を向かわせるとも述べています。

作り直す運用では、イメージを作る側に回る

Red Hat の比較記事は、Ansible を稼働中のシステムをその場で変更するミュータブルなインフラ向けの道具と位置づけています。

簡単には置き換えられないサーバーへのパッチやファイアウォールの調整が、その代表例として挙がっていました。

一方で、作り直す運用に切り替えても Ansible の出番がなくなるわけではありません。

マシンイメージを作る Packer には Ansible のプロビジョナーがあり、イメージを作る途中のマシンへプレイブックを流せます。

仕組みの説明は Ansible Provisioner(HashiCorp Developer) にあります。

Ansible Provisioner | Integrations | Packer | HashiCorp Developer

置き換えにくいサーバーは稼働中のまま Ansible でそろえ、置き換えられるサーバーはイメージを作る段階でプレイブックを流す、という分け方ができます。

最後に

構成管理ツールは、サーバーの中身のあるべき状態を書き、何度流しても同じ結果になるように当て直す道具です。

2 回目に changed=0 が並ぶかを見れば、書いたプレイブックが状態を記述できているかを確かめられます。

check mode と diff でずれの中身を見てから反映できるのも、サーバーに入って直接直すやり方との大きな違いでした。

書いていない部分は管理の外に残るので、作り直す運用と組み合わせるかどうかまで含めて、道具の受け持ちを決めておくと迷いにくくなります。

以上です。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA