MicroAd Developers Blog

マイクロアドのエンジニアブログです。インフラ、開発、分析について発信していきます。

Moleculeのdelegatedドライバ + OpenStackでAnsibleのテストを行う

インフラエンジニアの長田です。 今回は、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.ymlos_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した際に自動で行われるようにしています。

f:id:nagata_yasuhiro:20200612131821p:plain
Moleculeテスト実行フロー

おわりに

Moleculeのdelegatedドライバを用いることで、Dockerでは実現困難なテストをOpenStackインスタンス上で行うことができました。現在はマイクロアドで使っているA10ネットワークス社のネットワーク機器についても導入を進めています。今後もAnsibleとMoleculeの活用範囲を広げ、CI/CDと組み合わせた強力な自動化の推進に励みたいと思います。

参考資料

Molecule入門:Ansible流でITインフラテストの自動化を実現するソフトウェアのご紹介(オンデマンド登録することでMoleculeに関するpdfが閲覧できます)

Moleculeに入門してみたよ(v3差分確認編) - Qiita

moleculeのdelegatedドライバを使ってVMware環境にテスト用インスタンスを作成する方法 | 日常系エンジニアのTech Blog