Ansibleのクロスプラットフォームrole設計|ansible_os_familyでRHEL・Ubuntu対応のroleを作る方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Ansible > Ansibleのクロスプラットフォームrole設計|ansible_os_familyでRHEL・Ubuntu対応のroleを作る方法
「PlaybookをRHELで書いて動作確認まで終えた。同じPlaybookをUbuntuのサーバーに流したら、パッケージが見つからないというエラーで全部止まった」

RHEL/CentOS/Rocky Linux系ではWebサーバーのパッケージ名がhttpdですが、Ubuntu/Debian系ではapache2になります。サービス名や設定ファイルのパスも異なるため、1本のPlaybookをそのまま複数ディストリビューションに流し込むのは難しいのです。

この記事では、Ansibleが自動収集するansible_os_familyというファクト変数と、include_varsによる動的変数ロードを組み合わせたクロスプラットフォームrole設計を解説します。RHEL系とDebian/Ubuntu系の両方に対応したroleをひとつの構成で管理する考え方と、実際のディレクトリ構造・コード例を順に説明します。

この記事のポイント

・ansible_os_familyはRHEL・Debianなど主要ディストリを自動判定するファクト変数
・vars/RedHat.yml・Debian.ymlでOS別変数ファイルを分離するのが定番設計
・include_tasks: "{{ ansible_os_family }}.yml"でOS別タスクを自動切り替えできる
・gather_facts: falseのPlaybookではファクト変数が取得できない点に注意


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

クロスプラットフォームの壁はどこに潜んでいるか

ディストリビューションが混在する環境でAnsibleを使うと、次のような差異が至るところで出てきます。

・パッケージ名の違い:RHELはhttpd、UbuntuはApache2(パッケージ名はapache2)
・パッケージマネージャーの違い:dnf(RHEL系)とapt(Debian/Ubuntu系)
・設定ファイルのパスの違い:/etc/httpd/conf.dと/etc/apache2/sites-available
・ファイアウォールの違い:firewalld(RHEL系)とufw(Ubuntu系)
・ログの置き場の違い:/var/log/httpdと/var/log/apache2

こうした差異に対して「when: ansible_os_family == 'RedHat'」という条件分岐を1つのtasks/main.ymlに全部書き込んでしまうと、コードが複雑になってメンテナンスが困難になります。後から別のOS(AlmaLinuxやopenSUSEなど)を追加する際にも、あちこちを修正しなければなりません。

クロスプラットフォームrole設計の目標は「OS固有の処理をOS単位のファイルに分離して、tasks/main.ymlを薄く保つ」ことです。

ansible_os_familyとは何か

Ansibleはサーバーへ接続したときに、setupモジュールでホストの情報(ファクト)を自動収集します。その中にansible_os_familyという変数があり、ディストリビューションが属するファミリー名が格納されています。

主なansible_os_familyの値は以下のとおりです。

・RedHat:RHEL、CentOS、Rocky Linux、AlmaLinux、Fedora
・Debian:Debian、Ubuntu、Raspbian
・Suse:openSUSE、SLES
・Windows:Windows Server各バージョン

実際にsetupモジュールで確認してみましょう。以下はRocky Linux 9.4とUbuntu 24.04 LTSのサーバーに対してansible_os_familyをフィルタリングした例です。

# Rocky Linux 9.4(ホスト名: web01)の場合 $ ansible web01 -m setup -a 'filter=ansible_os_family' -i inventory/hosts web01 | SUCCESS => { "ansible_facts": { "ansible_os_family": "RedHat" }, "changed": false } # Ubuntu 24.04 LTS(ホスト名: web02)の場合 $ ansible web02 -m setup -a 'filter=ansible_os_family' -i inventory/hosts web02 | SUCCESS => { "ansible_facts": { "ansible_os_family": "Debian" }, "changed": false }

ansible_os_familyがRedHatまたはDebianという文字列を返すことが確認できます。この値をPlaybook内の条件分岐やvars_filesのロードに使います。

より細かいバージョン情報が必要なときはansible_distribution("Rocky"・"Ubuntu"など)やansible_distribution_major_version("9"・"24"など)を使うこともできます。Ansibleの構成管理の全体設計についてはAnsible実践学習サイト(ansible.linuxmaster.jp)でも体系的に解説していますが、まずはansible_os_familyの使い方を押さえておくと現場での応用が広がります。

クロスプラットフォームroleの設計パターン

代表的な設計パターンは「OS別の変数ファイルと、OS別のタスクファイルをディレクトリで分離する」方法です。順番に説明します。

1. ディレクトリ構成の設計

Webサーバー(Apache)をインストールするrole「webserver」を例にします。

roles/webserver/ ├── defaults/ │ └── main.yml # デフォルト変数(OS非依存) ├── vars/ │ ├── RedHat.yml # RHEL/Rocky Linux系の変数 │ └── Debian.yml # Ubuntu/Debian系の変数 ├── tasks/ │ ├── main.yml # エントリポイント(薄く保つ) │ ├── RedHat.yml # RHEL系専用タスク │ └── Debian.yml # Debian系専用タスク ├── handlers/ │ └── main.yml └── templates/ └── vhost.conf.j2

ポイントはvars/とtasks/の中にOSファミリー名と同名のYAMLファイルを置くことです。ansible_os_familyの値(RedHat、Debian)がそのままファイル名になります。これによりPlaybookのコードがシンプルになります。

2. OS別変数ファイルの分離

vars/RedHat.yml と vars/Debian.yml にそれぞれのパッケージ名・サービス名・設定パスを定義します。

# vars/RedHat.yml apache_package: httpd apache_service: httpd apache_conf_dir: /etc/httpd/conf.d apache_log_dir: /var/log/httpd

# vars/Debian.yml apache_package: apache2 apache_service: apache2 apache_conf_dir: /etc/apache2/sites-available apache_log_dir: /var/log/apache2

OS固有の値を変数にまとめて置くだけです。tasks/はこれらの変数名(apache_packageなど)だけを参照すれば済むため、OS差異を意識せずに書けるようになります。

3. tasks/main.ymlでのOS別タスク切り替え

tasks/main.yml はOS別の変数ファイルとタスクファイルを読み込むだけの薄いファイルにします。

# tasks/main.yml --- - name: OS別変数ファイルを読み込む ansible.builtin.include_vars: "{{ ansible_os_family }}.yml" - name: OS別タスクを実行する ansible.builtin.include_tasks: "{{ ansible_os_family }}.yml"

ansible_os_familyが "RedHat" のホストにはvars/RedHat.ymlとtasks/RedHat.ymlが読み込まれ、"Debian" のホストにはvars/Debian.ymlとtasks/Debian.ymlが読み込まれます。新しいOSファミリーを追加するときは、対応するYAMLファイルを追加するだけで済みます。

tasks/RedHat.yml と tasks/Debian.yml の例を示します。

# tasks/RedHat.yml --- - name: httpdをインストールする(RHEL系) ansible.builtin.dnf: name: "{{ apache_package }}" state: present - name: firewalldでhttpポートを開放する ansible.posix.firewalld: service: http permanent: true state: enabled immediate: true - name: httpdを起動・有効化する ansible.builtin.service: name: "{{ apache_service }}" state: started enabled: true

# tasks/Debian.yml --- - name: apache2をインストールする(Debian系) ansible.builtin.apt: name: "{{ apache_package }}" state: present update_cache: true - name: ufwでhttpポートを開放する community.general.ufw: rule: allow port: "80" proto: tcp - name: apache2を起動・有効化する ansible.builtin.service: name: "{{ apache_service }}" state: started enabled: true

タスクファイルはOS専用のため、dnfとaptを1ファイルに混在させる必要がありません。コードが読みやすく、テストもしやすくなります。

実際のPlaybook実行例

Rocky Linux 9.4(web01)とUbuntu 24.04(web02)の2台に対して上記のroleを含むPlaybookを実行した結果を示します。

$ ansible-playbook site.yml -i inventory/hosts PLAY [Webサーバーを構成する] ********************************** TASK [Gathering Facts] ******************************* ok: [web01] ok: [web02] TASK [webserver : OS別変数ファイルを読み込む] ****************** ok: [web01] # vars/RedHat.yml を読み込み ok: [web02] # vars/Debian.yml を読み込み TASK [webserver : OS別タスクを実行する] ************************ included: /home/ansible/roles/webserver/tasks/RedHat.yml for web01 included: /home/ansible/roles/webserver/tasks/Debian.yml for web02 TASK [webserver : httpdをインストールする(RHEL系)] ************* changed: [web01] TASK [webserver : apache2をインストールする(Debian系)] ********** changed: [web02] TASK [webserver : httpdを起動・有効化する] ********************** changed: [web01] TASK [webserver : apache2を起動・有効化する] ******************** changed: [web02] PLAY RECAP ******************************************* web01 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 web02 : ok=5 changed=3 unreachable=0 failed=0 skipped=0

1本のPlaybookがRHEL系・Debian系の両方に適用され、それぞれの環境に合ったパッケージとサービス名が使われていることが確認できます。

ansible_os_familyだけでは足りないケースへの対応

1. ディストリビューション固有の対応が必要な場合

Rocky Linux 9とRHEL 8で設定ファイルのパスが異なる、UbuntuとDebianでパッケージのバージョンが違うといった状況では、ansible_os_familyより細かい粒度の変数を使います。

・ansible_distribution:"Rocky"・"Ubuntu"・"Debian"など固有名称
・ansible_distribution_major_version:"9"・"24"などメジャーバージョン番号

変数ファイルを2段階にすることで、ファミリー共通の設定を上位ファイルで定義し、固有設定を下位ファイルで上書きできます。

# tasks/main.yml(2段階の変数ファイルロード) --- - name: OSファミリー別変数を読み込む ansible.builtin.include_vars: "vars/{{ ansible_os_family }}.yml" - name: ディストリビューション固有変数を上書き読み込む(存在する場合のみ) ansible.builtin.include_vars: "vars/{{ ansible_distribution }}.yml" failed_when: false

2. 対応OSをmeta/main.ymlで明示する

roleが対応しているOSをmeta/main.ymlに明記する習慣をつけましょう。チームで使う際に「このroleはAlmaLinuxに対応しているのか」という疑問が生じにくくなります。

# meta/main.yml galaxy_info: author: your_name description: Cross-platform web server role platforms: - name: EL # RHEL/Rocky Linux/AlmaLinux versions: - 8 - 9 - name: Ubuntu versions: - focal # 20.04 LTS - jammy # 22.04 LTS - noble # 24.04 LTS

トラブルシュート

「Could not find or access 'RedHat.yml'」エラーが出る

include_tasksやinclude_varsで指定したファイルが見つからない場合に発生します。考えられる原因と対処は以下のとおりです。

・ファイル名の大文字小文字ミス:LinuxはOSが大文字小文字を区別します。ansible_os_familyの値は"RedHat"(Rが大文字・Hが大文字)なので、ファイル名をredhat.ymlにすると一致しません
・ディレクトリの配置ミス:include_varsはroleのvars/から探しますが、include_tasksはtasks/からの相対パスになります。それぞれのディレクトリ構造を確認してください
・未対応OSへの適用:Suseなど非対応ファミリーにroleを流した場合、対応するファイルが存在せずエラーになります。meta/main.ymlで対応OSを制限し、テスト済み環境だけに流すようにしましょう

ansible_os_familyがundefinedになる

PlaybookにGather_facts: falseが設定されていると、setupモジュールが実行されずファクト変数が取得できません。ansible_os_familyを使う前にgather_factsがtrueになっていることを確認してください。

# gather_factsが明示的にfalseになっている例(NG) - name: Webサーバーを構成する hosts: webservers gather_facts: false # ansible_os_familyが使えない tasks: ... # gather_factsをtrueに戻す(OK) - name: Webサーバーを構成する hosts: webservers gather_facts: true # または省略(デフォルトはtrue) tasks: ...

パフォーマンス上の理由でgather_factsをfalseにしたい場合は、事前に別のPlayでfact収集専用のタスクを実行するか、ansible_os_familyをgroup_varsで直接定義する設計を取ります。

本記事のまとめ

クロスプラットフォームrole設計のポイントをまとめます。
やりたいこと 設計のアプローチ
OS種別を自動判定する ansible_os_family(RedHat/Debian/Suse等)を使う
OS別のパッケージ名・パスを分離する vars/RedHat.yml・vars/Debian.ymlに変数を定義する
OS別の変数ファイルを自動読み込みする include_vars: "{{ ansible_os_family }}.yml"
OS別のタスクファイルを切り替える include_tasks: "{{ ansible_os_family }}.yml"
ディストリ固有の差異に対応する ansible_distributionで2段階ロードする
ファクト変数が取得できないとき gather_factsがfalseになっていないか確認する
ansible_os_familyとvars_filesの組み合わせは、クロスプラットフォーム対応の中で最もシンプルで実績のある設計パターンです。tasks/main.ymlを「何をするか」だけを宣言するエントリポイントに保ち、OS固有の処理は専用ファイルに委ねる構造にすることで、あとから対応OSを追加するときも最小限の変更で済みます。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Ansibleを活用したサーバー自動化の設計から実装まで2日間のハンズオンで学べるAnsible実践セミナー(ansible.linuxmaster.jp)をぜひご覧ください。クロスプラットフォームrole設計を含む実務パターンを体系的に習得できます。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。