Ubuntu18.04でMoleculeとTravisCIを使用してAnsibleロールの継続的テストを実装する方法
著者は、 Write for DOnations プログラムの一環として、 MozillaFoundationを選択して寄付を受け取りました。
序章
Ansible は、YAMLテンプレートを使用して、ホストで実行されるタスクのリストを定義するエージェントレス構成管理ツールです。 Ansibleでは、 roles は、単一の複雑な機能を実行するために一緒に使用される変数、タスク、ファイル、テンプレート、およびモジュールのコレクションです。
Molecular は、Ansibleの役割の自動テストを実行するためのツールであり、一貫して適切に記述され維持されている役割の開発をサポートするように特別に設計されています。 Moleculeの単体テストを使用すると、開発者は複数の環境に対して、異なるパラメーターで同時に役割をテストできます。 開発者は、頻繁に変更されるコードに対して継続的にテストを実行することが重要です。 このワークフローにより、コードライブラリを更新するときに役割が引き続き機能することが保証されます。 Travis CI などの継続的インテグレーションツールを使用してMoleculeを実行すると、テストを継続的に実行できるため、コードへの貢献によって重大な変更が発生することはありません。
このチュートリアルでは、UbuntuおよびCentOSサーバーにApacheWebサーバーとファイアウォールをインストールして構成する事前に作成された基本ロールを使用します。 次に、そのロールでMoleculeシナリオを初期化してテストを作成し、ターゲット環境でロールが意図したとおりに機能することを確認します。 Moleculeを構成した後、Travis CIを使用して、新しく作成した役割を継続的にテストします。 コードに変更が加えられるたびに、TravisCIはmolecule test
を実行して、役割が引き続き正しく実行されることを確認します。
前提条件
このチュートリアルを開始する前に、次のものが必要です。
- Ubuntu 18.04初期サーバーセットアップガイドに従ってセットアップされた1つのUbuntu18.04サーバー(sudo非rootユーザーとファイアウォールを含む)。
- AnsibleとMoleculeが構成されています。これは、 Ubuntu18.04でMoleculeを使用してAnsibleロールをテストする方法のステップ1に従って実行できます。
- オープンソースに貢献する方法:Gitの使用開始に従ってインストールされたGit。
- 継続的インテグレーションとそのユースケースに精通していること。 詳細については、継続的インテグレーション、デリバリー、およびデプロイメントの概要を確認してください。
- GitHubのアカウント。
- TravisCIのアカウント。
ステップ1—基本ロールリポジトリをフォークする
Apacheをインストールし、DebianベースおよびRedHatベースのディストリビューションにファイアウォールを構成するansible-apacheと呼ばれる事前に作成されたロールを使用します。 この役割をフォークしてベースとして使用し、その上に分子テストを構築します。 フォークを使用すると、リポジトリのコピーを作成できるため、元のプロジェクトを改ざんすることなくリポジトリに変更を加えることができます。
ansible-apacheロールのフォークを作成することから始めます。 ansible-apache リポジトリに移動し、フォークボタンをクリックします。
リポジトリをフォークすると、GitHubがフォークのページに移動します。 これはベースリポジトリのコピーになりますが、自分のアカウントで行います。
緑色のクローンまたはダウンロードボタンをクリックすると、クローンとHTTPSのボックスが表示されます。
リポジトリに表示されているURLをコピーします。 これは次のステップで使用します。 URLは次のようになります。
https://github.com/username/ansible-apache.git
username
をGitHubユーザー名に置き換えます。
フォークを設定したら、サーバーにフォークを複製し、次のセクションで役割の準備を開始します。
ステップ2—役割の準備
前提条件Ubuntu18.04 でMoleculeを使用してAnsibleロールをテストする方法のステップ1に従うと、MoleculeとAnsibleが仮想環境にインストールされます。 この仮想環境を使用して、新しい役割を開発します。
まず、以下を実行して、前提条件に従って作成した仮想環境をアクティブ化します。
- source my_env/bin/activate
次のコマンドを実行して、手順1でコピーしたURLを使用してリポジトリのクローンを作成します。
- git clone https://github.com/username/ansible-apache.git
出力は次のようになります。
OutputCloning into 'ansible-apache'...
remote: Enumerating objects: 16, done.
remote: Total 16 (delta 0), reused 0 (delta 0), pack-reused 16
Unpacking objects: 100% (16/16), done.
新しく作成したディレクトリに移動します。
- cd ansible-apache
ダウンロードした基本ロールは、次のタスクを実行します。
-
変数を含める:役割は、ホストの分布に従って、必要なすべての変数を含めることから始まります。 Ansibleは変数を使用して、異なるシステム間の不一致を処理します。 Ubuntu18.04とCentOS7をホストとして使用しているため、ロールはOSファミリがそれぞれDebianとRed Hatであることを認識し、
vars/Debian.yml
とvars/RedHat.yml
の変数を含みます。 -
配布関連のタスクが含まれます:これらのタスクには、
tasks/install-Debian.yml
およびtasks/install-RedHat.yml
が含まれます。 指定されたディストリビューションに応じて、関連するパッケージをインストールします。 Ubuntuの場合、これらのパッケージはapache2
およびufw
です。 CentOSの場合、これらのパッケージはhttpd
およびfirewalld
です。 -
最新のindex.htmlが存在することを確認します:このタスクは、ApacheがWebサーバーのホームページとして使用するテンプレート
templates/index.html.j2
をコピーします。 -
関連するサービスを開始し、起動時にそれらを有効にします:最初のタスクの一部としてインストールされた必要なサービスを開始して有効にします。 CentOSの場合、これらのサービスは
httpd
とfirewalld
であり、Ubuntuの場合はapache2
とufw
です。 -
トラフィックを許可するようにファイアウォールを構成します:これには、
tasks/configure-Debian-firewall.yml
またはtasks/configure-RedHat-firewall.yml
のいずれかが含まれます。 Ansibleは、FirewalldまたはUFWのいずれかをファイアウォールとして構成し、http
サービスをホワイトリストに登録します。
この役割がどのように機能するかを理解したので、それをテストするようにMoleculeを構成します。 これらのタスクのテストケースを作成して、それらが行う変更をカバーします。
ステップ3—テストを書く
基本ロールが意図したとおりにタスクを実行することを確認するには、Moleculeシナリオを開始し、ターゲット環境を指定して、3つのカスタムテストファイルを作成します。
次のコマンドを使用して、この役割の分子シナリオを初期化することから始めます。
- molecule init scenario -r ansible-apache
次の出力が表示されます。
Output--> Initializing new scenario default...
Initialized scenario in /home/sammy/ansible-apache/molecule/default successfully.
CentOSとUbuntuをプラットフォームとしてMolecule構成ファイルに含めることにより、ターゲット環境として追加します。 これを行うには、テキストエディタを使用してmolecule.yml
ファイルを編集します。
- nano molecule/default/molecule.yml
次の強調表示されたコンテンツをMolecule構成に追加します。
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
platforms:
- name: centos7
image: milcom/centos7-systemd
privileged: true
- name: ubuntu18
image: solita/ubuntu-systemd
command: /sbin/init
privileged: true
volumes:
- /lib/modules:/lib/modules:ro
provisioner:
name: ansible
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8
ここでは、systemdサービスを使用しているため、特権モードで起動される2つのターゲットプラットフォームを指定しています。
centos7
は最初のプラットフォームであり、milcom/centos7-systemd
イメージを使用します。ubuntu18
は2番目のプラットフォームであり、solita/ubuntu-systemd
イメージを使用します。 特権モードを使用して必要なカーネルモジュールをマウントすることに加えて、起動時に/sbin/init
を実行して、iptablesが稼働していることを確認します。
ファイルを保存して終了します。
特権コンテナーの実行の詳細については、公式のMoleculeドキュメントにアクセスしてください。
デフォルトのMoleculeテストファイルを使用する代わりに、3つのカスタムテストファイルを作成します。1つはターゲットプラットフォームごとに1つ、もう1つはすべてのプラットフォームに共通のテストを作成するためのファイルです。 次のコマンドを使用して、シナリオのデフォルトのテストファイルtest_default.py
を削除することから始めます。
- rm molecule/default/tests/test_default.py
これで、ターゲットプラットフォームごとに、test_common.py
、test_Debian.py
、およびtest_RedHat.py
の3つのカスタムテストファイルの作成に進むことができます。
最初のテストファイルtest_common.py
には、各ホストが実行する共通のテストが含まれています。 共通テストファイルtest_common.py
を作成および編集します。
- nano molecule/default/tests/test_common.py
次のコードをファイルに追加します。
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
@pytest.mark.parametrize('file, content', [
("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
file = host.file(file)
assert file.exists
assert file.contains(content)
test_common.py
ファイルに、必要なライブラリをインポートしました。 また、test_files()
と呼ばれるテストを作成しました。このテストは、役割が実行するディストリビューション間の唯一の共通タスク、つまりテンプレートをWebサーバーのホームページとしてコピーすることを保持します。
次のテストファイルtest_Debian.py
には、Debianディストリビューションに固有のテストが含まれています。 このテストファイルは、特にUbuntuプラットフォームを対象としています。
次のコマンドを実行して、Ubuntuテストファイルを作成および編集します。
- nano molecule/default/tests/test_Debian.py
これで、必要なライブラリをインポートして、ubuntu18
プラットフォームをターゲットホストとして定義できます。 このファイルの先頭に次のコードを追加します。
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('ubuntu18')
次に、同じファイルにtest_pkg()
テストを追加します。
test_pkg()
テストを定義する次のコードをファイルに追加します。
...
@pytest.mark.parametrize('pkg', [
'apache2',
'ufw'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
このテストでは、apache2
およびufw
パッケージがホストにインストールされているかどうかを確認します。
注: Moleculeテストファイルに複数のテストを追加する場合は、各テストの間に2つの空白行があることを確認してください。そうしないと、Moleculeから構文エラーが発生します。
次のテストtest_svc()
を定義するには、ファイルのtest_pkg()
テストの下に次のコードを追加します。
...
@pytest.mark.parametrize('svc', [
'apache2',
'ufw'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
test_svc()
は、apache2
およびufw
サービスが実行され、有効になっているかどうかを確認します。
最後に、最後のテストtest_ufw_rules()
をtest_Debian.py
ファイルに追加します。
ファイルのtest_svc()
テストに次のコードを追加して、test_ufw_rules()
を定義します。
...
@pytest.mark.parametrize('rule', [
'-A ufw-user-input -p tcp -m tcp --dport 80 -j ACCEPT'
])
def test_ufw_rules(host, rule):
cmd = host.run('iptables -t filter -S')
assert rule in cmd.stdout
test_ufw_rules()
は、ファイアウォール構成がApacheサービスによって使用されるポートでのトラフィックを許可することを確認します。
これらの各テストを追加すると、test_Debian.py
ファイルは次のようになります。
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('ubuntu18')
@pytest.mark.parametrize('pkg', [
'apache2',
'ufw'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
@pytest.mark.parametrize('svc', [
'apache2',
'ufw'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
@pytest.mark.parametrize('rule', [
'-A ufw-user-input -p tcp -m tcp --dport 80 -j ACCEPT'
])
def test_ufw_rules(host, rule):
cmd = host.run('iptables -t filter -S')
assert rule in cmd.stdout
test_Debian.py
ファイルには、test_pkg()
、test_svc()
、およびtest_ufw_rules()
の3つのテストが含まれるようになりました。
test_Debian.py
を保存して終了します。
次に、test_RedHat.py
テストファイルを作成します。このファイルには、CentOSプラットフォームを対象とするRedHatディストリビューションに固有のテストが含まれています。
次のコマンドを実行して、CentOSテストファイルtest_RedHat.py
を作成および編集します。
- nano molecule/default/tests/test_RedHat.py
Ubuntuテストファイルと同様に、test_RedHat.py
ファイルに含める3つのテストを作成します。 テストコードを追加する前に、ファイルの先頭に次のコードを追加することで、必要なライブラリをインポートし、centos7
プラットフォームをターゲットホストとして定義できます。
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('centos7')
次に、test_pkg()
テストを追加します。このテストでは、httpd
およびfirewalld
パッケージがホストにインストールされているかどうかを確認します。
ライブラリインポートのコードに従って、test_pkg()
テストをファイルに追加します。 (繰り返しますが、新しいテストの前に2つの空白行を含めることを忘れないでください。)
...
@pytest.mark.parametrize('pkg', [
'httpd',
'firewalld'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
これで、test_svc()
テストを追加して、httpd
およびfirewalld
サービスが実行されて有効になっていることを確認できます。
test_pkg()
テストに続いて、test_svc()
コードをファイルに追加します。
...
@pytest.mark.parametrize('svc', [
'httpd',
'firewalld'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
test_RedHat.py
ファイルの最終テストはtest_firewalld()
で、Firewalldにhttp
サービスがホワイトリストに登録されているかどうかがチェックされます。
test_svc()
コードの後にtest_firewalld()
テストをファイルに追加します。
...
@pytest.mark.parametrize('file, content', [
("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>")
])
def test_firewalld(host, file, content):
file = host.file(file)
assert file.exists
assert file.contains(content)
ライブラリをインポートして3つのテストを追加すると、test_RedHat.py
ファイルは次のようになります。
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('centos7')
@pytest.mark.parametrize('pkg', [
'httpd',
'firewalld'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
@pytest.mark.parametrize('svc', [
'httpd',
'firewalld'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
@pytest.mark.parametrize('file, content', [
("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>")
])
def test_firewalld(host, file, content):
file = host.file(file)
assert file.exists
assert file.contains(content)
test_common.py
、test_Debian.py
、およびtest_RedHat.py
の3つのファイルすべてでテストの記述が完了したので、役割をテストする準備が整いました。 次のステップでは、Moleculeを使用して、新しく構成されたロールに対してこれらのテストを実行します。
ステップ4—自分の役割に対するテスト
次に、Moleculeを使用して、基本ロールansible-apache
に対して新しく作成したテストを実行します。 テストを実行するには、次のコマンドを使用します。
- molecule test
Moleculeがすべてのテストの実行を終了すると、次の出力が表示されます。
Output...
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /home/sammy/ansible-apache/molecule/default/tests/...
============================= test session starts ==============================
platform linux -- Python 3.6.7, pytest-4.1.1, py-1.7.0, pluggy-0.8.1
rootdir: /home/sammy/ansible-apache/molecule/default, inifile:
plugins: testinfra-1.16.0
collected 12 items
tests/test_common.py .. [ 16%]
tests/test_RedHat.py ..... [ 58%]
tests/test_Debian.py ..... [100%]
========================== 12 passed in 80.70 seconds ==========================
Verifier completed successfully.
出力にVerifier completed successfully
が表示されます。 これは、ベリファイアがすべてのテストを実行し、それらを正常に返したことを意味します。
役割の開発が正常に完了したので、変更をGitにコミットし、継続的テスト用にTravisCIをセットアップできます。
ステップ5—Gitを使用して更新された役割を共有する
このチュートリアルでは、これまでにansible-apache
という役割のクローンを作成し、UbuntuおよびCentOSホストに対して機能することを確認するためのテストを追加しました。 更新された役割を一般の人々と共有するには、これらの変更をコミットしてフォークにプッシュする必要があります。
次のコマンドを実行してファイルを追加し、行った変更をコミットします。
- git add .
このコマンドは、現在のディレクトリで変更したすべてのファイルをステージング領域に追加します。
また、正常にコミットするには、git config
に名前とメールアドレスを設定する必要があります。 次のコマンドを使用してこれを行うことができます。
- git config user.email "[email protected]"
- git config user.name "John Doe"
変更したファイルをリポジトリにコミットします。
- git commit -m "Configured Molecule"
次の出力が表示されます。
Output[master b2d5a5c] Configured Molecule
8 files changed, 155 insertions(+), 1 deletion(-)
create mode 100644 molecule/default/Dockerfile.j2
create mode 100644 molecule/default/INSTALL.rst
create mode 100644 molecule/default/molecule.yml
create mode 100644 molecule/default/playbook.yml
create mode 100644 molecule/default/tests/test_Debian.py
create mode 100644 molecule/default/tests/test_RedHat.py
create mode 100644 molecule/default/tests/test_common.py
これは、変更が正常にコミットされたことを意味します。 次に、次のコマンドを使用して、これらの変更をフォークにプッシュします。
- git push -u origin master
GitHubクレデンシャルの入力を求めるプロンプトが表示されます。 これらの資格情報を入力すると、コードがリポジトリにプッシュされ、次の出力が表示されます。
OutputCounting objects: 13, done.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (13/13), 2.32 KiB | 2.32 MiB/s, done.
Total 13 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 2 local objects.
To https://github.com/username/ansible-apache.git
009d5d6..e4e6959 master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
github.com/username/ansible-apache
にあるフォークのリポジトリに移動すると、ファイルに加えた変更を反映したConfigured Molecule
という新しいコミットが表示されます。
これで、Travis CIを新しいリポジトリと統合して、ロールに加えられた変更によってMoleculeテストが自動的にトリガーされるようになりました。 これにより、あなたの役割が常にUbuntuおよびCentOSホストで機能するようになります。
ステップ6—TravisCIの統合
このステップでは、TravisCIをワークフローに統合します。 有効にすると、フォークにプッシュする変更があれば、TravisCIビルドがトリガーされます。 これの目的は、寄稿者が変更を加えるたびにTravisCIが常にmolecule test
を実行するようにすることです。 重大な変更が加えられた場合、Travisはビルドステータスをそのように宣言します。
Travis CI に進み、リポジトリを有効にします。 GitHubのActivateボタンをクリックできるプロファイルページに移動します。
TravisCIでのリポジトリのアクティブ化に関する詳細なガイダンスここを見つけることができます。
Travis CIを機能させるには、その手順を含む構成ファイルを作成する必要があります。 Travis構成ファイルを作成するには、サーバーに戻り、次のコマンドを実行します。
- nano .travis.yml
このチュートリアルで作成した環境を複製するには、Travis構成ファイルでパラメーターを指定します。 次のコンテンツをファイルに追加します。
---
language: python
python:
- "2.7"
- "3.6"
services:
- docker
install:
- pip install molecule docker
script:
- molecule --version
- ansible --version
- molecule test
このファイルで指定したパラメーターは次のとおりです。
language
:Pythonを言語として指定すると、CI環境はpython
キーで指定したPythonバージョンごとに個別のvirtualenv
インスタンスを使用します。python
:ここでは、TravisがPython2.7とPython3.6の両方を使用してテストを実行することを指定しています。services
:Moleculeでテストを実行するにはDockerが必要です。 TravisがDockerがCI環境に存在することを確認する必要があることを指定しています。install
:ここでは、TravisCIがvirtualenv
で実行する予備的なインストール手順を指定しています。pip installmolecule dockerを使用して、DockerリモートAPIのPythonライブラリとともにAnsibleとMoleculeが存在することを確認します。 script
:これは、TravisCIが実行する必要のあるステップを指定するためのものです。 ファイルでは、次の3つのステップを指定しています。Molecular –versionは、Moleculeが正常にインストールされている場合、Moleculeバージョンを出力します。 ansible –versionは、Ansibleが正常にインストールされている場合、Ansibleバージョンを出力します。 分子テストは最終的に分子テストを実行します。
molecule --version
およびansible --version
を指定する理由は、バージョン管理によるansible
またはmolecule
の設定ミスの結果としてビルドが失敗した場合に、エラーをキャッチするためです。
コンテンツをTravisCI構成ファイルに追加したら、.travis.yml
を保存して終了します。
これで、リポジトリに変更をプッシュするたびに、TravisCIは上記の構成ファイルに基づいてビルドを自動的に実行します。 script
ブロックのコマンドのいずれかが失敗した場合、TravisCIはビルドステータスをそのように報告します。
ビルドステータスを簡単に確認できるように、ロールのREADME
にビルドステータスを示すバッジを追加できます。 テキストエディタを使用してREADME.md
ファイルを開きます。
- nano README.md
README.md
に次の行を追加して、ビルドステータスを表示します。
[](https://travis-ci.org/username/ansible-apache)
username
をGitHubユーザー名に置き換えます。 以前と同じように、変更をコミットしてリポジトリにプッシュします。
まず、次のコマンドを実行して、.travis.yml
とREADME.md
をステージング領域に追加します。
- git add .travis.yml README.md
次に、以下を実行して、変更をリポジトリにコミットします。
- git commit -m "Configured Travis"
最後に、次のコマンドを使用して、これらの変更をフォークにプッシュします。
- git push -u origin master
GitHubリポジトリに移動すると、最初に build:unknownが報告されていることがわかります。
数分以内に、TravisはTravisCIWebサイトで監視できるビルドを開始します。 ビルドが成功すると、GitHubはリポジトリにもステータスを報告します—READMEファイルに配置したバッジを使用します。
Travis CI Webサイトにアクセスすると、ビルドの完全な詳細にアクセスできます。
新しいロール用にTravisCIを正常にセットアップしたので、Ansibleロールへの変更を継続的にテストして統合できます。
結論
このチュートリアルでは、GitHubからApache Webサーバーをインストールして構成する役割をフォークし、テストを記述して、UbuntuとCentOSを実行するDockerコンテナーで機能するようにこれらのテストを構成することにより、Moleculeの統合を追加しました。 新しく作成したロールをGitHubにプッシュすることで、他のユーザーが自分のロールにアクセスできるようになります。 コントリビューターによってロールに変更があった場合、TravisCIは自動的にMoleculeを実行してロールをテストします。
ロールの作成とMoleculeでのテストに慣れたら、これを Ansible Galaxy と統合して、ビルドが成功するとロールが自動的にプッシュされるようにすることができます。