如何在Ubuntu18.04上使用Molecule和TravisCI实现Ansible角色的持续测试
作为 Write for DOnations 计划的一部分,作者选择了 Mozilla 基金会 来接受捐赠。
介绍
Ansible 是一个无代理配置管理工具,它使用 YAML 模板定义要在主机上执行的任务列表。 在 Ansible 中,roles 是变量、任务、文件、模板和模块的集合,它们一起用于执行单一、复杂的功能。
Molecule 是一种用于对 Ansible 角色进行自动化测试的工具,专门设计用于支持开发始终如一地编写和维护良好的角色。 Molecule 的单元测试允许开发人员同时针对多个环境和不同参数测试角色。 开发人员不断地针对经常更改的代码运行测试,这一点很重要。 此工作流程可确保角色在您更新代码库时继续工作。 使用持续集成工具运行 Molecule,例如 Travis CI,允许测试持续运行,确保对代码的贡献不会引入重大更改。
在本教程中,您将使用预制的基本角色在 Ubuntu 和 CentOS 服务器上安装和配置 Apache Web 服务器和防火墙。 然后,您将在该角色中初始化一个 Molecule 场景以创建测试并确保该角色在您的目标环境中按预期执行。 配置 Molecule 后,您将使用 Travis CI 不断测试您新创建的角色。 每次对代码进行更改时,Travis CI 都会运行 molecule test
以确保角色仍然正确执行。
先决条件
在开始本教程之前,您需要:
- 按照 Ubuntu 18.04 Initial Server Setup 指南设置一台 Ubuntu 18.04 服务器,包括 sudo 非 root 用户和防火墙。
- Ansible 和 Molecule 已配置,您可以按照 如何在 Ubuntu 18.04 上使用 Molecule 测试 Ansible 角色的第 1 步来完成。
- 按照如何为开源做贡献:Git入门安装Git。
- 熟悉持续集成及其用例。 要了解更多信息,请查看 持续集成、交付和部署简介 。
- GitHub 上的帐户。
- Travis CI 上的帐户。
第 1 步 - 分叉基本角色存储库
您将使用一个名为 ansible-apache 的预制角色,该角色安装 Apache 并在基于 Debian 和 Red Hat 的发行版上配置防火墙。 您将分叉并使用此角色作为基础,然后在其之上构建 Molecule 测试。 Forking 允许您创建存储库的副本,以便您可以对其进行更改而不会篡改原始项目。
首先创建一个 ansible-apache 角色的分支。 转到 ansible-apache 存储库并单击 Fork 按钮。
分叉存储库后,GitHub 将引导您进入分叉页面。 这将是基本存储库的副本,但在您自己的帐户上。
单击绿色的 Clone 或下载 按钮,您将看到一个带有 Clone with HTTPS 的框。
复制为您的存储库显示的 URL。 您将在下一步中使用它。 URL 将与此类似:
https://github.com/username/ansible-apache.git
您将 username
替换为您的 GitHub 用户名。
设置好 fork 后,您将在服务器上克隆它并开始准备下一部分中的角色。
第 2 步——准备你的角色
按照先决条件 如何在 Ubuntu 18.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 使用变量来处理不同系统之间的差异。 由于您使用 Ubuntu 18.04 和 CentOS 7 作为主机,因此该角色将识别出操作系统系列分别是 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 场景,指定您的目标环境,并创建三个自定义测试文件。
首先使用以下命令为此角色初始化 Molecule 场景:
molecule init scenario -r ansible-apache
您将看到以下输出:
Output--> Initializing new scenario default... Initialized scenario in /home/sammy/ansible-apache/molecule/default successfully.
通过将 CentOS 和 Ubuntu 作为平台包含在 Molecule 配置文件中,您将添加 CentOS 和 Ubuntu 作为目标环境。 为此,请使用文本编辑器编辑 molecule.yml
文件:
nano molecule/default/molecule.yml
将以下突出显示的内容添加到分子配置中:
~/ansible-apache/molecule/default/molecule.yml
--- 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 服务:
centos7
是第一个平台,使用milcom/centos7-systemd
镜像。ubuntu18
是第二个平台,使用solita/ubuntu-systemd
图像。 除了使用特权模式和挂载所需的内核模块之外,您还在启动时运行/sbin/init
以确保 iptables 已启动并运行。
保存并退出文件。
有关运行特权容器的更多信息,请访问 官方分子文档 。
您将不使用默认的 Molecule 测试文件,而是创建三个自定义测试文件,一个用于每个目标平台,一个用于编写所有平台之间通用的测试。 首先使用以下命令删除场景的默认测试文件 test_default.py
:
rm molecule/default/tests/test_default.py
您现在可以继续为每个目标平台创建三个自定义测试文件 test_common.py
、test_Debian.py
和 test_RedHat.py
。
第一个测试文件 test_common.py
将包含每个主机将执行的常见测试。 创建和编辑通用测试文件,test_common.py
:
nano molecule/default/tests/test_common.py
将以下代码添加到文件中:
~/ansible-apache/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
平台定义为目标主机。 将以下代码添加到此文件的开头:
~/ansible-apache/molecule/default/tests/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')
然后,在同一个文件中,您将添加 test_pkg()
测试。
将以下代码添加到文件中,该文件定义了 test_pkg()
测试:
~/ansible-apache/molecule/default/tests/test_Debian.py
... @pytest.mark.parametrize('pkg', [ 'apache2', 'ufw' ]) def test_pkg(host, pkg): package = host.package(pkg) assert package.is_installed
该测试将检查主机上是否安装了 apache2
和 ufw
软件包。
注意: 将多个测试添加到 Molecule 测试文件时,请确保每个测试之间有两个空白行,否则您将收到 Molecule 的语法错误。
要定义下一个测试 test_svc()
,请在文件中的 test_pkg()
测试下添加以下代码:
~/ansible-apache/molecule/default/tests/test_Debian.py
... @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()
:
~/ansible-apache/molecule/default/tests/test_Debian.py
... @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
文件将如下所示:
~/ansible-apache/molecule/default/tests/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()
。
保存并退出test_Debian.py
。
接下来,您将创建 test_RedHat.py
测试文件,其中将包含特定于 Red Hat 发行版的测试,以针对您的 CentOS 平台。
通过运行以下命令创建和编辑 CentOS 测试文件 test_RedHat.py
:
nano molecule/default/tests/test_RedHat.py
与 Ubuntu 测试文件类似,您现在将编写三个测试以包含在 test_RedHat.py
文件中。 在添加测试代码之前,您可以导入所需的库并将 centos7
平台定义为目标主机,方法是在文件开头添加以下代码:
~/ansible-apache/molecule/default/tests/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')
然后,添加 test_pkg()
测试,它将检查 httpd
和 firewalld
包是否安装在主机上。
在您的库导入代码之后,将 test_pkg()
测试添加到您的文件中。 (同样,请记住在每个新测试之前包含两个空白行。)
~/ansible-apache/molecule/default/tests/test_RedHat.py
... @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()
代码添加到您的文件中:
~/ansible-apache/molecule/default/tests/test_RedHat.py
... @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()
测试添加到您的文件中:
~/ansible-apache/molecule/default/tests/test_RedHat.py
... @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_RedHat.py
文件将如下所示:
~/ansible-apache/molecule/default/tests/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
这三个文件中完成了编写测试,您的角色已准备好进行测试。 在下一步中,您将使用 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 并设置 Travis CI 以进行持续测试。
第 5 步 — 使用 Git 分享您更新后的角色
在本教程中,到目前为止,您已经克隆了一个名为 ansible-apache
的角色并向其添加了测试,以确保它适用于 Ubuntu 和 CentOS 主机。 要与公众分享您更新的角色,您必须提交这些更改并将它们推送到您的 fork。
运行以下命令以添加文件并提交您所做的更改:
git add .
此命令会将您在当前目录中修改的所有文件添加到暂存区。
您还需要在 git config
中设置您的姓名和电子邮件地址才能成功提交。 您可以使用以下命令执行此操作:
git config user.email "sammy@digitalocean.com" 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
这表示您已成功提交更改。 现在,使用以下命令将这些更改推送到您的 fork:
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
的 fork 存储库,您将看到一个名为 Configured Molecule
的新提交,反映了您在文件中所做的更改。
现在,您可以将 Travis CI 与您的新存储库集成,以便对您的角色所做的任何更改都会自动触发 Molecule 测试。 这将确保您的角色始终适用于 Ubuntu 和 CentOS 主机。
第 6 步 - 集成 Travis CI
在这一步中,您要将 Travis CI 集成到您的工作流程中。 启用后,您推送到 fork 的任何更改都将触发 Travis CI 构建。 这样做的目的是确保 Travis CI 在贡献者进行更改时始终运行 molecule test
。 如果进行了任何重大更改,Travis 将声明构建状态。
继续 Travis CI 以启用您的存储库。 导航到您的个人资料页面,您可以在其中单击 GitHub 的 Activate 按钮。
您可以在 此处 找到有关在 Travis CI 中激活存储库的更多指导。
要使 Travis CI 正常工作,您必须创建一个包含相关说明的配置文件。 要创建 Travis 配置文件,请返回您的服务器并运行以下命令:
nano .travis.yml
要复制您在本教程中创建的环境,您将在 Travis 配置文件中指定参数。 将以下内容添加到您的文件中:
~/ansible-apache/.travis.yml
--- 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 将使用 Python 2.7 和 Python 3.6 来运行您的测试。services
:你需要 Docker 在 Molecule 中运行测试。 您指定 Travis 应确保 Docker 存在于您的 CI 环境中。install
:在这里,您指定 Travis CI 将在您的virtualenv
中执行的初步安装步骤。 pip install molecular docker 检查 Ansible 和 Molecule 是否与 Docker 远程 API 的 Python 库一起存在。script
:这是指定 Travis CI 需要执行的步骤。 在您的文件中,您指定了三个步骤: 如果 Molecule 已成功安装,则分子 --version 打印分子版本。 ansible --version 如果 Ansible 已成功安装,则打印 Ansible 版本。 分子测试最终运行您的分子测试。
您指定 molecule --version
和 ansible --version
的原因是为了在由于版本控制导致 ansible
或 molecule
配置错误而导致构建失败的情况下捕获错误。
将内容添加到 Travis CI 配置文件后,保存并退出 .travis.yml
。
现在,每次您将任何更改推送到存储库时,Travis CI 都会自动运行基于上述配置文件的构建。 如果 script
块中的任何命令失败,Travis CI 将报告构建状态。
为了更容易查看构建状态,您可以在角色的 README
中添加一个指示构建状态的徽章。 使用文本编辑器打开 README.md
文件:
nano README.md
将以下行添加到 README.md
以显示构建状态:
~/ansible-apache/README.md
[![Build Status](https://travis-ci.org/username/ansible-apache.svg?branch=master)](https://travis-ci.org/username/ansible-apache)
将 username
替换为您的 GitHub 用户名。 像之前所做的那样提交并将更改推送到您的存储库。
首先,运行以下命令将.travis.yml
和README.md
添加到暂存区:
git add .travis.yml README.md
现在通过执行以下命令将更改提交到您的存储库:
git commit -m "Configured Travis"
最后,使用以下命令将这些更改推送到您的 fork:
git push -u origin master
如果您导航到您的 GitHub 存储库,您将看到它最初报告 build: unknown。
在几分钟内,Travis 将启动一个构建,您可以在 Travis CI 网站上对其进行监控。 一旦构建成功,GitHub 也会在您的存储库中报告状态 - 使用您放置在 README 文件中的标记:
您可以访问 Travis CI 网站访问构建的完整详细信息:
现在您已经成功地为您的新角色设置了 Travis CI,您可以持续测试并将更改集成到您的 Ansible 角色。
结论
在本教程中,您派生了一个从 GitHub 安装和配置 Apache Web 服务器的角色,并通过编写测试并将这些测试配置为在运行 Ubuntu 和 CentOS 的 Docker 容器上工作,为 Molecule 添加了集成。 通过将新创建的角色推送到 GitHub,您已允许其他用户访问您的角色。 当贡献者更改您的角色时,Travis CI 将自动运行 Molecule 来测试您的角色。
一旦您熟悉角色的创建并使用 Molecule 进行测试,您可以将其与 Ansible Galaxy 集成,以便在构建成功后自动推送角色。