Hello!
ฉันทำงานเป็นวิศวกร DevOps สำหรับบริการจองโรงแรม
ที่ Ostrovok.ru เราใช้ ansible เป็นตัวจัดการการกำหนดค่า เมื่อเร็ว ๆ นี้ เรามาถึงความจำเป็นในการทดสอบบทบาท แต่เมื่อปรากฏว่ามีเครื่องมือไม่มากนักสำหรับสิ่งนี้ - บางทีที่นิยมมากที่สุดคือเฟรมเวิร์กโมเลกุล ดังนั้นเราจึงตัดสินใจใช้มัน แต่ปรากฎว่าเอกสารของเขาเงียบเกี่ยวกับข้อผิดพลาดหลายประการ เราไม่พบคำแนะนำโดยละเอียดในภาษารัสเซีย ดังนั้นเราจึงตัดสินใจเขียนบทความนี้
อณู
คำอธิบายแบบง่าย: Molecule สร้างอินสแตนซ์บนแพลตฟอร์มที่คุณระบุ (คลาวด์ เครื่องเสมือน คอนเทนเนอร์ สำหรับรายละเอียดเพิ่มเติม ดูหัวข้อ
ตอนนี้มากขึ้น
ทฤษฎีเล็กน้อย
ลองพิจารณาเอนทิตีสำคัญสองประการของโมเลกุล: สถานการณ์และตัวขับเคลื่อน
สถานการณ์
สคริปต์ประกอบด้วยคำอธิบายว่าจะดำเนินการอะไร ที่ไหน อย่างไร และในลำดับใด บทบาทหนึ่งสามารถมีได้หลายสคริปต์ และแต่ละบทบาทเป็นไดเร็กทอรีตามเส้นทาง <role>/molecule/<scenario>
ซึ่งมีคำอธิบายการดำเนินการที่จำเป็นสำหรับการทดสอบ จะต้องมีสคริปต์ default
ซึ่งจะถูกสร้างขึ้นโดยอัตโนมัติหากคุณเริ่มต้นบทบาทโดยใช้ Molecule ชื่อของสคริปต์ต่อไปนี้ขึ้นอยู่กับดุลยพินิจของคุณ
ลำดับของการดำเนินการทดสอบในสคริปต์เรียกว่า เมทริกซ์และโดยค่าเริ่มต้นจะเป็นดังนี้:
(ขั้นตอนที่ทำเครื่องหมายไว้ ?
จะถูกข้ามไปโดยค่าเริ่มต้นหากผู้ใช้ไม่ได้ระบุ)
lint
- วิ่ง linters โดยค่าเริ่มต้นyamllint
иflake8
,destroy
— การลบอินสแตนซ์จากการเปิดตัว Molecule ครั้งล่าสุด (หากยังมีเหลืออยู่)dependency
? — การติดตั้งการพึ่งพา ansible ของบทบาทที่ทดสอบsyntax
- ตรวจสอบไวยากรณ์บทบาทโดยใช้ansible-playbook --syntax-check
,create
- การสร้างอินสแตนซ์prepare
? — การเตรียมตัวอย่าง; เช่น การตรวจสอบ/การติดตั้ง python2converge
— เปิดตัว Playbook ที่ทดสอบแล้วidempotence
- รัน Playbook อีกครั้งสำหรับการทดสอบ idempotencyside_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 จะใช้โมดูลนี้
คุณยังสามารถดูตัวอย่างการใช้ไดรเวอร์ต่างๆ ได้
มาแทนที่ที่นี่ เซนโตส: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
- config_options:
การกำหนดค่าที่ยอมรับได้
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