Мистецтво віртуального диригування OpenStack: робота з Heat

Heat

У попередній статті ми описали базові принципи роботи з API і консольними утилітами, керуючими окремими компонентами платформи Openstack (nova, cinder, glance, neutron). Сьогодні ми розглянемо, як з за допомогою модуля оркестрации Heat можна побудувати готову інфраструктуру з віртуальних пристроїв.


Для роботи з Heat знадобиться пакунок python-heat, який присутній в репозиторіях більшості сучасних дистрибутивів. Якщо ви читали нашу попередню статтю, то, швидше за все, вже встановили його разом з іншими консольними утилітами управління Openstack. Якщо немає — його можна встановити з репозиторію PyPI c допомогою утиліти PIP. Всі інструкції з установці можна знайти на вкладці «Доступ» в панелі керування.

Основні поняття
Перш ніж починати розмову про конкретних практичних аспектах роботи з Heat, з'ясуємо значення основних понять — «стек» і «шаблон».

Стеком (англ. stack) називається набір хмарних ресурсів (машин, логічних томів, мереж і тощо), об'єднаних в цілісну структуру.

Шаблон — це опис стека. Зазвичай воно представлено в вигляді текстового файлу в особливому форматі. Шаблон містить опис ресурсів і  зв'язку. При цьому ресурси можуть бути описані в будь-якому порядку: збірка стека здійснюється автоматичному режимі. Створені раніше стеки можна використовувати в якості ресурсу для опису інших шаблонах, що дозволяє створювати так звані вкладені стеки (nested stacks).

Структуру шаблонів і правила їх написання ми розглянемо на практичному прикладі. Ми будемо створювати стек, що складається з двох серверів, локальної мережі та роутера, через який буде здійснюватися вихід в відкриту мережу.

Формати шаблонів
Шаблони можуть бути представлені в кількох форматах. Ми будемо використовувати формат HOT. Він був створений спеціально для проекту Heat і відрізняється досить простим і зрозумілим синтаксисом. Формат заснований на YAML, тому при редагуванні тексту важливо слідкувати за використанням прогалин у відступи і  ієрархії.
Для забезпечення сумісності з шаблонами, що використовуються у Amazon EC2, підтримується також формат CFN (AWS CloudFormation).

Структура шаблону
Створювати стек ми будемо за допомогою наступного шаблону
heat_template_version: 2013-05-23

description: Basic template of two servers, one network and one router

parameters:
key_name:
type: string
description: Name of keypair to assign to servers for authentication ssh
public_net_id:
type: string
description: UUID of public network to world outer
server_flavor:
type: string
description: UUID of virtual hardware configurations that are called flavors in openstack
private_net_name:
type: string
description: Name of private network (L2 level)
private_subnet_name:
type: string
description: Name of private network subnet (L3 level)
router_name:
type: string
description: Name of router that connects private and public networks
server1_name:
type: string
description: Custom name of server1 virtual machine
server2_name:
type: string
description: Custom name of server2 virtual machine
image_centos7:
type: string
description: UUID of glance image with centos 7 distro
image_debian7:
type: string
description: UUID of glance image with debian 7 distro

resources:

private_net:
type: OS::Neutron::Net
властивості:
name: { get_param: private_net_name }

private_subnet:
type: OS::Neutron::Subnet
властивості:
name: { get_param: private_subnet_name }
network_id: { get_resource: private_net }
allocation_pools:
- start: "192.168.0.10"
end: "192.168.0.254"
cidr: "192.168.0.0/24"
enable_dhcp: True
gateway_ip: "192.168.0.1"

router:
type: OS::Neutron::Router
властивості:
name: { get_param: router_name }
external_gateway_info: { "enable_snat": True, "network": { get_param: public_net_id }}

router_interface:
type: OS::Neutron::RouterInterface
властивості:
router_id: { get_resource: router }
subnet_id: { get_resource: private_subnet }

server1:
type: OS::Nova::Server
властивості:
name: { get_param: server1_name }
block_device_mapping:
- volume_size: 5
volume_id: { get_resource: "server1_disk" }
device_name: "/dev/vda"
config_drive: "False"
flavor: { get_param: server_flavor }
image: { get_param: image_centos7 }
key_name: { get_param: key_name }
networks:
- port: { get_resource: server1_port }

server1_disk:
type: OS::Cinder::Volume
властивості:
name: server1_disk
image: { get_param: image_centos7 }
size: 5

server1_port:
type: OS::Neutron::Port
властивості:
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }

server1_floating_ip:
type: OS::Neutron::FloatingIP
властивості:
floating_network_id: { get_param: public_net_id }
port_id: { get_resource: server1_port }
depends_on: router_interface

server2:
type: OS::Nova::Server
властивості:
name: { get_param: server2_name }
block_device_mapping:
- volume_size: 5
volume_id: { get_resource: "server2_disk" }
device_name: "/dev/vda"
config_drive: "False"
flavor: { get_param: server_flavor }
image: { get_param: image_debian7 }
key_name: { get_param: key_name }
networks:
- port: { get_resource: server2_port }

server2_disk:
type: OS::Cinder::Volume
властивості:
name: server2_disk
image: { get_param: image_debian7 }
size: 5

server2_port:
type: OS::Neutron::Port
властивості:
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }

outputs:
server1_private_ip:
description: private ip within local subnet of server1 installed with Centos 7 distro
value: { get_attr: [ server1_port, fixed_ips, 0, ip_address ] }
server1_public_ip:
description: floating_ip that is assigned to server server1
value: { get_attr: [ server1_floating_ip, floating_ip_address ] }
server2_private_ip:
description: private ip within local subnet of server2 installed with Debian 7 distro
value: { get_attr: [ server2, first_address ] }


Розглянемо його структуру більш докладно.

Шаблон складається з кількох блоків. У першому вказується версія шаблону і використовуваний формат опису. У кожному новому випуску платформи openstack підтримується власний набір властивостей і атрибутів, який поступово змінюється. У наведених нами прикладах використовується версія 2013-05-23. Вона підтримує всі властивості, реалізовані при випуску релізу Icehouse.

heat_template_version: 2013-05-23

description: >
Basic template of two servers, one network and one router

У другому блоці наводиться загальний опис шаблону та його призначення
parameters:
key_name:
type: string
description: Name of keypair to assign to servers for authentication ssh
public_net_id:
type: string
description: UUID of public network to world outer
default: 98863f6c-638e-4b48-a377-01f0e86f34ae
server_flavor:
type: string
description: UUID of virtual hardware configurations that are called flavors in openstack
private_net_name:
type: string
description: The Name of private network (L2 level)
private_subnet_name:
type: string
description: the Name of private subnet (L3 level)
router_name:
type: string
description: The Name of router that connects private and public networks
server1_name:
type: string
description: Custom name of server1 virtual machine
server2_name:
type: string
description: Custom name of server2 virtual machine
image_centos7:
type: string
description: UUID of glance image with centos 7 distro
image_debian7:
type: string
description: UUID of glance image with debian 7 distro


Потім ми перераховуємо деякі додаткові параметри, які будуть передані Heat при створенні стеку. У параметрі key_name вказується пара ключів для підключення до створеному сервером ssh. А в параметрах server_flavor і public_net_id   ідентифікатори (UUID) «апаратної конфігурації віртуальної машини і публічної мережі. Тут ми вказуємо довільні імена для нових пристроїв і машин.

Спойлер
resources:

private_net:
type: OS::Neutron::Net
властивості:
name: { get_param: private_net_name }

private_subnet:
type: OS::Neutron::Subnet
властивості:
name: { get_param: private_subnet_name }
network_id: { get_resource: private_net }
allocation_pools:
- start: "192.168.0.10"
end: "192.168.0.254"
cidr: "192.168.0.0/24"
enable_dhcp: True
gateway_ip: "192.168.0.1"

router:
type: OS::Neutron::Router
властивості:
name: { get_param: router_name }
external_gateway_info: { "enable_snat": True, "network": { get_param: public_net_id}}

router_interface:
type: OS::Neutron::RouterInterface
властивості:
router_id: { get_resource: router }
subnet_id: { get_resource: private_subnet }

server1:
type: OS::Nova::Server
властивості:
name: { get_param: server1_name }
block_device_mapping:
- volume_size: 5
volume_id: { get_resource: "server1_disk" }
device_name: "/dev/vda"
config_drive: "False"
flavor: { get_param: server_flavor }
image: { get_param: image_server1 }
key_name: { get_param: key_name }
networks:
- port: { get_resource: server1_port }

server1_disk:
type: OS::Cinder::Volume
властивості:
name: server1_disk
image: { get_param: image_server1 }
size: 5

server1_port:
type: OS::Neutron::Port
властивості:
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }

server1_floating_ip:
type: OS::Neutron::FloatingIP
властивості:
floating_network_id: { get_param: public_net_id }
port_id: { get_resource: server1_port }
depends_on: router_interface

server2:
type: OS::Nova::Server
властивості:
name: { get_param: server2_name }
block_device_mapping:
- volume_size: 5
volume_id: { get_resource: "server2_disk" }
device_name: "/dev/vda"
config_drive: "False"
flavor: { get_param: server_flavor }
image: { get_param: image_server2 }
key_name: { get_param: key_name }
networks:
- port: { get_resource: server2_port }

server2_disk:
type: OS::Cinder::Volume
властивості:
name: server2_disk
image: { get_param: image_server2 }
size: 5

server2_port:
type: OS::Neutron::Port
властивості:
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }


У наступному блоці описуються створювані ресурси: мережі, роутер, сервери і інші. У цієї частини шаблону ми описуємо загальну локальну мережу (private_net) і  підмережа, для якої вказується діапазон використовуваних адрес і включається підтримка DHCP.

Наступний етап   створення роутера і інтерфейсу на нім. Через цей інтерфейс роутер підключається до створення локальної мережі. Потім перераховуються сервери. У кожного сервера повинно бути порту диску. Для першого сервера, в відміну від другого, вказано також плаваючий IP-адреса (floating_ip), з допомогою якого зовнішній адресу з публічної мережі можна асоціювати з «сірим» адресою локальної.

server1_floating_ip:
type: OS::Neutron::FloatingIP
властивості:
floating_network_id: { get_param: public_net_id }
port_id: { get_resource: server1_port }
depends_on: router_interface

Зверніть увагу на те, як використовуються параметри і ресурси при описі нових пристроїв. Вище ми привели фрагмент опису ресурсу плаваючого IP-адреси для першого сервера. У його властивостях нам потрібно вказати UUID публічної мережі, звідки буде взяти плаваючий IP-адреса (floating_network_id) і UUID порту сервера (port_id), з яким ця адреса буде пов'язаний. У функції get_param ми вказуємо, що значення слід брати з параметра public_net_id (нижче ми ще опишемо, як використовувати параметри). Ідентифікатор порту першого сервера ще немає; він з'явиться тільки після того, як сервер буде створений. Функція get_resource як раз і вказує, що відразу після створення ресурсу server1_port його значення повинно використовуватися якості UUID для port_id.

Resource DELETE failed: Conflict: Router interface for subnet 8958ffad-7622-4d98-9fd9-6f4423937b59 on router 7ee9754b-beba-4301-9bdd-166117c5e5a6 cannot be deleted, as it is required by one or more floating IPs.

Згідно з цим повідомленням, роутер не може бути вилучений, тому що до мережі, асоційованої з цим роутером, прив'язані плаваючі IP-адреси. Цілком очікувано, що при видаленні стека необхідно в першу чергу видалити плаваючі IP-адреси, а вже потім роутер і пов'язану з ним мережу. Проблема полягають у те, що всі ресурси компонентів neutron, cinder, nova, glance є незалежними один від друга сутностями, між якими встановлюються зв'язки і відносини.

У більшості випадків Heat визначає потрібний порядок створення ресурсів і побудови між ними зв'язків при створенні стеку. При видаленні стека ці зв'язки також будуть враховуватися: ним буде визначено порядок видалення ресурсів. Але іноді, як у наведеному вище прикладі, виникають помилки. З допомогою директиви depends_on ми явно вказали, що плаваючий IP-адреса пов'язаний з роутером і інтерфейсом на нім. Завдяки цьому зв'язку, тепер IP-адреса буде створюватися після того, як буде створено роутер і інтерфейс нім. При видаленні все буде відбуватися в зворотному порядку: спочатку буде видалено плаваючий IP-адресу, а    роутер і його інтерфейс.

У останній секції шаблону ми описуємо потрібні нам параметри віртуальних пристроїв, щоб отримати їх значення після створення стека.

outputs:
server1_private_ip:
description: private ip address within local subnet of server 1 installed with Centos7 distro
value: { get_attr: [ server1_port, fixed_ips, 0, ip_address]}
server1_public_ip:
description: floating ip that is assigned to server server1
value: { get_attr: [ server1_floating_ip, floating_ip_address ]}
server2_private_ip:
description: private ip address within local subnet of server2 installed with Debian7 distro
value: { get_attr: [ server2, first_address ]}

У наведеному фрагменті ми вказуємо, що хочемо отримати наступні значення для створюваних у процесі складання стека ресурсів: адреса першого сервера в локальної мережі, публічний адресу першого сервера (плаваючий IP-адреса) і адреса другого сервера в локальної мережі. Для кожного параметра ми вказали його короткий опис і запитувана значення (value). Для цього ми використовуємо функцію get_attr, якій необхідно два значення, де перше   ім'я ресурсу, другий — його атрибути.

Зверніть увагу на різні способи отримання адреси локальної мережі у першого і другого серверів. Обидва варіанти допустимі і рівнозначні. Різниця в те, що першому випадку відбувається звернення до компоненту Neutron (якщо пам'ятаєте, то у server1_port тип «OS::Neutron::Port») і береться перший IP-адресу з атрибута fixed_ips. У другому випадку часто згадуваний в прикладах шаблонів в мережі, відбувається звернення до компоненту nova (ресурс server2 з типом «OS::Nova::Server») і атрибуту first_address.

Такі компоненти платформи Openstack, як Neutron і Cinder, з'явилися пізніше, ніж Nova. Тому Nova раніше використовувався для набагато більшої кількості функцій, в тому числі і для управління дисками і мережами. З повноцінним розвитком Neutron і Cinder така необхідність відпала, але залишена в метою сумісності. Політика щодо Nova поступово переглядається, і деякі функції з часом оголошуються застарілими. Можливо, що і атрибут first_address скоро не буде підтримуватися.

value: { get_attr: [ server1_port, fixed_ips, 0, ip_address]}
value: { get_attr: [ server2, first_address ]}

Більш детально про шаблони і правила їх складання можна прочитати в офіційному керівництві.

Створення стека
Підготувавши шаблон, перевіримо його на наявність синтаксичних помилок і на відповідність стандарту:

$ heat template-validate-f publication.yml 

Якщо шаблон складений правильно, то в якості відповіді буде представлений висновок у форматі JSON
{
"Description": "Basic template of two servers, one network and one router\n", 
"Parameters": {
"server2_name": {
"NoEcho": "false", 
"Type": "String", 
"Description": "", 
"Label": "server2_name"
}, 
"private_subnet_name": {
"NoEcho": "false", 
"Type": "String", 
"Description": "the Name of private subnet", 
"Label": "private_subnet_name"
}, 
"key_name": {
"NoEcho": "false", 
...


Потім приступимо безпосередньо до створення стека:

$ heat stack-create TESTA-f testa.yml-P key_name="testa" \
-P public_net_id="ab2264dd-bde8-4a97-b0da-5fea63191019" \
-P server_flavor="1406718579611-8007733592" \
-P private_net_name=localnet-P private_subnet_name="192.168.0.0/24" \
-P router_name=router-P server1_name=Centos7-P server2_name=Debian7 \
-P image_server1="CentOS 7 64-bit" \
-P image_server2="ba78ce9b-f800-4fb2-ad85-a68ca0f19cb8"

Кожен раз передавати параметри клієнту Heat вручну незручно: легко можна зробити помилку. Щоб уникнути цього недоліку, ми створимо додатковий файл, що повторює формат основного шаблону, але містить тільки основні параметри.

parameters:
key_name: testa
public_net_id: ab2264dd-bde8-4a97-b0da-5fea63191019
server_flavor: myflavor
private_net_name: localnet
private_subnet_name: 192.168.0.0/24
router_name: router
server1_name: Centos7
server2_name: Debian7
image_server1: CentOS 7 64-bit
image_server2: ba78ce9b-f800-4fb2-ad85-a68ca0f19cb8

У цьому разі створення стека з допомогою консольної утиліти Heat буде спрощено.

image
Щоб дізнатися значення передаються Heat параметрів, ми можемо використовувати стандартний набір утиліт для роботи з Openstack. Наприклад, дізнатися ідентифікатор публічної мережі public_net_id, можна з використанням Neutron:

image
Щоб дізнатися ім'я або ідентифікатор server_flavor і image_server1, image_server2 можна аналогічним чином скористатися відповідним утилітами.

Операції зі стеком
Після створення стека потрібно переконатися в те, що всі пройшло без помилок, а також дізнатися, які IP-адреси були присвоєні серверів (насамперед   публічний IP першого сервера).

Список всіх створених стеків можна отримати з допомогою команди heat-list. &Nbsp; висновок буде включена інформація про стан кожного стека:

image
Як видно з висновку, ми неправильно вказали UUID локальної мережі, до якої повинен бути підключений порт створюваного нами сервера — з-за цього і виникла помилка. Помилки часто трапляються із-за відсутності вільних ресурсів (для кожного проекту виставляються ліміти кількості використовуваних ядер, RAM та інші).

Якщо стек успішно створено, то в загальному висновку команди stack-show з'явиться також секція outputs, в якій містяться нас цікавлять значення.

Спойлер

Для більшості випадків вивід команди heat stack-show занадто великий і докладний. Знайти в цьому висновку яку-небудь невелику, але важливу деталь (наприклад, IP-адреса першого сервера) вкрай важко. Якщо нас цікавить тільки значення плаваючого адреси першого сервера, то отримати його можна такою командою, де після імені стека ми вказуємо також описаний нами висновок про публічному IP-адресі:

$ heat output-show TESTA server1_public_ip
"95.213.154.192"

Видалення стека здійснюється просто — за допомогою команди heat stack-delete:


У ситуації, коли необхідно тимчасово звільнити системні ресурси, не видаляючи при цьому сам стек, його можна призупинити командою heat action-suspend і повернути в робочий стан пізніше через heat action-resume.

Ми розглянули тільки найбільш часто використовувані, на наш погляд, операції з стеками (і не стосувалися управління окремими ресурсами), події, оновлення стека в час його роботи і інших можливостей. Більш детальну інформацію можна отримати в офіційній документації або з допомогою команди heat help.

Висновок
У цій статті ми познайомилися з основними принципами роботи модуля оркестрации openstack Heat, який дає нам додатковий рівень абстракції при роботі з хмарою і позбавляє від безлічі рутинних дій.

Зрозуміло, цим можливості Heat не обмежуються. Ми не сказали про важливою здатності передавати створюваної машині так звані дані користувача (user_data), які будуть виконуватися всередині машини при її первинної завантаженні. Строго кажучи, Heat передає дані на машині виконання не самостійно, а через компонент Nova. Але за рахунок можливості описувати зв'язку між ресурсами Нeat дозволяє не обмежувати умови виконання переданих даних рамками однієї машини.

Наприклад, потрібно створити кілька машин, одна з яких буде виконувати роль сервера баз даних, а решта підключатися до ній IP-адресою. Завдяки використанню шаблонів, ми можемо не замислюватися про послідовності створення машин і  налаштуваннях. Як тільки відповідні ресурси будуть створені всі необхідні значення, в тому числі ip адреса сервера баз даних, будуть передані в user_data.

Щоб повноцінно використовувати ці можливості, потрібно розуміти, як дані передаються всередину машини і як вони обробляються. Про ми поговоримо більш докладно в наступній статті.

Читачів, які з тих чи інших причин не можуть залишати коментарі тут, запрошуємо в наш блог.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.