ストラテジーのテスト

Ansible Playbook とテストの統合

「Ansible Playbook とテストを最適な方法で統合するにはどうしたらいいのか」との質問が多くあります。 統合のオプションは、多数あります。 Ansible は実際には、 「フェイルファースト (Fail fast)」の順番ベースのシステムとなるように設計されているため、Ansible Playbook に直接、簡単にテストを埋め込むことができます。 本章では、 インフラストラクチャーのテスト統合パターンについて触れ、適切にテストするための正しいレベルについても説明します。

Note

以下は、デプロイするアプリケーションをテストする内容で、開発時に Ansible モジュールをテストする方法を説明する章ではありません。 Ansible のテスト方法は、開発のセクションで参照してください。

開発のワークフローにテストをある程度組み込むことで、実稼働環境でコードを使用時に、慌てることが少なくなります。 多くの場合は、実稼働環境でテストを活用して、更新の失敗が、インストール全体に移行されてしまうのを防ぐことができます。 また、プッシュベースになっているため、 ローカルホストやテストサーバーで手順を非常に簡単に実行できます。Ansible は、多くのチェックを挿入できるため、アップグレードのワークフローを希望のレベルに調節できます。

適切なレベルのテスト

Ansible リソースは、任意の状態のモデルです。 サービスの起動、 パッケージのインストールなどのテストする必要はありません。 Ansible は、上記の内容が True と宣言されていることを確認するシステムで、 代わりに、 以下の項目を Playbook にアサートします。

tasks:
  - service:
      name: foo
      state: started
      enabled: yes

サービスが起動しない可能性がある場合には、起動するように要求するのが一番です。 サービスが起動に失敗した場合には、 Ansible により随時、指示がでます。(上記と、サービスが機能的な内容を実行しているかどうかを混同しないでください。 この点については、後述します)。

ドリフトテストとしての check モード

上記の設定では、Ansible で –check モードをテストのレイヤーとして使用できます。 既存システムに対してデプロイメント Playbooks を実行する場合は、 ansible コマンドに –check フラグを使用して、 システムを希望する状態にするために Ansible が変更が必要と判断するかどうかを報告します。

このレポートにより、特定のシステムにデプロイメントが必要かどうかが、最初の時点で把握できます。 通常、スクリプトとコマンドはチェックモードで実行されないため、 スクリプトモジュールへの呼び出しなど、特定の手順を常にチェックモードで実行させたい場合は、これらのタスクのチェックモードを無効にします:

roles:
  - webserver

tasks:
  - script: verify.sh
    check_mode: no

テストに便利なモジュール

Playbook のモジュールでは、特にテストに適しているものもあります。 以下の例は、ポートが開放されていることを確認します:

tasks:

  - wait_for:
      host: "{{ inventory_hostname }}"
      port:22
    delegate_to: localhost

以下の例は、URI モジュールを使用して、Web サービスが返されることを確認します:

tasks:

  - action: uri url=http://www.example.com return_content=yes
    register: webpage

  - fail:
      msg: 'service is not happy'
    when: "'AWESOME' not in webpage.content"

リモートホストで任意のスクリプト (言語は問わない) が簡単にプッシュされるため、ゼロ以外のリターンコードが指定されていると、スクリプトは自動的に失敗します:

tasks:

  - script: test_script1
  - script: test_script2 --parameter value --parameter2 value

ロールを使用する場合 (ロールは便利なので使用を推奨)、スクリプトモジュールがプッシュするスクリプトは、ロールの「files/」ディレクトリーに配置されます。

また、アサートモジュールを使用すると、さまざまな真偽の検証が非常に簡単にできます:

tasks:

   - shell: /usr/bin/some-command --parameter value
     register: cmd_result

   - assert:
       that:
         - "'not ready' not in cmd_result.stderr"
         - "'gizmo enabled' in cmd_result.stdout"

Ansible 設定で設定が宣言されていないファイルの存在をテストする必要がある場合には、「stat」モジュールが最適です:

tasks:

   - stat:
       path: /path/to/something
     register: p

   - assert:
       that:
         - p.stat.exists and p.stat.isdir

上記のように、コマンドのリターンコードなどをチェックする必要はありません。 Ansible がこのようなコードを自動的にチェックします。 ユーザーの存在をチェックする代わりに、ユーザーモジュールを使用してユーザーを存在させます。

Ansible はフェイルファーストシステムであるため、ユーザーの作成時にエラーがあると、Playbook の実行が停止します。 バックグラウンドで行われている内容を 確認する必要はありません。

ライフサイクルのテスト

アプリケーションの基本検証を Playbook に記述すると、デプロイ時に必ずこの検証が実行されます。

そのため、ローカルの開発仮想マシンやステージ環境にデプロイすると、 いずれも実稼働でのデプロイの前に計画通りに作業が進んでいるかどうかを検証します。

ワークフローは、次のようになります:

- 開発中は、テストが組み込まれた、同じ Playbook を常に使用します。
- その Playbook を使用して、実稼働環境をシミュレーションするステージ環境 (同じ Playbook を使用) にデプロイします。
- ステージ環境向けに QA チームが記述した統合テストを実行します。
- 同じ統合テストを使用して、実稼働環境にデプロイします。

実稼働の Web サービスを使用する場合には、QA チームが同様の統合テストを記述するようにしてください。 たとえば、 Selenium テストや自動化 API テストなどで、このようなテストは通常 Ansible Playbook には組み込まれていません。

ただし、基本的なヘルスチェックを Playbook に追加すると便利です。場合によっては、 リモートノードに対して QA 統合テストを実行することもできます。 この点について、次のセクションで説明します。

ローリングアップデートへのテストの統合

委任、ローリングアップデート、およびローカルアクション を参照された場合には、ローリングアップデートのパターンを拡張でき、 また、Playbook の成否でロードバランサーにマシンを 1 台追加するかどうかを決定できることが学習できたはずです。

以下は、統合テストをまとめたものです:

---

- hosts: webservers
  serial: 5

  pre_tasks:

    - name: take out of load balancer pool
      command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

  roles:

     - common
     - webserver
     - apply_testing_checks

  post_tasks:

    - name: add back to load balancer pool
      command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

上記では当然、「プールから取得する」手順や「追加し直す」手順は、Ansible のロードバランサーや、 適切な shell コマンドの呼び出しに置き換えられます。 また、 マシンのサービス停止期間を開始/終了するモニタリングモジュールを使用する手順なども含まれている場合があります。

上記で分かるように、テストはゲートとして使用されています。つまり、「apply_testing_checks」の手順が実行されない場合は、 マシンがプールに戻らないようになっています。

ローリングアップデートの続行を停止させるテストの失敗回数を制御できます。この点については、「max_fail_percentage」 向けの章を参照してください。

上記のアプローチを変更して、リモートのテストマシンから手順を実行することも可能です:

---

- hosts: webservers
  serial: 5

  pre_tasks:

    - name: take out of load balancer pool
      command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

  roles:

     - common
     - webserver

  tasks:
     - script: /srv/qa_team/app_testing_script.sh --server {{ inventory_hostname }}
       delegate_to: testing_server

  post_tasks:

    - name: add back to load balancer pool
      command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

上記の例では、プールにマシンを戻す前に、 リモートのノードに対してテストサーバーからスクリプトを実行します。

問題が発生した場合には、Ansible が自動で生成した再試行ファイルを使用して、失敗したサーバー数台を修正し、 そのサーバーだけにデプロイメントを繰り返し実行します。

継続的なデプロイメントの実現

任意で、上記の手法を拡張して、継続してデプロイメントができるようにします。

ワークフローは、次のようになります:

- ローカルの開発仮想マシンをデプロイする自動化を記述して使用します。
- コードの変更のたびに、Jenkins などの CI システムをステージ環境にデプロイします。
- デプロイメントジョブでテストスクリプトを呼び出し、全デプロイメントのビルドの合否を確認します。
- デプロイメントジョブに成功すると、実稼働環境のインベントリーに対して同じデプロイメント Playbook を実行します。

Ansible ユーザーによっては、上記のアプローチを使用して、すべてのインフラストラクチャーをオフラインにすることなく、 1 時間に 6 回または 12 回デプロイしています。 このレベルに到達するには、自動化 QA の文化が必要不可欠です。

大量の QA を手動で続けている場合には、手動でデプロイするべきかどうか決定する必要がありますが、 前項のようにローリングアップデートのパターンを使用して作業をし、 「script」、「stat」、「uri」、「assert」などのモジュールで基本的なヘルスチェックを組み込むだけでも役立つ場合があります。

まとめ

Ansible では、インフラストラクチャーの基本的な内容が正しいかを検証するフレームワークを別に用意する必要はないと考えます。 これは、 Ansible は順序ベースのシステムで、ホストに未処理のエラーがあると即座に失敗し、 そのホストの設定がこれ以上進まないようにします。 こうすることで、エラーが表面化し、Ansible の実行の最後にまとめとして、エラーが表示されます。

ただし、Ansible は、複数階層のオーケストレーションシステムとして設計されているため、 非常に簡単に単発のタスクまたはロールを使用して Playbook 実行の最後にテストを組み込むことができます。 ローリングアップデートで使用する場合は、 テスト手順によりマシンをロードバランサープールに配置するかどうかが決まります。

最後に、Ansible のエラーは、Ansible のプログラム自体のリターンコードにまで伝搬され、また Ansible はデフォルトで簡単なプッシュベースモードで実行されるため、 上記のセクションで説明されているように、 継続的な統合/デリバリーパイプラインの一部としてシステムを展開する場合に Ansible をビルド環境に活用すると大きな一歩になります。

インフラストラクチャーではなく、アプリケーションのテストに焦点を当てるため、 QA チームと連携して、どのようなテストを、開発仮想マシンのデプロイメント時に毎回実行すると便利か、またデプロイメント時に毎回、 ステージ環境にどのようなテストを実行するかを確認してください。 当然、開発段階ではユニットテストも便利ですが、 Playbook のユニットテストは 実行しないでください。 Ansible は、リソースの状態を宣言的に記述するため、Playbook のユニットテストは必要ありません。 テストして確認する内容がある場合には、役に立ちますし、 そのような目的には、stat/assert のモジュールが適しています。

結局、テストは非常に組織的で、サイト固有の内容となっています。 テストは必ず行うべきですが、 お使いの環境に最も有用なテストは、デプロイメントの内容や、使用するものにより異なります。 しかし、誰もが、強力で信頼性の高いデプロイメントシステムから恩恵を受けることができます。

See also

All modules
Ansible モジュールの全ドキュメント
Playbook の使用
Playbook の概要
委任、ローリングアップデート、およびローカルアクション
委譲 (ロードバランサー、クラウド、ローカルに実行されたステップを使用する際に役に立ちます)
ユーザーメーリングリスト
ご質問はございますか。 Google Group をご覧ください。
irc.freenode.net
#ansible IRC chat channel