Archives de catégorie : Ansible

Faire son premier playbook avec ansible

Vous êtes débutant sur ansible ? ou peut être un peu plus avancé mais vous cherchez à vous faire une piqûre de rappel pour remettre les choses en place ? Je vous propose de suivre ma formation ansible en ligne et totalement gratuite via des vidéos youtube mais également à travers les articles de ce blog devops.

Alors depuis quelques vidéos nous avons posé les bases avec :

  • l’inventaire
  • les groupes
  • les hosts
  • l’installation
  • la CLI

Nous allons continuer sur ce chemin pour débuter petit à petit avec cet orchestrateur.

Depuis le temps il est peut être venu le moment de faire quelques actions sur nos serveurs.

A quoi sert le playbook ??

C’est assez simple, dans les définitions et concepts, nous avons vu qu’il y a deux bulles. Avec d’un côté l’inventaire et de l’autre les tasks qui sont des actions individuels que l’on peut rassembler dans des rôles.

Mais comment savoir quelles tâches sont jouées sur quelles machines cibles ?

C’est là qu’intervient le playbook. Il permet de coordonner les deux bulles l’inventaire et les rôles/tasks.

Il s’ait donc ni plus ni moins d’un fichier qui va dire quels groupes ou machines reçoivent quelles actions ? et bien sûr tout cela en format yaml comme la plupart des fichiers dans ansible.

Le playbook minimum resemble à ceci en terme de contenu :

- name: Mon Playbook !!
  hosts: all
  tasks:
  - name: je debug
    debug:
      msg: "{{ var1 }}"

Découvrons un peu plus ce que l’on a écrit :

  • le nom du playbook « mon premier playbook »
  • son périmètre ou ses serveurs cibles, donc notre cas le groupe all (c’est à dire toutes les machines)
  • un bloc permettant de décrire les tasks
  • le nom de la première tâche « je debug »
  • l’appel à un module, en l’occurence « debug »
  • les paramètres passés à ce module « msg » (pour indiquer un message composé d’une variable au format jinja).

On est pas mal pour débuter avec notre playbook.

A côté il ne faut pas oublier d’avoir un inventaire, comme par exemple :

all:
  hosts:
    172.17.0.2:

Comprendre la ligne de commande ansible-playbook ?

Comment lancer le playbook ? Comprendre est peut être un bien grand mot mais au moins savoir un peu ce que l’on fait et ce que l’on peut faire avec ansible et ses playbooks.

Dans ce qui est dessous nous allons parler du binaire ansible-playbook.

Voyons déjà une ligne de commande minimaliste pour lancer ce fichier :

ansible-playbook -i inventory.yml playbook.yml

Très simple non ? bon généralement il y aura souvent un peu plus d’options que cela dans votre terminal.

Voici donc quelques options plus ou moins connues (mais même les moins connues sont intéressantes… le diable se cache dans les détails).

	* -l : réduire le run à certaines machines ou certains groupes de votre inventaire
	* -u : spécifier un user particulier utilisé sur la où les machines distantes
	* -b : become est équivalent à taper sudo devant vos commandes pour élever vos privilèges
	* -k : pour recevoir la demande de saisie du mot de passe pour la connexion ssh
	* -K : le password pour l'élévation de privilège liée au sudo (become)
	* -C : check c'est le dry run... très utile quand on ne connait pas la fréquence des runs ansible
	* -D : diff c'est très utile pour avoir les différences et les modifications de ce que va faire ansible
	* --ask-vault : permet de saisir un password pour déchiffrer les secrets que vous aurez chiffré avec vault
	* --syntax-check : vérfier la syntax
	* --vault-password-file : passer le vault password par un fichier
	* -e : surcharger n'importe quelle variable
	* -f : forks, nombre de parallélisation
	* -t : filtrer sur les tags (--skip-tags)
	* --flush-cache : éviter l'utilisation du cache
	* --step : une tâche à la fois (confirmation via prompt)
	* --start-at-task : commencer à une tâche spécifiquement
	* --list-tags : lister tous les tags rencontrés
	* --list-tasks : liste les tâches qui vont être exécutées

Là on y voit plus clair.

Et maintenant commençons à jouer avec les fichiers grâce au module file.

Débuter avec le module FILE

Créer un fichier, un répertoire, leur affecter des droits, des propriétaires… ce sont des commandes courrantes en linux et généralement on apprend cela en débutant : mkdir, touch, chown, chmod, ln…. Et bien pour ansible c’est un peu pareil.

Le module file peut prendre différents paramètres :

* attribute : définitions des paramètres particuliers d'un fichier : immutabilité etc... cf https://fr.wikipedia.org/wiki/Chattr
* force : pour les liens symboliques (créer même si le fichier source existe pas, la destination existe)
* group/owner : le propriétaire et le groupe du fichier ou du répertoire
* mode : les permissions sous les deux formats : "0755" ou "u=rwx,g=rx,o=rx"
* path : la localisation du fichier ou des répertoires
* recurse : création du chemin intermédiaire si n'existe pas (yes/no), attention cela est valable uniquement pour les répertoires
* src : pour les liens (hard ou symbolique)
* state : le type absent / directory / file / hard / link / touch
		touch > créé le fichier vide
		file > vérifie l'existence et les caractéristiques

Et donc nous voici prêt à écrire notre première task. Par exemple pour créer un répertoire :

- name: créer un répertoire
  file:
    path: /tmp/xavki/
    state: directory
    owner: root

Ici ansible va créer le répertoire xavki dans /tmp car le state est de type directory. Et le propriétaire de ce répertoire sera l’utilistaur root.

Avec l’utilisation du mode récursif :

  - name: création du répertoire /tmp/xavki
    file:
      path: /tmp/xavki/1/2/3/4
      recurse: yes
      state: directory
      owner: root
      group: root
      mode: 0755

Ou encore simplement créer un fichier vide avec touch :

  - name: création du répertoire /tmp/xavki
    file:
      path: /tmp/xavki/1/2/3/4/fichier.txt
      state: touch
      owner: root
      group: root
      mode: 0755

Ou encore sous forme de lien :

  - name: création du répertoire /tmp/xavki
    file:
      src: /tmp/xavki/1/2/3/4/
      dest: /tmp/symlink
      state: link  #hard
      owner: root
      group: root
      mode: 0755

Et enfin pour supprimer des fichiers ou des répertoires, il suffit d’utiliser le state absent très courrant dans les modules ansible.

  - name: dir sans idempotence
    file:
      path: /tmp/xavki.txt
      state: absent

Voilà j’espère que cette prise en main de ansible et que vous pouvez débuter en toute tranquilité à votre rythme. Je vous rappelle que vous pouvez venir découvrir plus de 900 vidéos sur la chaine xavki avec de nombreuses autres thématiques autour du devops bien sûr. Et retrouvez tous les fichiers de ces présentations sur le dépôt dédié à ansible.

Raspberry et Ansible : gestion des clefs SSH et suppression du user par défaut

Précédemment, nous avions découvert comment créer nos users avec ansible. Mais nous avons quelques lacunes avec cette gestion minimaliste des users. En effet, d’une part la connexion ne se fait que par mot de passe (ce n’est pas pratique et très sécurisé). D’autre part, l’existence du user pi est une vraie faille de sécurité. Toute installation d’un raspbian dispose systématiquement de ce user et bien sûr avec le même mot de passe (pratique mais pas sécurisé tout ça).

Création de nos users

Pour cela nous allons industrialiser tout cela avec Ansible. Pour appel, nous avions nos mots de passe stockés sous les varaiables de group_vars/all/all.yml avec ceci :

users_password_xavki: "{{ vault_users_password_xavki }}"
users_password_xavier: "{{ vault_users_password_xavier }}"

Et ces variables faisaient appel à un fichier vault situé au même endroit dans group_vars/all/vault.yml mais chiffré par un mot de passe :

$ANSIBLE_VAULT;1.1;AES256
613362633865666266653364373533323263663137623536316431313933653331336239623437613232343965386434353761386436333663303533653862350a333939363463653663656563643966653035353666623165323434613434616334313163616663643763396635313636323666356437393665366339636561650a623032376634633634353132343539613962306132373230343232306234346664336231343866326532363930303566313262376264383231306664386462303461646432646433386535333432643337613138653365396132386261373362366463646564373830656265613239366334636361336462313039323064666432646161326463666635366663313539303136313965623430386435303466386634663161393438363134376138393137373033323565353065383465356638

Maintenant créons un répertoire dédié au stockage de nos clefs publiques (PUBLIQUES j’insiste) : files. Il s’agit des clefs que nous allons diffuser et installer dans la home de nos users. S’agissant de clefs publiques, un chiffrement n’est pas nécessaire, seule la clef privée est d’une importance capitale.

Maintenant modifions notre playbook de création des users :

- name: deploy users
  hosts: all
  become: yes
  vars:
    users_admin:
    - { name: "xavki", password: "{{ vault_users_password_xavki }}" }
    users_no_admin:
    - { name: "xavier", password: "{{ vault_users_password_xavier }}" }
  tasks:
  - name: create group admin
    group:
      name: "admin"
      state: present

  - name: create admin user accounts
    user:
      name: "{{ item.name }}"
      password: "{{ item.password | password_hash('sha512')}}"
      groups: "admin"
    with_items:
    - "{{ users_admin }}"
    no_log: true
 
  - name: create standard user accounts
    user:
      name: "{{ item.name }}"
      password: "{{ item.password | password_hash('sha512')}}"
    with_items:
    - "{{ users_no_admin }}"
    no_log: true

  - name: add authorized keys
    authorized_key:
      user: "{{ item.name }}"
      key: "{{ lookup('file', 'files/'+ item.name + '.key.pub') }}"
    with_items: 
    - "{{ users_admin }}"
    - "{{ users_no_admin }}"
 
  - name: "Allow admin users to sudo without a password"
    lineinfile:
      dest: "/etc/sudoers"
      state: "present"
      regexp: "^%sudo"
      line: "%admin ALL=(ALL) NOPASSWD: ALL"

La tâche « add authorized keys » nous permet donc de récupérer ces clefs et de les placer par défaut dans la hoem de chaque users et selon la bonne pratique avec un répertoire « .ssh ».

Voici donc pour le déploiement de nos clefs correspondantes pour chaque utilisateurs.

Retrouvez les fichiers ici.

Suppression du user PI

Pour supprimer un utilisateur, la démarche est très simple, il suffit d’ajouter la tâches suivante :

- name: remove pi user
  user:
    name: "pi"
    state: absent

Profitons-en pour faire un peu d’organisation et de bonnes pratiques ansible. Pour cela nous allons utliser le principe de rôle. Un rôle est une sorte de module/librairie/fonction qui peut être appelé autant que nécessaire.

Pour cela nous allon créer un répertoire « roles » à la racine de notre ansible et taper la commande :

mkdir roles && cd roles
ansible-galaxy init users

Cette commande créée toute la structure nécessaire pour créer le contenu d’un rôle. A l’heure actuelle ce qui nous intéresse c’est uniquement le répertoire task et son fichier main.yml. C’ets la première chose qui est appelé quand on lance un rôle. Donc dans ce fichier nous recopions le conteneur de notre users.yml.

Ensuite il nous faut modifier notre playbook pour appeler le rôle et non plus les tâches une à une :

- name: deploy users
  become: yes
  hosts: all
  roles:
  - users

Et voilà le tour est joué. il ne reste plus qu’à jouer le playbook :

ansible-playbook -i list_servers.yml -u xavki users.yml

Retrouvez les fichiers ici.

Ansible – Comment installer la stack haproxy, consul et consult template de manière orchestrée ?

Ansible est pratiquement inévitable à l’heure actuelle. Il faut dire que cet orchestrateur est surement l’un des plus simples à utiliser et à apprendre.

Pour apprendre à l’utiliser, je vous propose d’installer une stack très utile :

  • haproxy : reverse-proxy et load-balancer très réputé
  • consul : la registry de service dont je vous ai déjà parlé sur le blog et la chaine youtube
  • consul-template : un binaire qui va permettre de modifier automatiquement la conf de haproxy en cas de modification dans la composition et l’état des services de consul

En 9 courtes vidéos, vous allez pouvoir faire coup double :

  1. Installation de haproxy : rôle, apt
  2. Installation de consul : unarchive, wget
  3. Création du user consul : user, group
  4. Structurer ses tâches : include task
  5. Gestion des configurations : templates
  6. Création d’une application de test
  7. Mise en place d’un service système D
  8. Installation du binaire consul-template
  9. Finalisation de consul-template : utilisation de blockinfile, du jinja dans du jinja

Grâce à ces vidéos, vous allez pouvoir rendre dynamique votre configuration haproxy et réaliser un haproxy reload si nécessaire.

[Ansible] – comment utiliser les gather_facts et tenir à jour un début de cmdb ?

Ansible regorge d’astuces qu’il faut engranger pour pouvoir aller plus loin et ne pas simplement installer des paquets sur des machines distantes. Par exemple, nous allons voir aujourd’hui une valorisation des gather_facts de ansible. Sur puppet, je crois que la même chose est réalisable avec facter.

Gather_facts c’est la possibilité d’utiliser des variables d’environnement ansible. Ce sont principalement des caractéristiques de la machine (interface, os…). Ces données vous vous en doutez seraient bien utiles dans une cmdb (centralisation d’informations de votre parc de machine).

Pour commencer cette démos nous allons commencer par créer quelques machines à l’aide de mon script maison (vous pouvez aussi le découvrir en vidéo désormais). Dans mon cas, j’ai 2 vm debian qui fonctionnent avec du ssh (merci docker).

└─ $ ▶ ./deploy-centre-sans-proxy-v2.sh --infos
#### Récap des conteneurs de tests ####
=> /oki-deb-vmparc3 - 172.17.0.3 - Utilisteur : oki / mdp:password
=> /oki-deb-vmparc2 - 172.17.0.2 - Utilisteur : oki / mdp:password

Maintenant nous allons configurer notre inventory hosts.yml très simplement :

all:  hosts:    172.17.0.2:    172.17.0.3:

Objectif ?

Notre souhait c’est d’utiliser les gather_facts et faire en sorte que ansible centralise tout cela sous forme de fichiers.html. Ainsi nous pourrons faire de la datavisualisation avec un apache installé localement (attention c’est pour la démo, c’est très moyen d’installer un serveur web sur une machine qui a la main sur tout votre parc).

Enfin, grâce à notre apache on pourra afficher dans un navigateur web. Et vous verrez ce qui compte c’est le principe car derrière c’est assez facile d’avoir des idées pour compléter tout cela pour avoir un bon début de cmdb.

Comment lister les gather_facts à notre dispostion ?

Les gather_facts sont accessibles via le module setup de ansible. Donc pour consulter toutes ces variables voici la commande à réaliser :

ansible all - i hosts.yml -u oki -m "setup"

La liste est assez longue et c’est plutôt une bonne nouvelle.

Faisons nos courses et retenons les variables :

  • inventory_hostname
  • ansible_default_ipv4.alias
  • ansible_architecture
  • ansible_distribution
  • ansible_distribution_version

Et on met tout cela en forme dans un template jinja2 pour générer à terme un fichier html :

<h1>Machine : {{ inventory_hostname }}</h1>
<ul>
<li>Interfaces : {{ ansible_default_ipv4.alias }}</li>
<li>Architecture : {{ ansible_architecture }}</li>
<li>OS : {{ ansible_distribution }}</li>
<li>Version : {{ ansible_distribution_version }}</li>
</ul>

Maintenant le playbook !

Voici le contenu de notre répertoire :

.
├── hosts.yml
├── playbook-cmdb.yml
└── templates
└── listing_ipv4.html.j2

Editons donc le playbook-cmdb.yml de la manière suivante :

---
- name: "[IP v4 listing]"
  hosts: all
  gather_facts: yes
  tasks:
    - name: "[IP v4 listing] - generate html"
      template:
        src: listing_ipv4.html.j2
        dest: "/var/www/html/{{ inventory_hostname }}.html"
      connection: local

Première chose on spécifie l’utilisation des gather_acts. Ensuite on utilise le module template pour faire appel au fichier jinja2. Puis on indique comme destination le répertoire de notre apache /var/www/html/ (de la machine master host). C’est aussi pour cela que nous spécifions connection : local.

Maintenant lançons la commande ansible-playbook :

ansible-playbook -i hosts.yml -u oki playbook-cmdb.yml

Et bingo :

PLAY [[IP v4 listing]] *******************************

TASK [Gathering Facts] *******************************
ok: [172.17.0.3]
ok: [172.17.0.2]

TASK [[IP v4 listing] - generate html] ***************
changed: [172.17.0.3]
changed: [172.17.0.2]

PLAY RECAP *******************************************
172.17.0.2 : ok=2 changed=1 unreachable=0 failed=0 
172.17.0.3 : ok=2 changed=1 unreachable=0 failed=0

Supprimons l’index de notre apache local : rm -f /var/www/html/index.html

Remarque : j’ai confié les droits de ce réprtoire à oki pour cet exercice

Et voici le résultat, une fiche par machine :

fiche-arbre

Puis la fiche de la machine :

fiche-cmdb.png

C’est bon tout ça non ? On comprend mieux l’intérêt des facts avec ce genre d’outils d’orchestration. Et ne vous inquiétez pas vous pouvez aisément pousser les fichier vers une autre machine qui portera un service apache de manière isolée.

Ansible – installer un applicatif (ex. WordPress)

Poursuivons notre découverte de ansible avec l’installation d’un applicatif en l’occurence wordpress. Chaque applicatif possède toutefois des manières différentes de s’installer, il s’agit juste là de prendre un exemple pour se faire la main.

Vous pouvez retrouver cet article en vidéo :

 

La première chose à faire dans notre rôle est de vérifier si wordpress existe déjà :

- name: "[WORDPRESS] - check if exist"
  stat: 
    path: "/var/www/html/wordpress/"
  register: check_wordpress

Avec stat, nous vérifions si le répertoire wordpress est déjà là et nous alimentons l’état de ce répertoire dans une variable avec register. Notre variable s’appelle donc check_wordpress.

Utilisons maintenant cette variable pour conditonner à celle-ci le téléchargement de l’archive tar.gz.

- name: "[WORDPRESS] - download tar.gz"
  unarchive:
    src: "{{ wordpress_source }}"
    dest: "/var/www/html/"
    remote_src: yes
  when: check_wordpress.stat.exists == False

Avec unarchive, nous procédons au téléchargement en ligne car la variable wordpress_source est une url de téléchargement. Par ailleurs pour pouvoir télécharger une url on utilise remote_src. Et enfin notre condition avec le when.

Après nous procédons à la suppression de index.html que notre apache à installer par défaut.

- name: "[WORDPRESS] - index.html"
  file: 
    path: "/var/www/html/index.html"
    state: absent

Pour cela nous utilisons le module file avec un state à absent.

A la première installation, le fichier wp-config.php se nomme wp-config-sample.php. Nous vérifions donc si nous sommes dans ce cas.

- name: "[WORDPRESS] - exist wp-config-sample.php"
  stat:
    path: "/var/www/html/wordpress/wp-config-sample.php"
  register: check_sample

Nous procédons comme pour le répertoire wordpress en vérifiant la présence de wp-config-sample.php.

Si c’est le cas nous le renommons.

- name: "[WORDPRESS] - rename wp-config-sample.php"
  command: mv /var/www/html/wordpress/wp-config-sample.php /var/www/html/wordpress/wp-config.php 
  when: check_sample.stat.exists == True

Enfin nous procédons à  la modification de plusieurs ligne dans ce même fichier pour configurer celui-ci en fonction de notre serveur base de données.

- name: "[WORDPRESS] - config wp-config.php"
  lineinfile:
    dest: "/var/www/html/wordpress/wp-config.php"
    regexp: "{{ item.search }}"
    line: "{{ item.new }}"
    backrefs: yes
  with_items:
    - {'search': '^(.*)database_name_here(.*)$', 'new': '{{ mysql_db }}'}
    - {'search': '^(.*)username_here(.*)$', 'new': '{{ mysql_user }}'}
    - {'search': '^(.*)password_here(.*)$', 'new': '{{ mysql_password }}'}

Pour chaque ligne définie dans le with_items, nous recherchons un élément (ex : database_name_here) et compturons les éléments présents avant et après. Ensuite nous remplaçons cette ligne par une nouvelle ligne contenant le premier élément capturé puis notre variable puis le deuxième élément capturé.

Et voilà pour notre installation de wordpress. Vous pouvez retrouver tout le code sur mon github.

Ansible – Comment créer un user et une base mysql ?

Cela fait quelques temps que je n’ai pas posté d’article ansible. La chaîne youtube prend pas mal de temps mais j’aime beaucoup le blog. Le mode écrit est tout de même bien sympa.

Mon orchestrateur préféré est une vraie mine d’or pour ce qui est de gérer les petites actions du quotidien et c’est aussi le cas en matière de base de données. De nombreuses actions sont natives sur ansible (moyennant d’avoir installé le module python qui va bien pour pouvoir le faire).

Si vous souhaitez la version vidéo de cette article, la voici :

Sinon pour la version écrite. Commençons par définir quelques variables dans notre rôle mysql.

Commençons par définir quelques variables dans notre répertoire defaults de notre rôle mysql.

mysql_packages:
  - mysql-server
  - python-mysqldb
mysql_db: "wordpress"
mysql_user: "user1"
mysql_password: "password"

On y retrouve la liste des paquets à installer à savoir mysql-server et python-mysqldb. C’est ce dernier qui permet à ansible d’interagir avec le moteur mysql.

Ensuite éditons notre fichier main.yml dans le répertoire tasks.

- name: "[MYSQL] - update cache"
  apt:
    update_cache: yes

- name: "[MYSQL] - install"
  apt:
    name: "{{ mysql_packages }}"
    state: latest

- name: "[MYSQL] - start mysql"
  service:
    name: "mysqld"
    state: started
    enabled: yes

Jusque là rien de neuf si ce n’est que la dernière version de ansible nous demande de ne plus utiliser le with_items. En effet, nous pouvons désormais passer directement un tableau. On installe les paquets et active le démarrage de mysql au lancement de notre machine.

Ensuite rentrons dans l’administration mysql.

- name: "[MYSQL] - create database"
  mysql_db:
    name: "{{ mysql_db}}"
  become: yes

Là on vient de créer la base de données dont le nom a été défini dans nos variables par défauts (et donc que l’on peut écraser facilement par les variables d’inventory).

- name: "[MYSQL] - create user"
  mysql_user:
    name: "{{ mysql_user }}"
    password: "{{ mysql_password }}"
    priv: "*.*:ALL"
    host: "127.0.0.1"
  become: yes

Là on vient de créer un user et de lui donner tous les droits sur les bases de notre moteur (nous aurions pu limiter tout ceci bien sûr).

Voici donc pour la découverte du module mysql de ansible. Mais sachez qu’il existe la même chose pour :

  • influxdb
  • mongodb
  • postgres
  • proxysql

En savoir plus sur la documentation officielle de ansible à ce sujet.

Ansible – installer un serveur LAMP automatiquement

Hello la team ! j’espère que vous allez bien. A priori, c’est kool vous êtes nombreux à revenir sur le blog vu les statistiques.

La chaîne youtube marche bien aussi et là encore c’est grâce à vous.

Aujourd’hui, je vous propose de revenir sur ansible avec une vidéo qui vous fait découvrir assez simplement comment installer un serveur LAMP de manière orchestrée. Cela est assez simple et tient sur quelques lignes.

Voici la vidéo :

N’oubliez pas que vous pouvez vous abonner à la chaine ou aussi mettre des petits pouces bleus pour m’encourager.

Qu’est-ce que l’on fait dans cette vidéo ?

1- Création du squelette d’un rôle ansible

A la racine, c’est à dire là on l’on trouve notre playbook et notre fichier d’inventory, on créé un répertoire rôles. Comme son nom l’indique c’est ici que nous stockerons les rôles développés ou récupérés de Galaxy. Pour créer la structure d’un rôle lançons :

ansible-galaxy init wordpress

Il ne nous reste plus qu’à éditer nos fichiers et en particulier le fichier mai.yml situé dans le répertoire tasks. C’est la clef d’entrée dans notre rôle.

2- Edition de main.yml dans tasks

La première chose à faire c’est de commencer par mettre à jour le cache de apt. C’est la moindre des choses avant d’installer des paquets.

- name: "[WORDPRESS] - update cache"
  apt:
    update_cache: yes
  become: yes

Le become à yes permet de réaliser une élévation de privilèges comme pour faire un sudo. Et nous utilisons le module apt.

- name: "[WORDPRESS] - install LAMP"
  apt:
   name: "{{ item }}"
   state: latest
  become: yes
  with_items: 
    - apache2
    - mysql-server
    - php7.0-common
    - php7.0-mysql
    - libapache2-mod-php7.0
    - python-mysqldb
    - wget

Vous pouvez le voir nous allons plus loin qu’un simple LAMP. Cela nous permettra d’aller plus loin par la suite dans notre installation de notre wordpress.

Puis lançons le démarrage de nos services :

- name: "[WORDPRESS] - start apache2 mysql"
  service:
    name: "{{ item }}"
    state: started
    enabled: yes
  become: yes
  with_items:
    - apache2
    - mysqld

Ici nous avons utilisé le module service qui nous permet d’intervenir sur systemd.

Vous pouvez aller plus loin en consultant les articles et vidéos spécifiques à ansible sur cette page.

Ansible – les différents niveaux de variables

Ansible est un orchestrateur simple d’utilisation dès lors que l’on en retient les concepts de base. Parmi ces concepts, la hiérarchie des variables fait partie des essentiels.

Je vous propose d’y revenir en vidéo.

[youtube https://www.youtube.com/watch?v=_8Az2egUKwY&w=700&h=500]

D’ailleurs n’hésitez pas à vous abonner à la chaine xavki.

Au total, Ansible possède plus de 20 manières pour définir des variables. On peut les classer par ordre hiérarchique en fonction de s’imposer les unes aux autres. Je ne reviendrais pas sur l’ensemble des 20 variables mais sur les plus courantes. Néanmoins voici la liste de ces variables ordonnée hierarchiquement :

ansible-v2-and-beyond-ansible-nyc-meetup-19-638

Les principales qui me semblent importantes à retenir sont :

  • 1. extra vars : passable à partir de la command line
  • 2. task vars : variable de tâche (dans le playbook)
  • 3. block vars : la variable de block
  • 4. role vars : dans le répertoire /vars d’un rôle
  • 5. set_facts : définie dans les set_facts
  • 6. playbook vars : dans le fichier playbook
  • 7. host_vars : précisée dans les fichiers individuels des machines de l’inventaire
  • 8. inventory vars : dans l’inventaire (par exemple hosts.yml)
  • 9. group_vars : dans les fichiers de groupes de l’inventaire
  • 10. role defaults vars : la variable par défaut

Un rôle bien rédigé doit être très largement paramétrable : OS, paquets, version, user, passwords… Ainsi un bon rôle possède souvent un fichiers de variables par défaut (répertoire defaults du rôle) très fourni. Cela vous permet de personnaliser très largement l’application de ce rôle.

En outre, un bon rôle doit aussi avoir l’intégratilité de ses variables définies dans son README, en tout cas au moins toutes les variables par défaut. Elles doivent alors être documentées sur la manière de les définir (liste, string, booléens…).

Par ailleurs, vous pouvez utiliser le module debug pour afficher le contenu des variables à n’importe quel moment de votre playbook.

- name: "affichage contenu de var1"
  debug:
    msg: "Voici le contenur de var1 => {{ var1 }}"

Il faut aussi savoir que ansible n’aime pas traiter les variables vides.

Enfin ansible possède des variables particulières propres à son fonctionnement, on parle de facts (version OS, etc…). Celles-ci sont utilisables à tout moment. Pour les connaître utilisez le module setup :

 ansible all -m setup

Vous pouvez également créer vos propres facts.

Ansible – prise en main de checksum, set_facts, register et block

Ansible c’est un peu le fil rouge du moment. Certains l’ont bien vu avec le lancement de la chaine youtube xavki. L’idée c’est de vous montrer comment j’ai appris à utiliser et développer avec ansible.

La vidéo ci-dessous présente ce que nous allons découvrir dans l’article.

[youtube https://www.youtube.com/watch?v=YqKAXnmAetY&w=700&h=300]

L’objectif du jour, c’est de charger un fichier avec l’aide d’ansible, de le modifier et surtout faire en sorte qu’une fois ces opérations réalisées nous gérions la réentrance (ou idempotence). C’est à dire que si nous relançons aussitôt le playbook ansible aucun opération ne soit réalisée.

Or sans vérification du contenu du fichier, le fichier est systématiquement rechargé. Pourquoi ?

Tout simplement car ansible va contrôler l’état du fichier présent sur la cible avec celui présent sur la source. Et malheureusement ansible ne sait pas prendre en compte les diverses opérations qu’il aura réalisé sur le fichier après son chargement. Donc il est systématiquement différent du fichier source.

Comment vérifier l’état du fichier ? de cette manière peut être (si vous avez mieux je suis preneur.

---
- name: "[XAVKI]"
  hosts: all
  vars:
    - check5: "2789ebeb61ab3b1985e9f6df9256d8a1" 
  tasks:

      - name: "[XAVKI] - check md5"
        stat:
          path: /tmp/xavki.txt
          get_checksum: yes
          checksum_algorithm: md5
        register: sum5

      - set_fact:
          data: "0"
        when: sum5.stat.checksum is not defined

      - set_fact:
          data: "{{ sum5.stat.checksum }}"
        when: sum5.stat.checksum is defined

      - name: "[XAVKI] - Bloc"
        block:
        - name: "[XAVKI] - copie du fichier" 
          copy:
            src: ./monfichier.txt
            dest: /tmp/xavki.txt

        - name: "[XAVKI] - add line"
          lineinfile:
            path: /tmp/xavki.txt
            line: "ajout d'une ligne" 

        when: data != check5

Voici le cheminement :

  • tout d’abord on calcul et on place dans une variable le md5 du fichier dans l’état final souhaité (check5 en l’occurence), c’est notre référence
  • à l’aide du module stat on place avec register la valeur du fichier déjà présent dans une variable (sum5)
  • nous devons traiter alors 2 cas de figures : la variable est définie car le fichier existe ou il n’existe pas et la variable n’est pas définie
  • si la variable existe on attribue à la variable data la valeur du sum5 sinon on lui donne la valeur « 0 »
  • enfin on encapsule dans un bloc avec le module block les actions de chargement de fichier et de modification si data ne vaut pas la valeur finale souhaitée (avec when)

[Ansible] : comment installer et configurer logrotate ? et trouver un rôle adapté

Aujourd’hui, après l’installation ntp (client et serveur), je vous propose l’installation et la configuration de logrotate. Il s’agit donc d’une action de niveau plutôt débutant à réaliser avec ansible. En effet, logrotate est un service facile à mettre en place (souvent déjà installé et bien pris en compte par d’autres services) et surtout car ansible-galaxy regorge de rôles dans ce domaine.

La plus grosse difficulté réside dans le choix du rôle en essayant de trouver celui qui corrrespond le plus à notre besoin. Dans notre cas, on cherche un rôle qui installe logrotate si celui-ci n’est pas déjà installé et qu’il permettent de réaliser la conf par défaut de logrotate et au cas par cas pour différents services que l’on souhaite ajouter.

ansible_logrotate

Avant de commencer, je vous rappelle que j’utilise un réseau de conteneurs docker pour mettre en place mes tests de rôles ansible. Pour en savoir plus c’est ici et c’est super simple.

1. Logrotate

C’est un service basique dans les différents OS linux de base. Logrotate est souvent déjà installé comme c’est le cas sur Debian et sur Redhat.

Sa configuration par défaut se situe dans /etc/logrotate.conf et se résume souvent à :

weekly
su root syslog
rotate 4
create
dateext

# packages drop log rotation information into this directory
include /etc/logrotate.d

# no packages own wtmp, or btmp -- we'll rotate them here
/var/log/wtmp {
    missingok
    monthly
    create 0664 root utmp
    rotate 1
}

/var/log/btmp {
    missingok
    monthly
    create 0660 root utmp
    rotate 1
}

Que dire ?

  • weekly : la fréquence de rotation (daily, mais aussi d’autres formats peuvent être utilisés)
  • su root syslog : avec quel user et quel groupe est réalisé l’archivage (important pour les permissions par la suite)
  • rotate 4 : nombre de fichiers de logs conservés
  • create : création d’un nouveau fichier de log après la rotation

De très nombreuses options sont possibles. Elles sont consultables sur la page man de logrotate : mail, action réalisée après rotation, compression…

Par ailleurs, il faut savoir que les services les plus répandus sur Linux ajoutent leur propre configuration dans /etc/logrotate.d/ (apache2, nginx, varnish, postgres…). Le travail est donc généralement simplifié,  ce qui permet de ne pas trop s’inquiéter si vous cherchez un rôle ansible.

2. Le rôle ansible logrotate

Nsou avons vu à peu près ce que nous souhaitions  trouver comme rôle. Après en avoir feuilleté une petite dizaine, j’ai retenu celui-ci : arillso.logrotate

Il réalise ce que j’ai listé ci-dessus (configuration par défaut et ajout possible). Pour toucher à la configuration par défaut il suffit simplement de compléter la variable logrotate_options avec un tableau du type : [ ‘weekly’, ‘su root syslog’, ‘rotate 4’, ‘create’ ].

Sa struture est la suivante :

└─ $ ▶ tree
.
├── defaults
│   └── main.yml
├── handlers
│   └── main.yml
├── LICENSE
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
│   ├── application.j2
│   └── logrotate.conf.j2
└── vars
    ├── CentOS.yml
    ├── Debian.yml
    └── defaults.yml

Il s’appuie aussi sur deux fichiers jinja2 un pour la configuration par défaut et un autre pour gérer des configurations par application.

Ainsi pour spécifier une conf particulière pour une machine notre inventory ressemblerait à ceci :

all:
  hosts:
    172.17.0.2:
      logrotate_options: [ 'daily', '', 'rotate 4', 'create' ]
    172.17.0.3:
    172.17.0.4:
    172.17.0.5:

Ainsi on a part défaut : [‘weekly’, ‘su root syslog’, ‘rotate 4’, ‘create’]
et sinon autre chose comme pour la 172.17.0.2.

Mes tests se déroulent sur 4 machines comme pour le test du rôle ntp : 2 debian et 2 redhat (centos).

Mon inventory est celui indiqué ci-dessus et mon playbook contient simplement :

---
- name: Lancement roles
  hosts: all
  remote_user: oki
  become: yes

  roles:
  - arillso.logrotate

Lançons tout ceci :

└─ $ ▶ ansible-playbook -i hosts.yml --user=oki playbook-logrotate.yml 

PLAY [Lancement roles] **************************************************************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************************************************************
ok: [172.17.0.2]
ok: [172.17.0.3]
ok: [172.17.0.4]
ok: [172.17.0.5]

TASK [arillso.logrotate : add OS specific variables] ********************************************************************************************************************************************
ok: [172.17.0.2] => (item=/home/oki/autoform_ansible/roles/arillso.logrotate/vars/Debian.yml)
ok: [172.17.0.3] => (item=/home/oki/autoform_ansible/roles/arillso.logrotate/vars/Debian.yml)
ok: [172.17.0.4] => (item=/home/oki/autoform_ansible/roles/arillso.logrotate/vars/CentOS.yml)
ok: [172.17.0.5] => (item=/home/oki/autoform_ansible/roles/arillso.logrotate/vars/CentOS.yml)

TASK [arillso.logrotate : install logrotate] ****************************************************************************************************************************************************
ok: [172.17.0.5]
ok: [172.17.0.4]
ok: [172.17.0.3]
ok: [172.17.0.2]

TASK [arillso.logrotate : Create logrotate configuration file] **********************************************************************************************************************************
ok: [172.17.0.2]
ok: [172.17.0.4]
ok: [172.17.0.5]
ok: [172.17.0.3]

TASK [arillso.logrotate : Create logrotate application configuration files] *********************************************************************************************************************

PLAY RECAP **************************************************************************************************************************************************************************************
172.17.0.2                 : ok=0    changed=4   unreachable=0    failed=0   
172.17.0.3                 : ok=0    changed=4    unreachable=0    failed=0   
172.17.0.4                 : ok=0    changed=4    unreachable=0    failed=0   
172.17.0.5                 : ok=0    changed=4    unreachable=0    failed=0   

Une fos de plus Ansible nous propose un jolie récapitulatif. On voit que le playbook est passé sur toutes les machines. On voit également qu’il a bien utilisé la conf debian et la conf redhat quand cela était nécessaire.

Bref que du bonheur.

Et sur la machine ayant une conf spécifique ?

root@69a5076d127d:~# cat /etc/logrotate.conf 
# Ansible managed

# see "man logrotate" for details
weekly

rotate 4
create

Bingo pas de souci. Et vous quel est votre expérience avec ce genre de rôle sur ansible.

[Ansible] : lister les variables de rôles non documentées dans le README.md

Les rôles de ansible sont plus ou moins faciles à utiliser. Un des principaux facteurs limitants, c’est la documentation du README. Du coup j’ai créé un petit script pour lister les variables de vos rôles et ensuite vérifier si ces variables sont bien documentées dans le README.md (si vous avez d’autres idées n’hésitez pas).

#!/usr/bin/python3


# coding: utf8



import re
import os

def list_variables (pattern, dir):
    
  variables=[]
    
  r = re.compile(pattern)
    
  for parent, dnames, fnames in os.walk(dir):
 
       for fname in fnames:

            filename = os.path.join(parent, fname)

            if os.path.isfile(filename):

                with open(filename,encoding="latin-1") as f:

                    for line in f:

                        if r.search(line):

                          reg=r"pattern"

                          for item in re.findall(r''+pattern+'',line):

                            if item not in variables:

                              variables.append(item)

  return variables



# lister les roles

def list_roles (dir):

        roles=[]

        regexp = re.compile(r'.*\/roles\/[a-z0-1_-]+$')

        for dirname, dirnames, filenames in os.walk('.'):

                for subdirname in dirnames:

                        rep=os.path.join(dirname, subdirname)

                        if regexp.search(rep):

                                roles.append(rep)

        return roles



# avec la fonction grep on liste toutes les variables contenant un _


for role in list_roles("."):

    r = re.compile('[.]?roles/(.*)')

    match = r.search(role)

    print("")

    print("#################################################################")

    print("         Analyse du role : "+ match.group(1))

    print("#################################################################")
    compteur=0

    list_var=list_variables("\{\{\s?([a-z]+[_]+[a-z_]+)\s?\}\}",role)

    for variable in list_var:

        compteur+=1

        with open(roles+"/README.md",encoding="latin-1") as f:

            concaten=""

            for line in f:

                concaten+=line

            if not re.search(variable,concaten):

                print("Variable à éditer :"+variable)

    print(" = "+str(compteur)+" variable(s) analysé(es)")

Explications :

  •  une fonction qui liste les variables ansible :
    • on parcourt les directories (à l’aide du module OS de python)
    • et avec une regex on applique un pattern pour retrouver ce qui ressemble à une variable ({{ variable }}
    • enfin on met tout cela dans un tableau bien propre
  • une fonction qui liste les rôles :
    • idem on parcourt les directories et on cherche les répertoires contenus dans « roles »
    • à l’aide d’une regex on nettoie le nom du rôle
  • Pour finir on orchestre tout cela :
    • on boucle sur la liste des variables
    • on ouvre le README.md que l’on place dans une variable simple(on concatène les lignes)
    • et on y cherche la variable
    • si elle n’est pas dedans on affiche qu’elle est à éditer
    • et pour finir on affiche à la fin de chaque rôle le nombre de variables analysées.

Si vous voyez d’autres scripts de ce genre qui pourrait être utiles indiquez le en commentaire j’y jeterez un coup d’oeil.

[Ansible] : comment installer un client et un serveur ntp ?

Cet article fait suite à mon fil rouge sur ansible. L’idée c’est de vous relater quelques moments de mon autoformation dans ce domaine. Pour moi c’est aussi le moment de me pencher différement sur ce que je fais et de constituer mon bloc-note.

Avant de commencer, je ne vais pas revenir dessus dans le détail mais j’utilise un script sympa (enfin je trouve). Il permet de me créer un ensemble de conteneurs façon VM (debian et redhat depuis la dernière évolution). Ainsi j’ai une plateforme de développement et je peux simuler des lancements de playbook sur un parc de machines. Pour en savoir plus rendez-vous sur cet article qui présente le script et les grandes lignes.

Donc j’ai lancé deux fois le script et je possède pour l’exemple 4 machines (2 debian stretch et 2 centos7).

Et voilà. Maintenant comment je peux faire pour avoir 1 serveur ntp et 3 serveurs clients ? L’idée c’est de faire en sorte qu’un seul serveur soit exposé et soit utilisé pour contacter les ntp externes.

ansible-ntp

Ainsi on veut avoir le schéma :

ntp_externes >> ntp_serveur >> ntp_clients

1. Les principes des clients et du serveur (sans ansible)

Là encore on va aller à l’essentiel de la configuration. Vous trouverez facilement des articles clairs et courts comme celui-ci qui vous donnera ce qu’il faut pour installer un ntp.

D’un côté, sur le serveur ntp est installé ntp. Il doit au niveau de sa conf taper vers les serveurs externes et surtout accepter les requêtes des autres serveurs. Pour cela il faut supprimer simplement dans /etc/ntp.conf la ligne :

restrict default nomodify notrap nopeer noquery

Et ajouter la localisation ntp :

ntp_area: 'fr'

De l’autre côté pour les serveurs clients, il faut juste modifier les serveurs vers lesquels on fait la synchro dans /etc/ntp.conf. Les serveurs par défaut deviennent par exemple :

server 172.17.0.2 ibusrt

Pour vérifier le bon fonctionnement, il suffira de faire un ntpq -p et voir où les machines vont se synchroniser.

2. NTP avec ansible c’est facile

Bien sûr que c’est facile car ntp est un utilitaire de base des serveurs web ou autres. Ansible Galaxy vous propose donc de très bon rôles. Et j’aurais tendance à dire comme d’habitude c’est geerlingguy qui va vous le proposer avec geerlingguy.ntp.

Alors pour le mettre en place dans votre répertoire de rôles il suffit de faire :

ansible-galaxy install -p roles geerlingguy.ntp

et dedans on a ceci mais on ne va toucher à rien :

.
├── defaults
│   └── main.yml
├── handlers
│   └── main.yml
├── LICENSE
├── meta
│   └── main.yml
├── README.md
├── tasks
│   ├── clock-rhel-6.yml
│   └── main.yml
├── templates
│   ├── clock.j2
│   └── ntp.conf.j2
├── tests
│   ├── README.md
│   └── test.yml
└── vars
├── Archlinux.yml
├── Debian.yml
├── FreeBSD.yml
├── RedHat.yml
└── Suse.yml

Nous avons donc un rôle ntp qui ne demande qu’à tourner. Pour cela mettons à jour notre liste de hosts.yml de notre inventory :

all:
  children:
    ntp-client:
      hosts:
        172.17.0.3:
        172.17.0.4:
        172.17.0.5:
    ntp-server:
      hosts:
        172.17.0.2:

Vous voyez que j’ai entré les ip de mes conteneurs docker. Elles sont classées en 2 groupes ntp-server et ntp-client.

Maintenant je vais créer 2 group_vars (deux répertoires avec un fichier yaml chacun) :

.
├── group_vars
│   ├── ntp-client
│   │   └── ntp-client.yml
│   └── ntp-server
│       └── ntp-server.yml

Puis je complète les fichiers de la manière suivante (en prenant en compte ce que nous avons abordé dans le paragraphe 1.).

ntp-client.yml

ntp_manage_config: True
ntp_servers:
- "172.17.0.2 ibusrt"

ntp-server.yml

ntp_manage_config: True
ntp_restrict:
- "127.0.0.1" 
- "::1" 
- "-4 default kod notrap nomodify nopeer noquery" 
- "-6 default kod notrap nomodify nopeer noquery"

Et là c’est terrible car je vous dis « nous avons fait le plus dur » ! Car maintenant on lance le playbook :

ansible-playbook -i hosts.yml --user=oki playbook-ntp.yml

Et la bingo

PLAY RECAP ************************************************************************************
172.17.0.2 : ok=7 changed=0 unreachable=0 failed=0 
172.17.0.3 : ok=8 changed=0 unreachable=0 failed=0 
172.17.0.4 : ok=8 changed=0 unreachable=0 failed=0 
172.17.0.5 : ok=8 changed=0 unreachable=0 failed=0

Comme vous le voyez moi je n’ai pas de changement car j’ai fait tourner le playbook deux fois.

Maintenant rendez vous sur le serveur ntp :

[root@7dcab8fb52ed ~]# ntpq -p
remote refid st t when poll reach delay offset jitter
================================================================
dev.rudloff.pro 209.176.15.122 3 u 53 64 1 35.024 -29.279 8.299
clients0.arcani 131.188.3.223 2 u 53 64 1 41.120 -23.491 9.220
myminers.net 10.21.137.1 2 u 53 64 1 28.079 -27.255 13.304
*master.servme.f 213.251.128.249 2 u 53 64 1 22.207 -30.641 11.5

et sur le client :

[root@6c6c89bc23ac ~]# ntpq -p
remote refid st t when poll reach delay offset jitter
================================================
172.17.0.2 178.33.111.48 3 u 1 64 1 0.079 0.018 0.000

Nos avons donc bien installé un serveur ntp qui se met à jour sur des serveurs externes et des clients ntp qui se mettent à jour sur notre serveur maitre.

Si cet article vous a plus partagez-le et n’hésitez pas à vous abonner pour ne pas manquer les prochains articles.