คำแนะนำ: วิธีทดสอบบทบาท Ansible และค้นหาปัญหาก่อนการผลิต

Hello!

ฉันทำงานเป็นวิศวกร DevOps สำหรับบริการจองโรงแรม Ostrovok.ru. ในบทความนี้ ฉันต้องการพูดคุยเกี่ยวกับประสบการณ์ของเราในการทดสอบบทบาท Ansible

ที่ Ostrovok.ru เราใช้ ansible เป็นตัวจัดการการกำหนดค่า เมื่อเร็ว ๆ นี้ เรามาถึงความจำเป็นในการทดสอบบทบาท แต่เมื่อปรากฏว่ามีเครื่องมือไม่มากนักสำหรับสิ่งนี้ - บางทีที่นิยมมากที่สุดคือเฟรมเวิร์กโมเลกุล ดังนั้นเราจึงตัดสินใจใช้มัน แต่ปรากฎว่าเอกสารของเขาเงียบเกี่ยวกับข้อผิดพลาดหลายประการ เราไม่พบคำแนะนำโดยละเอียดในภาษารัสเซีย ดังนั้นเราจึงตัดสินใจเขียนบทความนี้

คำแนะนำ: วิธีทดสอบบทบาท Ansible และค้นหาปัญหาก่อนการผลิต

อณู

โมเลกุล — กรอบงานเพื่อช่วยทดสอบบทบาท Ansible

คำอธิบายแบบง่าย: Molecule สร้างอินสแตนซ์บนแพลตฟอร์มที่คุณระบุ (คลาวด์ เครื่องเสมือน คอนเทนเนอร์ สำหรับรายละเอียดเพิ่มเติม ดูหัวข้อ คนขับรถ) ดำเนินบทบาทของคุณ จากนั้นรันการทดสอบและลบอินสแตนซ์ หากมีข้อผิดพลาดในขั้นตอนใดขั้นตอนหนึ่ง โมเลกุลจะแจ้งให้คุณทราบ

ตอนนี้มากขึ้น

ทฤษฎีเล็กน้อย

ลองพิจารณาเอนทิตีสำคัญสองประการของโมเลกุล: สถานการณ์และตัวขับเคลื่อน

สถานการณ์

สคริปต์ประกอบด้วยคำอธิบายว่าจะดำเนินการอะไร ที่ไหน อย่างไร และในลำดับใด บทบาทหนึ่งสามารถมีได้หลายสคริปต์ และแต่ละบทบาทเป็นไดเร็กทอรีตามเส้นทาง <role>/molecule/<scenario>ซึ่งมีคำอธิบายการดำเนินการที่จำเป็นสำหรับการทดสอบ จะต้องมีสคริปต์ defaultซึ่งจะถูกสร้างขึ้นโดยอัตโนมัติหากคุณเริ่มต้นบทบาทโดยใช้ Molecule ชื่อของสคริปต์ต่อไปนี้ขึ้นอยู่กับดุลยพินิจของคุณ

ลำดับของการดำเนินการทดสอบในสคริปต์เรียกว่า เมทริกซ์และโดยค่าเริ่มต้นจะเป็นดังนี้:

(ขั้นตอนที่ทำเครื่องหมายไว้ ?จะถูกข้ามไปโดยค่าเริ่มต้นหากผู้ใช้ไม่ได้ระบุ)

  • lint - วิ่ง linters โดยค่าเริ่มต้น yamllint и flake8,
  • destroy — การลบอินสแตนซ์จากการเปิดตัว Molecule ครั้งล่าสุด (หากยังมีเหลืออยู่)
  • dependency? — การติดตั้งการพึ่งพา ansible ของบทบาทที่ทดสอบ
  • syntax - ตรวจสอบไวยากรณ์บทบาทโดยใช้ ansible-playbook --syntax-check,
  • create - การสร้างอินสแตนซ์
  • prepare? — การเตรียมตัวอย่าง; เช่น การตรวจสอบ/การติดตั้ง python2
  • converge — เปิดตัว Playbook ที่ทดสอบแล้ว
  • idempotence - รัน Playbook อีกครั้งสำหรับการทดสอบ idempotency
  • side_effect? — การกระทำที่ไม่เกี่ยวข้องโดยตรงกับบทบาท แต่จำเป็นสำหรับการทดสอบ
  • verify — รันการทดสอบการกำหนดค่าผลลัพธ์โดยใช้ testinfra(ค่าเริ่มต้น) /goss/inspec,
  • cleanup? - (ในเวอร์ชันใหม่) - พูดคร่าวๆ ว่า "ทำความสะอาด" โครงสร้างพื้นฐานภายนอกที่ได้รับผลกระทบจากโมเลกุล
  • destroy — การลบอินสแตนซ์

ลำดับนี้ครอบคลุมกรณีส่วนใหญ่ แต่สามารถแก้ไขได้หากจำเป็น

แต่ละขั้นตอนข้างต้นสามารถเรียกใช้แยกกันได้โดยใช้ molecule <command>. แต่คุณควรเข้าใจว่าสำหรับแต่ละคำสั่ง cli อาจมีลำดับการดำเนินการของตัวเอง ซึ่งคุณสามารถค้นหาได้โดยการรัน molecule matrix <command>. เช่น เมื่อรันคำสั่ง converge (การรัน Playbook ที่ทดสอบแล้ว) จะดำเนินการดังต่อไปนี้:

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

ลำดับของการกระทำเหล่านี้สามารถแก้ไขได้ หากบางสิ่งจากรายการเสร็จสมบูรณ์แล้ว รายการนั้นจะถูกข้ามไป สถานะปัจจุบันและการกำหนดค่าอินสแตนซ์จะถูกจัดเก็บไว้ในไดเร็กทอรี Molecule $TMPDIR/molecule/<role>/<scenario>.

เพิ่มขั้นตอนด้วย ? คุณสามารถอธิบายการกระทำที่ต้องการได้ในรูปแบบ Playbook Ansible และตั้งชื่อไฟล์ตามขั้นตอน: prepare.yml/side_effect.yml. คาดว่าไฟล์ Molecule เหล่านี้จะอยู่ในโฟลเดอร์สคริปต์

คนขับรถ

โปรแกรมควบคุมคือเอนทิตีที่สร้างอินสแตนซ์สำหรับการทดสอบ
รายการไดรเวอร์มาตรฐานที่ Molecule มีเทมเพลตสำเร็จรูปคือ: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated

ในกรณีส่วนใหญ่ เทมเพลตจะเป็นไฟล์ create.yml и destroy.yml ในโฟลเดอร์สคริปต์ซึ่งอธิบายการสร้างและการลบอินสแตนซ์ตามลำดับ
ข้อยกเว้นคือ Docker และ Vagrant เนื่องจากการโต้ตอบกับโมดูลสามารถเกิดขึ้นได้หากไม่มีไฟล์ข้างต้น

คุ้มค่าที่จะเน้นไดรเวอร์ Delegated เนื่องจากหากใช้งาน เฉพาะงานที่มีการกำหนดค่าอินสแตนซ์เท่านั้นที่จะอธิบายไว้ในไฟล์การสร้างและการลบอินสแตนซ์ ส่วนที่เหลือควรอธิบายโดยวิศวกร

ไดรเวอร์เริ่มต้นคือ Docker

ตอนนี้เรามาดูการฝึกฝนและพิจารณาคุณสมบัติเพิ่มเติมกันดีกว่า

เริ่มต้นใช้งาน

ในฐานะ "สวัสดีชาวโลก" เราจะทดสอบบทบาทการติดตั้ง nginx แบบง่ายๆ ลองเลือกนักเทียบท่าเป็นไดรเวอร์ - ฉันคิดว่าพวกคุณส่วนใหญ่ติดตั้งไว้แล้ว (และจำไว้ว่านักเทียบท่าเป็นไดรเวอร์เริ่มต้น)

มาเตรียมตัวกัน 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

ผลลัพธ์ที่ได้คือบทบาทที่สามารถเข้าใจได้ทั่วไป นอกจากนี้ การโต้ตอบทั้งหมดกับ Molecules 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

เมืองขึ้น

ส่วนนี้จะอธิบายแหล่งที่มาของการขึ้นต่อกัน

Возможныеварианты: กาแล็กซี, ทอง,เปลือก.

เชลล์เป็นเพียงเชลล์คำสั่งที่ใช้หากกาแล็กซีและทองไม่ครอบคลุมความต้องการของคุณ

ฉันจะไม่อยู่ที่นี่นาน มีคำอธิบายเพียงพอแล้ว เอกสาร.

คนขับรถ

ชื่อคนขับ. สำหรับเรานี่คือนักเทียบท่า

ผ้าสำลี

Yamllint ใช้เป็น linter

ตัวเลือกที่มีประโยชน์ในการกำหนดค่าส่วนนี้คือความสามารถในการระบุไฟล์การกำหนดค่าสำหรับ 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 ให้การจัดการอินสแตนซ์แก่โมดูล Ansible คุณจึงควรมองหารายการการตั้งค่าที่เป็นไปได้ที่นั่น ตัวอย่างเช่น สำหรับ Docker จะใช้โมดูลนี้ docker_container_module. โมดูลใดบ้างที่ใช้ในไดรเวอร์อื่น ๆ สามารถพบได้ใน เอกสาร.

คุณยังสามารถดูตัวอย่างการใช้ไดรเวอร์ต่างๆ ได้ ในการทดสอบโมเลกุลนั่นเอง.

มาแทนที่ที่นี่ เซนโตส:7 บน อูบุนตู.

ผู้จัดเตรียม

“ผู้ให้บริการ” คือหน่วยงานที่จัดการอินสแตนซ์ ในกรณีของ Molecule สิ่งนี้สามารถตอบได้ แต่ไม่ได้วางแผนการสนับสนุนสำหรับผู้อื่น ดังนั้นส่วนนี้จึงสามารถเรียกว่าการกำหนดค่าแบบ ansible แบบขยายได้
คุณสามารถชี้ให้เห็นได้มากมายที่นี่ แต่ฉันจะเน้นประเด็นหลักในความคิดของฉัน:

  • playbooks: คุณสามารถระบุได้ว่าควรใช้ Playbook ใดในบางขั้นตอน

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

  • ตัวเลือก: พารามิเตอร์ที่เข้าใจได้และตัวแปรสภาพแวดล้อม

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

สถานการณ์

ชื่อเรื่องและคำอธิบายของลำดับสคริปต์
คุณสามารถเปลี่ยนเมทริกซ์การดำเนินการเริ่มต้นของคำสั่งได้โดยการเพิ่มคีย์ <command>_sequence และเป็นคุณค่าของการกำหนดรายการขั้นตอนที่เราต้องการ
สมมติว่าเราต้องการเปลี่ยนลำดับของการกระทำเมื่อรันคำสั่ง run playbook: molecule converge

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

ตรวจสอบ

จัดทำกรอบการทำงานสำหรับการทดสอบและบรรทัดฐานสำหรับการทดสอบ ตามค่าเริ่มต้น 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 นั้นไม่ได้ซับซ้อนมากนัก และเมื่อใช้เทมเพลตของคุณเอง คุณสามารถลดการปรับใช้สคริปต์ใหม่เพื่อแก้ไขตัวแปรใน Playbooks สำหรับการสร้างและการลบอินสแตนซ์ได้ โมเลกุลจะทำงานร่วมกับระบบ CI ได้อย่างราบรื่น ซึ่งช่วยให้คุณเพิ่มความเร็วในการพัฒนาโดยลดเวลาในการทดสอบ Playbooks ด้วยตนเอง

ขอขอบคุณสำหรับความสนใจของคุณ. หากคุณมีประสบการณ์ในการทดสอบบทบาทที่เข้าใจได้ และไม่เกี่ยวข้องกับโมเลกุล โปรดบอกเราเกี่ยวกับเรื่องนี้ในความคิดเห็น!

ที่มา: will.com

เพิ่มความคิดเห็น