Ansible Roleをすこれ ～ 構造化したPlaybookの書き方

この記事は[Qiita Ansible Advent Calendar 2024](https://qiita.com/advent-calendar/2024/ansible)の19日目の記事です。

---

今年はAnsible関連で比較的大きな案件を複数担当しました。

その中で「Ansibleで本格的に自動化を始める時のデザインパターン」みたいなのが自分の中にできてきたので、そのノウハウを共有する記事となります。

タイトルにもあるので結論だけ先に書きますが、roleをすこることでAnsible設計と開発が圧倒的にしやすくなります。

# AnsibleのPlaybookを書くときのポイント

Ansibleは比較的自由にディレクトリ構造を作ることができるフレームワークです。
なので、サーバ設定を自動化するPlaybookを一つ作成するだけでも、色々な書き方があります。

書き始める時にどうやって書いていけばいいか、Playbook設計はどうすればいいか。最初はわからないですよね。

一つのPlaybookファイルにタスクを詰め込む？
`import_role` や `include_tasks` を使って他のファイルに切り出す？
そもそもどういう単位で分割する？作業単位？機器単位？一つのファイルに1タスクだけになってもいい？

おそらくそういう疑問を持ったことがあると思います。僕も当然あります。
というかここ最近はずっとそういうことに取り組んできました。

いま触ってるPlaybookはいずれ僕の手を離れ他のエンジニアにメンテナンスされる予定でいます。
そのため、誰が手を加えても改修をしやすいよう、最初の設計をしっかり考える必要があります。

で、ようやく少しずつ形が固まってきました。
運用を見据えてPlaybook設計をするのであれば、ポイントは2つです。

- **作業手順書を作成し、実運用に則った構造にする**
- **Roleを書くときは「作業内容」ではなく「最終的になる状態」を意識する**

今回はこの2つのことについてまとめました。
ぶっちゃけまだ実装も運用もしていないので机上の空論的な側面もあるかと思いますが、何かの参考にしていただければ幸いです。

なお、このポイントに注意して作成しているPlaybookを、途中ですが皆さんに公開します。
当記事では、このリポジトリの内容を例にあげていますので、併せて確認していただければと思っています。

[guskma/ansible_for_mastodon: マストドン自動構築用Playbook(書きかけ)(AdventCalendar2024用)](https://github.com/guskma/ansible_for_mastodon)

こちらはTwitterライクなOSSのSNSであるマストドンを自動構築するために作成しているPlaybookとなります。
AdventCalendarのため、ドラクエ3をバラモス倒すところで中断して作りました。なので完全にオープンに出来るコードとなっています。

最終的にはGalaxyで公開する予定です。
年内に終わらせてAdventCalendarでドヤ顔できればよかったんですが、 ~~やる気がでｎ~~ 忙しかったため完成には至れませんでした。
来年前半には公開できるようにがんばります。

# ポイントその１. 作業手順書を作成し、実運用に則った構造にする。

まずは作業手順書を作りましょう。
インフラエンジニアを経験したことがある人であれば、見たことはあると思います。
あれです。あの一つ一つチェックを入れていくやつです。

今回公開したマストドン自動構築用Playbookは、マストドン公式が公開している手動インストール手順を参考に作成しています。

[Installing from source - Mastodon documentation](https://docs.joinmastodon.org/admin/install/)

この手順にはチェックボックスがついていませんが、ちゃんと手作業で構築するために十分な情報が書かれています。
この手順をベースにして、どうやってPlaybookを書いていくかを考えていこうと思います。

基本的な流れはほぼ同じですが、ある程度手順のブラッシュアップをしています。
上記手順を、インフラエンジニアの方々がわかりやすいように章立てをしてみます。

> - 1章、システム設定
>     - **1-1節、システムパッケージのインストール・最新化**
>         - 1-1-1項、既存パッケージの更新 (apt upgrade)
>         - 1-1-2項、システムパッケージのインストール (apt install)
> - 2章、データベース設定
>     - **2-1節、データベース用パッケージのインストール**
>         - 2-1-1項、データベース用パッケージのリポジトリ追加 (add-apt-repository)
>         - 2-1-2項、データベース用パッケージのインストール (apt install)
>     - **1-2節、ユーザ作成**
>         - 1-2-1項、データベース管理ユーザ作成 (useradd)
> - 3章、アプリケーション設定
>     - **3-1節、アプリケーション用パッケージのインストール**
>         - 3-1-1項、アプリケーション用パッケージのインストール (apt install)
>     - **3-2節、ユーザ作成**
>         - 3-2-1項、アプリケーション管理ユーザ作成 (useradd)
>     - **3-3節、\*\*env設定**
>         - 3-3-1項、リポジトリからanyenvをクローン (git clone)
>         - 3-3-2項、anyenvインストール (anyenv init -)
>         - 3-3-3項、nodenvインストール (anyenv install nodenv)
>         - 3-3-4項、rbenvインストール (rbenv install rbenv)
>     - **3-4節、アプリケーション導入**
>         - 3-4-1項、リポジトリからアプリケーションをクローン (git clone)
>     - **3-5節、アプリケーション設定**
>         - 3-5-1項、サンプルコンフィグファイルをコピー (cp)
>         - 3-5-2項、コンフィグファイルの設定を変更 (sed)
>     - **3-6節、アプリケーション用サービス登録**
>         - 3-6-1項、サンプルserviceファイルをコピー (cp)
>         - 3-6-2項、serviceファイルの設定を変更 (systemctl start)

Ansibleに最適化するため構成をややおかしくしたところもありますが、章立てをすることで、何をしているかわかる雰囲気になってきたのではないでしょうか。
※例えばユーザ作成とかパッケージインストールとか、各章でやらずにまとめて全部作成しろや！って突っ込みたくなるやつ

とはいえ、このくらいの粒度に落とし込めればほぼ設計部分は完成です。

というのも、上記の **「節」に当たる部分をそのままRoleに落とし込めるようになる** からです。
そうなれば後はひたすら機械的にRoleを作っていけばよいということになります。

このやり方のメリットとして

- Ansibleを触れないエンジニアとも意思疎通を取ることができるようになる
- Ansible障害時であっても手作業で再現することが可能になる

という点が挙げられます。
Ansibleとは関係のないところにいる、運用をする側の人を考えた設計を取っています。

運用に渡せるPlaybookを書くというのは、Ansibleを触れないエンジニアと意思疎通を取る事ができる情報に落とし込めている、ということが特に重要だと考えています。
Ansibleなんもわからん。コードとか論理的思考とかナンノコッチャ。みたいなインフラエンジニアはいて当然だという前提での設計が必要です。

そういう人たちを無下にせず、むしろ尊重して連携できる体制づくりをしていくのが、自動化をするための第一歩なんだと考えています。

そしてITインフラのほぼ全てを支えている存在です。
その人達が手作業で支えてきたものを自動化という工程で昇華させることが出来るようになった。
少しずつ積み上げてきたノウハウやナレッジをもうワンランク上げるという役割を担っているのがプラットフォームエンジニアリングというものなんじゃないかなと、最近は思ってます。

# ポイントその２. Roleを書くときは「実行後の状態が常に同じ状態になること」を意識する

ポイントその１は設計の話でした。ポイントその２は実装の話になります。

最近気づいたことですが、実はAnsibleはマクロやスクリプトとは書き方が違います。

そりゃまぁYAML形式だし、書き方が違うのは当然のことなんですが、今回僕が気づいたのは **「冪等性」** についてです。

よく言われるキーワードですよね。冪等性。
でもAnsible界隈でしか使われない言葉なので、イマイチぴんと来ないものでもあります。

冪等性とは何なのかをざっくり説明すると、「実行後の状態が常に同じ状態になること」です。

これだけの説明で「うんうんそうだね冪等性は大事だね」ってなれる人は神か何かなので始めはわからなくてもいいと思います。

冪等性とは何か、実感できるようにするためには「冪等性を持ったRoleを作る」ことを意識してみることです。

例として、先ほどの章立てしたマストドン自動構築用Playbookをベースに考えてみましょう。
「節」の作業を実施した後でどういう状態になっているべきかを整理してみます。

> - 1章、システム設定
>     - 1-1節、システムパッケージのインストール・最新化
>         - システムパッケージが最新化されている状態
>         - 指定したパッケージがインストールされている状態
> - 2章、データベース設定
>     - 2-1節、データベース用パッケージのインストール 
>         - データベース用パッケージのリポジトリが登録されている状態
>         - 指定したデータベース用パッケージがインストールされている状態
>     - 1-2節、ユーザ作成
>         - データベース管理ユーザが存在する状態
> - 3章、アプリケーション設定
>     - 3-1節、アプリケーション用パッケージのインストール 
>         - 指定したアプリケーション用パッケージがインストールされている状態
>     - 3-2節、ユーザ作成
>         - アプリケーション管理ユーザが存在する状態
>     - 3-3節、\*\*env設定
>         - anyenvがインストールされ、最新の状態になっている状態
>         - nodenvがanyenv経由でインストールされている状態
>         - rbenvがanyenv経由でインストールされている状態
>     - 3-4節、アプリケーション導入
>         - アプリケーションのローカルリポジトリがクローンされている状態
>     - 3-5節、アプリケーション設定
>         - アプリケーションのコンフィグファイルが存在している状態
>         - 指定したパラメータでコンフィグが設定されている状態
>     - 3-6節、アプリケーション用サービス登録
>         - systemd設定ファイル格納ディレクトリにアプリケーション用serviceファイルが設置されている状態
>         - アプリケーションのサービスが起動している状態

具体例として、アプリケーション設定のところを使って説明しましょう。

> - 3章、アプリケーション設定
>     - 3-5節、アプリケーション設定
>         - アプリケーションのコンフィグファイルが存在している状態
>         - 指定したパラメータでコンフィグが設定されている状態

サンプルRole的には、以下のあたりのファイルでこの処理を行っています。
[ansible_for_mastodon/roles/mastodon_app_config/tasks at main · guskma/ansible_for_mastodon](https://github.com/guskma/ansible_for_mastodon/tree/main/roles/mastodon_app_config/tasks)

今回の実装では「サンプルコンフィグをコピー」→「各パラメータを置換」という方法でコンフィグファイルを作成しています。
動作しているアプリケーションに一番適したコンフィグファイルとは、そのバージョンのアプリケーションに同梱されているサンプルコンフィグだろうという考えからです。

もし、仮にとあるバージョンからサンプルコンフィグが廃止された場合のことを考えてみましょう。

現在の設計では、「最終的にアプリケーションを動かすためのコンフィグが設定されていれば、どう操作をしてもかまわない」という考えがあります。

なので新しい実装として「テンプレートファイルからコンフィグファイルを生成する」という選択肢が取れるようになります。

「最終的に生成されたコンフィグファイルが存在する」状態に持っていければいいわけですから、もし情勢が変化したとしてもバージョンを確認して処理を分岐させてあげればよいわけです。
このように「実行後の状態が常に同じ状態になる」ようにRoleを作ることが冪等性であると考えます。

冪等性がある状態というのは他にも実はメリットがあります。
副次的な効果ではありますが、例えば「この状態にするRoleだったらあっちのPlaybookでも使いまわせそうだな」というパターンが出てくると思います。

つまり、言い換えれば「拡張性」と「可用性」が出てくる、ということになります。

「このRoleではこの冪等性を確立させることが目的である」というゴールをはっきりさせることで、新規開発の時だけでなく、保守として追加改修を行う必要が出たときにも役立ちます。

※ちなみに、冪等性を見失わないよう `DESIGN.md` に冪等性 (Idenpotency) の項目を用意することをお勧めします。
最終的なゴールを明確にすることで、リリース後も方針にブレが生じず、継続した保守をすることが出来るようになります。

# まとめ

今回はRoleをすこれ！をテーマに、Roleを中心としたAnsible Playbook開発のポイントを説明させていただきました。

この記事で書いたことの要約を以下にまとめます

- Ansible Playbookを書く時に気を付けるポイント
    - 作業手順書の章立てのうち「節」の粒度でRoleを作るべし
        - 作業イメージがしやすいので、運用チームとの連携がとりやすい
        - 最悪Ansible無しでも作業ができ、自動化に囚われすぎない運用ができる
    - 冪等性のあるRoleを作るべし
        - 冪等性：実行後の状態が常に同じ状態になること
        - 冪等性を意識することで、拡張性、可用性が担保できる

本当はもっと、argument_specsの話やら、galaxyの話やら、書きたいことは様々ありましたが、 ~~怠けてて前日~~ 時間が足りず文章量が増えてしまいそうだったので、また次回以降に書こうと思います。

文章ばっかになってしまいましたがここまで読んでいただきありがとうございました。僕はゾーマを倒すための旅に出かけることにします。