說明:如何在生產前測試ansible角色並找出問題

大家好!

我在酒店預訂服務部門擔任 DevOps 工程師。 奧斯特羅沃克。 在這篇文章中,我想談談我們在測試 ansible 角色方面的經驗。

在 Ostrovok.ru,我們使用 ansible 作為配置管理器。 最近,我們需要測試角色,但事實證明,沒有那麼多工具可以實現這一點 - 最流行的也許是 Molecule 框架,所以我們決定使用它。 但事實證明,他的文檔對許多陷阱隻字不提。 我們找不到足夠詳細的俄語手冊,因此我們決定寫這篇文章。

說明:如何在生產前測試ansible角色並找出問題

分子

分子 - 幫助測試 ansible 角色的框架。

簡化描述:分子在您指定的平台(雲、虛擬機、容器)上創建一個實例;有關更多詳細信息,請參閱 司機),在其上運行您的角色,然後運行測試並刪除該實例。 如果其中一個步驟失敗,分子會通知您。

現在更多。

一點理論

考慮分子的兩個關鍵實體:場景和驅動程序。

<span class="notranslate">EventXtra 6大解決方案</span>

該腳本包含對執行內容、執行地點、執行方式以及執行順序的描述。 一個角色可以有多個腳本,每個腳本都是路徑上的一個目錄 <role>/molecule/<scenario>,其中包含測試所需操作的描述。 必須包含腳本 default,如果您使用 Molecule 初始化角色,則會自動創建該角色。 以下腳本的名稱由您決定。

腳本中的測試操作序列稱為 矩陣,默認情況下它是:

(步驟標記為 ?,如果用戶不指定則默認跳過)

  • lint - 運行短絨。 默認情況下使用 yamllint и flake8,
  • destroy - 刪除上次啟動 Molecule 時的實例(如果有),
  • dependency? — 安裝測試角色的 ansible 依賴項,
  • syntax - 使用檢查角色的語法 ansible-playbook --syntax-check,
  • create - 創建一個實例,
  • prepare? ——實例的準備; 例如檢查/安裝 python2
  • converge — 發布正在測試的劇本,
  • idempotence - 重新啟動劇本以進行冪等性測試,
  • side_effect? - 與角色不直接相關,但測試所必需的操作,
  • verify - 使用以下命令運行結果配置的測試 testinfra(默認) /goss/inspec,
  • cleanup? -(在新版本中)-粗略地說,“清理”受分子影響的外部基礎設施,
  • destroy - 刪除實例。

此順序涵蓋了大多數情況,但可以根據需要進行更改。

上述每個步驟都可以單獨運行 molecule <command>。 但您應該明白,對於每個這樣的 cli 命令,可能都有其自己的操作序列,您可以通過執行來找出這些操作序列 molecule matrix <command>。 例如,運行命令時 converge (運行被測試的劇本),將執行以下操作:

$ molecule matrix converge
...
└── default         # название сценария
    ├── dependency  # установка зависимостей
    ├── create      # создание инстанса
    ├── prepare     # преднастройка инстанса
    └── converge    # прогон плейбука

可以編輯這些操作的順序。 如果列表中的某些內容已經完成,它將被跳過。 Molecule 存儲在目錄中的當前狀態以及實例的配置 $TMPDIR/molecule/<role>/<scenario>.

添加步驟 ? 您可以用ansible-playbook格式描述所需的操作,並按照步驟命名文件名: prepare.yml/side_effect.yml。 預期這些文件分子將位於腳本文件夾中。

司機

驅動程序是創建測試實例的實體。
Molecule 已準備好模板的標準驅動程序列表如下:Azure、Docker、EC2、GCE、LXC、LXD、OpenStack、Vagrant、Delegate。

大多數情況下,模板是文件 create.yml и destroy.yml 在分別描述實例的創建和刪除的腳本文件夾中。
Docker 和 Vagrant 是例外,因為與它們的模塊的交互可以在沒有上述文件的情況下進行。

值得強調的是委託驅動程序,因為如果在文件中使用它來創建和刪除實例,則僅描述與實例配置相關的工作,其餘部分應由工程師描述。

默認驅動程序是 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 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 (僅替換 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

依賴

本節描述依賴項的來源。

可能的選擇: 星系, 鍍金, 殼。

Shell 只是一個命令 shell,用於在 Galaxy 和 gilt 不能滿足您的需求的情況下使用。

我不會在這裡停留太久,描述已經足夠了 文件.

司機

司機的姓名。 我們的是碼頭工人。

皮棉

linter 是 yamllint。

這部分配置中的有用選項是能夠為 yamllint 指定配置文件、轉發環境變量或禁用 linter:

lint:
  name: yamllint
  options:
    config-file: foo/bar
  env:
    FOO: bar
  enabled: False

平台

描述實例的配置。
在使用 docker 作為驅動程序的情況下,Molecule 會在此部分上進行迭代,並且列表中的每個元素都可在 Dockerfile.j2 作為變量 item.

如果駕駛員需要 create.yml и destroy.yml,該部分在其中可用 molecule_yml.platforms,並且這些文件中已經描述了它的迭代。

由於 Molecule 向 ansible 模塊提供實例控制,因此還應該在那裡查找可能的設置列表。 以docker為例,使用模塊 docker_container_module。 其他驅動中使用了哪些模塊可以在 文件.

以及各種驅動程序的使用示例可以找到 在分子本身的測試中.

在這裡替換 分:7Ubuntu的.

供應商

“供應商”- 管理實例的實體。 就 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

provisioner:
  name: ansible
  config_options:
    defaults:
      fact_caching: jsonfile
    ssh_connection:
      scp_if_ssh: True

  • 連接選項: 選項 連接

provisioner:
  name: ansible  
  connection_options:
    ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"

  • 方案:Ansible 選項和環境變量

provisioner:
  name: ansible  
  options:
    vvv: true
    diff: true
  env:
    FOO: BAR

腳本

腳本序列的名稱和描述。
您可以通過添加鍵來更改任何命令的默認操作矩陣 <command>_sequence 並通過定義我們需要的步驟列表作為它的值。
假設我們想要更改運行 playbook run 命令時的操作順序: molecule converge

# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
  name: default
  converge_sequence:
    - create
    - converge

驗證

建立一個測試框架和一個 linter。 默認的 linter 是 testinfra и flake8。 可能的選項與上面相同:

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 對於這種:

---
- 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>

如果這是角色中的第一個場景,則參數 -s 可以省略,因為它將創建一個腳本 default.

結論

正如您所看到的,Molecule 並不是很複雜,通過使用您自己的模板,部署新腳本可以簡化為在實例創建和刪除 playbook 中編輯變量。 該分子與 CI 系統無縫集成,使您可以通過減少手動測試劇本的時間來提高開發速度。

感謝您的關注。 如果您有測試 ansible 角色的經驗,並且與 Molecule 無關,請在評論中告訴我們!

來源: www.habr.com

添加評論