Leçons tirées des tests Plus de 200 000 lignes de code d'infrastructure


L'IaC (Infrastructure as Code) est une approche moderne et je crois que l'infrastructure est du code. Cela signifie que nous devrions utiliser la mĂȘme philosophie pour l'infrastructure que pour le dĂ©veloppement de logiciels. Si nous parlons que l'infrastructure est du code, nous devons rĂ©utiliser les pratiques de dĂ©veloppement pour l'infrastructure, c'est-Ă -dire les tests unitaires, la programmation de paires, la rĂ©vision de code. Veuillez garder Ă  l'esprit cette idĂ©e lors de la lecture de l'article.


Version russe


Il s'agit de la traduction de mon discours ( vidéo RU ) à DevopsConf 2019-05-28 .



L'infrastructure comme histoire bash



Imaginons que vous embarquiez sur un projet et que vous entendiez quelque chose comme: "Nous utilisons l' infrastructure comme approche de code ". Malheureusement, ce qu'ils signifient vraiment, c'est Infrastructure en tant qu'historique bash ou Documentation en tant qu'historique bash . C'est presque une situation rĂ©elle. Par exemple, Denis Lysenko a dĂ©crit cette situation dans son discours Comment remplacer les infrastructures et arrĂȘter de s'inquiĂ©ter (RU) . Denis a partagĂ© l'histoire sur la façon de convertir l'histoire de bash en une infrastructure haut de gamme.


Vérifions la définition du code source: a text listing of commands to be compiled or assembled into an executable computer program . Si nous voulons, nous pouvons présenter Infrastructure comme un historique bash comme du code. Ceci est un texte et une liste de commandes. Il décrit comment un serveur a été configuré. De plus, c'est:


  1. Reproductible : vous pouvez obtenir l'historique de bash, exécuter des commandes et probablement obtenir une infrastructure fonctionnelle.
  2. Versioning : vous savez qui s'est connecté, quand et ce qui a été fait.
    Malheureusement, si vous perdez le serveur, vous ne pourrez rien faire car il n'y a pas d'historique bash, vous l'avez perdu avec le serveur.

Que faut-il faire?


L'infrastructure comme code



D'une part, ce cas anormal, Infrastructure en tant qu'historique bash , peut ĂȘtre prĂ©sentĂ© comme Infrastructure en tant que code , mais d'autre part, si vous voulez faire quelque chose de plus complexe que le serveur LAMP, vous devez gĂ©rer, maintenir et modifier le code . Parlons des parallĂšles entre Infrastructure en tant que dĂ©veloppement de code et dĂ©veloppement de logiciels.


SEC



Nous dĂ©veloppions SDS (stockage dĂ©fini par logiciel). La SDS se composait de serveurs OS distributifs personnalisĂ©s et haut de gamme, beaucoup de logique mĂ©tier, par consĂ©quent, elle devait utiliser du matĂ©riel rĂ©el. PĂ©riodiquement, il y avait une sous-tĂąche d' installation SDS . Avant de publier une nouvelle version, nous devions l'installer et vĂ©rifier. Au dĂ©but, cela semblait ĂȘtre une tĂąche trĂšs simple:


  • SSH pour hĂ©berger et exĂ©cuter la commande.
  • SCP un fichier.
  • Modifiez une configuration.
  • ExĂ©cutez un service.
  • ...
  • PROFIT!

Je crois que Make CM, not bash est une bonne approche. Cependant, bash n'est utilisĂ© que dans des cas extrĂȘmes et limitĂ©s, comme au tout dĂ©but d'un projet. Donc, bash Ă©tait un choix assez bon et raisonnable au tout dĂ©but du projet. Le temps tournait. Nous Ă©tions confrontĂ©s Ă  diffĂ©rentes demandes de crĂ©ation de nouvelles installations dans une configuration lĂ©gĂšrement diffĂ©rente. Nous Ă©tions SSHing dans les installations, et exĂ©cutions les commandes pour installer tous les logiciels nĂ©cessaires, Ă©ditant les fichiers de configuration par des scripts et, enfin, configurant SDS via Web HTTP rest API. AprĂšs tout, l'installation a Ă©tĂ© configurĂ©e et fonctionne. C'Ă©tait une pratique assez courante, mais il y avait beaucoup de scripts bash et la logique d'installation devenait chaque jour plus complexe.


Malheureusement, chaque script était comme un petit flocon de neige selon qui le copiait. C'était aussi une vraie douleur lorsque nous créions ou recréions l'installation.


J'espÚre que vous avez eu l'idée principale, qu'à ce stade, nous devions constamment modifier la logique des scripts jusqu'à ce que le service soit OK. Mais il y avait une solution à cela. C'était SEC



Il existe une approche SÈCHE (ne vous rĂ©pĂ©tez pas). L'idĂ©e principale est de rĂ©utiliser du code dĂ©jĂ  existant. Cela semble extrĂȘmement simple. Dans notre cas, DRY signifiait: configurations et scripts sĂ©parĂ©s.


SOLIDE pour CFM



Le projet grandissait, nous avons donc décidé d'utiliser Ansible. Il y avait des raisons à cela:


  1. Bash ne doit pas contenir de logique complexe .
  2. Nous avions une certaine expertise dans Ansible.

Il y avait une quantité de logique métier dans le code Ansible. Il existe une approche pour mettre les choses dans le code source pendant le processus de développement logiciel. Il s'appelle SOLID . De mon point de vue, nous pouvons réutiliser SOLID for Infrastructure comme code . Laissez-moi vous expliquer étape par étape.


Le principe de responsabilité unique



Une classe ne devrait avoir qu'une seule responsabilité, c'est-à-dire que seules les modifications apportées à une partie de la spécification du logiciel devraient pouvoir affecter la spécification de la classe.


Vous ne devez pas crĂ©er de code spaghetti dans votre code d'infrastructure. Votre infrastructure doit ĂȘtre constituĂ©e de simples briques prĂ©visibles. En d'autres termes, ce pourrait ĂȘtre une bonne idĂ©e de diviser l'immense livre de jeu Ansible en rĂŽles Ansible indĂ©pendants. Il sera plus facile Ă  entretenir.


Les principes ouvert-fermé



Les entitĂ©s logicielles ... devraient ĂȘtre ouvertes pour extension, mais fermĂ©es pour modification.


Au début, nous déployions le SDS sur des machines virtuelles, un peu plus tard, nous avons ajouté le déploiement sur des serveurs bare metal . Nous l'avions fait. Cela a été aussi simple que facile pour nous car nous venons d'ajouter une implémentation pour des piÚces spécifiques au métal nu sans modifier la logique d'installation de SDS.


Le principe de substitution de Liskov



Les objets d'un programme doivent ĂȘtre remplaçables par des instances de leurs sous-types sans altĂ©rer l'exactitude de ce programme.


Soyons ouverts d'esprit. SOLID est possible d'utiliser dans CFM en gĂ©nĂ©ral, ce n'Ă©tait pas un projet chanceux. Je voudrais dĂ©crire un autre projet. Il s'agit d'une solution d'entreprise prĂȘte Ă  l'emploi. Il prend en charge diffĂ©rentes bases de donnĂ©es, serveurs d'applications et interfaces d'intĂ©gration avec des systĂšmes tiers. Je vais utiliser cet exemple pour dĂ©crire le reste de SOLID


Par exemple dans notre cas, il y a un accord au sein de l'équipe d'infrastructure: si vous déployez le rÎle java ibm ou l'oracle java ou openjdk, vous aurez un exécutable java binaire. Nous en avons besoin car les rÎles Ansible de niveau supérieur * en dépendent. En outre, cela nous permet d'échanger l'implémentation java sans modifier la logique d'installation des applications.


Malheureusement, il n'y a pas de sucre de syntaxe pour cela dans les playbooks Ansible. Cela signifie que vous devez en tenir compte lors du développement des rÎles Ansible.


Le principe de ségrégation des interfaces



De nombreuses interfaces spécifiques au client valent mieux qu'une interface à usage général.


Au début, nous mettions la logique d'installation des applications dans le playbook unique, nous essayions de couvrir tous les cas et les tranchants. Nous avions fait face au problÚme qu'il est difficile de maintenir, alors nous avons changé notre approche. Nous avons compris qu'un client avait besoin d'une interface de notre part (c'est-à-dire https au port 443) et nous avons pu combiner nos rÎles Ansible pour chaque environnement spécifique.


Le principe d'inversion de dépendance



Il faut "dépendre d'abstractions, pas de concrétions".


  • Les modules de haut niveau ne doivent pas dĂ©pendre de modules de bas niveau. Les deux devraient dĂ©pendre d'abstractions (par exemple des interfaces).
  • Les abstractions ne devraient pas dĂ©pendre des dĂ©tails. Les dĂ©tails (implĂ©mentations concrĂštes) doivent dĂ©pendre des abstractions.

Je voudrais décrire ce principe via anti-pattern.


  1. Il y avait un client avec un cloud privé.
  2. Nous demandions des machines virtuelles dans le cloud.
  3. Notre logique de déploiement dépendait de l'hyperviseur sur lequel une machine virtuelle était située.

En d'autres termes, nous n'avons pas pu réutiliser notre IaC dans un autre cloud car la logique de déploiement de niveau supérieur dépendait de l'implémentation de niveau inférieur. S'il te plait ne le fais pas


L'interaction



L'infrastructure n'est pas seulement du code, il s'agit aussi du code d'interaction <-> DevOps, DevOps <-> DevOps, IaC <-> personnes.


Facteur de bus



Imaginons, il y a l'ingénieur DevOps John. John sait tout sur votre infrastructure. Si John se fait heurter par un bus, qu'arrivera-t-il à votre infrastructure? Malheureusement, c'est presque un cas réel. Parfois, des choses se produisent. Si cela s'est produit et que vous ne partagez pas les connaissances sur l'IaC, l'infrastructure entre les membres de votre équipe, vous serez confronté à de nombreuses conséquences imprévisibles et délicates. Il existe certaines approches pour y faire face. Laissez-nous en parler.


Pair DevOpsing



C'est comme la programmation par paires. En d'autres termes, il y a deux ingénieurs DevOps et ils utilisent un seul ordinateur portable \ clavier pour configurer l'infrastructure: configurer un serveur, créer un rÎle Ansible, etc. Cela semble génial, cependant, cela n'a pas fonctionné pour nous. Il y avait des cas personnalisés quand cela fonctionnait partiellement.


  • IntĂ©gration : le mentor et la nouvelle personne obtiennent une tĂąche rĂ©elle Ă  partir d'un carnet de commandes et travaillent ensemble - transfĂšrent les connaissances du mentor Ă  la personne.
  • Appel incident : Lors du dĂ©pannage, il y a un groupe d'ingĂ©nieurs, ils recherchent une solution. Le point clĂ© est qu'il y a une personne qui dirige cet incident. La personne partage l'Ă©cran et les idĂ©es. D'autres personnes le suivent attentivement et remarquent les astuces bash, les erreurs, l'analyse des journaux, etc.

Examen du code



De mon point de vue, la révision de code est l'un des moyens les plus efficaces de partager des connaissances au sein d'une équipe sur votre infrastructure. Comment ça marche?


  • Il existe un rĂ©fĂ©rentiel qui contient la description de votre infrastructure.
  • Tout le monde fait ses changements dans une branche dĂ©diĂ©e.
  • Au cours de la demande de fusion, vous ĂȘtes en mesure d'examiner les changements de delta dans votre infrastructure.

La chose la plus intéressante est que nous faisions tourner un relecteur. Cela signifie que tous les deux jours, nous avons élu un nouvel examinateur et l'examinateur examinait toutes les demandes de fusion. Par conséquent, théoriquement, chaque personne devait toucher une nouvelle partie de l'infrastructure et avait une connaissance moyenne de notre infrastructure en général.


Style de code



Le temps avançait, nous nous disputions parfois lors de la rĂ©vision car le rĂ©viseur et le committer pouvaient utiliser un style de code diffĂ©rent: 2 espaces ou 4, camelCase ou snake_case . Nous l'avons mis en Ɠuvre, cependant, ce n'Ă©tait pas un pique-nique.


  • La premiĂšre idĂ©e a Ă©tĂ© de recommander l'utilisation de linters. Chacun avait son propre environnement de dĂ©veloppement: IDE, OS ... c'Ă©tait difficile de tout synchroniser & unifier.
  • L'idĂ©e a Ă©voluĂ© en un robot lĂąche. AprĂšs chaque commit, le bot vĂ©rifiait le code source et poussait dans les messages lĂąches avec une liste de problĂšmes. Malheureusement, dans la grande majoritĂ© des cas, il n'y a eu aucun changement de code source aprĂšs les messages.

Maßtre de la construction écologique



Ensuite, l'Ă©tape la plus douloureuse a Ă©tĂ© de restreindre la poussĂ©e Ă  la branche principale pour tout le monde. Seulement via les demandes de fusion et les tests verts doivent ĂȘtre corrects. Cela s'appelle Green Build Master . En d'autres termes, vous ĂȘtes sĂ»r Ă  100% que vous pouvez dĂ©ployer votre infrastructure Ă  partir de la branche principale. C'est une pratique assez courante dans le dĂ©veloppement de logiciels:


  • Il existe un rĂ©fĂ©rentiel qui contient la description de votre infrastructure.
  • Tout le monde fait ses changements dans une branche dĂ©diĂ©e.
  • Pour chaque branche, nous effectuons des tests.
  • Vous ne pouvez pas fusionner dans la branche principale si les tests Ă©chouent.

Ce fut une décision difficile. Espérons que, lors du processus de révision, il n'y ait eu aucune discussion sur le style de code et la quantité d'odeur de code diminuait.


Test IaC



Outre la vĂ©rification du style de code, vous pouvez vĂ©rifier que vous pouvez dĂ©ployer ou recrĂ©er votre infrastructure dans un sandbox. À quoi ça sert? C'est une question sophistiquĂ©e et je voudrais partager une histoire au lieu d'une rĂ©ponse. Était un scaler automatique personnalisĂ© pour AWS Ă©crit en Powershell. Le dĂ©tartreur automatique n'a pas vĂ©rifiĂ© les paramĂštres de coupe pour les paramĂštres d'entrĂ©e, par consĂ©quent, il a créé des tonnes de machines virtuelles et le client n'Ă©tait pas satisfait. C'est une situation dĂ©licate, espĂ©rons-le, il est possible de l'attraper dĂšs les premiĂšres Ă©tapes.


D'une part, il est possible de tester le script et l'infrastructure, mais d'autre part, vous augmentez la quantité de code et complexifiez l'infrastructure. Cependant, la vraie raison sous le capot est que vous mettez vos connaissances sur l'infrastructure à l'épreuve. Vous décrivez comment les choses devraient fonctionner ensemble.


Pyramide de test IaC



Tests IaC: analyse statique


Vous pouvez créer l'intégralité de l'infrastructure à partir de zéro pour chaque validation, mais, généralement, il existe certains obstacles:


  • Le prix est stratosphĂ©rique.
  • Cela demande beaucoup de temps.

Espérons qu'il y ait quelques astuces. Vous devriez avoir beaucoup de tests simples, rapides et primitifs dans votre fondation.


Bash est délicat


Prenons un exemple extrĂȘmement simple. Je voudrais crĂ©er un script de sauvegarde:


  • RĂ©cupĂšre tous les fichiers du rĂ©pertoire courant.
  • Copiez les fichiers dans un autre rĂ©pertoire avec un nom modifiĂ©.

La premiÚre idée est:


 for i in * ; do cp $i /some/path/$i.bak done 

Assez bien. Cependant, que se passe-t-il si le nom de fichier contient de l' espace ? Nous sommes des gars intelligents, nous utilisons des citations:


 for i in * ; do cp "$i" "/some/path/$i.bak" done 

Avons-nous fini? Non! Et si le répertoire est vide? Globing échoue dans ce cas.


 find . -type f -exec mv -v {} dst/{}.bak \; 

Avons-nous fini? Pas encore ... Nous avons oublié que le nom de fichier peut contenir \n caractÚre.


 touch x mv x "$(printf "foo\nbar")" find . -type f -print0 | xargs -0 mv -t /path/to/target-dir 

Outils d'analyse statique


Vous pouvez intercepter certains problÚmes de l'exemple précédent via Shellcheck . Il existe de nombreux outils de ce type, appelés linters, et vous pouvez trouver celui qui convient le mieux à votre IDE, à votre pile et à votre environnement.


La langueOutil
bashShellcheck
RubisRubocop
pythonPylint
AnsiblePeluche ansible

Tests IaC: tests unitaires



Comme vous pouvez le voir, les linters ne peuvent pas tout attraper, ils ne peuvent que prédire. Si nous continuons de penser aux parallÚles entre le développement de logiciels et l'infrastructure en tant que code, nous devrions mentionner les tests unitaires. Il existe de nombreux systÚmes de tests unitaires comme shunit , JUnit , RSpec , pytest . Mais avez-vous déjà entendu parler des tests unitaires pour Ansible, Chef, Saltstack, CFengine?


Lorsque nous parlions de SOLID for CFM, j'ai mentionnĂ© que notre infrastructure devait ĂȘtre faite de briques / modules simples. Le moment est maintenant venu:


  1. Divisez l'infrastructure en modules / coupures simples, c'est-Ă -dire des rĂŽles Ansible.
  2. Créez un environnement, c'est-à-dire Docker ou VM.
  3. Appliquez votre unique rupture / module Ă  l'environnement.
  4. Vérifiez que tout va bien ou non.
    ...
  5. PROFIT!

Tests IaC: outils de tests unitaires


Quel est le test pour CFM et votre infrastructure? c'est-Ă -dire que vous pouvez simplement exĂ©cuter un script ou utiliser une solution prĂȘte pour la production comme:


CFMOutil
AnsibleTestinfra
Chef cuisinierInspec
Chef cuisinierServerspec
saliĂšreGoss

Jetons un Ɠil Ă  testinfra, je voudrais vĂ©rifier que les utilisateurs test1 , test2 existent et font partie du groupe sshusers :


 def test_default_users(host): users = ['test1', 'test2' ] for login in users: assert host.user(login).exists assert 'sshusers' in host.user(login).groups 

Quelle est la meilleure solution? Il n'y a pas de réponse unique à cette question, cependant, j'ai créé la carte de chaleur et comparé les changements dans ces projets au cours de 2018-2019:



Cadres de test IaC


AprĂšs cela, vous pouvez vous poser une question sur la façon de gĂ©rer tout cela ensemble? D'une part, vous pouvez tout faire par vous-mĂȘme si vous avez suffisamment de grands ingĂ©nieurs, mais d'autre part, vous pouvez utiliser des solutions open source prĂȘtes pour la production:


CFMOutil
AnsibleMolécule
Chef cuisinierCuisine d'essai
TerraformTerratest

J'ai créé la carte de chaleur et comparé les changements de ces projets au cours de 2018-2019:



Molécule vs. Testkitchen



Au début, nous avons essayé de tester les rÎles ansibles via testkitchen dans hyper-v :


  1. Créez des VM.
  2. Appliquer des rĂŽles Ansible.
  3. Exécutez Inspec.

Il a fallu 40 à 70 minutes pour 25 à 35 rÎles Ansible. C'était trop long pour nous.



L'Ă©tape suivante consistait Ă  utiliser Jenkins / docker / Ansible / molĂ©cule. C'est Ă  peu prĂšs la mĂȘme idĂ©e:


  1. Playbooks Lint Ansible.
  2. RĂŽles de Lint Ansible.
  3. Exécutez un conteneur Docker.
  4. Appliquer des rĂŽles Ansible.
  5. Exécutez testinfra.
  6. Vérifiez l'idempotence.


Linting pour 40 rĂŽles et les tests pour dix d'entre eux ont pris environ 15 minutes.



Quelle est la meilleure solution? D'une part, je ne veux pas ĂȘtre l'autoritĂ© finale, mais d'autre part, je voudrais partager mon point de vue. Il n'y a pas de solution miracle, cependant, dans le cas oĂč la molĂ©cule Ansible est une solution plus appropriĂ©e que la cuisine de test.


Tests IaC: tests d'intégration



Au niveau suivant de la pyramide de tests IaC , il y a les tests d'intégration . Les tests d'intégration pour l'infrastructure ressemblent à des tests unitaires:


  1. Divisez l'infrastructure en modules / coupures simples, c'est-Ă -dire des rĂŽles Ansible.
  2. Créez un environnement, c'est-à-dire Docker ou VM.
  3. Appliquer une combinaison de rupture / module simple Ă  l'environnement.
  4. Vérifiez que tout va bien ou non.
    ...
  5. PROFIT!

En d'autres termes, lors des tests unitaires, nous vérifions un module simple (ie rÎle Ansible, script python, module Ansible, etc.) d'une infrastructure, mais dans le cas des tests d'intégration, nous vérifions la configuration complÚte du serveur.


Tests IaC: tests de bout en bout



En plus de la pyramide des tests IaC , il existe des tests de bout en bout . Dans ce cas, nous ne vĂ©rifions pas le serveur dĂ©diĂ©, le script, le module de notre infrastructure; Nous vĂ©rifions que l'ensemble de l'infrastructure fonctionne correctement. Malheureusement, il n'y a pas de solution prĂȘte Ă  l'emploi pour cela ou je n'en ai pas entendu parler (veuillez me signaler si vous les connaissez). Habituellement, les gens rĂ©inventent la roue, car il y a une demande de tests de bout en bout pour les infrastructures. Donc, je voudrais partager mon expĂ©rience, j'espĂšre qu'elle sera utile Ă  quelqu'un.



Tout d'abord, je voudrais dĂ©crire le contexte. Il s'agit d'une solution d'entreprise prĂȘte Ă  l'emploi, elle prend en charge diffĂ©rentes bases de donnĂ©es, serveurs d'applications et interfaces d'intĂ©gration avec des systĂšmes tiers. Habituellement, nos clients sont une immense entreprise avec un environnement complĂštement diffĂ©rent. Nous avons des connaissances sur les diffĂ©rentes combinaisons d'environnements et nous les stockons dans diffĂ©rents fichiers de composition de docker. En outre, il existe une correspondance entre les fichiers de composition docker et les tests, nous les stockons en tant que travaux Jenkins.



Ce schĂ©ma fonctionnait pendant une pĂ©riode de temps calme et logicielle lorsque, au cours de la recherche OpenShift, nous avons essayĂ© de le migrer vers OpenShift. Nous avons utilisĂ© Ă  peu prĂšs les mĂȘmes conteneurs (encore une fois DRY) et nous avons uniquement changĂ© l'environnement.



Nous continuons à rechercher et à trouver APB (Ansible Playbook Bundle). L'idée principale est que vous emballiez toutes les choses nécessaires dans un conteneur et que vous exécutiez le conteneur dans Openshift. Cela signifie que vous disposez d'une solution reproductible et testable.



Tout allait bien jusqu'à ce que nous rencontrions un autre problÚme: nous devions maintenir une infrastructure hétérogÚne pour les environnements de test. En conséquence, nous stockons nos connaissances sur la façon de créer une infrastructure et d'exécuter des tests dans les travaux Jenkins.


Conclusion



L'infrastructure en tant que code est une combinaison de:


  • Code
  • Interaction avec les gens.
  • Test d'infrastructure.

Source: https://habr.com/ru/post/fr467169/


All Articles