دستورالعمل: چگونه می توان نقش های قابل قبول را آزمایش کرد و از مشکلات قبل از تولید مطلع شد

خوش آمدید!

من به عنوان یک مهندس DevOps در خدمات رزرو هتل کار می کنم. Ostrovok.ru. در این مقاله، می‌خواهم در مورد تجربه‌مان در تست نقش‌های قابل قبول صحبت کنم.

در Ostrovok.ru، ما از ansible به عنوان مدیر پیکربندی استفاده می کنیم. اخیراً به نیاز به آزمایش نقش ها رسیدیم ، اما همانطور که مشخص شد ، ابزارهای زیادی برای این کار وجود ندارد - شاید محبوب ترین آنها چارچوب Molecule باشد ، بنابراین تصمیم گرفتیم از آن استفاده کنیم. اما معلوم شد که اسناد او در مورد بسیاری از دام ها ساکت است. ما نتوانستیم یک کتابچه راهنمای با جزئیات کافی به زبان روسی پیدا کنیم، بنابراین تصمیم گرفتیم این مقاله را بنویسیم.

دستورالعمل: چگونه می توان نقش های قابل قبول را آزمایش کرد و از مشکلات قبل از تولید مطلع شد

مولکول

مولکول - چارچوبی برای کمک به آزمایش نقش های قابل قبول.

توضیح ساده شده: مولکول یک نمونه در پلتفرمی که شما مشخص کرده اید (ابر، ماشین مجازی، ظرف، برای جزئیات بیشتر، به بخش مراجعه کنید) ایجاد می کند. راننده)، نقش شما را روی آن اجرا می کند، سپس تست ها را اجرا می کند و نمونه را حذف می کند. در صورت عدم موفقیت در یکی از مراحل، مولکول به شما اطلاع خواهد داد.

حالا بیشتر

کمی تئوری

دو عنصر کلیدی مولکول را در نظر بگیرید: سناریو و درایور.

سناریو

فیلمنامه حاوی توضیحاتی در مورد اینکه چه، کجا، چگونه و در چه ترتیبی اجرا خواهد شد. یک نقش می تواند چندین اسکریپت داشته باشد و هر کدام یک دایرکتوری در طول مسیر هستند <role>/molecule/<scenario>، که حاوی توضیحاتی در مورد اقدامات مورد نیاز برای آزمون است. اسکریپت باید گنجانده شود default، که اگر نقش را با یک Molecule مقداردهی اولیه کنید به طور خودکار ایجاد می شود. نام اسکریپت های زیر با شماست.

دنباله ای از اقدامات آزمایشی در یک اسکریپت نامیده می شود ماتریس، و به طور پیش فرض این است:

(مراحل با برچسب ?، به طور پیش فرض در صورتی که توسط کاربر مشخص نشده باشد از آن صرفنظر می شود)

  • lint - لنگرهای در حال اجرا به طور پیش فرض استفاده می شود yamllint и flake8,
  • destroy - حذف نمونه هایی از آخرین راه اندازی مولکول (در صورت وجود)،
  • dependency? - نصب وابستگی قابل قبول نقش آزمایش شده،
  • syntax - بررسی نحو نقش با استفاده از ansible-playbook --syntax-check,
  • create - ایجاد یک نمونه،
  • prepare? - آماده سازی نمونه؛ به عنوان مثال python2 را بررسی/نصب کنید
  • converge - راه اندازی کتاب بازی در حال آزمایش،
  • idempotence - راه اندازی مجدد کتاب بازی برای تست ناتوانی،
  • side_effect? - اقداماتی که مستقیماً به نقش مربوط نمی شود، اما برای آزمایشات ضروری است.
  • verify - اجرای تست های پیکربندی حاصل با استفاده از testinfra(پیش فرض) /goss/inspec,
  • cleanup? - (در نسخه های جدید) - به طور کلی، "تمیز کردن" زیرساخت خارجی تحت تأثیر مولکول،
  • destroy - حذف یک نمونه

این توالی بیشتر موارد را پوشش می دهد، اما در صورت لزوم می توان آن را تغییر داد.

هر یک از مراحل بالا را می توان به طور جداگانه اجرا کرد molecule <command>. اما باید درک کرد که برای هر یک از چنین دستورات کلیک ممکن است دنباله ای از اقدامات وجود داشته باشد که می توانید با اجرای آن متوجه شوید. 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، Delegated.

در بیشتر موارد، قالب ها فایل هستند create.yml и destroy.yml در پوشه اسکریپت که به ترتیب ایجاد و حذف یک نمونه را توصیف می کند.
استثناها Docker و Vagrant هستند، زیرا تعامل با ماژول های آنها می تواند بدون فایل های فوق الذکر رخ دهد.

شایان ذکر است که درایور Delegated را برجسته کنید، زیرا اگر در فایل ها برای ایجاد و حذف یک نمونه استفاده شود، فقط کار با پیکربندی نمونه ها شرح داده شده است، بقیه باید توسط مهندس شرح داده شود.

درایور پیش فرض Docker است.

حالا بیایید به تمرین و بررسی ویژگی های بیشتر در آنجا برویم.

شروع

به عنوان یک "سلام جهان"، بیایید یک نقش نصب ساده 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

معلوم شد که یک نقش قابل قبول معمولی است. علاوه بر این، تمام تعاملات با مولکول های 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 (فقط تصویر داکر را جایگزین کنید):

---
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 فقط یک پوسته فرمان است که در مواردی استفاده می شود که کهکشان و gilt نیازهای شما را پوشش ندهند.

من برای مدت طولانی در اینجا ساکن نخواهم شد، به اندازه کافی توضیح داده شده است مستندات.

راننده

نام راننده. مال ما داکر است.

خط

لینتر یامللینت است.

گزینه های مفید در این قسمت از پیکربندی، امکان تعیین یک فایل پیکربندی برای yamllint، متغیرهای محیطی فوروارد یا غیرفعال کردن linter است:

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

سیستم عامل

پیکربندی نمونه ها را شرح می دهد.
در مورد داکر به عنوان درایور، مولکول روی این بخش تکرار می شود و هر عنصر لیست در دسترس است Dockerfile.j2 به عنوان یک متغیر item.

در مورد راننده ای که نیاز دارد create.yml и destroy.yml، بخش در آنها به عنوان موجود است molecule_yml.platforms، و تکرارهای آن قبلاً در این فایل ها توضیح داده شده است.

از آنجایی که Molecule کنترل نمونه‌ها را برای ماژول‌های قابل تنظیم فراهم می‌کند، فهرست تنظیمات ممکن را نیز باید در آنجا جستجو کرد. برای داکر، به عنوان مثال، ماژول استفاده می شود docker_container_module. کدام ماژول ها در درایورهای دیگر استفاده می شوند را می توان در آنها یافت مستندات.

و همچنین نمونه هایی از استفاده از درایورهای مختلف را می توان یافت در آزمایشات خود مولکول.

اینجا جایگزین کنید سنت: 7 بر اوبونتو.

تامین کننده

"تامین کننده" - نهادی که نمونه ها را مدیریت می کند. در مورد Molecule، این قابل حل است، پشتیبانی از دیگران برنامه ریزی نشده است، بنابراین این بخش را می توان پیکربندی توسعه یافته 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

تأییدکننده

راه اندازی یک چارچوب برای تست ها و یک لنگر برای آن. لینتر پیش فرض است 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 خیلی پیچیده نیست، و با استفاده از الگوهای خود، استقرار یک اسکریپت جدید را می توان به ویرایش متغیرها در کتاب های بازی ایجاد و حذف نمونه کاهش داد. این مولکول به طور یکپارچه با سیستم های CI ادغام می شود، که به شما امکان می دهد با کاهش زمان آزمایش دستی کتاب های بازی، سرعت توسعه را افزایش دهید.

با تشکر از توجه شما. اگر تجربه ای در تست نقش های قابل تشخیص دارید و به مولکول مربوط نمی شود، در نظرات در مورد آن به ما بگویید!

منبع: www.habr.com

اضافه کردن نظر