ロール — 規約で畳む再利用の単位
一言でいうと
ロールは、プレイブックの断片を決まったディレクトリ名に分けて収めたものであり、その規約のおかげで、設定なしで再利用できます。
なぜ必要なのか
プレイブック1つにnginxのインストール、アプリのデプロイ、ログローテーション、監視エージェントまで全部入れると、300行になります。そのあと2つ目のサービスができると、人々はそのファイルをコピーします。コピーされた瞬間から、2つのファイルは別々の速さで古くなっていきます。6か月後の「なぜAサーバーだけログが消えないのか」の答えは、いつも「コピーが更新されていないから」です。
ロールはこの問題を、「再利用の単位をファイルシステムの構造で強制する」ことで解決します。ロールの中ではタスク・デフォルト値・ハンドラー・テンプレートがそれぞれの置き場所を持つので、他人が作ったロールも開かずに、どこに何があるかがわかります。
どう動くのか
| ディレクトリ | 入れるもの | 自動ロード |
|---|---|---|
tasks/main.yml |
実行するタスク | ロールの呼び出し時 |
defaults/main.yml |
オーバーライドされることを想定したデフォルト値 | 最も低い優先度 |
vars/main.yml |
変更されてはならない内部の値 | 高い優先度 |
handlers/main.yml |
ハンドラー | 自動登録 |
templates/ |
Jinja2テンプレート | templateモジュールがパスなしで見つける |
files/ |
そのままコピーするファイル | copyモジュールがパスなしで見つける |
meta/main.yml |
依存ロール、メタデータ | 依存ロールを先に実行 |
要点は、defaultsとvarsの使い分けです。ユーザーが変更しそうな値は、必ずdefaultsに置きます。varsに置くと優先度が高く、外からのオーバーライドが事実上不可能になり、そのロールは再利用できなくなります。
meta/main.ymlのdependenciesは、「このロールの前にあのロールが必要だ」を宣言します。同じ依存ロールが何度出てきても、デフォルトでは1回だけ実行されます。
現場での姿
1つ目は名前の衝突です。ロールが複数あると、変数名が重なります。そのため、ロール変数にはロール名をプレフィックスとして付けるのが慣例です。portではなくwebapp_portとします。
2つ目は、ロールの中で絶対パスを使わないことです。templates/とfiles/はロールを基準に自動的に探索されるので、ファイル名だけを書けば済みます。絶対パスを直接書くと、そのロールはそのサーバーでしか動作しません。
3つ目は、ロールも冪等でなければならないことです。ロールを使うプレイブックを2回実行して、changed=0になるかを確認する習慣は、ロールでもそのまま通用します。
変数の優先度が事故を生む
Ansibleの変数には、22段階の優先度があります。すべてを覚える必要はありませんが、 よく衝突する5つの順序は知っておく必要があります(下にいくほど強くなります)。
1. 롤의 defaults/main.yml ← 가장 약하다. 덮어쓰라고 있는 것
2. inventory group_vars
3. inventory host_vars
4. play 의 vars
5. -e (extra vars) ← 가장 강하다. 무엇도 못 이긴다
ここで出てくるルールは2つです。
- ロールのデフォルト値は
defaults/に置きます。vars/に置くと優先度が高いので、 ユーザーがオーバーライドできません。「ロール変数を変えたのに反映されない」の原因は、 たいていこれです。 -eはデバッグ用です。スクリプトに常時入れると、インベントリの設定がすべて無視され、 あとでなぜその値になるのか、誰も突き止められなくなります。
値がどこから来たのかは、問い合わせればわかります。
ansible -i inv host -m debug -a "var=app_port"
ansible-inventory -i inv --host web-01 --yaml # 그 호스트에 적용된 전부
冪等性を壊すモジュール
Ansibleの価値は、「2回実行しても同じ」ことです。これを壊すものがいくつかあります。
| 使ってはいけないもの | 代わりに |
|---|---|
shell: echo x >> /etc/hosts |
lineinfile、blockinfile |
command: mkdir /opt/app |
file: state=directory |
shell: sed -i … |
lineinfile、template |
command: curl … | bash |
get_url + command(検証後) |
シェルをどうしても使わなければならない場合は、createsやremovesで条件を付けます。
- name: 설치 스크립트 (한 번만)
command: /opt/setup.sh
args:
creates: /opt/.installed # 이 파일이 있으면 건너뛴다
changed_when: falseは別の話です。これは「このコマンドは状態を変えない」という宣言なので、参照コマンドにだけ使います。実際に状態を変えるコマンドに付けると、レポートが嘘をつくことになります。
ロールを分ける基準
1つのロールが大きくなると分けたくなりますが、基準がないと断片が増えるだけです。
- ほかの場所でそのまま使えるか: 使えなければ、分ける理由がありません。
- 依存が一方向か: ロールAがBを、BがAを呼ぶと循環です。
- 変数名が重ならないか: ロールごとにプレフィックスを付けます(
nginx_port)。
3つ目が実務の落とし穴です。2つのロールがportという変数を使うと、あとのほうが勝ちます。Ansibleは警告しません。ロール名をプレフィックスとして付ける慣例が、これを防ぎます。
次のラボですること
webappロールを骨組みから作って、デフォルト値・タスク・テンプレート・ハンドラーを埋め、baselineロールを依存として設定して実行順序を確認します。同じロールを別の変数で2回呼び出して再利用性を示し、2回目の実行でchanged=0にします。