インフラエンジニアの長田です。 今回は、Ansibleのテスト支援ツールであるMoleculeの実践例について記事にしたいと思います。MoleculeはDockerやPodmanでコンテナを起動し、その中でAnsibleロールのテストを簡潔に行うことができる便利なツールですが、Ansibleで実行したいロールの中にはgrub2のようなブートローダのコンフィグやカーネルパラメータの変更、ネットワーク機器の操作など、コンテナ上でのテストが困難であるケースも存在します。そこで、Moleculeのdelegatedドライバを用いてOpenStack上にインスタンスを構築し、これらのテストを実現したいと思います。
以下、各ツールのバージョンについて、Ansibleは 2.9.9
を、Moleculeは 3.0.4
を前提としています。
OpenStackインスタンスの作成・削除
Moleculeのdelegatedドライバとは、ユーザが任意の環境でMoleculeのテストを実行するための機能で、インスタンスの作成を行う create.yml
、削除を行う destroy.yml
を用意することでVMWareやOpenStackインスタンス上でのテストが実現できます。
(delegatedドライバ以外の手段としてMoleculeの2.x系ではOpenStackドライバが同梱されていましたが、3.x系では標準パッケージに含まれなくなりました。)
create.ymlの作成
まず、 create.yml
に os_server
モジュールを用いてOpenStackのインスタンスを作成するタスクを記述します。
--- - name: Create hosts: localhost gather_facts: false no_log: "{{ not (lookup('env', 'MOLECULE_DEBUG') | bool or molecule_yml.provisioner.log | default(false) | bool) }}" tasks: - name: launch a compute instance os_server: state: present auth: auth_url: "{{ item.auth_url }}" user_domain_name: "{{ item.domain_name }}" username: "{{ item.username }}" password: "{{ vault_os_auth_password }}" os_project_domain_name: "{{ item.domain_name }}" project_name: "{{ item.project_name }}" name: "{{ item.name }}" image: "{{ item.image }}" key_name: "{{ item.key_name }}" timeout: "{{ item.timeout | default(200) }}" flavor: "{{ item.flavor }}" nics: - net-name: backend security_groups: - 'default' terminate_volume: true meta: hostname: "{{ item.name }}" register: server_info with_items: "{{ molecule_yml.platforms }}" # 起動したインスタンスへの接続設定 - name: Populate instance config dict set_fact: instance_conf_dict: { 'instance': "{{ item.server.hostname }}", 'address': "{{ item.server.accessIPv4 }}", 'user': "{{ molecule_yml.driver.user | default('centos') }}", 'port': "22", 'identity_file': "{{ molecule_yml.driver.ssh_identity_file }}" } with_items: "{{ server_info.results }}" register: instance_config_dict when: - server_info.changed - name: Convert instance config dict to a list set_fact: instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" - name: Dump instance config copy: content: "{{ instance_conf | to_json | from_json | molecule_to_yaml | molecule_header }}" dest: "{{ molecule_instance_config }}"
変数 molecule_instance_config
はMoleculeのデフォルトで定義されている変数で、最終的に作成したインスタンスへの接続設定をこのパスへ出力することで、Ansibleロールのテスト実行が可能となります。
作成したインスタンスへのSSH接続に使用する秘密鍵 molecule_yml.driver.ssh_identity_file
は、あらかじめ作成しておいたOpenStackキーペアの秘密鍵を、CI実行時にコンテナ内へ出力しています。
公式のos_serverモジュールに関するドキュメントには戻り値に関する記載がありませんが、タスクに register
を記載することで作成したインスタンスのホスト名、および自動割当されたIPを変数に設定することが可能です。
destroy.ymlの作成
destroy.yml
にインスタンスを削除するタスクを記述します。この処理はMoleculeのテスト開始時、 create.yml
より前にも行われるため、インスタンスが存在しない場合もエラーが発生しないよう記述する必要があります。OpenStackインスタンスの場合は、os_serverモジュールを state: absent
とすることで特にエラーは発生しないようでした。
--- - name: Destroy hosts: localhost gather_facts: false no_log: "{{ not (lookup('env', 'MOLECULE_DEBUG') | bool or molecule_yml.provisioner.log | default(false) | bool) }}" tasks: - name: remove a compute instance os_server: state: absent auth: auth_url: "{{ item.auth_url }}" user_domain_name: "{{ item.domain_name }}" username: "{{ item.username }}" password: "{{ vault_os_auth_password }}" os_project_domain_name: "{{ item.domain_name }}" project_name: "{{ item.project_name }}" name: "{{ item.name }}" with_items: "{{ molecule_yml.platforms }}"
molecule.ymlの作成
molecule.yml
に、Moleculeの実行時に使用する変数を記載します。createやdestroyの際、 molecule_yml
という変数で使用することができます。
上記でも使用している、OpenStack認証用のパスワード vault_os_auth_password
は、別途ansible-vaultで暗号化したファイルに定義しています。
--- dependency: name: galaxy driver: name: delegated user: centos ssh_identity_file: "${PWD}/../../.ssh/id_rsa" become_method: valid_ansible_become_method ssh_connection_options: - '-o UserKnownHostsFile=/dev/null' platforms: - name: "ansible-ci-instance" image: centos7 key_name: key_ansible-ci-user flavor: c1.m4.10G auth_url: http://xxxx:5000/v3 domain_name: MicroAd project_name: username: ansible-ci-user provisioner: name: ansible inventory: links: group_vars: ./group_vars/ options: become: true timeout: 300 verifier: name: ansible
prepare.ymlの作成(任意)
必要であればロールのテスト実行前に必要なタスクを記述した prepare.yml
を用意します。
今回は特に必要ないため、省略します。
テストの作成・実行
インスタンスの作成・削除用プレイブックができたら、次にテスト項目を記載したファイルを作成します。
各ロールのディレクトリ配下で molecule init scenario
を実行すると作成される verify.yml
に、Ansibleの assert
モジュールの形式でテスト項目を記載します。
--- - name: Verify hosts: all tasks: - name: Assert that transparent hugepage is disabled assert: that: "'transparent_hugepage=never' in {{lookup('file', '/boot/grub2/grub.cfg') }}"
テストの実行は、Jenkins Pipelineを用いてGitHubへAnsibleロールのリソースをpushした際に自動で行われるようにしています。
おわりに
Moleculeのdelegatedドライバを用いることで、Dockerでは実現困難なテストをOpenStackインスタンス上で行うことができました。現在はマイクロアドで使っているA10ネットワークス社のネットワーク機器についても導入を進めています。今後もAnsibleとMoleculeの活用範囲を広げ、CI/CDと組み合わせた強力な自動化の推進に励みたいと思います。
参考資料
Molecule入門:Ansible流でITインフラテストの自動化を実現するソフトウェアのご紹介(オンデマンド登録することでMoleculeに関するpdfが閲覧できます)
Moleculeに入門してみたよ(v3差分確認編) - Qiita
moleculeのdelegatedドライバを使ってVMware環境にテスト用インスタンスを作成する方法 | 日常系エンジニアのTech Blog