みなさんこんにちは!
私はホテル予約サービスOstrovok.ruでDevOpsエンジニアとして働いています。 この記事では、ansibleロールをテストした経験についてお話したいと思います。
Ostrovok.ruでは、ansibleを構成マネージャーとして使用しています。 最近、ロールテストが必要になりましたが、判明したように、このためのツールはあまりありません。Moleculeフレームワークがおそらく最も人気があるので、使用することにしました。 しかし、彼のドキュメンテーションは多くの落とし穴について沈黙していることが判明しました。 ロシア語で十分に詳細なガイドを見つけることができなかったため、この記事を書くことにしました。
分子
Moleculeは、ansibleロールのテストに役立つフレームワークです。
簡略化された説明:分子は、指定したプラットフォーム(クラウド、仮想マシン、コンテナー。詳細については、 ドライバーのセクションを参照)でインスタンスを作成し、その上でロールを実行し、テストを実行してインスタンスを削除します。 ステップの1つで障害が発生した場合、分子はこれを通知します。
より詳細に。
理論のビット
分子の2つの重要なエンティティ、シナリオとドライバーを検討してください。
シナリオ
このスクリプトには、何を、どこで、どのように、どの順序で実行するかの説明が含まれています。 1つのロールには複数のスクリプトを含めることができ、各スクリプトはパス<role>/molecule/<scenario>
に沿ったディレクトリであり、テストに必要なアクションの説明が含まれています。 default
スクリプトが存在する必要があります。これは、Moleculeを使用してロールを初期化すると自動的に作成されます。 以下のシナリオの名前は、お客様の裁量で選択されます。
スクリプトのテストフローはmatrixと呼ばれ、デフォルトでは次のとおりです。
( ?
マークされたステップは、ユーザーが説明しない場合、デフォルトでスキップされます)
- lint-リンターを実行します。 デフォルトでは、
yamllint
とflake8
。 - destroy-最後のMolecule起動からインスタンスを削除します(残っている場合)。
-
dependency
? -テストされたロールのansible-dependencyのインストール、 -
syntax
ansible-playbook --syntax-check
したロール構文ansible-playbook --syntax-check
、 -
create
インスタンスを作成し、 -
prepare
ますか? -インスタンスの準備; 例:python2の確認/インストール -
converge
-テスト済みのプレイブックの起動、 -
idempotence
-べき等性テストのためにプレイブックを再起動し、 -
side_effect
? -ロールに直接関係しないが、テストに必要なアクション、 -
verify
-testinfra
(デフォルト)/testinfra
/inspec
を使用して、結果の構成のテストを実行します。 -
cleanup
? -(新しいバージョン)-大まかに言えば、分子の影響を受ける外部インフラストラクチャの「クリーニング」、 -
destroy
インスタンスを削除します。
このシーケンスはほとんどの場合に対応しますが、必要に応じて変更できます。
上記の各ステップは、 molecule <command>
を使用して個別に起動できます。 ただし、このような各cliコマンドには、 molecule matrix <command>
実行することで認識できる独自のアクションシーケンスが存在する可能性があることを理解する価値があります。 たとえば、 converge
コマンドを実行する(テスト済みのプレイブックを実行する)と、次のアクションが実行されます。
$ molecule matrix converge ... └── default # ├── dependency # ├── create # ├── prepare # └── converge #
これらのアクションのシーケンスは編集できます。 リストから何かがすでに完了している場合は、スキップされます。 インスタンスの構成と同様に、現在の状態は$TMPDIR/molecule/<role>/<scenario>
ディレクトリに保存されます。
?
ステップを追加し?
目的のアクションをansibleプレイブックの形式で記述し、ステップ: prepare.yml
/ side_effect.yml
に従ってファイル名を作成できます。 分子は、スクリプトフォルダー内のこれらのファイルを待ちます。
運転手
ドライバーは、テストインスタンスが作成されるエンティティです。
Moleculeにテンプレートが用意されている標準ドライバーのリストは、Azure、Docker、EC2、GCE、LXC、LXD、OpenStack、Vagrant、Delegatedです。
ほとんどの場合、テンプレートは、スクリプトフォルダー内のdestroy.yml
とdestroy.yml
であり、それぞれインスタンスの作成と削除を記述しています。
例外はDockerおよびVagrantです。これらのモジュールとのやり取りは上記のファイルなしで発生する可能性があるためです。
インスタンスを作成および削除するためにファイルで使用される場合、インスタンスの構成での作業のみが説明されているため、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 <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の役割でした。 さらに、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
configを分析しましょう(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
依存関係
このセクションでは、依存関係のソースについて説明します。
可能なオプション: galaxy 、 gilt 、shell。
シェルは、銀河と金箔がニーズを満たしていない場合に使用される単なるコマンドシェルです。
私はここで長い間停止しません、それはドキュメントで十分に説明されています 。
運転手
ドライバーの名前。 このドッカーがあります。
糸くず
リンターとして、yamllintが使用されます。
構成のこの部分で役立つオプションは、yamllintの構成ファイルを指定する機能、環境変数を転送する機能、またはリンターを無効にする機能です。
lint: name: yamllint options: config-file: foo/bar env: FOO: bar enabled: False
プラットフォーム
インスタンスの構成について説明します。
ドライバーとしてのdockerの場合、MoleculeはこのセクションでDockerfile.j2
れ、各リスト項目はDockerfile.j2
でitem
変数として使用できます。
create.yml
とdestroy.yml
ドライバーの場合、セクションはそれらでmolecule_yml.platforms
として利用可能であり、その繰り返しはこれらのファイルに既に記述されています。
Moleculeはansibleモジュールのインスタンス制御を提供するため、可能な設定のリストをそこに探す必要があります。 たとえば、 dockerの場合、 docker_container_moduleモジュールが使用されます。 他のドライバーで使用されているモジュールは、 ドキュメントに記載されています 。
また、さまざまなドライバの使用例は、分子自体のテストで見つけることができます。
ここでUbuntuの centos:7を置き換えます 。
プロビジョナー
「プロバイダ」は、インスタンスを制御するエンティティです。 Moleculeの場合、これはansibleであり、他のサポートは計画されていません。したがって、このセクションは、ansibleの拡張構成を予約して呼び出すことができます。
ここでは多くのことを指定できますが、私の意見では、主要な瞬間を強調します:
- プレイブック :特定のステージで使用するプレイブックを指定できます。
provisioner: name: ansible playbooks: create: create.yml destroy: ../default/destroy.yml converge: playbook.yml side_effect: side_effect.yml cleanup: cleanup.yml
- config_options : ansible config
provisioner: name: ansible config_options: defaults: fact_caching: jsonfile ssh_connection: scp_if_ssh: True
- connection_options : 接続パラメーター
provisioner: name: ansible connection_options: ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"
- options :可能なオプションと環境変数
provisioner: name: ansible options: vvv: true diff: true env: FOO: BAR
シナリオ
スクリプトシーケンスの名前と説明。
<command>_sequence
を追加し、その値として必要なステップのリストを決定<command>_sequence
ことにより、 <command>_sequence
デフォルトのアクションマトリックスを変更できます。
playbook runコマンドを実行するときにアクションのシーケンスを変更するとします。 molecule converge
# : # - dependency # - create # - prepare # - converge scenario: name: default converge_sequence: - create - converge
検証者
テスト用のフレームワークとそれにリンターを設定します。 デフォルトでは、 testinfra
とflake8
がflake8
として使用さtestinfra
ます。 可能なオプションは上記と同様です。
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
を次のように編集し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>
これが役割の最初のスクリプトである場合、 default
スクリプトが作成されるため、 -s
パラメーターは省略できます。
おわりに
ご覧のとおり、Moleculeはそれほど複雑ではなく、独自のテンプレートを使用する場合、新しいスクリプトの展開を減らして、インスタンスを作成および削除するためのプレイブックの変数を編集できます。 この分子はCIシステムとシームレスに統合されるため、プレイブックを手動でテストする時間を短縮することで開発速度を向上させることができます。
ご清聴ありがとうございました。 ansibleロールのテストの経験があり、Moleculeとは関係がない場合は、コメントで教えてください!