Mardi 9 avril 2019 j'ai présenté une définition de Cloud Native aux HumanTalks Grenoble.
Voici une (il est possible que je n'ai pas employé exactement les mêmes formules mais le message est similaire) transcription de cette présentation.
A la fin de l'article vous pourrez retrouver le pdf à télécharger.
Cloud Native. C'est un terme qu'on entend partout aujourd'hui dans la tech. Mais qui serait capable de précisément expliquer ce que ça veut dire, quelles en sont les limites, les caractéristiques.
Souvent, dès qu'on va parler de Cloud Native on va penser à des microservices sous forme de conteneurs qui tournent dans kubernetes. Avouez, vous avez tous pensé à ça, non ?
Et pourtant, Cloud Native ce n'est aucun de ces termes.
Et si on commençait par chercher une définition.
Et ça tombe bien, il y a une fondation qui a pour but d'héberger des outils Cloud natifs
Ok, donc la définition nous dit que ça permet de créer et exécuter des applications scalables. Hum, si vous êtes plus avancés avec ça c'est super, moi pas vraiment.
Ok, ça dit aussi que ça peut tourner dans des clouds publics, privés, hybrides. Bien.
Ho ben non, j'avais dit que Cloud Native ce n'était pas des microservices, des conteneurs, etc. Je fais comment moi maintenant ?
Et si on regardait du côté de Pivotal ? Depuis des années ils ont des pages qui présentent leur vision du Cloud Native. Bon à chaque fois que j'y vais la page à changé, mais pourquoi pas.
Donc là on nous dit que du Cloud Native revient à exploiter les avantages du cloud.
Et juste après on retombe sur les microservices en conteneurs. Bien bien...
Ceci est la plus vieille définition de "Cloud Native" que je connaisse.
Au premier abord c'est logique, une application Cloud Native doit bien fonctionner dans un environnement cloud. Mais on commence à rajouter quelques précisions sur ce qu'on entend par cloud, ici on parle d' Infrastructure as a Service.
Et si on continue la définition, on attaque un point intéressant : il ne suffit pas de s'exécuter dans le cloud pour être Cloud Native. Il faut aller plus loin.
On vient de lire trois définitions de plutôt bonne qualité, l'élément principal qu'on peut en retirer c'est qu'être Cloud Native c'est exploiter les possiblités du cloud.
Merci merci (c'est le moment où tout le monde se lève et applaudit).
Comment ça, c'est pas fini ?
Bon d'accord, on va continuer. Et d'ailleurs si on revient sur la dernière définition, il y a un point vraiment intéressant. Imaginez, la définition a aussi 9 ans...
Ce point c'est la notion d' infrastructure as a service.
Avant les IaaS, vous aviez un (ou plusieurs) serveurs que vous bichonniez. Vous leurs donniez des petits nom, et quand ça se passait mal vous les répariez.
Avec des IaaS c'est terminé, les serveurs deviennent jetable, ont une faible durée de vie. C'est l' élasticité.
Vous en avez besoin de plus, hop un appel d'API. Vous en avez trop, hop un appel d'API. Il en faut un plus gros, un plus petit ? Il y en a un qui est plus lent, qui n'est plus à jour et il faut le remplacer ? Hop un appel d'API.
Et au fait, pourquoi on voudrait ça ? Pour aller plus vite, pour moins cher et de manière plus fiable. Et oué.
Plus besoin de provisionner pour une consommation variable, on ne paye que ce que l'on consomme, quand on le consomme. Du matériel qui brule ? Ce n'est plus notre problème, le fournisseur s'en charge.
Ok, on y vient. Exploiter ces nouvelles possibilités, ça a justement des conséquences. Dans un hébergement traditionnel, les serveurs ne sont pas jetable, on en prend soin. Et nos applications qui tournent dessus sont relativement statiques. Mais là, on veut du dynamique de partout.
Vous l'aurez bien compris, il n'est plus question ici de fonctionner sur une seule machine. Il s'agit de pouvoir fonctionner sur plusieurs machines simultanément, et donc cela implique de changer des choses, entre autre au niveau des partages de données, fichiers, etc.
Bien évidemment il faut que votre application soit concue pour être élastique. Votre nombre de serveurs, votre nombre d'instances d'application va fréquement varier. Vos clusters doivent être dynamiques.
Dans le but de mutualiser certains coûts, il n'est plus non plus question de déployer votre application pour chaque client. Au lieu de ça vous allez créer des applications multi tenant, et héberger tous vos clients sur une seule plateforme.
De part la nature dynamique de tout ceci, vous voulez réaliser le moins d'action manuelle possible. Si vous vous basez sur une API pour gérer vos serveurs, ce n'est pas pour avoir besoin d'un humain pour lancer les commandes. Automatisation est le maître mot ici.
Avec tout ça en place, vous voulez aussi aller vers du déploiement incrémental. Déjà parce que c'est à Gilles, mais aussi parce que vous avez tout pour le faire. Fini la corvée de déployer, de gérer les serveurs, tout est automatisé. Le déploiement de votre application aussi.
Ok, c'est bien beau tout ça. Ça fait joli sur les CV, sur les articles de blog (oups) mais comment on fait en vrai ?
Et bien on se base sur un concept clé : l'idempotence
L'idempotence c'est l'idée qu'une opération a toujours le même effet.
abs(abs(abs(x)))
c'est toujours pareil queabs(x)
. C'est ça l'idempotence.Dans ce qui nous intéresse c'est l'idée que démarrer un serveur, ça a toujours le même effet, qui est d'avoir des ressources. Si vous en démarrez 10, et que vous en démarrez un onzième, ça a toujours le même effet, vous ajoutez des ressources.
Si vous avez trois instance de votre application et que vous en démarrez une quatrième, vous avez toujours le même effet qui est que votre application est disponible. Avec plus de ressources, mais l'effet est que démarrer une fois de plus votre application n'aura pas eu de comportement différent à la quatrième qu'à la première.
Et pour ça, au niveau de notre application on va utiliser des conteneurs. Ceux-ci vont permettre de garantir que le fonctionnement, l'effet, est toujours le même, quelque soit l'instance que vous démarrez. Si l'instance démarre à partir de la même image, l'effet est le même.
Et comme on veut quand même automatiser les choses, on va se faire aider d'un orchestrateur dont le but va être de les démarrer à notre place. Self service n'oubliez pas.
Au niveau de notre infrastructure, on va s'appuyer sur des images sytèmes figées. L'idée est que chaque démarrage d'un serveur a toujours le même effet. Et donc pour ça tout est figé. Pas question d'avoir des mises à jours sur un serveur et pas sur un autre. Tout est dans l'image (tout le système, pas votre application) et toutes les machines peuvent se comporter de la même manière. Imaginez l'image système comme un conteneur identique, ça marche aussi.
Et pour organiser tout ça, on va utiliser le concept d' Infrastructure as Code. Pas question ici d'avoir des actions manuelles. Et si en plus vous avez une solution d'IaC déclarative, avec gestion d'état, vous pouvez appliquer autant de fois que vous voulez votre infrastructure, l'effet sera toujours exactement le même : avoir votre infrastructure.
Quelques outils qui peuvent vous aider. Et oui, on retombe sur nos petits. Mais ce ne sont que des exemples.
Vous pouvez utiliser docker pour vos applications. Mais un jar peut aussi faire le job, pas de problème, tant que tout est contenu.
Kubernetes ou nomad vont permettre d'orchestrer cela.
Packer va vous permettre de définir votre image système.
Et Terraform va vous permettre de gérer toute votre infrastructure de manière déclarative.
On arrive au bout (faut pas oublier que le but était que ça rentre dans un talk de 10 minutes...)
La notion la plus importante à retenir est l'idempotence. C'est elle la clé du Cloud Native. Si vous la respectez, ça devrait bien se passer.
Faut aussi avouer que Cloud Native ça claque un peu plus qu'idempotent, non ?
Vos outils... sont des outils. Ils vous aident, mais ils ne font pas de votre application une application Cloud Native. N'oubliez pas qu'il est tout à fait possible de faire une application avec des conteneurs sur kubernetes qui s'exécute dans un cloud sans jamais ne serait-ce qu'approcher la notion de Cloud Native.
Vous noterez que j'ai soigneusement évité de parler de microservices. Les microservices ne sont ni bon, ni mauvais. Ils sont une réponse à un problème.
Extraire un composant qui a des besoins spécifiques de scalabilité (par exemple un traitement particulier qui a besoin de beaucoup plus de ressources que tout le reste) est tout à fait valable, même couplé à un bon gros monolithe.
Les microservices sont surtout un mode d'organisation et de communication, qui dépasse largement le cadre de l'application mais qui a aussi des conséquences sur vos équipes.
Il est tout à fait valable de créer une application Cloud Native monolithique ou hybride.
Si vous voulez aller plus loin que ce survol, je ne peux que vous conseiller d'aller voir cette conférence de Holly Cummins.
On est arrivé au bout, j'espère que vous aurez appris quelque chose.
Si vous voulez poursuivre la discussion, n'hésitez pas à me contacter sur twitter @_crev_
Si vous préférez, vous pouvez lire ces slides en ligne ou télécharger la version pdf.
Mardi 12 décembre j'ai présenté un court sujet aux HumanTalks Grenoble. Il s'agissait de présenter rapidement le principe de programmation par contrat et surtout comment l'utiliser avec un langage très dynamique, ruby. Le choix de ruby est presque à contre courant de la programmation par contrat, qu'on associe facilement avec des langages plus strictes tels Ada.
Si vous voulez avoir directement les slides, vous pouvez les retrouver à la fin de l'article.
Il est question de supprimer un maximum de bug.
Pour ça on a déjà plein de solutions de tests. Mais qui dit test dit "vérifier que le programme écrit fait bien ce qu'on lui demande"
Donc comment augmenter la qualité du code avant même les tests ?
Et oui, c'est pas tout jeune. Etiez-vous déjà né d'ailleurs ?
La programmation par contrat se trouve beaucoup dans l'aviation, le féroviaire, le spatial, le militaire, etc. Tout un tas de domaines où on va parler de criticité du logiciel, de vies.
Les 3 grands concepts de la programmation par contrat
La précondition s'applique sur les paramètres en entrée
La postcondition sur les valeurs en sortie
Je vous ai prévu un exemple hyper complexe pour bien comprendre
Trop facile, n'est pas ? Pourquoi vouloir rajouter des contrats sur un tel code ? Il n'y a aucun problème dans ce code, c'est vrai. Qui s'arrête ici si on lui demande un tel code ?
hum...
Oops. A noter que c'est la méthode écrite qui va lever l'exception, vous ne saurez pas forcément qui a passé la mauvaise valeur, d'où elle vient.
Programmation défensive FTW!
Bon ben voilà, c'était pas si compliqué
...
-_- Et oui, il va falloir rajouter plein d'autres cas à vérifier
En voilà des questions qu'elles sont primordiales
Dans les exemples précédent tout est inversé. Si la méthode retourne
nil
quand une mauvaise valeur est entrée c'est l'appelant qui va la recevoir et doit se débrouiller avec. Et si la valeur en entrée est mauvaise, c'est à la méthode qui effectue le traitement qui doit s'en occuper.
Et si au lieu de corriger les problèmes ont ne les laissaient juste pas rentrer ?
Indiquons les "types" (ce ne sont pas nécessairements des types mais des contraintes) sur les entrées et sorties
Comme vous pouvez le voir, c'est pas juste un type. C'est un type + une contrainte
Un exception est levée. Comme avant ? Pas exactement, l'exception est levée au moment de l'appel, pas dans le corps de la méthode.
Idem ici (et pour tout ce qui ne correspond pas à la contrainte)
Comme dit, l'exception est au moment de l'appel. La responsabilité de fournir la donnée correcte incombe donc à l'appelant, ce n'est plus à la méthode de le gérer
Et si on s'amusait avec la sortie ?
Encore une exception de levée. Dans le corps de la méthode.
L'exception est levée dans la méthode, pas dans le code appelant. C'est donc à la méthode de respecter son propre contrat et non à l'appelant de faire avec une mauvaise donnée reçue.
Imaginons qu'on veuille ce comportement. Pourquoi ? Parce que ;-)
Le plus simple, rajoutons de la logique dans la méthode
Ca marche, certes
Mais grace à cette lib de contrat, on peut maintenant faire de l'overloading de méthode, en matchant sur les contrats. Suivant la valeur du paramètre, une méthode ou une autre va être exécutée -> Code plus clair, plus lisible, plus simple
Ok, on a joué avec un positif. Il y a autre chose de dispo ?
On peut explicitement dire qu'on ne veut pas de paramètres, et qu'on retourne n'importe quoi
On peut aussi dire que notre tableau ne doit contenir que des
String
. Pas d'autres types pas denil
. Ou spécifier notreHash
. Ou tiens, un tableau qui contient desHash
chacun contenant une clé avec le symbole:date
et une valeur numérique.Expressif, simple, clair, lisible
En ruby on peut nommer les arguments. Ca permet de faire
connect("sqsc", user: current_user.name", password: input.value)
Ici on indique que
host
est optionnel (et la valeur par défaut est dans la méthode) mais aussi quepassword
est une chaine de caractères avec au moins 8 caractères
Bien évidemment ca fonctionne aussi sur des fonctions
Et sur du duck typing. Tout objet ayant une méthode
parent_user
sera valide. Et associé avec de l'overloading on va gérer et renvoyer une erreur propre si ce n'est pas le cas. Encore une fois, clair, très concis, beaucoup plus lisible et utilisable.
La bibliothèque utilisée ici
Plus que de la documentation, on a une documentation exécutable et donc forcément à jour. Il peut néanmoins y avoir un coût à l'exécution mais on peut désactiver en production par exemple. Et par certains côtés ont s'éloigne un peu de l'idiomatique ruby, mais je pense que ca vaut le coût !
Bref, allez tester !
Si vous préférez voici la version pdf.
Mardi 10 octobre (oups, c'était déjà il y a deux mois) j'ai présenté deux sujets passionnant aux HumanTalks Grenoble :
Docker Multi Stage Builds
Métriques applicatives avec prometheus et grafana
Voici les slides des deux interventions qui devraient vous donner quelques informations pour ceux qui ne connaissent pas le sujet.
A noter que je serai présent à Snowcamp 2018 pour présenter une version plus longue du talk sur les métriques applicatives :-)
Les slides contiennent quelques exemples, à base de Go et de Node (react).
Si vous préférez voici la version pdf.
Si vous préférez voici la version pdf.
Mardi 11 avril 2017 j'ai présenté mgmt: next generation config management aux HumanTalks Grenoble.
Cette présentation a été principalement réalisée à base de démo. Mais voici les différentes slides que j'ai présenté (très légèrement modifiés) avec quelques commentaires et surtout des vidéos des différentes démonstrations utilisées.
Note : la présentation durant 10 minutes, je ne suis pas rentré dans les détails, le but était juste de présenter un outil que probablement beaucoup ne connaissent encore pas.
Si vous voulez avoir directement les slides, vous pouvez les retrouver à la fin de l'article.
mgmt est un système de gestion de configuration, au même titre qu'Ansible, Puppet, Chef, etc. Néanmoins il est assez différent sur beaucoup d'aspects.
Il est réalisé par @purpleidea qui est employé par Red Hat.
En général il y a deux principaux modèles de fonctionnement au niveau des outils de gestion de configuration
Un agent sur chaque machine à configurer va, avec un intervalle de temps donné, contacter un serveur contenant la configuration pour savoir quoi faire.
Ou un orchestrateur va régulièrement pousser la configuration sur chacun des noeuds à opérer.
Globalement ces deux architectures sont très similaires et possèdent un certain nombre de problèmes.
Que ce passe-t-il si le serveur ou orchestrateur est indisponible ? (reboot pour mise à jour, impossible à joindre, crash, etc)
La configuration ne pourra pas être appliquée durant l'indisponibilité
Si on prend le cas de l'orchestrateur en exemple, on va vouloir réaliser des commandes au travers de ssh sur chacune des machines. Si on a quelques machines ça peut fonctionner. Mais une fois qu'on commence à en avoir un nombre conséquent, est-ce vraiment une bonne idée ? Quelles sont les limites de parallélisation possibles ? Quelle durée pour appliquer une configuration ?
Attention je ne dis pas que ça ne peut pas scaler, juste que ce n'est pas nécessairement trivial
Et d'ailleurs, alors qu'il est aujourd'hui facile de disposer de clusters (par exemple avec CoreOS) l'approche de se connecter à chaque machine est-elle encore valable ?
Vous voulez vraiment attendre une heure que vos changements soient appliqués ? Alors même qu'on met en oeuvre des orchestrateurs pour déployer des services en quelques secondes ?
Imaginez plutôt qu'on puisse avoir des changements en temps réel : on parle de délais inférieurs à la seconde, du même ordre de grandeur que les autres composants que vous opérez, containers, serverless, etc.
Pour commencer, prenons un example tout simple. Voici un fichier
yaml
qui va décrire la configuration. Pour info, ce format sera probablement changé par la suite.
---
graph: mygraph
resources:
file:
- name: file1
path: "/tmp/mgmt/f1"
content: |
i am f1
state: exists
- name: file2
path: "/tmp/mgmt/f2"
content: |
i am f2
state: exists
- name: file3
path: "/tmp/mgmt/f3"
content: |
i am f3
state: exists
- name: file4
path: "/tmp/mgmt/f4"
content: |
i am f4 and i should not be here
state: absent
Ce fichier
file1.yaml
décrit trois fichiers/tmp/mgmt/f1
,/tmp/mgmt/f2
et/tmp/mgmt/f3
ainsi que leurs contenu. Il décrit égalemment que/tmp/mgmt/f4
sera absent.Dans la vidéo qui suit, vous pouvez voir que
/tmp/mgmt/
est dans un premier temps vide. Au lancement demgmt
les trois fichiers vont être créés.
Dans un second temps on peut voir que modifier les fichiers n'a pas d'impact visible, y compris en rafraichissant la liste de fichiers leur contenu toutes les 0.1 seconde.
On remarque aussi qu'il nous est impossible de rajouter un fichier
f4
, dès qu'il est détecté parmgmt
celui-ci le supprime aussi tôt.
Dernier détail sur cet aspect,
mgmt
surveille égalemment son fichieryaml
de config. Ainsi si le contenu est modifié, l'état est automatiquement changé. Voici par exemple la modification du contenu d'un des fichiers créé parmgmt
au travers du fichier de configuration.
Dans cette démonstration, 3 machines vont être simulées. Elles sont différentiées par le paramètre
--hostname
demgmt
et par leur répertoire de travail,/tmp/mgmtA
,/tmp/mgmtB
et/tmp/mgmtC
Chaque machine va créer un fichier et va permettre aux autres de récupérer ce fichier.
Les machines vont être lancées successivement pour bien voir ce qui va se passer :
- au lancement de la première machine, un fichier va être créé dans
/tmp/mgmtA
- au lancement de la deuxième machine, un fichier va être créé dans
/tmp/mgmtB
, fichier qui va aussi se retrouver dans/tmp/mgmtA
tout comme le fichier de la première machine va se retrouver dans l'emplacement de la deuxième- la troisième machine va rajouter un fichier dans
/tmp/mgmtC
et chaque machine va maintenant avoir les 3 fichiersToute cette synchronisation est réalisée de pair à pair, sans intervention d'un serveur central ou d'un orchestrateur. Cette synchronisation passe par etcd qui est un système de clé/valeur distribué.
Voici le fichier de la première machine, les autres étant similaires:
---
graph: mygraph
resources:
file:
- name: "@@filea"
path: "/tmp/mgmtA/fA"
content: |
i am fA, exported from host A
state: exists
collect:
- kind: file
pattern: "/tmp/mgmtA/"
edges: []
La communication entre les différents
mgmt
se fait grace à--seeds
qui va indiquer le serveur etcd à utiliser.
mgmt
crée son propre cluster etcd mais il est possible d'utiliser un cluster etcd existant. C'est une solution lorsqu'on utilise déjà des clusters CoreOS par exemple.Dans la vidéo qui suit vous pouvez voir l'exécution des trois
mgmt
et les différents fichiers apparaissant dans les trois environnements.Cette différence de fonctionnement, via etcd et donc une discussion de pair à pair au lieu du passage par un point central est une importante différence de fonctionnement par rapport aux autres solutions. Et si vous opérez déjà des clusters cela vous semblera aussi terriblement logique !
Dans cette dernière démonstration on va pouvoir créer un service au niveau de systemd. Le service va être composé d'un fichier de service ainsi que du contrôle de ce service:
---
graph: svc
resources:
file:
- name: mgmt-test.service
path: "/etc/systemd/system/mgmt-test.service"
content: |
[Unit]
Description=Test service
[Service]
ExecStart=/bin/sh -c "while true; do echo plop; sleep 2s; done"
svc:
- name: mgmt-test
state: running
startup: enabled
La partie
svc
va s'assurer que le servicemgmt-test
(dont la description est dans le fichier juste au dessus) est bien exécuté.Le point critique à comprendre est surtout que, comme pour les fichiers,
mgmt
va s'assurer que l'état ne change pas. Si on arrête le service,mgmt
va le détecter et appliquer la correction, à savoir que le service va être redémarré. Attention tout de même, ça peut être perturbant quand, pour des question de debug, vous tentez d'arrêter un service directement sur une machine...
Il faut par contre noter que la gestion des services est pour le moment assez réduite, entre autre un changement du fichier de service ne va pas redémarrer le service correspondant.
mgmt
est (très) jeune. Il n'en est pas encore à une version0.1
. Néanmoins nous croyons beaucoup dans son potentiel ! La manière proposée nous semble correspondre aux nouveaux besoins auxquels il faut faire face lorsqu'on opère de multiples clusters entre autre.
Un point hyper important à noter : il est très simple d'aller discuter de
mgmt
avec son créateur, que ce soit au travers d'irc #mgmtconfig, github ou twitter.L'accueil aux nouveaux (\o/ mildred) n'est pas en reste, avec des issues github specifiquement marquées pour quiconque voudrait contributer facilement : #mgmtlove
Si vous préférez voici la version pdf.
Mardi 08 novembre 2016 j'ai présenté une introduction pratique à CoreOS et Terraform — pour les développeurs aux HumanTalks Grenoble.
Cette présentation n'a pas pour but de vous apprendre la théorie sur CoreOs et Terraform mais, partant d'un exemple simple et concret, de vous donner envie d'aller voir plus loin et pourquoi pas les utiliser. Pour information l'exemple sur lequel je me base m'est réellement arrivé il y a quelques semaines seulement.
A la fin de l'article vous pouvez retrouver divers liens pour aller plus loin.
Voici donc les slides présentées, ainsi que des captures des démos (slides avec une fusée) et une démo supplémentaire sur la destruction de l'infrastructure.
Si vous préférez voici la version pdf.
Si vous avez envie de discuter du sujet, n'hésitez pas à me contacter sur @_crev_.