Outils de provisionning – Ansible, Chef, Puppet et Salt – Provisionning



Outils de provisionning – Ansible, Chef, Puppet et Salt – Provisionning

0 0


presentation-provisionning

Slides pour présentation soirée provisionning

On Github agallou / presentation-provisionning

Outils de provisionning

Ansible, Chef, Puppet et Salt

AFUP

Apéro PHP les 29 du mois à l'Antre Autre

Talks dans cette salle tous les deux mois

http://lyon.afup.org

AFPY

Apéro python tous les 4émes mercredi du mois à l'Antre Autre

http:/afpy.org

Hello!

Michael Scherer

Ansible

Julien Bianchi

Chef

Benoît Marcelin

Puppet

Gaston Tjebbes

Salt

Provisionning

C'est quoi ?

“Le provisioning, mot anglais désignant l'approvisionnement, est un terme utilisé dans le monde de l'informatique, désignant l'allocation automatique de ressources. Les outils de provisioning sont des outils de gestion de configuration (on parle également de «gestion de paramétrage») permettant d'installer et de configurer des logiciels à distance...”

http://fr.wikipedia.org/wiki/Provisioning

Pourquoi ?

  • Pour décrire les procédures d'installation et de configuration
  • Pour installer/configurer des applications de manière automatique
  • Pour installer/configurer des applications à distance

Comment ?

  • Ansible
  • Chef
  • Puppet
  • Salt
  • CFEngine
  • Docker (builder)
  • ...

Radically simple ITautomation platform

En résumé

Python, GPL v3

Exeécution de tâchessur des serveurs

via ssh

Commandes ad-hoc

ansible prod -m shell -a "yum upgrade -y"

Playbook

YAML + des bouts de Jinja

Orchestration

---
- hosts: jboss_servers
  serial: 10%
  tasks:
  - nagios: action=disable_alerts service=host host={{ inventory_hostname }}
    delegate_to: 127.0.0.1

  - shell: yum upgrade -y

  - shell: reboot

  - wait_for: port=80 delay=10 host={{ inventory_hostname }}
    delegate_to: 127.0.0.1

  - nagios: action=enable_alerts service=host host={{ inventory_hostname }}
    delegate_to: 127.0.0.1
						

Gestion de configuration

---
- hosts: web_servers
  roles:
  - role: website
    url: www.example.org
    document_root: /srv/sites/www/

  - role: website
    url: webmail.example.org
    document_root: /srv/sites/webmail/
    modules:
    - php

  - role: website
    url: calendar.example.org
    redirect: webmail.example.org
						

Avec plus de details

Modules

Idempotents

Couvrent de nombreux besoins

Inventaire

  • Fichier plat
  • Dossier
  • Script
[web_servers]
www[1,5].example.org ansible_ssh_user=root

[redis_servers]
redis[1,3].example.org ansible_ssh_user=centos ansible_sudo=yes
redis-test.example.org ansible_ssh_user=fedora ansible_sudo=yes
						

Variables

Utilisables en Jinja

Dans le playbook

---
- hosts: webbuilder
  vars:
    builder_user: builder_middleman
    error_mail:   misc@example.org
  roles:
  - role: builder
    name: manageiq
    git_url: "https://git.example.com/example/website.git"
    git_version: master
    remote_location: /var/www/html
    remote_server: www.example.org
    remote_user: root
						

Fournies par Ansible

$ ansible all -m setup -c local -i '127.0.0.1,'
127.0.0.1 | success >> {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.76.131"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::ea2a:eaff:fe15:9d20"
        ],
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "03/28/2014",
        "ansible_bios_version": "CJFT85RW (2.24 )",
        "ansible_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-3.10.0-210.el7.x86_64",
            "LANG": "fr_FR.UTF-8",
						

Stocké dans des fichiers à part

$ ls *
deploy.yml    hosts    requirements.yml

group_vars:
webservers.yml    redis_servers.yml

host_vars:
redis-test.yml

roles:
builder    website    redis
						

Anatomie d'un rôle

$ ls roles/builder/*
roles/builder/defaults:
mail.yml

roles/builder/files:
builder.sh

roles/builder/handlers:
mail.yml

roles/builder/meta:
main.yml

roles/builder/tasks:
main.yml

roles/builder/templates:
config.ini
						

Structure de contrôle

  • when
  • with_items
  • register

When

- name: install Epel
  yum: pkg=epel-release state=installed
  when: ansible_distribution == 'CentOS'
						

With_items

- name: install base rpms
  yum: pkg={{ item }} state=installed
  with_items:
  - screen
  - htop
  - iftop
  - iotop
  - strace
  - vim-enhanced
  - tcpdump
  - chrony
						

Register

- user:
   name=builder_middleman
   generate_ssh_key=yes
  register: result

- authorized_key:
      key="{{ result.ssh_public_key }}"
      user=copy_user
  delegate_to: www.example.org
						

Plus d'informations

http://www.ansible.com/

#ansible sur Freenode

ansible-users sur Google groups

Questions

OpsCode Chef

Chef, c'est quoi donc ?

“Chef is a systems and cloud infrastructure automation framework that makes it easy to deploy servers and applications to any physical, virtual, or cloud location, no matter the size of the infrastructure.”

http://docs.chef.io

Un framework

Un cadre

Des conventions

Des outils

Un peu de vocabulaire

Cookbooks Recettes (recipes) Ressources Attributs (attributes)

Attributs

“An attribute is a specific detail about a node”

http://docs.chef.io/attributes.html

En gros, c'est une variable configurable et/ou dépendante de la plateforme.

Attributs

Défini par

# my_app/attributes/default.rb

default['my_app']['environment'] = 'prod'

Accédé par

# my_app/recipes/default.rb

puts node['my_app']['environment']

Ressource

“A resource is a statement of configuration policy. It describes the desired state of an element of your infrastructure, along with the steps needed to bring that item to the desired state.”

http://docs.chef.io/resources.html

Ressource

# my_app/recipes/packages.rb

package 'hhvm' do
    action :install
end
“Chef, installe le paquet hhvm s'il n'est pas déjà installé. Merci !”

Ressources

50+ ressources natives

file, directory, execute, package, ruby_block, service, user, group, ...

http://docs.chef.io/resources.html#platform-resources

Recette (recipe)

“A recipe is the most fundamental configuration element within the organization.“

http://docs.chef.io/recipes.html

Recette (recipe)

Une recette contient une ou plusieurs ressources

# recipes/packages.rb

yum_repository 'epel' do
    mirrorlist 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-5&arch=$basearch'
    gpgkey 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'
    action :create

    notifies :run, 'execute[yum-clean-all]', :immediately
end

execute 'yum-clean-all' do
    command 'yum clean all'
    action :nothing
end

package 'php'
“Chef, pour installer le paquet php, il faut commencer par ajouter le dépôt EPEL. Ensuite tu pourras installer php”

Cookbook

“A cookbook is the fundamental unit of configuration and policy distribution. Each cookbook defines a scenario, such as everything needed to install and configure MySQL”

http://docs.chef.io/cookbooks.html

Cookbook

.
├── attributes/
│   └── default.rb
├── files/
│   └── default/
├── libraries/
├── metadata.rb
├── providers/
├── recipes/
│   ├── default.rb
│   └── packages.rb
├── resources/
└── templates/
    └── default/

Et c'est pas fini !

  • Databags
  • Providers
  • Portabilité (value_for_platform, ohai)
  • Notifications (notifies) et Abonnements (subscribes)
  • Gardes (only_if, not_if)
  • Surcharges d'attributs

Configuration

Un peu de JSON

{
    "run_list": [
        "recipe[etc_environment]",
        "recipe[my_app::packages]",
        "recipe[my_app]"
    ],
    "etc_environment": {
        "SYMFONY_ENV": "prod",
        "SYMFONY_DEBUG": 0
    }
}

La liste des recettes (run_list) à exécuter ainsi que les attributs

chef-solo

Pour le mode standalone

# /opt/chef/solo.rb

cookbook_path ['/opt/chef/cookbooks']
json_attribs '/opt/chef/solo.json'
$ chef-solo -c /opt/chef/solo.rb

Exécution (run)

Starting Chef Client, version 11.12.8
resolving cookbooks for run list: ["ohai", "proxmox-ohai", "resolver", "timezone", "consul", ...]
Synchronizing Cookbooks:
- ohai
- proxmox-ohai
- resolver
- timezone
- consul
...
Compiling Cookbooks...
Converging 26 resources
Recipe resolver::default
  * template[/etc/resolv.conf] action create (up to date)
Recipe timezone::default
  * package[tzdata] action install (up to date)
  * template[/etc/timezone] action create (up to date)
  * bash[dpkg-reconfigure tzdata] action nothing (skipped due to action :nothing)
Recipe consul::default
...
Chef Client finished, 2/31 resources updated in 3.391670365 seconds

Exécution (run)

Plusieurs étapes

  • Calcul de la configuration du noeud
  • Résolution de la run_list
  • Compilation des ressources
  • Convergence

Compilation / Convergence

Recette

# my_app/recipes/default.rb

# Une fonction Ruby
puts 'Yo'

# Une ressource Chef
log 'Man'

puts 'Hello'
log 'World'

Résultat

Yo
Hello
[2015-01-22T20:51:04+00:00] INFO: Man
[2015-01-22T20:51:04+00:00] INFO:World

Chef Server & chef-client

Pour le mode client/serveur

chef_server_url  "https://chef.myorg.com"
validation_client_name "chef-validator"
node_name "my_app_web"
environment "production"
$ chef-client

Un peu de vocabulaire, encore

Noeuds (nodes) Le système cible décrit par des rôles, des cookbooks/recettes et des attributs Rôles Une collection de cookbooks/recettes et des attributs Environnements Un ensemble de contraintes sur les cookbooks et des attributs

Chef Server

  • Héberge les cookbooks
  • Héberge les configurations (noeuds, rôles, environnements et databags)
  • Authentifie les tiers
  • Maintient un index des données

Orchestration

Grâce à l'index :node et à la méthode search.

search(:node, "role:db",
    :filter_result => { 'ip' => [ 'ipaddress' ]}).each do |result|
    puts result['ip']
end

                        

L'écosystème

  • Des outils
  • Des cookbooks libres
  • Des patterns
  • Une communauté

Les outils

knife Commande pour gérer chef server berks Gestionnaire de dépendances foodcritic Linter chefspec Tests unitaires kitchen CI Tests d'intégration multi-plateforme

Place to be #1

Documentation

http://docs.chef.io

Documentation

Le lien magique

https://docs.chef.io/resource_<resource-name>.html

La liste des ressources

https://docs.chef.io/resources.html#platform-resources

Place to be #2

Supermarket

Dépôt central des cookbooks communautaires

https://supermarket.chef.io

Place to be #3

Github

Code source des cookbooks communautaires

https://github.com/opscode-cookbookshttps://github.com/chef-cookbooks

Les points forts

  • Du ruby partout
  • Une documentation assez énorme
  • Beaucoup de ressources (cookbooks, articles, ...)
  • Un DSL puissant et extensible
  • Un très bon écosystème
  • Une communauté très active

Les points faibles

  • Les débuts peuvent être compliqués
  • La documentation est parfois dure à exploiter
  • Chef server un peu compliqué à installer
  • Manque de qualité de certains cookbooks communautaires

Questions

Ça fait quoi ?

À partir d’un jeu de règles

  • Le master génère un catalogue décrivant l’état cible
  • Le catalogue est envoyé sur la cible
  • L’agent vérifie pour chaque élément du catalogue si c’est l’état actuel, le cas échéant il change l’état
  • Un rapport est renvoyé au master

On peut aussi générer le catalogue depuis les cibles à condition de tout dupliquer.

Exemple simple

node default {
    service { 'sshd':
        enable  => true,
    }
    package { 'sshd':
        ensure  => latest,
        name    => 'openssh-server',
    }
    file { '/etc/ssh/sshd_config':
        file    => 'puppet:///files/sshd_config',
        notify  => Service['sshd'],
        require => Package['sshd'],
    }
}
Liste exhaustive des types natifs supportés.

Différence entre “type” et “class”

Déclaration d’un type de ressource

define monmodule::montype (
    $variable1 = 'valeur par défaut',
    $variable2,
    ) {
    file { "/etc/httpd/conf.d/${variable1}":
        content => $variable2,
    }
}

Déclaration d’une classe

class monmodule::maclasse (
    $variable1 = 'valeur par défaut',
    $variable2,
    ) {
    package { 'httpd': }
    service { 'httpd': }
    file { '/etc/httpd/httpd.conf':
        content => template('monmodule/httpd.conf.erb'),
        notify  => Service['httpd'],
    }
    monmodule::montype { 'default':
        variable2 => '',
    }
}

Comment charger une classe

Deux manières : la bonne et la mauvaise

La bonne

include monmodule::maclasse

La mauvaise

class { monmodule::maclasse: }
class { monmodule::maclasse:
    variable1 => 'valeur',
}

À la place on met les variables de classe dans hiera.

Arborescence

/etc/puppet
├── auth.conf
├── environments
│   └── production
│       ├── manifests
│       │   └── site.pp
│       └── modules
├── fileserver.conf <!-- Utilisé par les requêtes puppet://[serveur]/loc -->
├── hiera.yaml      <!-- Configuration de l’entrepôt de variables -->
├── manifests
│   └── site.pp
├── modules
├── puppet.conf
│   <!-- Mes recommandations -->
├── hieradata
└── sensitive

/etc/puppet/fileserver.conf

[sensitive]
path /etc/puppet/sensitive/%d/%h
allow *

Hiera

hierdata/

├── common.yaml
├── developement.yaml
├── afpy.org.yaml
├── integration.yaml
└── server1.afpy.org.yaml

common.yaml

---
nginx::confd_purge: true
postgresql::globals::encoding: 'UTF-8'
default_vhost_name: "%{fqdn}"
root_keys:
    - user1
jvm_properties:
    Xmx: 5g

Les fonctions

hiera('key', 'default', 'override')
hiera_array('key', 'default', 'override')
hiera_hash('key', 'default', 'override')
Override sert pour redéfinir la hiérarchie de hiera.

Facter

Permet de récupérer des informations à propos de nœuds.

architecture => x86_64
bios_version => MWPNT10N.86A.0083.2011.0524.1600
blockdevice_sda_model => TOSHIBA DT01ACA1
blockdevice_sda_size => 1000204886016
blockdevices => sda sdb sdc
domain => afpy.org
fqdn => exemple1.afpy.org
hostname => exemple1
interfaces => br0,eth0,lo,virbr0,virbr0_nic
ipaddress => 10.10.12.12
ipaddress6_br0 => 2001::1
is_virtual => false
kernel => Linux
kernelmajversion => 3.10
memoryfree => 2.85 GB
memorysize_mb => 3775.95
operatingsystem => CentOS
operatingsystemmajrelease => 7
os => {"name"=>"CentOS", "family"=>"RedHat", "release"=>{"major"=>"7", "minor"=>"0", "full"=>"7.0.1406"}}
osfamily => RedHat
partitions => {"sda1"=>{"uuid"=>"aa6f8f14-9e42-495d-8b1b-2a17849494d0", "size"=>"40957952", "mount"=>"/", "filesystem"=>"ext3"}, "sda2"=>{"uuid"=>"5bca139c-67a4-416c-8d6d-b2eda0404b64", "size"=>"1046528", "filesystem"=>"swap"}}
processorcount => 2
selinux => true
virtual => physical

PuppetDB

  • Base de données
  • Facts
  • Catalogues

Ressources exportées

@@dns::record::aaaa { 'wsgi':
    data => $::ipaddress6,
    zone => $::domain,
}
Dns::Record:Aaaa <<| zone == $::domain |>> 

Anatomie d’un module

modules/monmodule/
├── files
│   └── sshd_config
├── lib
│   ├── facter
│   └── puppet
│       ├── parser
│       ├── provider
│       └── type
├── manifests
│   ├── init.pp      <!-- monmodule                 -->
│   ├── maclasse.pp  <!-- monmodule::maclasse       -->
│   ├── montype.pp   <!-- monmodule::montype        -->
│   └── montype
│       └── autre.pp <!-- monmodule::montype::autre -->
├── Modulefile
├── spec / tests
└── templates
    └── httpd.conf.erb

Questions

Saltstack

Gaston TJEBBES @majerti.fr>

Le contexte

  • L’hébergement de solutions développées en interne
  • Le réseau interne d’une entreprise

Le besoin

  • Gain de temps
  • Gestion des problématiques de sécurité
  • Automatisation du workflow
  • Uniformiser les configurations
  • Anticiper la croissance

Autrement dit

  • Lancer des commandes de manière unifiée
  • Centraliser des configurations éparses
  • Gérer des dépendances (logiciels, fonctionnels)
  • Cibler des machines

Pourquoi Saltstack

  • Libre
  • Python
  • Une architecture simple
  • Un système de ciblage avancé (grains + pillar)
  • Des fonctionnalités qui matchent nos besoins et nos ambitions (salt-virt, reactor, salt-mine …)

Le projet

  • Projet initié en 2011 par Thomas S Hatch
  • Licence Apache 2.0
  • Une ML très active
  • Près d’un millier de contributeurs
  • Porté par une entreprise du même nom

Les composants

  • Protocole : ZeroMQ ou RAET
  • Format : MsgPack + AES
  • Authentification : clés RSA

Disponibilité

  • BSD
  • Linux
  • Windows
  • Osx

Architecture

Trois niveaux de complexité:

  • Appel local par le biais de salt-call
  • Un master et des minions
  • salt-syndic, l’architecture à plusieurs étages

Un master des minions

Salt-syndic

Commencer

Installer un “master”

yum install salt-master

Installer des “minion”

yum install salt-minion

Référencer

salt-key -a minion.example.com

On peut désormais jouer

Les modules fournissent un panel de commandes exécutables

salt '*' test.ping
salt 'minion.example.com' pkg.upgrade

Modules

  • De nombreuses librairies (mount, pkg, rabbitmq, …)
  • Des modules “méta” ( ex: pkg )
  • Développables facilement
#/srv/salt/_modules/hello.py
def message(filepath, message):
    with open(filepath, w) as file_buffer:
        file_buffer.write(message)
salt 'minion.example.com' hello.message /tmp/test "Hello world"

Bien viser, le rôle des grains (1)

Les minions fournissent par défaut des variables d’environnement les ‘grains’ :

  • Fournissent des informations :

    : - Matériel

    -   Logiciel
  • Sont personnalisables

  • Permettent de cibler les minion

Bien viser, le rôle des grains (2)

salt 'minion.example.com' grains.get os
minion

Configuration : Les états

  • Les fichiers d’état sont des templates jinja ayant pour destination le format yaml
  • Ils sont stockés par défaut dans /srv/salt
  • Un état correspond à une exigence : “Installer le paquet nginx”
  • Il peut dépendre d’autre état : “Nginx doit être lancé” requiert “Installation du paquet Nginx”

Exemple d’état avec utilisation de grain

# /srv/salt/nginx.sls
{% if grains['node_type'] == 'django' %}
nginx:
    pkg.installed:
        - nginx
    service.runing:
        - name: nginx
        - require:
          - pkg: nginx
{% endif %}
salt 'minion.example.com' state.sls nginx

top.sls + highstate : le point d’entrée

top.sls

le fichier d’entrée qui associe les états aux machines

# /srv/salt/top.sls
base:
    'minion.example.com':
        - django_project
        - nginx

Ce qui va nous permettre de lancer

salt '*' state.highstate

Gestion des dépendances : require

require permet de requérir :

  • Un état
  • Un fichier d’état
include:
 - nginx

collect_static:
 cmd.run:

  - name: /root/collect_static.sh

  - require:


 - sls: nginx

Gestion des dépendances : watch

Observe les modifications apportées par un autre état

gunicorn_conf_file:

file.managed:



- source: salt://django/source/etc/gunicorn.d/project.conf



- name: /etc/gunicorn.d/project.conf

gunicorn:
service.running:

- enable: True

- reload: True

- watch:


- file: gunicorn_conf_file

Le fileserver

  • Un serveur de fichier en ZeroMQ intégré au service salt-master
  • Permet de fournir des fichiers au gestionnaire d’états
  • Les fichiers sont des templates
  • Par défaut, les fichiers sont placés dans la même arborescence que les états
gunicorn_conf_file:

file.managed:



- source: salt://django/source/etc/gunicorn.d/project.conf



- name: /etc/gunicorn.d/project.conf



- template: jinja

Pillar : des variables de configuration

Composant permettant de distribuer des variables de configuration :

  • Stockées sur le master
  • Associées aux minions par le biais des grains
  • Cloisonnées
  • Utilisables dans les states

Pillar : exemple

#/srv/pillar/top.sls
base:

'minion.example.com':



- db_pass

#/srv/pillar/db_pass.sls
sql_user: django
sql_password: ma donnéessuper secret
# /srv/salt/django_project/sources/etc/django/settings.py

Questions