TT Lab
はじめる
学ぶ 学習パス コース

Ansible基礎

プレイブック — 命令ではなく状態を書く

TT Labで続きを見る

一言でいうと

プレイブックの各タスクは、「このコマンドを実行せよ」ではなく、「終わったときにこの状態になっていなければならない」という宣言です。

なぜ必要なのか

シェルスクリプトでサーバーをセットアップしたことがある人は、スクリプトがだんだん長くなる経験をします。最初はmkdir /opt/appの1行だったのが、2回目の実行で「すでに存在する」というエラーが出てmkdir -pに変え、パーミッションが違うことがあるのでchmodを付け、それがすでに合っているなら、わざわざやらなくてもよいのでifで囲みます。結局、スクリプトの半分が、今の状態がどうなっているかを確認するコードになります。

Ansibleのモジュールは、その半分を代わりに担います。fileモジュールにpath、state=directory、mode=0755を渡すと、なければ作成し、あればそのままにし、パーミッションが違えば修正します。そして、実際に何かを変更したときにだけ、changedとして報告します。この報告が、次のモジュールで学ぶ冪等性とハンドラーの土台になります。

どう動くのか

プレイブックはプレイの一覧であり、プレイは「どのホストに、どのタスクを、順番に」を含みます。

- name: 웹 서버 기본 설정          # 플레이 이름
  hosts: web                      # 대상 (인벤토리의 그룹/호스트/패턴)
  gather_facts: true              # 대상의 정보를 먼저 수집할지
  tasks:
    - name: 앱 디렉터리 준비       # 태스크 이름
      ansible.builtin.file:       # 모듈
        path: /opt/app
        state: directory
        mode: "0755"

覚えておくべきルールは3つです。

  1. タスクは上から下へ順番に実行されます。ただし、複数のホストに対しては、タスク1つがすべてのホストで終わってから、次のタスクへ進みます。
  2. すべてのタスクにnameを付けます。名前がないと、ログにモジュール名と引数がそのまま出力されて読みにくく、タグで選ぶのも難しくなります。
  3. command/shellは最後の手段です。この2つは状態を知らないので、毎回changedと報告します。専用のモジュールがあるならそれを使い、どうしてもシェルを使う必要があるなら、createsのようなガードを付けます。

実行前に確認できる手段もあります。--syntax-checkはYAMLとプレイの構造を検査し、--checkは、実際には変更せず、「何が変わるか」だけを報告します。--diffを一緒に付けると、ファイル内容の差分まで表示してくれます。本番サーバーで初めて実行するプレイブックは、必ず--check --diffを先に通します。

現場での姿

1つ目は、タグの2つの顔です。タグを付けると、--tags configで設定だけをもう一度適用できて便利です。ところが、タグで一部だけを実行する習慣が定着すると、「全体を実行したことのないプレイブック」ができます。そのようなプレイブックは、ある日、全体の実行で初めて見るエラーを出します。タグは、デバッグ用の加速装置であり、通常の実行経路ではありません。

2つ目は、チェックモードの限界です。--checkは、モジュールがそのモードをサポートしている場合にのみ正確です。commandで作った結果に依存する後ろのタスクは、チェックモードで見当違いの結果を出すことがあります。そのため、チェックモードがクリーンであることと、実際の実行が安全であることは、別の命題です。

3つ目は、名前がそのままドキュメントになることです。事故が起きたとき、人が読むのはコードではなく実行ログです。「Install nginx」よりも、「nginxのインストール。設定ファイルは次のタスクで上書きする」のほうが、はるかに役に立ちます。

失敗したとき、どこで止まるのか

プレイブックが途中で失敗すると、すでに適用されたものと、まだ適用されていないものが混在した状態が残ります。スクリプトと違って、Ansibleは複数のホストを同時に扱うので、この状態がより複雑になり、デフォルトの動作を知っておかないと、事故のあとの判断が難しくなります。

デフォルトは、そのホストだけが外れることです。タスクがあるホストで失敗すると、そのホストは以降のタスクから除外され、残りのホストは続行します。そのため、10台のうち1台だけが失敗すると、残りの9台は最後まで進み、クラスターが2つのバージョンに分かれた状態になります。

この動作を変える仕組みが、いくつかあります。

serialとハンドラーの関係は、特に忘れられがちです。ハンドラーは、デフォルトではプレイが終わるときに1回動きますが、serialを使うと、各バッチが終わるたびに動きます。そのため、再起動がバッチ単位で起き、これが、実は順次デプロイで望む動作です。

そして、失敗したあとにもう一度実行するのが安全かどうかを、あらかじめ判断しておく必要があります。すべてのタスクが冪等なら、そのまま再実行すればよいですが、途中に冪等でないものが1つでもあると、再実行が状況を悪化させます。前のモジュールで、冪等性をテストで強制するよう述べた理由が、ここで明らかになります。再実行してよいかどうか確信できない自動化は、失敗したときに、人が手作業で収拾しなければならない自動化です。

次のラボですること

/root/ans/play/site.ymlを作成して、ディレクトリの作成・設定ファイルの配置・既存ファイルの1行の修正を、タスクとして表現し、タグとチェックモードを自分で使ってみます。最後に、エラーが4か所仕込まれたプレイブックを直して、通過させます。