手順: Ansible ロールをテストし、運用前に問題を見つける方法

みなさん、こんにちは!

私はホテル予約サービスの DevOps エンジニアとして働いています。 Ostrovok.ru。 この記事では、Ansible ロールのテストにおける私たちの経験について話したいと思います。

Ostrovok.ru では、構成マネージャーとして ansible を使用しています。 最近、ロールをテストする必要があるようになりましたが、結局のところ、これを行うためのツールはあまり多くありません。おそらく最も人気のあるのは Molecule フレームワークなので、それを使用することにしました。 しかし、彼の文書には多くの落とし穴について記載されていないことが判明しました。 ロシア語で十分に詳細なガイドが見つからなかったので、この記事を書くことにしました。

手順: Ansible ロールをテストし、運用前に問題を見つける方法

分子

分子 — Ansible ロールのテストに役立つフレームワーク。

簡単な説明: Molecule は、指定したプラットフォーム (クラウド、仮想マシン、コンテナー) 上にインスタンスを作成します。詳細については、セクションを参照してください。 ドライバ)、その上でロールを実行し、テストを実行してインスタンスを削除します。 いずれかのステップで障害が発生した場合、Molecule はそれについて通知します。

今もっと。

いくつかの説

分子の XNUMX つの主要なエンティティ、シナリオとドライバーについて考えてみましょう。

<span class="notranslate">シナリオ</span>

スクリプトには、何を、どこで、どのように、どのような順序で実行するかについての説明が含まれています。 XNUMX つのロールに複数のスクリプトを含めることができ、それぞれがパス上のディレクトリになります。 <role>/molecule/<scenario>、テストに必要なアクションの説明が含まれています。 スクリプトを含める必要があります defaultこれは、Molecule を使用してロールを初期化すると自動的に作成されます。 次のスクリプトの名前は任意です。

スクリプト内の一連のテスト アクションは次のように呼ばれます。 マトリックス、デフォルトでは次のようになります。

(マークされたステップ ?、ユーザーが指定しない場合、デフォルトでスキップされます)

  • lint - リンターの実行。 デフォルトでは yamllint и flake8,
  • destroy — Molecule の前回の起動からインスタンスを削除します (残っている場合)。
  • dependency? — テストされたロールの ansible 依存関係のインストール、
  • syntax - を使用してロール構文をチェックする ansible-playbook --syntax-check,
  • create - インスタンスの作成、
  • prepare? — インスタンスを準備する。 たとえば、python2の確認/インストール
  • converge — テスト済みのプレイブックを起動する、
  • idempotence — 冪等性テストのプレイブックを再実行します。
  • side_effect? — 役割には直接関係しないが、テストには必要なアクション、
  • verify — を使用して、結果の構成のテストを実行します。 testinfra(デフォルト) /goss/inspec,
  • cleanup? - (新しいバージョンでは) - 大まかに言うと、Molecule の影響を受ける外部インフラストラクチャを「クリーニング」します。
  • destroy — インスタンスの削除。

この順序はほとんどのケースをカバーしますが、必要に応じて変更できます。

上記の各ステップは、次を使用して個別に実行できます。 molecule <command>。 ただし、そのような cli コマンドごとに独自のアクションのシーケンスが存在する可能性があることを理解しておく必要があります。これは、次のコマンドを実行して確認できます。 molecule matrix <command>。 たとえば、コマンドを実行すると、 converge (テストされたプレイブックを実行すると) 次のアクションが実行されます。

$ molecule matrix converge
...
└── default         # название сценария
    ├── dependency  # установка зависимостей
    ├── create      # создание инстанса
    ├── prepare     # преднастройка инстанса
    └── converge    # прогон плейбука

これらのアクションのシーケンスは編集できます。 リスト内の何かがすでに完了している場合、それはスキップされます。 現在の状態とインスタンス構成は、Molecule ディレクトリに保存されます。 $TMPDIR/molecule/<role>/<scenario>.

ステップを追加する ? Ansible Playbook 形式で必要なアクションを記述し、次の手順に従ってファイル名を付けることができます。 prepare.yml/side_effect.yml。 これらの Molecule ファイルはスクリプト フォルダー内にあると考えられます。

ドライバ

ドライバーは、テスト用のインスタンスが作成されるエンティティです。
Molecule が既製のテンプレートを備えている標準ドライバーのリストは、Azure、Docker、EC2、GCE、LXC、LXD、OpenStack、Vagrant、Delegated です。

ほとんどの場合、テンプレートはファイルです create.yml и destroy.yml スクリプト フォルダー内に、インスタンスの作成と削除がそれぞれ記述されています。
Docker と Vagrant は例外です。これらのモジュールとの対話は、上記のファイルがなくても発生する可能性があるためです。

Delegated ドライバーを使用する場合、インスタンスの作成ファイルと削除ファイルにはインスタンス構成に関する作業のみが記述され、残りの部分はエンジニアが記述する必要があるため、Delegated ドライバーを強調する価値があります。

デフォルトのドライバーは Docker です。

それでは、練習に移り、さらなる機能を検討してみましょう。

はじめに

「Hello world」として、単純な nginx インストール ロールをテストしてみましょう。 ドライバーとして docker を選択しましょう。ほとんどの人はそれをインストールしていると思います (docker がデフォルトのドライバーであることを覚えておいてください)。

準備をしましょう virtualenv そしてそこにインストールします molecule:

> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker  # molecule установит ansible как зависимость; docker для драйвера

次のステップは、新しいロールを初期化することです。
新しいロールと新しいスクリプトの初期化は、次のコマンドを使用して実行されます。 molecule init <params>:

> molecule init role -r nginx
--> Initializing new role nginx...
Initialized role in <path>/nginx successfully.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars

6 directories, 1 file

結果は典型的な ansible ロールになります。 さらに、Molecules CLI とのすべての対話は、ロール ルートから行われます。

役割ディレクトリの内容を見てみましょう。

> tree molecule/default/
molecule/default/
├── Dockerfile.j2  # Jinja-шаблон для Dockerfile
├── INSTALL.rst.   # Немного информации об установке зависимостей сценария
├── molecule.yml   # Файл конфигурации
├── playbook.yml   # Плейбук запуска роли
└── tests          # Директория с тестами стадии verify
    └── test_default.py

1 directory, 6 files

構成を見てみましょう molecule/default/molecule.yml (Docker イメージのみを置き換えます):

---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: instance
    image: centos:7
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8

依存関係

このセクションでは、依存関係のソースについて説明します。

可能なオプション: 銀河, 適用されます、シェル。

Shell は、Galaxy と Gilt ではニーズが満たされない場合に使用するコマンド シェルです。

私はここに長く滞在しません、それはで十分に説明されています ドキュメンテーション.

ドライバー

ドライバー名。 私たちにとって、これは docker です。

糸くず

Yamllint はリンターとして使用されます。

構成のこの部分の便利なオプションには、yamllint の構成ファイルを指定する機能、環境変数を転送する機能、またはリンターを無効にする機能があります。

lint:
  name: yamllint
  options:
    config-file: foo/bar
  env:
    FOO: bar
  enabled: False

プラットフォーム

インスタンスの構成について説明します。
ドライバーとして docker を使用する場合、Molecule はこのセクションを反復処理し、リストの各要素は以下で使用できます。 Dockerfile.j2 変数として item.

ドライバーの場合、 create.yml и destroy.yml、セクションは次のように利用できます。 molecule_yml.platforms、およびそれに対する反復は、これらのファイルにすでに記述されています。

Molecule は Ansible モジュールにインスタンス管理を提供するため、そこで可能な設定のリストを探す必要があります。 たとえば、Docker の場合、モジュールが使用されます。 docker_container_module。 他のドライバーでどのモジュールが使用されているかは、次の場所で確認できます。 ドキュメンテーション.

各種ドライバーの使用例もご覧いただけます 分子自体のテストで.

ここで交換しましょう セントス:7 на Ubuntuの.

プロビジョナー

「プロバイダ」とは、インスタンスを管理する主体です。 Molecule の場合、これは Ansible ですが、他のサポートは計画されていないため、このセクションは、留保付きで拡張 Ansible 構成と呼ぶことができます。
ここで指摘できることはたくさんありますが、私の意見では、主要な点を強調します。

  • プレイブック: 特定の段階でどの Playbook を使用するかを指定できます。

provisioner:
  name: ansible
  playbooks:
    create: create.yml
    destroy: ../default/destroy.yml
    converge: playbook.yml
    side_effect: side_effect.yml
    cleanup: cleanup.yml

provisioner:
  name: ansible
  config_options:
    defaults:
      fact_caching: jsonfile
    ssh_connection:
      scp_if_ssh: True

  • 接続オプション: オプション 接続性

provisioner:
  name: ansible  
  connection_options:
    ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"

  • オプション: Ansible パラメーターと環境変数

provisioner:
  name: ansible  
  options:
    vvv: true
    diff: true
  env:
    FOO: BAR

シナリオ

スクリプト シーケンスの名前と説明。
キーを追加すると、コマンドのデフォルトのアクション マトリックスを変更できます。 <command>_sequence 必要なステップのリストを定義することで、その値として使用します。
Playbook run コマンドを実行するときのアクションのシーケンスを変更したいとします。 molecule converge

# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
  name: default
  converge_sequence:
    - create
    - converge

検証者

テスト用のフレームワークとそのリンターをセットアップします。 デフォルトではリンターが使用されます testinfra и flake8。 可能なオプションは上記と同じです。

verifier:
  name: testinfra
  additional_files_or_dirs:
    - ../path/to/test_1.py
    - ../path/to/test_2.py
    - ../path/to/directory/*
  options:
    n: 1
  enabled: False
  env:
    FOO: bar
  lint:
    name: flake8
    options:
      benchmark: True
    enabled: False
    env:
      FOO: bar

私たちの役割に戻りましょう。 ファイルを編集しましょう tasks/main.yml この見た目に:

---
- name: Install nginx
  apt:
    name: nginx
    state: present

- name: Start nginx
  service:
    name: nginx
    state: started

そしてテストを追加します molecule/default/tests/test_default.py

def test_nginx_is_installed(host):
    nginx = host.package("nginx")
    assert nginx.is_installed

def test_nginx_running_and_enabled(host):
    nginx = host.service("nginx")
    assert nginx.is_running
    assert nginx.is_enabled

def test_nginx_config(host):
    host.run("nginx -t")

完了したら、あとは実行するだけです (ロールのルートから実行します)。

> molecule test

スポイラー下のロングエキゾースト:

--> Validating schema <path>/nginx/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy

--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in <path>/nginx/...
Lint completed successfully.
--> Executing Flake8 on files found in <path>/nginx/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on <path>/nginx/molecule/default/playbook.yml...
Lint completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'syntax'

    playbook: <path>/nginx/molecule/default/playbook.yml

--> Scenario: 'default'
--> Action: 'create'

    PLAY [Create] ******************************************************************

    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] => (item=None)

    TASK [Create Dockerfiles from image names] *************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Discover local Docker images] ********************************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Build an Ansible compatible image] ***************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Create docker network(s)] ************************************************

    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) creation to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=4    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
--> Scenario: 'default'
--> Action: 'converge'

    PLAY [Converge] ****************************************************************

    TASK [Gathering Facts] *********************************************************
    ok: [instance]

    TASK [nginx : Install nginx] ***************************************************
    changed: [instance]

    TASK [nginx : Start nginx] *****************************************************
    changed: [instance]

    PLAY RECAP *********************************************************************
    instance                   : ok=3    changed=2    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'idempotence'
Idempotence completed successfully.
--> Scenario: 'default'
--> Action: 'side_effect'
Skipping, side effect playbook not configured.
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in <path>/nginx/molecule/default/tests/...
    ============================= test session starts ==============================
    platform darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
    rootdir: <path>/nginx/molecule/default, inifile:
    plugins: testinfra-1.16.0
collected 4 items

    tests/test_default.py ....                                               [100%]

    ========================== 4 passed in 27.23 seconds ===========================
Verifier completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0

私たちの単純な役割は問題なくテストされました。
操作中に問題が発生した場合は、次のことを覚えておく価値があります。 molecule test、標準シーケンスを変更しなかった場合、Molecule はインスタンスを削除します。

次のコマンドはデバッグに役立ちます。

> molecule --debug <command> # debug info. При обычном запуске Молекула скрывает логи.
> molecule converge          # Оставляет инстанс после прогона тестируемой роли.
> molecule login             # Зайти в созданный инстанс.
> molecule --help            # Полный список команд.

既存の役割

既存のロールに新しいスクリプトを追加するには、 役割ディレクトリから 次のコマンドを使用します。

# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>

これがロール内の最初のスクリプトの場合、パラメータ -s スクリプトが作成されるため省略可能 default.

まとめ

ご覧のとおり、Molecule はそれほど複雑ではありません。独自のテンプレートを使用すると、新しいスクリプトのデプロイを、インスタンスの作成と削除のための Playbook 内の変数の編集に減らすことができます。 この分子は CI システムとシームレスに統合されているため、Playbook の手動テストの時間が短縮され、開発速度が向上します。

ご清聴ありがとうございました。 Ansible のロールをテストした経験があり、それが Molecule に関係ない場合は、コメントでそれについて教えてください。

出所: habr.com

コメントを追加します