I think debops developers are really good and I learn a lot of advanced stuff when I read their roles. I think they should be the reference for Ansible best practises. Here is 3 useful tricks that I did not know.
Je m'attaque aux API REST et en particulier celle de BitBucket pour y récupérer mes clés SSH que je déploie ensuite sur mes serveurs, le tout avec Ansible. Voilà un petit bout de code sur lequel j'ai passé une bonne soirée car autant c'est facile d'interroger l'API, autant ça l'est un peu moins de faire rentrer les bons champs dans des variables ;)
Pré requis:
Sur votre compte BitBucket
Settings > App passwords
Créez un password avec l'autorisation Account / read.
Allons-y (à mettre dans votre tasks/main.yml par exemple):
---
- name: GET authorized_keys from REST API
uri:
url: "{{ authorized_keys.api_url }}"
method: "{{ authorized_keys.api_method }}"
user: "{{ authorized_keys.api_user }}"
password: "{{ authorized_keys.api_password }}"
force_basic_auth: "{{ authorized_keys.api_force_basic }}"
register: authorized_keys__json_response
delegate_to: localhost
run_once: True
# Delegate to localhost and run 1 time because
# we don't need to query the API from each remote host
- name: Set Keys as a Var
set_fact:
authorized_keys__keys: "{{ authorized_keys__json_response.json | json_query('values[*].key') }}"
delegate_to: localhost
run_once: True
# Extract the fields 'key' in a variable
- name: Concatenate Keys
set_fact:
authorized_keys__keys_concatenate: "{{ authorized_keys__keys | join('\n') }}"
delegate_to: localhost
run_once: True
# Ansible authorized_keys module with 'exclusive' option requires all keys in one batch
# separated by a newline \n
La variable authorized_keys__keys contient la liste des clés publiques. La variable authorized_keys__keys_concatenate n'est pas une liste mais un champ contenant toutes les clés séparées par un retour à la ligne et elle est utile si vous souhaitez utiliser le module authorized_keys avec exclusive: yes car ce dernier ne marche pas bien avec une liste, il faut donc lui fournir 1 seul "batch".
Complétez ensuite vos variables, par exemple:
# group_vars/all/vars.yml
authorized_keys:
api_user: john
api_url: "https://api.bitbucket.org/2.0/users/john/ssh-keys"
api_method: GET
api_force_basic: yes
api_password: password créé dans l'interface bitbucket
Bien sûr en vrai on mettra le password dans un vault ;)
Et voilà, vous pouvez utiliser authorized_keys__keys ou authorized_keys__keys_concatenate pour la suite!
Article pense-bête. J'écris un petit rôle pour configurer firewalld, en gros je veux spécifier quelles interfaces et quels services il faut ajouter dans les zones. Mon idée c'est de définir tout ça dans un dictionnaire dans le playbook, et ensuite de récupérer les élements et sous-élements dans le rôle.
Mouais. C'est quand même loin d'être clair ou évident. Mais c'est le seul moyen que j'ai trouvé pour le moment. Si quelqu'un connaît une méthode plus propre/lisible je suis preneur.
L'informatique, comme tous les métiers, demande un savoir-faire, celui-ci vient avec l'expérience et la pratique. Mais s'il y a un autre point qui est important et souvent négligé, c'est de savoir refaire. Je vais expliquer.
Imaginez-vous administrateur système dans une entreprise, on vous charge d'installer un serveur web pour afficher une simple page html. Vous allez alors installer Debian + Apache puis placer le fichier html à la racine, rien de difficile jusque là.
Imaginez ensuite qu'au bout de 2 mois ce fichier html soit remplacé par un fichier php un peu plus avancé, vous allez alors installer libapache2-mod-php5 là encore c'est facile. Par la suite le fichier évolue encore et vous oblige à installer des modules, par exemple php5-gd et php5-curl.
Puis le serveur vit sa vie et au bout de 2 ans on vous demande d'en mettre en place un deuxième avec exactement le même rôle. Et là ça se complique car 2 ans c'est long et vous avez probablement oublié tout ce que vous et vos collègues avez fait pour configurer ce serveur au fur et à mesure. S'il est facile d'identifier que apache2 et php5 sont présents, en revanche les modules sont beaucoup moins évidents, surtout si vous avez utilisé des gestionnaires tiers tels que pecl, cpan, pip.
L'une des grandes problématiques est donc de trouver un moyen pour garder une trace et refaire rapidement toutes les étapes qui ont accompagné la vie du serveur. Alors bien sûr le grand classique est d'utiliser un wiki, mais qui vous garanti que ce qu'y s'y trouve représente bien ce qui est en production ? Personne n'est à l'abri d'une modification "urgente" en prod non rapportée sur le wiki.
Une solution que l'on retrouve souvent et qui est appliquée dans Ansible, Docker et NixOS, c'est d'éliminer toutes les manipulations sur la prod. On y touche plus, à la place on travaille sur un fichier de recette, que l'on va ensuite deployer et jouer. Sur Ansible ce sont les playbook, sur docker les Dockerfile, et sur NixOS le fichier configuration.nix.
Cela parait beaucoup plus propre et ça l'est, c'est une rigueur pas forcément naturelle qui paye sur le long terme. Néanmoins ce n'est pas toujours évident, une manipulation rapide à faire sur un serveur peut se transformer en plusieurs minutes voire heures de devops dans Ansible. Let's Encrypt est un exemple, son automatisation n'est pas simple car vous allez devoir gérer deux cas différents selon la présence ou non d'un certificat. En effet certbot peut soit utiliser le mode standalone et donc couper Nginx (car nécessité du port 80) ou alors utiliser le mode webroot et justement passer par Nginx + vhost de votre site, or ce dernier a justement besoin du certificat pour démarrer. Et quid du cas de Docker où vous devez créer un nouveau container et le relier à l'existant juste pour renouveler vos certificats.
En conclusion le boulot de sysadmin, développeur ou devops n'est pas seulement de faire ou réparer les choses mais également de prévoir l'avenir, s'imposer une rigueur même si elle parait contre-productive sur le moment. J'aurai probablement l'occasion de parler de plus en détail de Docker et de NixOS dans de prochains articles, restez branchés.