Affichage des articles dont le libellé est Puppet. Afficher tous les articles
Affichage des articles dont le libellé est Puppet. Afficher tous les articles

jeudi 9 octobre 2014

Comparaison de la gestion des FS avec Puppet et Ansible

Suite de mes aventures avec Ansible : aujourd'hui c'est du système Linux.

Les données du problème : je veux pouvoir gérer le LVM Linux et retailler mes FS à la demande.

La recette sous Puppet

Sous Puppet, en utilisant un fichier yaml/Hiera, l'écriture de tout ceci se ferait avec quelque chose de ce genre :

---
classes: ['lvm']

lvm::volume_groups:
  rootvg:
    physical_volumes:
      - /dev/sda2
    logical_volumes:
      tmp:
        size: 2G
        mountpath: /tmp
  datavg:
    physical_volumes:
      - /dev/sdb
    logical_volumes:
      test:
        size: 128
        mountpath: /test

Ne pas oublier non plus d'inclure le code suivant dans le fichier site.pp :

hiera_include('classes','')

Bien penser également à installer le module lvm, sinon ça ne marchera pas (puppetlabs-lvm).

Bon, sous Ansible, toutes ces belles choses n'existent pas (encore) en l'état mais sous forme de brique à assembler. On va voir comment recréer un mécanisme à peu près similaire.

La recette sous Ansible

Je me place dans le cadre d'un playbook Ansible avec un fichier system.yml se trouvant dans un répertoire de travail ~/ansible avec le contenu suivant :

---
# Playbook de gestion du système

- name: Propriétés communes système
  hosts: all
  remote_user: root

  roles:
    - system

Autre fichier présent : group_vars/all avec le contenu suivant :

---
# Variable applicable à tous

vgs:
  rootvg: "/dev/sda2"
  datavg: "/dev/sdb"

lvs:
  test:
    vg: rootvg
    mountpoint: /test
    size: 256

Ce fichier sera a spécialiser par groupe de machine mais est l'équivalent de notre fichier yaml/Hiera. Reste maintenant à écrire notre liste de tâche qui nous permettra de gérer ces FS (qu'on mettra dans le fichier roles/system/tasks/main.yml) :

---
- name: VG/PV Configuration
  lvg: vg={{ item.key }} pvs={{ item.value }} state=present
  with_dict: vgs

- name: LV creation
  lvol: vg={{ item.value.vg }} lv={{item.key}} size={{item.value.size}}
  register: task
  with_dict: lvs

- name: LV format
  filesystem: fstype=ext4 dev=/dev/mapper/{{item.value.vg}}-{{item.key}}
  with_dict: lvs

- name: Mount
  mount: name={{item.value.mountpoint}} src=/dev/mapper/{{item.value.vg}}-{{item.key}} dump=1 passno=2 fstype=ext4 state=mounted
  when: item.value.has_key('mountpoint')
  with_dict: lvs

- name: FS resize
  command: resize2fs /dev/mapper/{{item.item.value.vg}}-{{item.item.key}}
  with_items: task.results
  when: item.changed == True

NB : cette recette ne gère que des fs de type ext4. Si vous voulez gérer du xfs/btrfs/fegafatfs, il faudra changer la commande resize2fs par autre chose.

Le fonctionnement de la recette est assez simple : on enchaine les opérations (création du PV/VG, création du LV, formatage et montage) et on exécute la commande resize2fs qu'en cas de changement sur le LV (cf FS resize, ligne when: item.changed == True).

Reste maintenant à lancer notre playbook avec la commande ansible-playbook -i ./hosts ./system.yml. Vous devriez obtenir le résultat suivant :

PLAY [Propriétés communes système] ******************************

GATHERING FACTS *************************************************
ok: [machine1]

TASK: [common | VG/PV Configuration] ****************************
ok: [machine1] => (item={'key': 'datavg', 'value': '/dev/sdb'})
ok: [machine1] => (item={'key': 'rootvg', 'value': '/dev/sda2'})

TASK: [common | LV creation] ************************************
changed: [machine1] => (item={'key': 'test', 'value': ...})

TASK: [common | LV format] **************************************
changed: [machine1] => (item={'key': 'test', 'value': ...})

TASK: [common | Mount] ******************************************
changed: [machine1] => (item={'key': 'test', 'value': ...})

TASK: [common | FS resize] **************************************
changed: [machine1] => (item={u'msg': u'', 'item': ...})

PLAY RECAP ******************************************************
machine1        : ok=6    changed=4    unreachable=0    failed=0   

Changeons la taille de notre FS pour autre chose et relançons ansible pour vérifier la bonne prise en compte de tout ceci :

PLAY [Propriétés communes système] ******************************

GATHERING FACTS *************************************************
ok: [machine1]

[...]

TASK: [common | LV creation] ************************************
changed: [machine1] => (item={'key': 'test', 'value': ...})

[...]

TASK: [common | FS resize] **************************************
changed: [machine1] => (item={u'msg': u'', 'item': ...})

PLAY RECAP ******************************************************
machine1        : ok=6    changed=2    unreachable=0    failed=0   

Tout c'est bien passé. Il ne me reste plus qu'à vous souhaiter une bonne gestion de vos LV !

samedi 13 septembre 2014

Installation et présentation de puppetboard

Mais au fait, pourquoi puppetboard ?

Il était une fois un royaume dans lequel les devops étaient roi et n'avaient (presque plus) rien à faire. Passant par là, le développeur opensource du dit produit se dit qu'il fallait réveiller se peuple au bios dormant. C'est ainsi que naquis la notion de PuppetDB (bon OK, y'a d'autres raisons notamment le besoin d'avoir un mécanisme de stockage des faits un peu plus propre que ce qui était fait).

Concernant PuppetDB en lui même - dans les grandes lignes - sachez qu'il s'agit d'une base type NoSQL (notion à la mode) qu'on peut interroger à l'aide de requête de type REST (autre notion à la mode).

Pour la version un peu plus longue, sachez que le premier désigne un mécanisme de stockage d'objet au format JSON (et c'est là où je me rends compte que c'est pas forcément plus clair ...). En gros, c'est comme le SQL sauf qu'il n'y a pas de table et que les requêtes se font en javascript à la place du SQL. Si vous ne savez pas de quoi je parle là non plus, je ne peux plus rien faire pour vous.

Pour les requêtes REST (ou restfull), tout ceci désigne un moyen de faire des appels via l'utilisation de requête HTTP (et que c'est marrant de se rendre compte qu'on avait déjà fait du REST avant le verbe mais je m'égare). En gros, vous voulez la liste des machines, vous faîtes une requête sur l'URL http://localhost/nodes, ce dernier vous renverra alors la liste des nœuds sous la forme d'un hash map (autrement appelé JSON). Bon d'accord, c'est pas très clair. En gros, ça vous renvoie du texte qui devrait ressembler à un truc de ce genre :

{
  "node1": {"address": "192.168.0.1", "macadress": "00:00:01:00:00:01"},
  "node2": {"address": "192.168.0.2", "macadress": "00:00:01:00:00:02"},
}

Ne reste plus qu'à le lire et l'utiliser dans votre interface web, votre script perl etc.

Pour revenir à nos moutons, PuppetDB à donc pour rôle de stocker les informations qu'on va récolter suite aux applications de catalogue sur nos machines. Seul petit hic, le plugin puppet dashboard n'est plus trop compatible avec tout ceci et je me retrouve donc avec un superbe outil de présentation des données puppet vide et, de l'autre côté, une interface en JSON/Restfull absolument inutilisable pour mes utilisateurs (ainsi que moi-même étant donné que je ne lis pas encore le JSON dans le texte).

C'est donc à ce moment que je me suis mis en quête d'une alternative afin de rendre le sourire à mes utilisateurs et moi-même. Après quelques requêtes sur le moteur dont on ne doit pas prononcer le nom, je tombe sur le jeune et prometteur puppetboard (https://github.com/nedap/puppetboard).

Mais assez de blabla, passons maintenant à l'installation de ce merveilleux outils.

Installation

Autant vous le dire tout de suite, si vous voulez installer ce produit, c'est que vous utilisez puppet. Si vous ne voulez pas utiliser puppet pour l'installation, c'est que vous êtes maso. Comme il se trouve qu'en plus, les auteurs ont fait une recette puppet directement récupérable depuis le puppet forge, on ne va pas s'en priver.

En premier lieu, assurez-vous d'avoir déjà les modules puppet apache et puppetboard (puppet module install/list/etc.).

Ouvrez la structure du node puppet correspondant à la machine sur laquelle vous voulez installer le puppetboard et ajoutez-y la recette suivante :

  class { 'apache': }
  class { 'apache::mod::wsgi': }
  # Configure Puppetboard
  class { 'puppetboard':
    manage_virtualenv => true,
    puppetdb_port     => "9090",
    puppetdb_host     => "127.0.0.1",
  }
  # Access Puppetboard through pboard.example.com
  class { 'puppetboard::apache::vhost':
    vhost_name => 'puppetboard',
    port       => 8888,
  }

De là, je vous invite à lancer la commande d'application de votre recette sur la machine puppetboard :

puppet agent -t

Tada ! Vous avez un puppetboard fonctionnel.

Sortez maintenant votre navigateur préféré (firefox, chrome, Internet E nan, j'déconne) et pointez sur l'URL http://puppetboard:8888 (ou ce que votre cerveau malade aura décidé de choisir) et vous devriez obtenir l'interface suivante :
Un petit tour du propriétaire s'impose :
  • Le détail sur un serveur :
  • Consultation du résultat de l'application d'un catalogue sur une machine :
  • Requêtage des faits en base :

Pour finir

Autant le dire tout de suite, l'outil est encore un peu jeune. A noter un petit soucis que j'ai rencontré lors du déploiement : les librairies Javascript sont référencées sur Internet et non sur la machine. Il faut donc un navigateur avec un proxy valide pour que ça fonctionne. Je me fendrai peut-être d'un patch pour corriger ça ...

Autre chose, je n'ai plus mes courbes de tendance sur le temps d'application du catalogue sur mes machines. Argh ! ça va crier ...

Mais bon, en dehors de ça, l'interface est assez réactive et simple à comprendre. Je recommande donc !

dimanche 26 janvier 2014

Installation de razor (ainsi que puppet, postgresql, tftp et dhcp)

Il y a quelques années, j'avais mis en place un serveur d'installation Linux. C'était basé sur le classique dhcp, tftp et kickstart. Un de mes collègues m'a parlé dernièrement d'un projet relativement intéressant : razor. C'est un projet qui est toujours en cours de développement mais est tout à fait utilisable.

Son principe est assez simple : il se base sur des briques classiques pour gérer la partie boot réseau (DHCP + TFTP + kickstart) et utilise un serveur JBoss couplé à postgresql pour la partie gestion de la configuration.

Pour mémoire, l'installation se fera sur une CentOS 6.5 mais devrait s'adapter très facilement sur une autre distribution.

Mais trêve de bavardage, passons à la mise en oeuvre.

Installation et configuration de postgres

Pour faire fonctionner razor, nous allons avoir besoin d'une version très récente de posgresql (la 9.3). Le repository pour RHEL/CentOS contenant cette version de postgres s'installe avec la commande suivante :

yum install http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm

L'installation de postgresql à proprement parlé se fait ensuite simplement avec yum :

yum install postgresql93-server

De là, il nous faut initialiser le contenu de la base avec la commande suivante :

/etc/init.d/postgresql93 initdb

Ceci fait, nous pouvons démarrer la base :

/etc/init.d/postgresql93 start

Enfin, nous allons créer notre utilisateur ainsi que notre schéma :

psql
create user razor;
create database razor_prd owner razor;
\password razor

Modifier ensuite le fichier /var/lib/pgsql/9.3/data/pg_hba.conf afin de faire apparaître la ligne suivante (vers la fin du fichier) :

local   all         all     127.0.0.1/32        md5

Un arrêt/relance de postgresql plus tard :

/etc/init.d/postgresql restart

Nous voici prêt pour la suite.

Installation de puppet

Par la suite, je m'appuierai énormément sur puppet pour mettre en oeuvre le produit. Je vous invite donc à l'installer avec la série de commandes suivante :

yum install http://yum.puppetlabs.com/puppetlabs-release-el-6.noarch.rpm
yum install puppet

Installation de razor

Le plus simple et de se servir de puppet pour faire le gros du travail. Pour se faire, lançons l'installation du module razor :

puppet module install puppetlabs-razor

Vous devriez voir le résultat suivant si tout se passe bien :

Notice: Preparing to install into /etc/puppet/modules ...
Notice: Downloading from https://forge.puppetlabs.com ...
Notice: Installing -- do not interrupt ...
/etc/puppet/modules
└─┬ puppetlabs-razor (v0.11.0)
  ├─┬ puppetlabs-java (v1.0.1)
  │ └── puppetlabs-stdlib (v4.1.0)
  └─┬ puppetlabs-tftp (v0.2.1)
    └── puppetlabs-xinetd (v1.2.0)

Passons maintenant à l'installation de razor :

export FACTER_server=ADDRESSE_IP_RAZOR
puppet apply -e "include razor"

Au bout d'un petit moment, vous devriez avoir un process Java JBoss pour héberger l'application razor ainsi qu'un serveur tftpboot fonctionnel (ou presque).

Rendez-vous maintenant dans le répertoire /opt/razor afin de recopier le fichier config.yam.sample en config.yaml :

cd /opt/razor
cp config.yaml.sample config.yaml

Adapter ensuite le contenu du fichier config.yaml à votre plateforme et lancer la commande suivante :

razor-admin -e production migrate-database

De là, il ne nous reste plus qu'à installer une version valide du client razor avec la commande suivante :

puppet resource package razor-client provider=gem ensure=latest

Si vous avez un parefeu sur votre machine, il vous faudra ouvrir le port 8080.

Configuration du tftp

Rien de particulier à signaler si ce n'est de bien faire attention d'ouvrir le port udp 69 sur votre parefeu.

Récupération du microkernel

Nous allons déposer le micro kernel dans un sous répertoire de razor (/var/lib/razor/repo-store). Ci-dessous les commandes à lancer pour le faire :

wget http://links.puppetlabs.com/razor-microkernel-003.tar
tar xfv razor-microkernel-003.tar -C /var/lib/razor/repo-store

Configuration du serveur DHCP

Il existe un fichier d'exemple dans le module puppet que vous pouvez récupérer avec la commande suivante :

cp /etc/puppet/modules/razor/examples/isc-dhcpd-example.conf /etc/dhcp/dhcpd.conf

Reste ensuite à adapter le fichier /etc/dhcp/dhcpd.conf et démarrer notre serveur dhcp en s'assurant qu'il soit démarrer au lancement de la machine :

puppet resource service dhcpd ensure=running enable=true

Nous allons pouvoir commencer quelques tests ...

Installation d'une CentOS 6.5

Création d'un broker par défaut

Avec razor, il est possible de faire tout un tas de chose en post installation. Dans notre cas, on va se contenter quelque chose de simple avant d'aller plus loin :

razor create-broker --name=noop --broker-type=noop

Création d'un repository

Nous allons récupérer un repository CentOS. Cette opération se fait avec la commande razor create-repo :

razor create-repo --name centos-6.5 --iso-url=http://.../CentOS-6.5-x86_64-minimal.iso

Nous pouvons ensuite vérifier la présence de notre repository avec la commande razor repos :

# razor repos
From http://192.168.122.3:8080/api/collections/repos:

    id: "http://...:8080/api/collections/repos/centos-6.5"
  name: "centos-6.5"
  spec: "/razor/v1/collections/repos/member"

Ainsi que le détail du repos en ajoutant son nom :

# razor repos centos-6.5
From http://192.168.122.3:8080/api/collections/repos/centos-6.5:

       id: "http://...:8080/api/collections/repos/centos-6.5"
     name: "centos-6.5"
     spec: "/razor/v1/collections/repos/member"
  iso_url: "http://.../CentOS-6.5-x86_64-minimal.iso"

Création d'un tag

Nous allons procéder à la création d'un tag testant si la machine est virtuelle ou non. Pour se faire, alimenter le fichier virtual.json avec le contenu suivant :

# cat virtual.json
{
  "name": "virtual",
  "rule": ["=", ["fact", "is_virtual"], "true"]
}

Donnons ça à manger à razor avec la commande razor create-tag :

# razor create-tag --json virtual.json
From http://192.168.122.3:8080/api:

    id: "http://localhost:8080/api/collections/tags/virtual"
  name: "virtual"
  spec: "/razor/v1/collections/tags/member"

Création d'une policy

Alimentons maintenant le fichier centos-6.5.json avec le contenu suivant :

# cat centos-6.5.json
{
  "name": "centos-6.5",
  "repo": { "name": "centos-6.5" },
  "task": { "name": "centos" },
  "broker": { "name": "noop" },
  "enabled": true,
  "hostname": "host${id}.local",
  "root_password": "ChangezMoi",
  "max_count": "100",
  "rule_number": "100",
  "tags": [{ "name": "virtual"}]
}

Le contenu du fichier va donner un minimum d'information pour l'installation à savoir :
  • Le nom de la police (centos-6.5) ;
  • Le nom du repository (idem) ;
  • Le type de tâche d'installation (ici une CentOS). Pour connaître la liste complète, on peut utiliser la commande razor tasks ;
  • Le type de broker : Il s'agit d'une espèce de post install. Pour l'instant, l'installation s'arrête là ;
  • Enabled : pour savoir si la police est active ou non ;
  • Un template pour le nom de la machine ;
  • Le mot de passe root ;
  • Le nombre max de création et son numéro ;
  • Enfin, les tags sur lesquels cette police s'appliquera (ici virtual).
Donnons ça à manger à notre ami razor avec la commande razor create-policy :

razor create-policy --json centos-6.5.json

Dès que nous démarrerons une machine virtuelle sur notre réseau, cette dernière commencera son installation. Si vous aviez une machine en attente, cette dernière redémarrera et commencera son installation.

Le mot de la fin

Vous l'avez vu, il s'agit vraiment d'une aide pour mettre en oeuvre razor le plus rapidement possible sans forcément chercher à personnaliser les choses.

J'essaierai dans un second temps de revenir avec un nouvel article pour vous présenter le mécanisme permettant de personnaliser vos installations.

A noter que le produit est en pleine refonte et que certaines parties de ce document vont sûrement être amené à évoluer ou devenir un peu obsolète. Je vous laisse ci-dessous une bibliographie m'ayant permis de mettre en oeuvre le produit.

Bon courage et bon déploiement !

Bibliographie

http://technodrone.blogspot.com/2013/11/installing-razor-yes-new-version.html
http://technodrone.blogspot.com/2013/11/installing-razor-client-and-creating.html

mercredi 24 juillet 2013

Packager NSClient++ sous Windows (64 bits) à l'aide de puppet

Bon, pas la peine d'en rajouter sur le titre, j'ai honte. Mais bon voilà, j'avais commencé à vouloir packager NSClient++ avec l'OS alternatif propriétaire (avec ce plugin), et je me suis rendu compte que le package NSClient++ était réinstallé tout le temps. J'ai donc voulu savoir pourquoi.

En effet, en lançant l'application du catalogue sur mon noeud, j'avais systématiquement ce message :

Notice: /Stage[main]/Nscp/Package[nscp]/ensure: created
Notice: /Stage[main]/Nscp/File[C:\Program Files\NSClient++\nsclient.ini]/content:

Info: /Stage[main]/Nscp/File[C:\Program Files\NSClient++\nsclient.ini]: Filebucketed C:/Program Files/NSClient++/nsclient.ini to main with sum e3daba1
4bc76433649a9c4af706042ac
Notice: /Stage[main]/Nscp/File[C:\Program Files\NSClient++\nsclient.ini]/content: content changed '{md5}e3daba14bc76433649a9c4af706042ac' to '{md5}38b
2bb3dbe8530f33d983ccfd8394628'
Info: /Stage[main]/Nscp/File[C:\Program Files\NSClient++\nsclient.ini]: Scheduling refresh of Service[nscp]
Notice: /Stage[main]/Nscp/Service[nscp]: Triggered 'refresh' from 1 events
Notice: Finished catalog run in 11.64 seconds

En gros, mon package nscp était constamment réinstallé et ce dernier venait également mettre à jour le fichier nsclient.ini. Je me suis donc dit que le package n'avait pas le bon nom et qu'il fallait que je trouve le bon. Après avoir essayé NSClient++, NSClientpp, j'en ai eu marre de chercher et je me suis posé la question de savoir comment avoir le nom de ce fameux package. C'est là que j'ai découvert l'existence de la commande puppet resource package. Ci-dessous un extrait de la sortie de cette commande :

[...]
package { 'NSClient++ (x64)':
  ensure => 'installed',
}
package { 'Puppet Enterprise':
  ensure => 'installed',
}
[...]

J'avais enfin le nom de mon package ('NSClient++ (x64)') et du coup, NSClient arrêtait de s'installer systématiquement et donc, le fichier nsclient.ini n'était plus à mettre à jour. En gros, mon agent n'avait plus rien à faire. Bonne nouvelle donc !


lundi 22 juillet 2013

Utilisation de Puppet sous AIX pour la gestion des logicals volumes

Depuis quelques temps, j'ai découvert un nouveau sujet d'amusement : Puppet. Le produit est particulièrement intéressant et j'ai commencé à travailler sur un POC impliquant des agents pour différents OS (AIX, Linux et bien sûr Windows).

C'est à cette occasion que j'ai découvert l'existence de Puppet Forge et notamment l'excellente extension permettant de gérer les volumes groupes de Linux (extension lvm). Suite à ça j'ai voulu disposer de la même chose pour AIX. Au début, j'ai voulu implémenter ça directement dans le module lvm mais devant le travail que ça représentait, j'ai préféré faire quelque chose de spécifique mais surtout de plus simple. Je verrai plus tard pour faire ça comme il faut :)

Cette classe s'utilise sous la forme de module. Il vous faut donc créer un répertoire /etc/puppet/modules/aix avec un sous répertoire manifests:
mkdir -p /etc/puppet/modules/aix/manifests
Créer ensuite le fichier aix/manifests/logical_volume.pp avec le contenu suivant :

# Handle AIX LVM
define aix::logical_volume(
  $mount_point,
  $ensure = "present",
  $vg = "rootvg",
  $size = "",
  $lv_options = "",
  $fs_options = ""
) {

  $lv_name = $title
  $fs_type = "jfs2"

  case $ensure {
    # Clean up the whole chain.
    cleaned: {
        mount { $mount_point: ensure => "unmounted",
                atboot=> false, device => "/dev/$lv_name" } ->
        exec {"/usr/sbin/rmfs /dev/$lv_name ":
            onlyif => "/bin/test -n \"`/usr/sbin/lsfs $mount_point 2> /dev/null`\"",
        }
    }
    # Create the whole FS
    /^(present|created)$/: {
        # Creation du FS avec un nombre de block = 1
        exec {"/usr/sbin/mklv -t$fs_type -y $lv_name $options $vg 1":
            onlyif => "/bin/test ! -b /dev/$lv_name"
        } ->
        exec {"/usr/sbin/crfs -v$fs_type -d $lv_name -m $mount_point $options -A yes -a logname='INLINE'":
            onlyif => "/bin/test -z \"`/usr/sbin/lsfs $mount_point 2> /dev/null`\"",
        } ->
        mount { $mount_point: ensure => "mounted", atboot=> true, device => "/dev/$lv_name" }
        case $size {
            /^(\d+)([KMGT])$/: {
                $size_hash = { "K" => 1, "M" => 1024, "G" => 1048576, "T" => 1073741824 }
                $kilo_size = $1 * $size_hash[$2]
                $block_size = $kilo_size * 2
                exec {"/usr/sbin/chfs -a size=$size $mount_point":
                    onlyif => "/bin/test ! \"`/usr/sbin/lsfs $mount_point | /usr/bin/awk '/$lv_name/ { print \$5 }'`\" = \"$block_size\"",
                    require => Mount[$mount_point]
                }
            }
            /^\s*$/: { }
            default: {
                fail ( 'size is not valid (eg: 1G, 256M, 1T etc.)' )
            }
        }
    }
    default: {
        fail ( 'aix::logical_volume: ensure parameter can only be set to cleaned or present/created' )
    }
  }
}
De là, vous pourrez l'utiliser sous cette forme dans une déclaration de noeud :
node 'machineaix' {
  aix::logical_volume { "test":
      mount_point => "/test",
      size => "1G"
  }
}

Cette merveille a été testé avec succès sous AIX 7.1 et devrait fonctionner sans problème sous AIX 5.3 et 6.1.