10. Orchestration Openstack IaC
- 1. Concepts IaC
- 2. Langage YAML
- 3. Heat Orchestration Template (HOT)
- 4. Orchestration avec une seule instance
- 5. Orchestration du réseau
- 6. Orchestration multi-instances
1. Concepts IaC
"L'infrastructure en tant que code (IAC) est un type d'infrastructure informatique que les équipes d'exploitation peuvent automatiquement gérer et approvisionner via du code, plutôt que d'utiliser un processus manuel. L'infrastructure en tant que code est parfois appelée infrastructure programmable."1
1. Paradigme Infrastructure as a Code : "Infrastructure as Code (IAC) is a type of IT infrastructure that operations teams can automatically manage and provision through code, rather than using a manual process. Infrastructure as Code is sometimes referred to as programmable infrastructure." (What is Infrastructure as Code (IAC)? - Definition from WhatIs.com) ↩
1.1. Outils Infrastructure as Code
- Heat
- CloudFormation
- Terraform
- Ansible
- Chef, Puppet, CfEngine, Docker, Kubernetes, ...
2. Langage YAML
Pour Ansible, presque tous les fichiers YAML commencent par une liste. Chaque élément de la liste est une liste de paires clé / valeur, communément appelée "hash" ou "dictionary". Il est donc nécessaire de savoir écrire des listes et des dictionnaires en YAML.2
2. YAML Syntax ↩
2.1. Début du fichier
Tous les fichiers YAML (indépendamment de leur association avec Ansible ou non) peuvent éventuellement commencer par ---
et se terminer par ...
. Cela fait partie du format YAML et indique le début et la fin d'un document.
2.2. Listes
Tous les membres d'une liste sont des lignes commençant au même niveau d'indentation commençant par un tiret et un espace : -
---
# A list of tasty fruits
fruits:
- Apple
- Orange
- Strawberry
- Mango
...
2.3. Dictionnaires
Un dictionnaire est représenté sous une forme simple (les deux points doivent être suivis d'un espace): key: value
# An employee record
martin:
name: Martin D'vloper
job: Developer
skill: Elite
2.4. Dictionnaires et listes
Des structures de données plus complexes sont possibles, telles que des listes de dictionnaires, des dictionnaires dont les valeurs sont des listes ou un mélange des deux:
# Employee records
- martin:
name: Martin D'vloper
job: Developer
skills:
- python
- perl
- pascal
- tabitha:
name: Tabitha Bitumen
job: Developer
skills:
- lisp
- fortran
- erlang
2.5. Forme abrégée
Les dictionnaires et les listes peuvent également être représentés sous une forme abrégée si vous voulez vraiment:
---
martin: {name: Martin D'vloper, job: Developer, skill: Elite}
fruits: ['Apple', 'Orange', 'Strawberry', 'Mango']
Celles-ci sont appelées "collections de flux".
2.6. Valeur booléenne
On peut également spécifier une valeur booléenne (true / false) sous plusieurs formes :
create_key: yes
needs_agent: no
knows_oop: True
likes_emacs: TRUE
uses_cvs: false
2.7. Valeurs sur plusieurs lignes
Les valeurs peuvent couvrir plusieurs lignes en utilisant |
ou >
. Le fait de couvrir plusieurs lignes en utilisant un "Literal Block Scalar" |
inclura les nouvelles lignes et tous les espaces de fin. L'utilisation d'un "Folded Block Scala" >
pliera les lignes nouvelles dans les espaces; il est utilisé pour rendre ce qui serait autrement une très longue ligne plus facile à lire et à éditer. Dans les deux cas, l'indentation sera ignorée. Les exemples sont:
include_newlines: |
exactly as you see
will appear these three
lines of poetry
fold_newlines: >
this is really a
single line of text
despite appearances
Alors que dans l'exemple ci-dessus, toutes les nouvelles lignes sont repliées dans des espaces, il existe deux manières de faire respecter une nouvelle ligne à conserver:
fold_some_newlines: >
a
b
c
d
e
f
same_as: "a b\nc d\n e\nf\n"
Combinons ce que nous avons appris jusqu'ici dans un exemple YAML arbitraire. Cela n'a vraiment rien à voir avec Ansible, mais vous donnera une idée du format:
---
# An employee record
name: Martin D'vloper
job: Developer
skill: Elite
employed: True
foods:
- Apple
- Orange
- Strawberry
- Mango
languages:
perl: Elite
python: Elite
pascal: Lame
education: |
4 GCSEs
3 A-Levels
BSc in the Internet of Things
3. Heat Orchestration Template (HOT)
Le service Heat fournit une orchestration basée sur un modèle pour décrire une application cloud en exécutant des appels OpenStack API. Il utilise des Heat Orchestration Templates (HOT). Ces modèles définissent de multiples applications de cloud composite et lorsqu'ils sont transmis à Heat-api, ils sont interprétés et transmis à Heat-engine. Le heat-engine lance des tâches qui sont transmises aux services centraux pour créer les instances de stockage, réseau et VM définies dans le modèle. Heat dispose d'une deuxième API appelée heat-api-cfn qui lui permet d'interpréter également les modèles AWS CloudFormation.
3.1. Nomenclature
Tout modèle Heat suit une syntaxe YAML comme celle-ci :
heat_template_version: 2016-10-14
description:
# a description of the template
parameter_groups:
# a declaration of input parameter groups and order
parameters:
# declaration of input parameters
resources:
# declaration of template resources
outputs:
# declaration of output parameters
conditions:
# declaration of conditions
3.2. Version
La valeur de heat_template_version indique à Heat non seulement le format du modèle mais aussi les fonctionnalités qui seront validées et supportées. A partir de la version Newton, la version peut être soit la date de la version de Heat, soit le nom de code du Heat Release/Version. Heat supporte actuellement les valeurs suivantes pour la clé heat_template_version :
- 2013-05-23
- 2014-10-16
- 2015-04-30
- 2015-10-15
- 2016-04-08
- 2016-10-14 | newton
- 2017-02-24 | ocata
- 2017-09-01 | pike
- 2018-03-02 | queens
- 2018-08-31 | rocky
3.3. Description
...
3.4. Resources
La section ressources définit les ressources réelles qui constituent une pile déployée à partir du modèle HOT (par exemple, calculer les instances, les réseaux, les volumes de stockage).
Chaque ressource est définie comme un bloc séparé dans la section des ressources avec la syntaxe suivante
resources:
<resource ID>:
type: <resource type>
properties:
<property name>: <property value>
metadata:
<resource specific metadata>
depends_on: <resource ID or list of ID>
update_policy: <update policy>
deletion_policy: <deletion policy>
external_id: <external resource ID>
condition: <condition name or expression or boolean>
Exemples
resources:
my_instance:
type: OS::Nova::Server
properties:
flavor: m1.small
image: F18-x86_64-cfntools
resources:
server1:
type: OS::Nova::Server
depends_on: server2
server2:
type: OS::Nova::Server
resources:
server1:
type: OS::Nova::Server
depends_on: [ server2, server3 ]
server2:
type: OS::Nova::Server
server3:
type: OS::Nova::Server
3.5. Un modèle de base
heat_template_version: 2015-04-30
description: Simple template to deploy a single compute instance
resources:
my_instance:
type: OS::Nova::Server
properties:
key_name: my_key
image: ubuntu-trusty-x86_64
flavor: m1.small
3.6. Parameters
La section "parameters" permet de spécifier les paramètres d'entrée qui doivent être fournis lors de l'instanciation du modèle. Ces paramètres sont généralement utilisés pour personnaliser chaque déploiement (par exemple, en définissant des noms d'utilisateur ou des mots de passe personnalisés) ou pour se lier à des environnements spécifiques comme certaines images.
Chaque paramètre est spécifié dans un bloc imbriqué séparé avec le nom des paramètres définis dans la première ligne et des attributs supplémentaires tels que le type ou la valeur par défaut définis comme éléments imbriqués.
parameters:
<param name>:
type: <string | number | json | comma_delimited_list | boolean>
label: <human-readable name of the parameter>
description: <description of the parameter>
default: <default value for parameter>
hidden: <true | false>
constraints:
<parameter constraints>
immutable: <true | false>
tags: <list of parameter categories>
3.7. Paramètres d'entrée
heat_template_version: 2015-04-30
description: Simple template to deploy a single compute instance
parameters:
key_name:
type: string
label: Key Name
description: Name of key-pair to be used for compute instance
image_id:
type: string
label: Image ID
description: Image to be used for compute instance
flavor:
type: string
label: Instance Type
description: Type of instance (flavor) to be used
resources:
my_instance:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
image: { get_param: image_id }
flavor: { get_param: flavor }
3.8. Paramètres par défaut
parameters:
flavor:
type: string
label: Instance Type
description: Flavor to be used
default: m1.small
3.9. Parameters Group
La section parameter_groups permet de spécifier comment les paramètres d'entrée doivent être groupés et l'ordre dans lequel les paramètres doivent être fournis. Ces groupes sont généralement utilisés pour décrire le comportement attendu des interfaces utilisateur en aval.
Ces groupes sont spécifiés dans une liste, chaque groupe contenant une liste de paramètres associés. Les listes sont utilisées pour indiquer l'ordre attendu des paramètres. Chaque paramètre ne doit être associé à un groupe spécifique qu'une seule fois en utilisant le nom du paramètre pour le lier à un paramètre défini dans la section "parameters".
parameter_groups:
- label: <human-readable label of parameter group>
description: <description of the parameter group>
parameters:
- <param name>
- <param name>
3.10. Outputs
La section "output" définit les paramètres de sortie qui devraient être à la disposition de l'utilisateur après la création d'une pile. Il peut s'agir, par exemple, de paramètres tels que les adresses IP des instances déployées ou les URL des applications Web déployées dans le cadre d'une pile.
Dans l'exemple suivant, la section de sortie fournit l'adresse IP de la ressource my_instance :
outputs:
instance_ip:
description: The IP address of the deployed instance
value: { get_attr: [my_instance, first_address] }
3.11. Conditions
La section conditions définit une ou plusieurs conditions qui sont évaluées en fonction des valeurs des paramètres d'entrée fournies lorsqu'un utilisateur crée ou met à jour une pile. La condition peut être associée aux ressources, aux avoirs miniers et aux extrants. Par exemple, en fonction du résultat d'une condition, l'utilisateur peut créer conditionnellement des ressources, l'utilisateur peut définir conditionnellement différentes valeurs de propriétés et l'utilisateur peut donner conditionnellement les sorties d'une pile.
4. Orchestration avec une seule instance
4.1. HOT servers.yaml
#
# This is a hello world HOT template just defining a single compute server.
#
heat_template_version: 2016-04-08
description: Hello world HOT template defining a single server.
parameters:
key_name:
type: string
label: Key Name
description: Name of key-pair to be used for compute instance
default: mykey
flavor:
type: string
description: Flavour for the server to be created
default: m1.nano
constraints:
- custom_constraint: nova.flavor
image:
type: string
description: Image name
default: cirros
constraints:
- custom_constraint: glance.image
pub_net:
type: string
description: ID of public network
default: provider
constraints:
- custom_constraint: neutron.network
resources:
server:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
image: { get_param: image }
flavor: { get_param: flavor }
networks:
- network: { get_param: pub_net }
outputs:
server_networks:
description: The networks of the deployed server
value: { get_attr: [server, networks] }
4.2. Création d'un premier stack
osbash@controller:~$ openstack stack create --template servers.yaml stack1
+---------------------+----------------------------------------------------+
| Field | Value |
+---------------------+----------------------------------------------------+
| id | 6db09cdb-16da-42d3-ab45-d7750ffd2ef2 |
| stack_name | stack1 |
| description | Hello world HOT template defining a single server. |
| creation_time | 2019-03-11T20:30:59Z |
| updated_time | None |
| stack_status | CREATE_IN_PROGRESS |
| stack_status_reason | Stack CREATE started |
+---------------------+----------------------------------------------------+
osbash@controller:~$ openstack stack event list stack1
2019-03-11 20:31:01Z [stack1]: CREATE_IN_PROGRESS Stack CREATE started
2019-03-11 20:31:01Z [stack1.server]: CREATE_IN_PROGRESS state changed
osbash@controller:~$ openstack stack event list stack1
2019-03-11 20:31:01Z [stack1]: CREATE_IN_PROGRESS Stack CREATE started
2019-03-11 20:31:01Z [stack1.server]: CREATE_IN_PROGRESS state changed
2019-03-11 20:31:48Z [stack1.server]: CREATE_COMPLETE state changed
2019-03-11 20:31:49Z [stack1]: CREATE_COMPLETE Stack CREATE completed successfully
osbash@controller:~$ openstack server list -f json | jq '.[]'
{
"Status": "ACTIVE",
"Name": "stack1-server-rqfndbq4qbe4",
"Image": "cirros",
"ID": "41a36a45-83fe-44ba-b0bd-58c07b4054c9",
"Flavor": "m1.nano",
"Networks": "provider=203.0.113.110"
}
osbash@controller:~$ openstack server list
+--------------------------------------+----------------------------+--------+------------------------+--------+---------+
| ID | Name | Status | Networks | Image | Flavor |
+--------------------------------------+----------------------------+--------+------------------------+--------+---------+
| 581dcf3a-dda2-4698-ae82-dfb407335c46 | stack4-server-rpaahgv3dhtl | BUILD | | cirros | m1.nano |
| 2c07e6db-dad7-443a-a461-832a48c6817d | stack3-server-n6bnkqqktci7 | ACTIVE | provider=203.0.113.105 | cirros | m1.nano |
| c15ecb27-b6f5-4f6b-bdc6-385f6242ac55 | stack2-server-3utxgoihj2bw | ACTIVE | provider=203.0.113.111 | cirros | m1.nano |
| 41a36a45-83fe-44ba-b0bd-58c07b4054c9 | stack1-server-rqfndbq4qbe4 | ACTIVE | provider=203.0.113.110 | cirros | m1.nano |
+--------------------------------------+----------------------------+--------+------------------------+--------+---------+
4.3. Terminer un stack
osbash@controller:~$ openstack stack delete -y stack1
5. Orchestration du réseau
5.1. Paramètres
Ce fichier YAML commence par une section de paramètres décrivant le réseau public (pub_net) comme le fournisseur existant. Il définit ensuite les différents attributs nécessaires pour établir le réseau privé (pri_net) et le sous-réseau du réseau privé associé (pri_subnet).
5.2. Ressources
La section ressources définit le pri_net comme un réseau et génère le sous-réseau pri_subnet associé en appelant les paramètres de la section ci-dessus.
Un routeur est créé dont les informations de passerelle externe sont également extraites de la section paramètres, c'est-à-dire que pub_net pointe vers le réseau du fournisseur. Une interface supplémentaire est ajoutée au routeur et le pri_subnet lui est associé.
5.3. Sorties
La section Sorties renvoie les noms des réseaux sous forme de combinaisons clé/valeur :
Key | Value |
---|---|
pub_net_name | provider |
pri_net_name | Extract the name given at create time |
router_gw | Extract the External Gateway information |
5.4. HOT networks.yaml
heat_template_version: 2016-04-08
description: Template that creates a private network.
parameters:
pub_net:
type: string
label: Public network name or ID
description: Public network with floating IP addresses.
default: provider
pri_net_cidr:
type: string
default: '192.168.95.0/24'
description: Private network address (CIDR notation)
pri_net_gateway:
type: string
default: '192.168.95.1'
description: Private network gateway address
pri_net_nameserver:
type: comma_delimited_list
default: '8.8.8.8'
description: Private network DNS Server address
pri_net_enable_dhcp:
type: boolean
default: 'True'
description: enable DHCP Server
pri_net_pool_start:
type: string
default: '192.168.95.10'
description: Private network Start IP address allocation pool
pri_net_pool_end:
type: string
default: '192.168.95.20'
description: Private network End IP address allocation pool
pri_net_nexthop:
type: string
default: '203.0.113.1'
description: nexthop address for default route
resources:
pri_net:
type: OS::Neutron::Net
pri_subnet:
type: OS::Neutron::Subnet
properties:
network_id: { get_resource: pri_net }
cidr: { get_param: pri_net_cidr }
dns_nameservers: { get_param: pri_net_nameserver }
gateway_ip: { get_param: pri_net_gateway }
enable_dhcp: { get_param: pri_net_enable_dhcp }
allocation_pools:
- start: { get_param: pri_net_pool_start }
end: { get_param: pri_net_pool_end }
host_routes:
- destination: '0.0.0.0/0'
nexthop: { get_param: pri_net_nexthop }
router:
type: OS::Neutron::Router
properties:
external_gateway_info:
network: { get_param: pub_net }
router-interface:
type: OS::Neutron::RouterInterface
properties:
router_id: { get_resource: router }
subnet: { get_resource: pri_subnet }
outputs:
pub_net_name:
description: The public network.
value: provider
pri_net_name:
description: The private network.
value: { get_attr: [pri_net, name] }
router_gw:
description: Router gateway information
value: { get_attr: [router, external_gateway_info] }
osbash@controller:~$ openstack stack create --template networks.yaml netstack
+---------------------+------------------------------------------+
| Field | Value |
+---------------------+------------------------------------------+
| id | cb0178ae-6325-4613-bdc7-9a9704d8daa8 |
| stack_name | netstack |
| description | Template that creates a private network. |
| creation_time | 2019-03-11T21:02:34Z |
| updated_time | None |
| stack_status | CREATE_IN_PROGRESS |
| stack_status_reason | Stack CREATE started |
+---------------------+------------------------------------------+
osbash@controller:~$ openstack stack show netstack -f json | jq .
{
"parent": null,
"disable_rollback": true,
"description": "Template that creates a private network.",
"parameters": {
"pri_net_cidr": "192.168.95.0/24",
"OS::project_id": "e4911a8eb3e141a49d029e117486b4b0",
"OS::stack_id": "cb0178ae-6325-4613-bdc7-9a9704d8daa8",
"OS::stack_name": "netstack",
"pri_net_nexthop": "203.0.113.1",
"pri_net_nameserver": "8.8.8.8",
"pri_net_enable_dhcp": "True",
"pub_net": "provider",
"pri_net_pool_end": "192.168.95.20",
"pri_net_gateway": "192.168.95.1",
"pri_net_pool_start": "192.168.95.10"
},
"stack_status_reason": "Stack CREATE completed successfully",
"stack_name": "netstack",
"outputs": [
{
"output_value": "provider",
"output_key": "pub_net_name",
"description": "The public network."
},
{
"output_value": {
"network_id": "540174ee-749f-46a6-bd41-ff0298a68c4a",
"enable_snat": true,
"external_fixed_ips": [
{
"subnet_id": "b8bf4256-98cb-4984-b46d-c435324615b4",
"ip_address": "203.0.113.102"
}
]
},
"output_key": "router_gw",
"description": "Router gateway information"
},
{
"output_value": "netstack-pri_net-6e7ad52wufdq",
"output_key": "pri_net_name",
"description": "The private network."
}
],
"deletion_time": null,
"creation_time": "2019-03-11T21:02:34Z",
"links": [
{
"href": "http://controller:8004/v1/e4911a8eb3e141a49d029e117486b4b0/stacks/netstack/cb0178ae-6325-4613-bdc7-9a9704d8daa8",
"rel": "self"
}
],
"capabilities": [],
"updated_time": null,
"timeout_mins": null,
"stack_user_project_id": "e04dc95749c9432285f973536bdefd64",
"stack_status": "CREATE_COMPLETE",
"stack_owner": null,
"notification_topics": [],
"id": "cb0178ae-6325-4613-bdc7-9a9704d8daa8",
"tags": null
}
5.5. Topologie du réseau dans Horizon
6. Orchestration multi-instances
6.1. HOT servers_networks.yaml
heat_template_version: 2016-04-08
description: Template that creates hosts connected to two networks
parameters:
key_name:
type: string
label: Key Name
description: Name of key-pair to be used for compute instance
default: mykey
image:
type: string
label: Image name or ID
description: Image to be used for server.
default: cirros
flavor:
type: string
label: Flavor
description: Type of instance (flavor) for the compute instance.
default: m1.nano
resources:
networks:
type: networks.yaml
host1:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
image: { get_param: image }
flavor: { get_param: flavor }
networks:
- network: { get_attr: [networks, pub_net_name] }
host2:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
image: { get_param: image }
flavor: { get_param: flavor }
networks:
- network: { get_attr: [networks, pub_net_name] }
host3:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
image: { get_param: image }
flavor: { get_param: flavor }
networks:
- network: { get_attr: [networks, pri_net_name] }
host4:
type: OS::Nova::Server
properties:
key_name: { get_param: key_name }
image: { get_param: image }
flavor: { get_param: flavor }
networks:
- network: { get_attr: [networks, pri_net_name] }
outputs:
host1_networks:
description: The networks of the deployed server
value: { get_attr: [host1, networks] }
host2_networks:
description: The networks of the deployed server
value: { get_attr: [host2, networks] }
host3_networks:
description: The networks of the deployed server
value: { get_attr: [host3, networks] }
host4_networks:
description: The networks of the deployed server
value: { get_attr: [host4, networks] }
router_gateway:
description: The router gateway information
value: { get_attr: [networks, router_gw] }
osbash@controller:~$ openstack stack create --template servers_networks.yaml fullstack
+---------------------+-------------------------------------------------------+
| Field | Value |
+---------------------+-------------------------------------------------------+
| id | c35dc3fe-38c2-4e09-91eb-5c555e46fec5 |
| stack_name | fullstack |
| description | Template that creates hosts connected to two networks |
| creation_time | 2019-03-11T21:17:03Z |
| updated_time | None |
| stack_status | CREATE_IN_PROGRESS |
| stack_status_reason | Stack CREATE started |
+---------------------+-------------------------------------------------------+
osbash@controller:~$ openstack stack show fullstack -f json | jq .
{
"parent": null,
"disable_rollback": true,
"description": "Template that creates hosts connected to two networks",
"parameters": {
"OS::project_id": "e4911a8eb3e141a49d029e117486b4b0",
"OS::stack_id": "c35dc3fe-38c2-4e09-91eb-5c555e46fec5",
"OS::stack_name": "fullstack",
"key_name": "mykey",
"image": "cirros",
"flavor": "m1.nano"
},
"stack_status_reason": "Stack CREATE completed successfully",
"stack_name": "fullstack",
"outputs": [
{
"output_value": {
"540174ee-749f-46a6-bd41-ff0298a68c4a": [
"203.0.113.125"
],
"provider": [
"203.0.113.125"
]
},
"output_key": "host2_networks",
"description": "The networks of the deployed server"
},
{
"output_value": {
"7f32976a-722d-4892-a8a2-4e13ac2a0b82": [
"192.168.95.15"
],
"fullstack-networks-pwu3o7uibtlf-pri_net-rwpj64otav5u": [
"192.168.95.15"
]
},
"output_key": "host4_networks",
"description": "The networks of the deployed server"
},
{
"output_value": {
"540174ee-749f-46a6-bd41-ff0298a68c4a": [
"203.0.113.106"
],
"provider": [
"203.0.113.106"
]
},
"output_key": "host1_networks",
"description": "The networks of the deployed server"
},
{
"output_value": {
"network_id": "540174ee-749f-46a6-bd41-ff0298a68c4a",
"enable_snat": true,
"external_fixed_ips": [
{
"subnet_id": "b8bf4256-98cb-4984-b46d-c435324615b4",
"ip_address": "203.0.113.104"
}
]
},
"output_key": "router_gateway",
"description": "The router gateway information"
},
{
"output_value": {
"7f32976a-722d-4892-a8a2-4e13ac2a0b82": [
"192.168.95.13"
],
"fullstack-networks-pwu3o7uibtlf-pri_net-rwpj64otav5u": [
"192.168.95.13"
]
},
"output_key": "host3_networks",
"description": "The networks of the deployed server"
}
],
"deletion_time": null,
"creation_time": "2019-03-11T21:17:03Z",
"links": [
{
"href": "http://controller:8004/v1/e4911a8eb3e141a49d029e117486b4b0/stacks/fullstack/c35dc3fe-38c2-4e09-91eb-5c555e46fec5",
"rel": "self"
}
],
"capabilities": [],
"updated_time": null,
"timeout_mins": null,
"stack_user_project_id": "2b15c7a573bc408b93aba84b5bea8485",
"stack_status": "CREATE_COMPLETE",
"stack_owner": null,
"notification_topics": [],
"id": "c35dc3fe-38c2-4e09-91eb-5c555e46fec5",
"tags": null
}