À partir du deuxième commit, tout code devient hérité, car les idées initiales commencent à s’écarter de la dure réalité. Ce n’est ni bon ni mauvais, c’est une donnée avec laquelle il est difficile de discuter et avec laquelle il faut vivre. Une partie de ce processus consiste à refactoriser. Refactoriser l'infrastructure en tant que code. Que l'histoire commence sur la façon de refactoriser Ansible en un an et de ne pas devenir fou.
La naissance de l'héritage
Jour n°1 : Patient zéro
Il était une fois un projet conditionnel. Il y avait une équipe de développement Dev et des ingénieurs Ops. Ils résolvaient le même problème : comment déployer des serveurs et exécuter une application. Le problème était que chaque équipe a résolu ce problème à sa manière. Lors du projet, il a été décidé d'utiliser Ansible pour synchroniser les connaissances entre les équipes Dev et Ops.
Jour #89 : La naissance de l'héritage
Sans s’en rendre compte eux-mêmes, ils ont voulu le faire du mieux possible, mais cela s’est avéré être un héritage. Comment cela peut-il arriver?
Nous avons une tâche urgente ici, faisons un sale hack puis réparons-le.
Vous n’êtes pas obligé d’écrire de la documentation et tout est clair sur ce qui se passe ici.
Je connais Ansible/Python/Bash/Terraform ! Regardez comme je peux esquiver !
Je suis un développeur Full Stack Overflow et j'ai copié ceci depuis stackoverflow, je ne sais pas comment cela fonctionne, mais cela a l'air cool et résout le problème.
En conséquence, vous pouvez obtenir un type de code incompréhensible pour lequel il n'y a pas de documentation, on ne sait pas ce qu'il fait, s'il est nécessaire, mais le problème est que vous devez le développer, le modifier, ajouter des béquilles et des supports , rendant la situation encore pire.
Le modèle IaC initialement conçu et mis en œuvre ne répond plus aux exigences des utilisateurs/métiers/autres équipes, et le temps nécessaire pour apporter des modifications à l'infrastructure cesse d'être acceptable. À ce moment-là, on comprend qu’il est temps d’agir.
Refactorisation IaC
Jour #139 : Avez-vous vraiment besoin d'une refactorisation ?
Avant de vous précipiter dans une refactorisation, vous devez répondre à un certain nombre de questions importantes :
Pourquoi as-tu besoin de tout ça ?
As-tu du temps?
La connaissance est-elle suffisante ?
Si vous ne savez pas comment répondre aux questions, la refactorisation se terminera avant même de commencer, ou elle pourrait ne faire qu'empirer. Parce que avait de l'expérience ( Ce que j'ai appris en testant 200 000 lignes de code d'infrastructure), puis le projet a reçu une demande d'aide pour fixer les rôles et les couvrir par des tests.
Jour #149 : Préparer le refactoring
La première chose est de se préparer. Décidez de ce que nous ferons. Pour ce faire, nous communiquons, trouvons les problèmes et trouvons des moyens de les résoudre. Nous enregistrons les concepts résultants d'une manière ou d'une autre, par exemple un article en confluence, de sorte que lorsque la question se pose « qu'est-ce qui est le mieux ? » ou "qu'est-ce qui est correct?" Nous ne nous sommes pas perdus. Dans notre cas, nous sommes restés fidèles à l'idée diviser pour régner: nous divisons l'infrastructure en petits morceaux/briques. Cette approche permet de prendre une infrastructure isolée, de comprendre ce qu'elle fait, de la couvrir de tests et de la modifier sans craindre de casser quoi que ce soit.
Il s’avère que les tests d’infrastructure deviennent la pierre angulaire et il convient ici de mentionner la pyramide des tests d’infrastructure. Exactement la même idée qui est en développement, mais pour l'infrastructure : nous passons de tests rapides bon marché qui vérifient des choses simples, comme l'indentation, à des tests à part entière coûteux qui déploient l'ensemble de l'infrastructure.
Tentatives de tests Ansible
Avant de décrire comment nous avons couvert les tests Ansible sur le projet, je décrirai les tentatives et approches que j'ai eu l'occasion d'utiliser plus tôt afin de comprendre le contexte des décisions prises.
Jour N° -997 : Mise à disposition des FDS
La première fois que j'ai testé Ansible, c'était sur un projet de développement de SDS (Software Defined Storage). Il y a un article séparé sur ce sujet Comment casser des vélos avec des béquilles lors du test de votre distribution, mais en bref, nous nous sommes retrouvés avec une pyramide de tests inversée et nous avons passé 60 à 90 minutes de test sur un rôle, ce qui est long. La base était les tests e2e, c'est-à-dire nous avons déployé une installation à part entière puis l'avons testée. Ce qui était encore plus agaçant, c'était l'invention de son propre vélo. Mais je dois admettre que cette solution a fonctionné et a permis une version stable.
Jour #-701 : Ansible et cuisine de test
Le développement de l'idée de test Ansible a consisté à utiliser des outils prêts à l'emploi, à savoir test kitchen / kitchen-ci et inspec. Le choix a été déterminé par la connaissance de Ruby (pour plus de détails, voir l'article sur Habré : Les programmeurs YML rêvent-ils de tester Ansible ?) a travaillé plus vite, environ 40 minutes pour 10 rôles. Nous avons créé un pack de machines virtuelles et effectué des tests à l'intérieur.
En général, la solution a fonctionné, mais il y avait quelques sédiments dus à l'hétérogénéité. Lorsque le nombre de personnes testées a été augmenté à 13 rôles de base et 2 méta-rôles combinant des rôles plus petits, les tests ont soudainement commencé à durer 70 minutes, soit presque 2 fois plus longtemps. Il était difficile de parler des pratiques XP (programmation extrême) car... personne ne veut attendre 70 minutes. C'est la raison pour laquelle nous avons changé d'approche
Jour # -601 : Ansible et molécule
Conceptuellement, cela est similaire à testkitchen, sauf que nous avons déplacé les tests de rôle vers Docker et modifié la pile. En conséquence, le temps a été réduit à 20-25 minutes stables pour 7 rôles.
En augmentant le nombre de rôles testés à 17 et en peluchant 45 rôles, nous avons exécuté cela en 28 minutes sur 2 esclaves Jenkins.
Jour #167 : Ajout des tests Ansible au projet
Très probablement, il ne sera pas possible d’effectuer la tâche de refactoring à la hâte. La tâche doit être mesurable pour que vous puissiez la briser en petits morceaux et manger l'éléphant morceau par morceau avec une cuillère à café. Il faut comprendre si vous avancez dans la bonne direction, combien de temps il vous reste.
En général, peu importe comment cela sera fait, vous pouvez écrire sur un morceau de papier, vous pouvez mettre des autocollants sur le placard, vous pouvez créer des tâches dans Jira ou vous pouvez ouvrir Google Docs et noter l'état actuel. là. Les jambes grandissent du fait que le processus n'est pas immédiat, il sera long et fastidieux. Il est peu probable que quiconque veuille que vous soyez à court d’idées, fatigué et dépassé pendant la refactorisation.
La refactorisation est simple :
Mangez.
Sommeil.
Code.
Test IAC.
Répéter
et nous répétons cela jusqu'à ce que nous atteignions l'objectif visé.
Il n'est peut-être pas possible de commencer à tout tester tout de suite, notre première tâche a donc été de commencer par le peluchage et de vérifier la syntaxe.
Jour #181 : Maître de la construction verte
Le Linting est un petit premier pas vers Green Build Master. Cela ne cassera presque rien, mais cela vous permettra de déboguer les processus et de créer des builds écologiques dans Jenkins. L’idée est de développer des habitudes au sein de l’équipe :
Les tests rouges sont mauvais.
Je suis venu réparer quelque chose et en même temps rendre le code un peu meilleur qu'il ne l'était avant vous.
Jour #193 : Du peluchage aux tests unitaires
Après avoir construit le processus d'introduction du code dans le maître, vous pouvez commencer le processus d'amélioration étape par étape - en remplaçant le peluchage par des rôles de lancement, vous pouvez même le faire sans idempotence. Vous devez comprendre comment appliquer les rôles et comment ils fonctionnent.
Jour #211 : Des tests unitaires aux tests d'intégration
Lorsque la plupart des rôles sont couverts par des tests unitaires et que tout est lissé, vous pouvez passer à l'ajout de tests d'intégration. Ceux. tester non pas une seule brique de l'infrastructure, mais une combinaison de celles-ci, par exemple une configuration d'instance complète.
À l'aide de jenkins, nous avons généré de nombreuses étapes qui lintaient les rôles/playbooks en parallèle, puis les tests unitaires dans des conteneurs et enfin les tests d'intégration.
Jenkins + Docker + Ansible = Tests
Récupérez le dépôt et générez des étapes de construction.
Exécutez les étapes du playbook Lint en parallèle.
Exécutez les étapes du rôle Lint en parallèle.
Exécutez les étapes du rôle de vérification de la syntaxe en parallèle.
Exécutez les étapes de rôle de test en parallèle.
Rôle de peluche.
Vérifiez la dépendance sur d'autres rôles.
Vérifiez la syntaxe.
Créer une instance Docker
Exécutez molécule/default/playbook.yml.
Vérifiez l'idempotence.
Exécuter des tests d'intégration
Finition
Jour #271 : Facteur Bus
Au début, le refactoring était réalisé par un petit groupe de deux ou trois personnes. Ils ont examiné le code dans le maître. Au fil du temps, l’équipe a développé des connaissances sur la façon d’écrire du code et la révision du code a contribué à la diffusion des connaissances sur l’infrastructure et son fonctionnement. Le point fort ici était que les évaluateurs étaient sélectionnés un par un, selon un calendrier, c'est-à-dire avec un certain degré de probabilité, vous grimperez dans une nouvelle infrastructure.
Et ça devrait être confortable ici. Il est pratique de faire un bilan, de voir dans le cadre de quelle tâche cela a été réalisé et l’historique des discussions. Nous avons intégré jenkins + bitbucket + jira.
Mais en tant que tel, une révision n’est pas une panacée ; d’une manière ou d’une autre, nous sommes entrés dans le code maître, ce qui nous a fait échouer les tests :
Au fil du temps, il y a eu davantage de tests, les builds se sont déroulés plus lentement, jusqu'à une heure dans le pire des cas. Sur l'une des rétros, il y avait une phrase comme "c'est bien qu'il y ait des tests, mais ils sont lents". En conséquence, nous avons abandonné les tests d'intégration sur les machines virtuelles et les avons adaptés pour Docker pour le rendre plus rapide. Nous avons également remplacé testinfra par ansible verifier pour réduire le nombre d'outils utilisés.
À proprement parler, il y avait un ensemble de mesures :
Passez au docker.
Supprimez les tests de rôle, qui sont dupliqués en raison des dépendances.
Augmentez le nombre d'esclaves.
Ordre d’exécution des tests.
Capacité à pelucher TOUS localement avec une seule commande.
En conséquence, Pipeline sur Jenkins a également été unifié
Générez des étapes de construction.
Lint tout en parallèle.
Exécutez les étapes de rôle de test en parallèle.
Terminer.
Les leçons apprises
Évitez les variables globales
Ansible utilise des variables globales, il existe une solution de contournement partielle sous la forme private_role_vars, mais ce n'est pas une panacée.
Laisse moi te donner un exemple. Laissez-nous role_a и role_b
Ce qui est drôle, c’est que le résultat des playbooks dépendra de choses qui ne sont pas toujours évidentes, comme l’ordre dans lequel les rôles sont listés. Malheureusement, c'est la nature d'Ansible et la meilleure chose à faire est d'utiliser une sorte d'accord, par exemple, au sein d'un rôle, utilisez uniquement la variable décrite dans ce rôle.
BON: Dans les rôles pour les variables, utilisez des variables préfixées par le nom du rôle ; cela, en regardant l'inventaire, permettra de mieux comprendre ce qui se passe.
BAD: Utiliser la variable standard dans les boucles item, si cette tâche/playbook est inclus quelque part, cela peut entraîner un comportement inattendu
Nous avons convenu d'utiliser des préfixes de variables ; il ne serait pas superflu de vérifier qu'ils sont définis comme prévu et, par exemple, qu'ils n'ont pas été remplacés par une valeur vide.
BON: Vérifiez les variables.
- name: "Verify that required string variables are defined"
assert:
that: ahs_var is defined and ahs_var | length > 0 and ahs_var != None
fail_msg: "{{ ahs_var }} needs to be set for the role to work "
success_msg: "Required variables {{ ahs_var }} is defined"
loop_control:
loop_var: ahs_var
with_items:
- ahs_item1
- ahs_item2
- ahs_item3
Évitez les dictionnaires de hachage, utilisez une structure plate
Si un rôle attend un hachage/dictionnaire dans l'un de ses paramètres, alors si nous voulons modifier l'un des paramètres enfants, nous devrons remplacer l'intégralité du hachage/dictionnaire, ce qui augmentera la complexité de la configuration.
Les rôles et les playbooks doivent être idempotents, car réduit la dérive de configuration et la peur de casser quelque chose. Mais si vous utilisez une molécule, c'est le comportement par défaut.
Évitez d'utiliser des modules de shell de commande
L'utilisation d'un module shell aboutit à un paradigme de description impérative, au lieu du paradigme déclaratif, qui est le cœur d'Ansible.
Testez vos rôles via une molécule
La molécule est une chose très flexible, examinons quelques scénarios.
Molécule Plusieurs instances
В molecule.yml dans la section platforms vous pouvez décrire de nombreux hôtes que vous pouvez déployer.
Dans molécule, il est possible d'utiliser ansible pour vérifier que l'instance a été correctement configurée, d'ailleurs, c'est la valeur par défaut depuis la version 3. Ce n'est pas aussi flexible que testinfra/inspec, mais on peut vérifier que le contenu du fichier correspond à nos attentes :
Ou déployez le service, attendez qu'il soit disponible et effectuez un test de fumée :
---
- name: Verify
hosts: solr
tasks:
- command: /blah/solr/bin/solr start -s /solr_home -p 8983 -force
- uri:
url: http://127.0.0.1:8983/solr
method: GET
status_code: 200
register: uri_result
until: uri_result is not failed
retries: 12
delay: 10
- name: Post documents to solr
command: /blah/solr/bin/post -c master /exampledocs/books.csv
Mettez une logique complexe dans les modules et plugins
Ansible préconise une approche déclarative, donc lorsque vous effectuez un branchement de code, une transformation de données, des modules shell, le code devient difficile à lire. Pour lutter contre cela et rester simple à comprendre, il ne serait pas superflu de lutter contre cette complexité en créant vos propres modules.
Résumer les trucs et astuces
Évitez les variables globales.
Variables de rôle de préfixe.
Utilisez la variable de contrôle de boucle.
Vérifiez les variables d'entrée.
Évitez les dictionnaires de hachage, utilisez une structure plate.
Créez des playbooks et des rôles idempotents.
Évitez d'utiliser des modules de shell de commande.
Testez vos rôles via une molécule.
Mettez une logique complexe dans les modules et plugins.
Conclusion
Vous ne pouvez pas simplement refactoriser l’infrastructure d’un projet, même si vous disposez d’IaC. C'est un long processus qui demande de la patience, du temps et des connaissances.
UPD1 2020.05.01 20:30 — Pour le profilage principal des playbooks, vous pouvez utiliser callback_whitelist = profile_tasks pour comprendre ce qui fonctionne exactement pendant longtemps. Ensuite, nous passons par Classiques de l'accélération Ansible. Vous pouvez également essayer mitogène UPD2 2020.05.03 16:34 - Version anglaise