Vous êtes sur la page 1sur 325

Infrastructure

Cloud native
avec Azure
Création et gestion
d'applications Cloud
natives

Nishant Singh et Michael Kehoe


Infrastructure Cloud native avec Azure
Le Cloud devient la destination de prédilection des entreprises,
qu'il s'agisse de grandes organisations ou de startups. La migration « La référence Azure
vers le Cloud implique de migrer vos applications monolithiques indispensable pour
vers les microservices. Mais une fois que vous le faites, l’exécution
et la maintenance de ces services apportent leur propre niveau votre bureau. Ce manuel
de complexité. La solution ? La modularité, la déployabilité, démystifie la création
l’observation et la capacité de réparation spontanée grâce au d’applications pour le
développement Cloud natif.
Cloud ».
Grâce à ce manuel pratique, Nishant Singh et Michael Kehoe
—Todd Palino
vous montrent comment créer une véritable infrastructure Cloud Ingénieur principal en charge des
native à l’aide de Microsoft Azure ou d’une autre solution de cloud ressources humaines, LinkedIn,
computing en suivant les directives de la Cloud Native Computing co-auteur de Kafka: The Definitive Guide
Foundation (CNCF). Les ingénieurs DevOps et de fiabilité de site
découvriront comment tirer pleinement profit de l’élasticité et
de la nature distribuée du Cloud en adaptant les applications afin Nishant Singh est un ingénieur senior
qu'elles deviennent Cloud natives. spécialisé dans la fiabilité du site chez
LinkedIn. Son travail consiste à améliorer
Ce manuel couvre les points suivants : la fiabilité du site, tout en privilégiant la
• Pourquoi opter pour une option Cloud native ? réduction du temps moyen de détection
(MTTD) et du temps moyen de réponse
• Comment utiliser l'infrastructure en tant que code ? (MTTR) face aux incidents. Auparavant,
il était ingénieur DevOps chez Paytm et
• Le processus de conteneurisation d'une application
Gemalto, et consacrait son temps à la
• Pourquoi et comment Kubernetes est -t-il devenu le « grand création de solutions personnalisées pour les
orchestrateur » ? clients, ainsi que la gestion et la maintenance
des services sur le Cloud public.
• Comment créer un cluster Kubernetes sur Azure ?
Michael Kehoe est un ingénieur senior chargé
• Comment l’observabilité complète la surveillance ? de la sécurité du personnel chez Confluent.
• Comment utiliser la découverte de service et le maillage des Il travaillait auparavant comme ingénieur
senior de fiabilité de site chez LinkedIn, sur
services pour trouver de nouveaux territoires ?
des domaines tels que l'intervention en
• Comment la mise en réseau et la gestion des stratégies font cas d’incident, la récupération d’urgence,
office de gardiens ? l’ingénierie de visibilité et les principes de
fiabilité. Il a dirigé les initiatives de l’entreprise
• Comment fonctionnent les bases de données et le stockage visant à automatiser la migration vers
distribués ? Microsoft Azure.

CLOUD COMPUTING Twitter : @oreillymedia


linkedin.com/company/oreilly-media
59,99 USD 74,99 CAD youtube.com/oreillymedia
ISBN : 978-1-492-09096-0
55999

9 781492 090960
Infrastructure Cloud native
avec Azure
Création et gestion
d'applications Cloud natives

Nishant Singh et Michael Kehoe

Pékin • Boston • Farnham • Sébastopol • Tokyo


Infrastructure Cloud native avec Azure
par Nishant Singh et Michael Kehoe
Copyright © 2022 Nishant Singh et Michael Kehoe. Tous droits réservés.
Imprimé aux États-Unis d'Amérique.
Publié par O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sébastopol, CA 95472.
Les livres O’Reilly peuvent être achetés à des fins éducatives, commerciales ou promotionnelles. La plupart
des titres sont également disponibles en ligne (http://oreilly.com). Pour plus d’informations, contactez notre
service commercial corporatif/institutionnel : 800-998-9938 ou corporate@oreilly.com.

Rédacteur responsable des commandes : Jennifer Pollock Indexeur : Ellen Troutman-Zaig


Éditeur de développement : Rita Fernando Architecte d’intérieur : David Futato
Rédacteur responsable de la publication : Christopher Faucher Designer de la couverture : Karen Montgomery
Responsable révision : Audrey Doyle Illustrateur : Kate Dullea
Correcteur : Kim Cofer

Février 2022 : Première édition

Historique des révisions pour la première édition


09/02/2022 : Première version

Consultez http://oreilly.com/catalog/errata.csp?isbn=9781492090960 pour connaître les détails de la version.

Le logo O’Reilly est une marque déposée d’O’Reilly Media, Inc. Infrastructure Cloud native avec Azure, l’im-
age de couverture et l'habillage commercial associé sont des marques déposées d’O’Reilly Media, Inc.
Les opinions exprimées dans cet ouvrage sont celles des auteurs et ne représentent pas celles de l'éditeur. Bien
que l'éditeur et les auteurs se soient efforcés, en toute bonne volonté, de s'assurer que les informations et les
instructions contenues dans ce travail sont exactes, l'éditeur et les auteurs déclinent toute responsabilité en
cas d'erreurs ou d'omissions, y compris, entre autres, la responsabilité des dommages résultant de l'utilisation
ou de la confiance accordée à cet ouvrage. L’utilisation des informations et des instructions contenues dans
cet ouvrage se fait à vos propres risques. Si des exemples de code ou d’autres technologies que ce travail
contient ou décrit sont soumis à des licences open source ou à des droits de propriété intellectuelle de tiers,
il est de votre responsabilité de vous assurer que votre utilisation est conforme à ces licences et/ou droits.

978-1-492-09096-0
[LSI]
Table des matières

Préface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi

1. Introduction : Pourquoi opter pour une option Cloud native ? . . . . . . . . . . . . . . . . . . . . . . 1


Transition vers le Cloud  1
Défis liés au Cloud 2
Cloud Native Computing Foundation 4
Adopter une infrastructure Cloud native avec Azure 4
Résumé 5

2. Infrastructure en tant que code : configuration de la passerelle. . . . . . . . . . . . . . . . . . . . . 7


L’infrastructure en tant que code et son importance dans le monde Cloud natif 8
Mise en route avec Azure et configuration de l’environnement 11
Notions de base d’Azure et préparation de votre environnement Azure 11
Création d'un compte Azure 12
Installation d’Azure CLI 13
Outils IaC de premier plan 13
Terraform14
Packer29
Ansible31
Azure DevOps et infrastructure en tant que code 33
Résumé33

3. Conteneurisation de votre application : plus que des boîtes. . . . . . . . . . . . . . . . . . . . . . . . 35


Pourquoi opter pour les conteneurs ? 35
Isolation36
Sécurité36
Conditionnement et déploiement 37

iii
Primitives de conteneur basique 37
Cgroups38
Espaces de noms 39
Copie en écriture 40
Capacités40
Seccomp-BPF40
Composants de l’exécution d’un conteneur 40
Orchestrateurs de conteneur 41
Logiciel de conteneur 41
Runtimes de conteneur 42
Conteneurs43
Système d’exploitation 43
Spécification Open Container Initiative (OCI) 43
Spécification d’image OCI 44
Spécification de runtime OCI 45
Docker46
Création de votre première image Docker 46
Bonnes pratiques appliquées à l’utilisation de Docker 48
Autres plateformes de conteneurs 49
Containers Kata 49
LXC et LXD 49
Registres de conteneurs 50
Stockage d'images sécurisé avec Harbor 51
Stockage d'images sécurisé avec Azure Container Registry 55
Stockage d'images Docker dans un registre 59
Exécution de Docker sur Azure 60
Azure Container Instances 60
Déploiement d’Azure Container Instance 61
Exécution de Docker Container Engine 65
Résumé66

4. Kubernetes : le grand Orchestrator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67


Composants de Kubernetes 69
Plan de contrôle 70
Nœuds de travail 71
Objets de serveur d'API Kubernetes 72
Pods72
ReplicaSets73
Déploiements73
Services73
Espaces de noms 74

iv Table des matières


Étiquettes et sélecteurs 74
Annotations74
Contrôleur d'entrées 74
StatefulSets75
DaemonSets75
Tâches75
Observer, exploiter et gérer des clusters Kubernetes avec kubectl 76
Informations générales et commandes liées aux clusters 76
Gestion des pods 78
Kubernetes en production 85
Résumé98

5. Création d'un cluster Kubernetes dans Azure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99


Création d'un cluster Kubernetes à partir de zéro 99
Création d'un groupe de ressources 100
Création des images de machine pour les machines de travail et de contrôleur 100
Création d’un back-end de compte de stockage 101
Création d'un réseau virtuel Azure 102
Création d’adresses IP publiques pour l’équilibreur de charge 102
Création d’instances de travail et de contrôleur 104
Utilisation d'Ansible pour déployer et configurer des nœuds
de contrôleur Kubernetes106
Utilisation d'Ansible pour déployer et configurer des nœuds
de travail Kubernetes 109
Configuration de la mise en réseau et du routage des pods 109
Génération du fichier kubeconfig pour l’accès à distance
et la validation de cluster 110
Azure Kubernetes Service 111
Déploiement d’applications et de services avec Helm : un gestionnaire
de package pour Kubernetes 113
Notions de base d'Helm 114
Installation et gestion d'Helm 114
Gestion des versions d'Helm 117
Création de tableaux pour vos applications 118
Résumé120

6. Observabilité : suivre les miettes de pain. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121


Introduction à l’observabilité 121
Observabilité : plus que trois piliers 122
Observabilité : un sur-ensemble de surveillance 123
Développement axé sur l’observabilité 124
Surveillance des métriques avec Prometheus dans un monde Cloud natif 125
Composants et architecture de Prometheus 125
Installation et configuration de Prometheus 127

Table des matières v


node_exporter129
Instrumentation des applications 130
Recherche d'hôtes 134
Prometheus sur Kubernetes 136
La journalisation dans l’écosystème natif du cloud 138
La Journalisation avec Fluentd 138
Fluentd sur Kubernetes 146
Suivi distribué dans l’écosystème natif du cloud natif 150
Suivi : concepts clés 151
Architecture du système de suivi général et ensemble de suivi 153
Normes, outils et instrumentation de code de suivi 154
Azure Monitor 159
Résumé161

7. Découverte et maillage des services : Rechercher de nouveaux


territoires et traverser les frontières. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Découverte de services 164
Présentation de CoreDNS 165
Installation et configuration de CoreDNS 167
Découverte de services Kubernetes avec CoreDNS 169
Azure DNS 171
Le maillage de services 172
Présentation d’Istio 174
Installation d'Istio sur Azure Kubernetes Service 175
Injection automatique du proxy Sidecar (proxy Envoy) 177
Gestion des maillages de services Istio à l’aide de Kiali 179
Résumé187

8. Gestion des réseaux et des stratégies : De véritables gardiens. . . . . . . . . . . . . . . . . . . . 189


Container Network Interface (CNI) 190
Pourquoi utiliser un CNI ? 191
Comment CNI fonctionne-t-il sur Azure ? 191
Divers projets CNI 192
Calico193
Pourquoi utiliser Calico ? 193
Architecture de base 194
Déploiement de Calico 195
Exploration de Calico 197
Mise en œuvre de la stratégie de sécurité Calico 198
Cilium200
Déploiement de Cilium 201
Intégration de Cilium avec votre Cloud 204

vi Table des matières


Flannel207
Déploiement de Flannel 207
Exploration de Flannel 208
Stratégie Azure 210
Démarrage rapide de la stratégie Azure 210
Création de votre propre stratégie Azure 212
Stratégie Azure pour Kubernetes 213
Open Policy Agent 214
Déploiement de OPA sur Kubernetes 215
Déploiement de stratégie avec OPA 216
Résumé218

9. Bases de données et stockage distribués : la banque centrale. . . . . . . . . . . . . . . . . . . . 219


La nécessité d’avoir des bases de données distribuées dans l’architecture native du cloud  219
Options de stockage et de base de données dans Azure 220
Introduction à Vitess : MySQL distribué et partitionné 221
Pourquoi exécuter Vitess ? 221
Architecture Vitess222
Déploiement de Vitess sur Kubernetes 223
Présentation de Rook : orchestrateur de stockage pour Kubernetes 224
L’architecture de Rook 224
Déploiement de Rook sur Kubernetes 225
Présentation de TiKV 226
Pourquoi utiliser TiKV ? 226
Architecture TiKV 226
Déploiement de TiKV sur Kubernetes 228
Plus d’informations sur etcd 229
Plateforme matérielle 230
Mise à l’échelle et correction automatiques 230
Disponibilité et sécurité 231
Résumé231

10. Comprendre le message. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233


La besoin en matière de messagerie 233
Exemple de cas d’utilisation de messagerie : ingestion des journaux et
analyse de données235
Génération 1 : sans files d’attente 235
Génération 2 : avec les files d’attente Cloud et le stockage d’objets 236
Génération 3 : avec file d’attente de publication et d’abonnement
basé sur la mémoire  237
Les bases des plateformes de messagerie 238
Comparaison entre la messagerie et le streaming 238
Notions de base de la messagerie 238

Table des matières vii


Producteurs et consommateurs 239
Courtiers et clustering 240
Durabilité et persistance 241
Distribution de message 241
Sécurité242
Modèles de messagerie courants 242
File d’attente simple 242
Publication et abonnement 242
File d’attente durable 242
Une vue d’ensemble des plateformes de messagerie populaires natives du cloud 243
RabbitMQ243
Apache Kafka 243
CNCF CloudEvents 244
Exploration de la messagerie Cloud avec NATS 244
Architecture de protocole NATS 244
Persistance NATS avec JetStream 249
Sécurité NATS 249
Déploiement de NATS sur Kubernetes 251
Services de messagerie Azure 253
Azure Service Bus253
Azure Event Hubs 258
Azure Event Grid261
Résumé263

11. Sans serveur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265


Introduction à l'informatique sans serveur 265
Qu’est-ce que l’informatique sans serveur ? 265
Qu'est-ce qu'une fonction sans serveur ? 266
Le paysage sans serveur 266
Avantages de l'approche sans serveur 267
Inconvénients potentiels de l'approche sans serveur 268
Azure Function Apps 268
Architecture d’une application de fonction 269
Création d'une application de fonction 270
Knative272
Architecture de Knative 272
Installation et exécution de Knative Serving sur Kubernetes 272
Installation et exécution de Knative Eventing sur Kubernetes 274
KEDA276
Architecture de KEDA 276
Installation de KEDA sur Kubernetes 277

viii Table des matières


OpenFaaS281
Architecture d'OpenFaaS 281
Installation d'OpenFaaS 281
Écriture de votre première fonction OpenFaaS 282
Résumé283

12. Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285


Et ensuite ? 287

Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289

Table des matières ix


Préface

Le cloud computing a été largement adopté par les entreprises comme un modèle de
transformation numérique de nouvelle génération qui stimule la croissance et l’innovation.
Aujourd’hui, les clients recherchent un écosystème et une expérience rapides, qui s’intègrent
parfaitement à leurs services existants. Du point de vue de l’entreprise, le Cloud propose des
services évolutifs, très fiables et hautement disponibles aux clients et aux entreprises. Du
point de vue de l’utilisateur, le Cloud fournit un modèle simple pour acquérir des services
informatiques sans avoir besoin de comprendre pleinement l’infrastructure et la technologie
sous-jacentes.
Pour tirer pleinement parti de la rapidité et de l’agilité des services Cloud, de nombreuses
applications existantes ont été transformées en applications Cloud natives, et de nouvelles
solutions axées sur le Cloud sont en cours de conception. Les applications Cloud natives
sont conçues à partir de zéro en vue d'adopter des changements rapides, tout en présentant
de grandes évolutivité et résilience. Par défaut, l’infrastructure sous-jacente des applications
Cloud natives joue un rôle essentiel pour répondre efficacement aux besoins d’une
entreprise. Si l’infrastructure sous-jacente n’est pas conçue avec les bonnes pratiques, même
les meilleures applications Cloud natives s'avéreront défaillantes dans les environnements
de production.
Cet ouvrage explore la façon dont les infrastructures Cloud natives modernes sur Azure
peuvent être créées et gérées dans un environnement de production, ainsi que diverses
exigences et considérations de conception des applications Cloud natives.

À qui s’adresse cet ouvrage ?


Ce manuel présente de façon simple mais exhaustive le paysage Cloud natif et toutes les
principales technologies utilisées par les ingénieurs pour créer des environnements fiables.
Cet ouvrage est destiné aux ingénieurs de fiabilité de site, aux ingénieurs DevOps, aux
architectes de solutions, aux passionnés d’Azure et à toute personne impliquée dans la
création, la migration, le déploiement et la gestion des opérations quotidiennes d’une charge
de travail Cloud native.

xi
Ce manuel suppose que vous possédez des connaissances de base sur le Cloud et la culture
DevOps en général. Même si ce n’est pas le cas, et si vous souhaitez mieux comprendre l'en-
gouement autour du Cloud natif et d’autres technologies sophistiquées, cet ouvrage vous est
également destiné.

Objectifs de ce manuel
Une fois que vous aurez lu ce manuel, vous serez en mesure de suivre et de créer votre
propre infrastructure sur Microsoft Azure. Nous présentons une introduction séquentielle
aux principaux composants du monde Cloud natif et comment les utiliser, les déployer et
les maintenir sur Azure. Vous découvrirez également la nécessité d'adopter des technologies
Cloud natives dans le nouveau monde d’aujourd’hui, les problèmes qu’elles résolvent et les
bonnes pratiques concrètes.
Grâce à cet ouvrage, vous pourrez :

• Découvrir comment créer une infrastructure Cloud native sur Azure en suivant le par-
cours du paysage Cloud natif de la Cloud Native Computing Foundation
• Déterminer quelles technologies utiliser à différentes étapes de la conception
• 
Découvrir comment résoudre les problèmes rencontrés lors de la gestion et de
l’exploitation de l’infrastructure Cloud native, ainsi que les technologies nécessaires
pour y remédier

Parcourir cet ouvrage


Ce manuel est organisé comme suit :

• Le chapitre 1 fournit une introduction de base au Cloud, en expliquant le besoin


d'adopter des technologies Cloud natives, ainsi que leurs adaptations.
• Le chapitre 2 couvre les notions de base de l’infrastructure en tant que code (IaC)
avec Terraform et Packer, et introduit Azure et Ansible en tant que provisionneurs/
gestionnaires de configuration.
• Le chapitre 3 présente les conteneurs et les runtimes de conteneurs, tels que contain-
erd, Docker et CRI-O. Les différents types de registres de conteneurs seront également
abordés.
• Le chapitre 4 aborde Kubernetes et présente les détails nécessaires à l’utilisation
de l’infrastructure dans les chapitres à venir.
• Le chapitre 5 traite spécifiquement d'Azure Kubernetes Service et du gestionnaire
de package Helm.
• Le chapitre 6 traite du caractère observable de l’infrastructure Cloud native moderne.
• Le chapitre 7 concerne la découverte et le maillage de services. Ce chapitre présente
le serveur DNS CoreDNS et le maillage de services Istio.

xii Préface
• Le chapitre 8 couvre la gestion des réseaux et des stratégies, y compris Calico, Flannel,
Cilium, la stratégie Azure et les interfaces de mise en réseau de conteneurs Open Policy
Agent.
• Le chapitre 9 explique comment les systèmes de stockage persistants sont déployés sur
l’infrastructure Cloud native, en mettant l’accent sur Azure Storage, Vitess, Rook, TiKV,
et etcd.
• Le chapitre 10 se concentre principalement sur les plateformes de messagerie et de
streaming, telles que NATS et les services de messagerie Azure.
• Le chapitre 11 est une simple introduction au principe du sans serveur dans le paysage
Cloud natif.
• Le chapitre 12 synthétise tous les éléments abordés dans les chapitres précédents.

Conventions utilisées dans cet ouvrage


Les conventions typographiques suivantes sont utilisées dans cet ouvrage :
Italique
Indique de nouveaux termes, URL, adresses e-mail, noms de fichiers et extensions de fichier.
Largeur constante
Utilisé pour les listes de programmes, ainsi que dans les paragraphes pour désigner des
éléments de programme, tels que les noms de variables ou de fonctions, les bases de données,
les types de données, les variables d’environnement, les instructions et les mots-clés.

Largeur constante en gras


Affiche des commandes ou d’autres textes qui doivent être tapés littéralement
par l’utilisateur.

Cet élément correspond à une astuce ou une suggestion.

Cet élément désigne une remarque générale.

Cet élément indique un avertissement ou une mise en garde.

Préface xiii
Utilisation d’exemples de code
Le matériel supplémentaire (exemples de code, exercices, etc.) peut être téléchargé à l’adres-
se https://github.com/stormic-nomad-nishant/cloud_native_azure.
Si vous avez une question technique ou un problème concernant les exemples de code,
veuillez envoyer un e-mail à l'adresse bookquestions@oreilly.com.
Ce manuel est conçu pour vous aider dans votre travail. Les exemples de code proposés
dans cet ouvrage peuvent généralement être utilisés dans vos programmes et votre
documentation. Vous n’avez pas besoin de nous contacter pour obtenir une autorisation, à
moins que vous ne reproduisez une partie importante du code. L’écriture d’un programme
qui utilise plusieurs segments de code de ce manuel ne nécessite aucune autorisation.
La vente ou la distribution d’exemples des ouvrages O’Reilly nécessite une autorisation.
Aucune autorisation n'est nécessaire pour répondre à une question en citant cet ouvrage et
un exemple de code. L’intégration d'une quantité importante d’exemple de code tiré de ce
manuel dans la documentation de votre produit nécessite une autorisation.
Même si nous apprécions l'attribution, nous le l'exigeons généralement pas. Une attribution
inclut généralement le titre, l’auteur, l’éditeur et l’ISBN. Par exemple : « Infrastructure Cloud
native avec Azure par Nishant Singh et Michael Kehoe (O’Reilly). Copyright 2022 Nishant
Singh et Michael Kehoe, 978-1-492-09096-0 ».
Si vous estimez que votre utilisation d’exemples de code ne peut être qualifiée d'utilisation
raisonnable ou ne respecte pas les conditions des autorisations susmentionnées, n’hésitez
pas à nous contacter à l'adresse permissions@oreilly.com.

Apprentissage en ligne O’Reilly


Depuis plus de 40 ans, O’Reilly Media propose une formation, des
O’REILLY
®
connaissances et des informations technologiques et commerciales
afin de garantir la réussite des entreprises.

Notre réseau unique d’experts et d’innovateurs partage ces connaissances et ces compétences
grâce à des livres, des articles et notre plateforme d’apprentissage en ligne. La plateforme
d’apprentissage en ligne d'O’reilly offre un accès à la demande à des cours de formation en
direct, des parcours d’apprentissage détaillés, des environnements de codage interactifs et
une vaste collection de textes et de vidéos d’O’Reilly, ainsi que ceux de plus de 200 autres
éditeurs. Pour en savoir plus, consultez l'adresse http://oreilly.com.

xiv Préface
Comment nous contacter
Veuillez adresser vos commentaires et vos questions concernant cet ouvrage à l’éditeur :

O’Reilly Media, Inc.


1005 Gravenstein Highway North
Sébastopol, CA 95472
800-998-9938 (aux États-Unis ou au Canada)
707-829-0515 (international ou national)
707-829-0104 (fax)

Nous proposons une page Web dédiée à cet ouvrage, où nous répertorions les erreurs, les
exemples et toutes les informations supplémentaires. Vous pouvez accéder à cette page
à l’adresse https://oreil.ly/cloud-native-azure.
E-mail bookquestions@oreilly.com pour commenter ou poser des questions techniques sur
cet ouvrage.
Pour connaître les actualités et en savoir plus sur nos manuels et cours, consultez la page
http://oreilly.com.
Suivez-nous sur Facebook : http://facebook.com/oreilly.
Suivez-nous sur Twitter : http://twitter.com/oreillymedia.
Suivez-nous sur YouTube : http://youtube.com/oreillymedia.

Remerciements
Nous tenons à remercier Rita Fernando, Nicole Taché et Jennifer Pollock, nos éditrices
chez O’Reilly qui nous ont beaucoup soutenu en nous aidant considérablement dans le
processus d’écriture. Nous tenons également à remercier nos réviseurs techniques pour
leurs précieuses contributions à ce manuel. La rédaction de cet ouvrage n’aurait pas été
possible sans les révisions minutieuses et précieuses de Matt Franz, Peter Jausovec, Liudmila
Sinkevich, Steve Machacz et Alexander Kabonov.
En outre, nous tenons à remercier Matt Franz pour ses évaluations, suggestions et contributions
approfondies qui ont permis d'améliorer la qualité de cet ouvrage. Matt a apporté des
commentaires détaillés sur les chapitres et a contribué à la rédaction du chapitre 10.
Nishant souhaite remercier sa mère, son père et son épouse, Mahek, pour leur patience et
leur soutien alors qu'il travaillait sur ce manuel. Il aimerait également remercier l’équipe
d'O’Reilly et ses pairs de LinkedIn qui ont soutenu cette idée dès le début.
Michael aimerait remercier sa famille, qui l’a encouragé à suivre ses rêves et à ne jamais
abandonner. Il aimerait également remercier sa meilleure amie, Jennifer, pour son soutien
et sa patience pendant qu’il travaillait sur ce livre, le personnel d'O’Reilly pour l'avoir aidé
à concrétiser son idée en rédigeant cet ouvrage, et tous les réviseurs pour leur contribution.

Préface xv
CHAPITRE 1
Introduction : Pourquoi opter pour
une option Cloud native ?

Même si l’utilisation du Cloud permet de résoudre les problèmes liés à l’évolutivité, la


disponibilité et la fiabilité de nos applications, il ne s’agit pas d’une solution miracle. Nous ne
pouvons pas mettre nos applications dans le Cloud en espérant qu’elles soient indéfiniment
opérationnelles. Nous ne pouvons pas non plus packager des applications dans des
conteneurs pour les transformer en microservices afin qu'ils s’exécutent parfaitement dans
le Cloud. Pour tirer pleinement parti des avantages du Cloud, nous devons mettre en place
des infrastructures et des services en mettant l'accent sur le Cloud.
Pour vraiment comprendre le parcours Cloud natif et son importance, nous devons d’abord
étudier le passé, notamment l’infrastructure et les services aux premiers jours d’Internet.
C'est parti !

Transition vers le Cloud


À l’aube d’Internet, l’infrastructure d’application Web globale était hébergée sur des
serveurs physiques qui devaient être achetés et préparés avant de pouvoir recevoir les
applications. Les équipes informatiques devaient acheter physiquement les serveurs et les
configurer sur site, installer des systèmes d’exploitation de serveur appropriés, préparer
les environnements, puis déployer des applications sur ceux-ci. Cette approche posait de
nombreux problèmes : les serveurs sous-utilisés par exemple (étant donné qu'ils n'étaient
jamais utilisés entièrement). Il était donc difficile d’exécuter plusieurs applications, et les
coûts de configuration et de maintenance étaient élevés. La virtualisation a été développée
pour optimiser l'efficacité de l'utilisation des serveurs physiques. La virtualisation crée
une couche d’abstraction sur le matériel physique qui permet de diviser et de partager les
ressources sous-jacentes, telles que les processeurs, la mémoire et le stockage.
La virtualisation a résolu de nombreux problèmes liés à l’utilisation des ressources et au concept
de multilocataire, mais vous deviez toujours posséder le matériel requis pour déployer votre

1
application et vous deviez toujours payer les frais généraux liés à l’exécution de votre datacenter.
Cette situation a induit la nécessité d’exécuter une infrastructure en tant que service (IaaS),
où les serveurs appartiennent à des tiers qui sont responsables de l’infrastructure sous-jacente
de vos applications. Cette période à marqué l’avènement de l’ère du cloud computing. Les
entreprises ont ainsi pu se concentrer sur leurs applications et leurs environnements sous-
jacents sans se soucier des problèmes liés au matériel, aux frais généraux ou à la configuration.
Les solutions IaaS ont été suivies par la plateforme en tant que service (PaaS), qui visait à réduire
davantage le fardeau en séparant l’environnement logiciel sous-jacent et le runtime. Ainsi,
les développeurs devaient uniquement se concentrer sur l’écriture de leurs applications et la
définition des dépendances. La plateforme de service était quant à elle entièrement responsable
de l’hébergement, de l’exécution, de la gestion et de l’exposition des applications. Les solutions
PaaS ont ouvert la voie à des services Cloud entièrement gérés avec l’avènement du logiciel
en tant que service (SaaS), populairement connu sous le nom de « logiciel à la demande ». Ce
dernier propose aux clients l’application en tant que service basée sur leur consommation.
À mesure que le cloud computing gagnait en popularité, l’idée de disposer de technologies
Cloud natives qui utiliseraient le Cloud de manière plus efficace, tout en exploitant
pleinement le potentiel de l’infrastructure Cloud et de ses diverses offres a également séduit
de nombreux individus. C'est ainsi que l’infrastructure et les applications Cloud native sont
nées. L'infrastructure Cloud native crée une abstraction sur l’infrastructure sous-jacente
du fournisseur Cloud et expose l’infrastructure avec des API. Cette philosophie de gestion
d’infrastructure facilite énormément la mise à l’échelle, tout en réduisant la complexité sous-
jacente, en vue d'améliorer indirectement la disponibilité, la résilience et la maintenabilité. De
même, les applications Cloud natives renforcent le lien entre l’application et l’infrastructure
en intégrant des fonctions de prise en charge, telles que les contrôles d’intégrité, la télémétrie
et les métriques, la résilience, un environnement de microservices et la réparation spontanée.
Abordons désormais les défis de l’environnement de cloud computing.

Défis liés au Cloud


Les fournisseurs de Cloud public sont devenus une solution d’entreprise dominante pour
répondre aux besoins croissants de l’industrie et aux exigences des entreprises. Ils offrent
des avantages comme la haute disponibilité, l’évolutivité et la flexibilité de concevoir vos
applications à l'aide de services Cloud. Lorsque les solutions Cloud ont été introduites pour
la première fois, de nombreux défis, y compris la sécurité, la gestion des coûts efficace,
la conformité et les performances concernaient les clients potentiels. Ces premiers défis
appartiennent désormais au passé pour la majorité des clients du Cloud, car ils ont été
résolus grâce aux progrès réalisés au niveau des technologies de fournisseur Cloud et du
déploiement des solutions dans le Cloud par les entreprises.
Même si nous avons parcouru un long chemin, le Cloud n'est pas parfait pour autant. Le
paysage du Cloud présente encore certains défis, même s'ils ont l'air très différents de ceux
que nous avons rencontrés lors de l'avènement du Cloud. Les clients doivent désormais
tenir compte des défis suivants :

2 Chapitre 1 : Introduction : pourquoi opter pour une option Cloud native ?


Trop de choix
Nombreux sont les fournisseurs Cloud qui proposent un large éventail de services.
Vous devez donc embaucher des architectes experts et des équipes d'ingénierie qui
savent comment exploiter les services et les utiliser selon votre cas d’utilisation métier.
Non seulement l’embauche de ces ingénieurs est difficile, mais trouver des ingénieurs
qui sont des experts dans un domaine spécifique nécessite un investissement de temps
important.
Croissance et développement rapides des services et technologies Cloud
De nombreux nouveaux services Cloud sont publiés par des géants de la prestation
Cloud, tels qu'Amazon, Microsoft et Google. Cette tendance accentue la nécessité de
former des ingénieurs à ces nouveaux services et de maintenir ces services en pensant
à l'avenir, à mesure que les applications se développeront. Souvent, le niveau des
investissements dans ces services provoque également indirectement une dépendance
exclusive à l'égard d'un fournisseur, entraînant une multitude de contraintes ultérieures
dans la conception des applications.
Plusieurs générations de technologies
Au fil de notre adoption du Cloud, nous avons également répliqué nos piles
d’applications à partir de diverses générations de solutions d’infrastructure, qu'il s'agisse
de machines virtuelles, de conteneurs ou de technologies sans serveur. Cette migration
des applications nécessite un effort considérable pour comprendre les technologies
sous-jacentes et les prendre en charge à l’avenir.
Complexité opérationnelle croissante
Ces technologies en pleine croissance, combinées avec une migration accélérée des
charges de travail vers le Cloud, ont donné naissance à une complexité opérationnelle
et à une liste croissante de facteurs à prendre en compte, notamment les systèmes de
stockage, les modèles de sécurité, les modèles de gouvernance et les plateformes de
gestion, entre autres.
Évolution des besoins et des technologies de l’entreprise
De nouveaux domaines de technologie et de changement culturel ont également
rapidement transformé l’architecture de l’entreprise. Suite à l’avènement de la culture
DevOps, une nouvelle application qui nécessitait autrefois des semaines ou des mois
de développement peut maintenant être déployée en quelques minutes. Des domaines
plus avancés, tels que la science des données et le Machine Learning, entrent également
en jeu, en augmentant les besoins des entreprises et la maturité technique globale.
Ainsi, malgré la puissance du cloud computing, les entreprises ont dû surmonter
de nombreuses complexités. Il est finalement devenu évident que les entreprises souhaitaient
bénéficier de la vitesse, de l’évolutivité et de la marge du Cloud, en évitant également les
frais généraux. Pour y parvenir, l'adoption de l’approche Cloud native s'est avérée nécessaire
pour le développement d’applications, afin que les entreprises puissent tirer pleinement
parti du Cloud.

Défis liés au Cloud 3


Cloud Native Computing Foundation
À mesure que de plus en plus d’entreprises adoptent des technologies Cloud natives, en
créant des logiciels en interne et en collaborant étroitement avec d’autres entreprises pour
accélérer la commercialisation des produits, des progrès ont été réalisés pour améliorer le
domaine Cloud natif. La Cloud Native Computing Foundation (CNCF) est une organisation
qui contribue à montrer l'exemple dans cette initiative. Sa mission est la suivante :

Les technologies Cloud natives permettent aux entreprises de créer et d’exécuter des
applications évolutives dans des environnements modernes et dynamiques, tels que
les Clouds publics, privés et hybrides. Les conteneurs, les maillages de services, les
microservices, l’infrastructure immuable et les API déclaratives illustrent cette approche.
Ces techniques permettent d'utiliser des systèmes associés librement qui sont résilients,
gérables et observables. Combinés avec une automatisation robuste, ils permettent
aux ingénieurs d’effectuer fréquemment des changements à forte incidence et de façon
prévisible avec un minimum d'efforts.
La Cloud Native Computing Foundation vise à faciliter l’adoption de ce paradigme en favorisant
et en soutenant un écosystème de projets open source et non rattachés à un fournisseur. Nous
démocratisons les modèles de pointe pour rendre ces innovations accessibles à tous.
La CNCF a compilé un paysage Cloud natif interactif qui montre toute l’étendue des solutions
Cloud natives d'aujourd’hui. Le paysage CNCF agit comme une carte des technologies Cloud
natives et fournit des directives pour la création d’applications Cloud natives performantes.
Bien que le paysage Cloud natif fournisse beaucoup d’informations sur la conception des
services, celle-ci peut s'avérer délicate, en particulier pour les personnes inexpérimentées,
car elle comprend un certain nombre de services qui sont utilisés ensemble. Dans cet
ouvrage, nous avons choisi la meilleure technologie disponible, tout en suivant les directives
de la CNCF pour mieux naviguer dans le monde Cloud natif.

Adopter une infrastructure Cloud native avec Azure


Maintenant que vous comprenez les origines du cloud computing, les défis liés à l’exploitation
dans le Cloud et la façon dont les offres Cloud natives visent à changer le développement et
la livraison de services, étudions comment utiliser cet ouvrage pour aller de l’avant.
Si vous jetez un œil au mouvement Cloud natif et que vous retracez son histoire dès le début,
la première technologie qui se présente est Kubernetes. Celle-ci a d'ailleurs été adoptée
par de nombreuses personnes. En surface, vous pouvez constater que de nombreuses
entreprises ont intégré Kubernetes au cœur de leur pile. On peut par conséquent en déduire
que Kubernetes est un outil essentiel capable de résoudre tous vos problèmes et munir
votre environnement de la correction spontanée et de la tolérance aux pannes comme par
magie. Cette idée fausse a incité de nombreux individus à aborder Kubernetes comme une
solution miracle, sans comprendre pleinement sa signification profonde. Il est important de
comprendre la nécessité de ce type de solutions et d’obtenir des informations sur l’écosystème
complet, tout en tenant compte de la situation globale.

4 Chapitre 1 : Introduction : pourquoi opter pour une option Cloud native ?


Dans les chapitres suivants, nous fournissons des conseils sur la création d'environnements
Cloud natifs conformément aux directives suggérées par la CNCF. Nous avons écrit cet
ouvrage comme une initiation pour les ingénieurs et les passionnés qui commencent à
peine à s’initier aux transformations Cloud natives et qui souhaitent explorer le besoin
global d’architectures Cloud natives. Vous découvrirez des moyens pratiques de créer des
infrastructures Cloud natives sur Microsoft Azure en naviguant dans le paysage CNCF.
Vous apprendrez également les principes de l’infrastructure Cloud native comme si vous
étiez un débutant, et vous déploierez des solutions matures sur Azure.1 Étant donné
qu'Azure est l’un des principaux acteurs de l’écosystème du Cloud public, il fournit une
compréhension approfondie de la pile d’infrastructure. Nous allons vous présenter les
bases de l’infrastructure Cloud, en expliquant comment adopter le Cloud natif à travers
diverses technologies, tout en soulignant pourquoi ces technologies sont nécessaires. Nous
présenterons en détail les technologies Cloud natives et les services Azure à l’aide d’une
application pratique. Nous décrirons également les avantages des solutions basées dans
le Cloud (avec Azure) en les comparant à ceux qui sont offerts par le Cloud natif. Notre
objectif consiste à vous proposer un manuel qui présente toutes les principales technologies
Cloud natives et leur importance afin que vous puissiez comprendre le raisonnement
logique derrière les avantages de l’utilisation de ces technologies.

Résumé
Dans ce chapitre d'introduction, nous avons brièvement expliqué ce que représente le cloud
computing et la façon dont les technologies Cloud natives permettent d'améliorer l’adoption
du Cloud. Vous avez découvert comment le Cloud est devenu populaire et comment le
cloud computing a évolué du matériel physique à un environnement sans serveur. Vous
avez également découvert les défis liés au cloud computing et à la nécessité croissante de
s’adapter aux technologies Cloud natives. Nous avons expliqué la signification du terme
Cloud natif et la façon dont le reste du manuel abordera le parcours vers le Cloud natif
sur Azure. Nous espérons que vous trouverez ce parcours intéressant et que cet effort vous
permettra d'adapter plus efficacement les technologies Cloud natives.

1 Azure fournit également de nombreuses directives architecturales, qui présentent les bonnes pratiques
en matière de création de services sur Azure : consultez les adresses https://docs.microsoft.com/azure/
architecture et https://azure.microsoft.com/blog/azure-application-architecture-guide.

Résumé 5
CHAPITRE 2
Infrastructure en tant que code :
Configuration de la passerelle

Le Cloud, qui propose un riche assortiment de solutions complètes (mise en réseau aisée,
informatique à l’échelle mondiale etc.) devient la destination incontournable pour la plupart
des organisations, quelle que soit leur taille. L’écosystème de la plupart des fournisseurs
Cloud est désormais plus important que jamais et comprend des machines virtuelles
simples, des clusters gérés complexes et même une infrastructure hautement sophistiquée.
Microsoft Azure, par exemple, propose une variété de services bien conçus aux utilisateurs.
Ces solutions s’intègrent à une infrastructure résiliente par nature. Elles présentent des
contrats de niveau de service (SLA) bien définis1 en vue de répondre aux besoins de tous les
clients, qu'il s'agisse de petites start-ups ou de grandes entreprises.
Bien que la nature dynamique du cloud computing soit une aubaine pour les entreprises, il
peut être difficile de maintenir des services dans des environnements Cloud à mesure que
votre entreprise se développe. À mesure que vous exploitez de plus en plus de services basés
dans le Cloud pour répondre aux besoins croissants de votre entreprise, vous réaliserez
rapidement que vous ne pourrez plus maintenir vos applications et votre infrastructure
sous-jacente en cliquant simplement sur la console Web chaque fois que vous souhaitez
effectuer une nouvelle action. La conception évolutive du Cloud a été fondée sur les
générations précédentes de matériel et d’infrastructure virtualisée associés à un plan de
contrôle programmable qui fournit une certaine flexibilité, sous la forme de couches d'API.
Ces API sont exploitées par des outils d’infrastructure en tant que code (IaC) pour permettre
aux utilisateurs du Cloud de maintenir facilement leur environnement.
Lorsque vous souhaitez créer une infrastructure dédiée à la production à partir de zéro, vous
devez commencer à traiter vos définitions d’infrastructure de la même manière que vous traitez

1 Un SLA est un accord entre le fournisseur Cloud et le client (vous) sur des métriques mesurables, telles que
la disponibilité, la réactivité et les responsabilités.

7
votre code. La configuration et la mise à l’échelle manuelle de votre infrastructure ne sont pas
seulement fastidieuses, il s’agit également d’un processus sujet aux erreurs : vous ne pouvez pas
créer la même infrastructure deux fois avec le même niveau de confiance. Lorsque vous traitez
votre infrastructure en tant que code, vous héritez également des principes d’ingénierie logicielle,
tels que le contrôle de version, la testabilité, l’automatisation et la vitesse de développement
dans le cadre de la stratégie DevOps. L'IaC garantit que votre infrastructure peut être conçue
de manière cohérente et reproductible. À mesure que les équipes de développement logiciel ont
adopté les méthodologies agiles, elles ont été confrontées à la nécessité de déplacer davantage
de fonctions et de solutions plus fréquemment et plus rapidement vers le Cloud. À ce titre,
l’infrastructure est devenue étroitement liée aux applications, et les équipes sont tenues de
gérer les applications et l’infrastructure à l’aide d’un processus transparent. En outre, les équipes
doivent itérer et déployer à plusieurs reprises de nouvelles fonctions et une infrastructure sous-
jacente plus fréquemment. Les modèles prédéfinis offerts par les fournisseurs de Cloud, qui
fournissent des instructions sur la création d’une infrastructure, permette d'accélérer la mise
en œuvre de l'IaC. Azure prend en charge nativement les modèles Azure Resource Manager
(ARM), qui sont utilisés pour définir et créer des ressources d’infrastructure directement
associées à la philosophie de l’IaC. Cette combinaison du Cloud et de l'IaC permet aux
entreprises d’introduire des changements plus rapidement et plus efficacement.
Ce chapitre pose les fondements d'une compréhension approfondie de l'IaC et d’Azure.
Nous allons commencer par introduire le concept d'IaC et son importance dans la création
d’une infrastructure Cloud native. Nous présenterons ensuite Microsoft Azure, qui est le
fournisseur de Cloud que nous utiliserons tout au long de cet ouvrage.
Nous étudierons ensuite Terraform, qui nous servira d'outil pour la mise en œuvre de
l’IaC. Nous aborderons également Packer, qui est principalement un outil de création
d’images de machine et Ansible, afin de vous présenter la gestion de la configuration, tout
en développant des applications Cloud natives. Enfin, nous étudierons Azure DevOps avant
de clore le chapitre. Commençons dès maintenant.

L’infrastructure en tant que code et son importance dans


le monde Cloud natif
Autrefois, lorsqu’une équipe informatique souhaitait se procurer du nouveau matériel pour
le déploiement d’applications, elle devait d’abord lancer une demande de provisionnement
matériel, laquelle nécessitait plusieurs jours ou même plusieurs semaines. Une fois le
matériel livré sur site, celui-ci nécessitait encore un délai de plusieurs jours pour être
opérationnel, avant que les applications ne puissent être déployées. Ces processus lents ont
désormais disparus grâce à la croissance du DevOps et du Cloud. Les cycles de lancement
se sont accélérés et les besoins de l'entreprise sont satisfaits plus rapidement.
Toutefois, même avec l’introduction du Cloud, la gestion de l’infrastructure demeure
problématique. Bien que les serveurs physiques ont été remplacés par des serveurs virtuels et
que la gestion de l’infrastructure complexe relève désormais de la responsabilité du fournisseur
Cloud, la croissance et la mise à l’échelle de l’infrastructure demeurent une tâche difficile et sujette à

8 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


l’erreur. Les équipes d’ingénierie devaient constamment créer de nouvelles infrastructures et piles
logicielles, tout en continuant à maintenir l’ancienne infrastructure. Pour atténuer l'ennui et la
fatigue associés à ces tâches, la création et la gestion des systèmes basés dans le Cloud sont devenues
automatisées, en vue d'établir une infrastructure et un environnement d’application robustes.
Finalement, lorsque le code a été adapté pour configurer les couches de calcul, de mise en réseau
et de stockage de la pile, les avantages du traitement de l’infrastructure en tant que code sont
devenus évidents. Cette approche a initié l’adaptation de l'IaC dans les environnements Cloud.

L'infrastructure en tant que code est la philosophie de traitement de votre infrastructure en


tant qu’instructions programmables. À mesure que vous enregistrez des fichiers de contrôle
de code source, vous enregistrez vos définitions d’infrastructure. Le traitement de votre
infrastructure en tant que code offre de nombreux avantages, notamment :

• La possibilité d’enregistrer une version de vos définitions d’infrastructure et de restaurer


une version particulière en cas de problème
• Idempotence, qui vous permet de reproduire exactement la même infrastructure avec
un minimum d’effort
• Journalisation, surveillance et dépannage normalisés sur l’ensemble de votre pile
• La réduction des points de défaillance uniques de vos équipes d’ingénierie (c.-à-d., où
un seul ingénieur sait comment créer un type de système particulier)
• Augmentation de la productivité des développeurs (les ingénieurs de fiabilité de site
[SRE] et/ou les ingénieurs DevOps peuvent se concentrer sur l’écriture de code que les
développeurs peuvent utiliser pour créer un environnement de test)
• Interaction manuelle minimale, induisant une infrastructure moins sujette aux erreurs
• Réduction drastique du temps moyen de récupération (MTTR) suite à des défaillances
• La possibilité de tester votre infrastructure avant même sa création

Avec les avantages de l’IaC, il devient évident que ces outils jouent un rôle important dans
les environnements Cloud natifs modernes où les applications doivent être modifiées à
la volée. Les modifications peuvent être mineures, telles que l’ajout d’une nouvelle balise
de configuration ou importantes, comme l’ajout de nouveaux clusters pour répondre aux
exigences de capacité. Une stratégie IaC fiable permet aux développeurs de gérer facilement
ces changements, sans compromettre la rapidité du processus de développement logiciel.
Une fois que l'IaC a été intégré au cœur de votre processus de création d’infrastructure, il
contribue également à la conception d’une infrastructure anti-fragile.

Infrastructure anti-fragile
Les infrastructures anti-fragiles sont des systèmes capables de résister
au stress, tout en démontrant de l’élasticité, ainsi que de la résilience. Le
principe anti-fragile vise à créer une infrastructure capable de gérer des
événements imprévisibles et irréguliers, tout en renforçant la croissance.

L’infrastructure en tant que code et son importance dans le monde Cloud natif 9
Les applications Cloud natives actuelles sont considérées comme des applications en
mouvement, car l’infrastructure sous-jacente continue d'évoluer et de se mettre à niveau. Le
Cloud vous permet de traiter vos infrastructures comme des entités éphémères, plutôt que
des entités permanentes. L’approche Cloud native favorise la création d’une infrastructure
remplaçable, par opposition à la réparation d'une infrastructure défaillante. Dans la mesure
où l’approche Cloud native découple les applications de votre infrastructure, elle confère
aux ingénieurs le contrôle et la confiance nécessaires pour modifier l’infrastructure, sachant
qu’ils peuvent annuler les modifications et avancer très facilement.

Philosophie appliquée à l'infrastructure Cloud native


Lorsque vous concevez une infrastructure Cloud native, traitez
toujours votre infrastructure sous-jacente comme une entité éphémère,
et non comme un système durable et maintenable. Ainsi, si un serveur
tombe en panne, il sera facile de le supprimer et de mettre en place un
nouveau serveur instantanément, plutôt que d’essayer de le réparer.

La transition d’une architecture monolithique à des microservices a également permis à


l'IaC de prendre de l’ampleur, en veillant à ce que les applications modernes puissent suivre
la méthodologie «  application douze  facteurs  », qui permet de prendre des décisions en
matière d’infrastructure indépendamment du développement de l’application. L’IaC offre
un autre avantage dans la création d’une infrastructure Cloud native  : il permet la mise
en œuvre d'une infrastructure immuable. Autrement dit, une fois que l’infrastructure est
déployée, elle ne peut pas être modifiée ou configurée. Dans un scénario de déploiement
d’application mutable typique, une application est développée, puis elle est déployée
sur l’infrastructure sous-jacente, avant d'être configurée sur l’infrastructure sous-
jacente (figure 2-1). Ce processus introduit la dérive de configuration, qui permet à une
infrastructure d'évoluer au-delà de son état initial. L’infrastructure immuable, quant à elle,
se concentre sur le redéploiement de l’infrastructure et des applications chaque fois qu’une
nouvelle modification est ajoutée, en maintenant étroitement l’état exact de l'infrastructure.
L'immuabilité est réalisée en développant et en configurant votre application dans un état
préparé (par exemple, une image de machine), puis en la déployant (figure 2-2).

Figure 2-1. Le flux mutable

Figure 2-2. Le flux immuable

10 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Voici les principaux avantages de la création d’une infrastructure immuable :

• Infrastructure/systèmes prévisibles plutôt que des infrastructures/systèmes réactifs


• Des déploiements qui sont désormais de nature atomique
• Contrôle accru sur votre infrastructure, en induisant une meilleure télémétrie

Maintenant que nous avons discuté de l’IaC et de sa valeur lors de la création de


l’infrastructure Cloud native, examinons comment vous pouvez utiliser Microsoft Azure
dans votre infrastructure Cloud native.

Mise en route avec Azure et configuration de


l’environnement
Microsoft Azure est un fournisseur Cloud comptant des dizaines de régions à travers le
monde. Azure vous permet de créer et de déployer rapidement une infrastructure à l’aide
de moyens manuels et automatisés, comme nous le verrons tout au long de cet ouvrage.
Azure en tant que PaaS (plateforme en tant que service) confère une flexibilité importante
à la communauté des développeurs pour innover, créer et déployer des applications. Nous
avons choisi Azure comme environnement Cloud en raison de la vitesse, la flexibilité,
la sécurité, la récupération d’urgence et la courbe d’apprentissage simple qu'il offre.
Cette section couvre les concepts de base autour d’Azure et la création d'un compte Azure.

Notions de base d’Azure et préparation de votre environnement Azure


Avant de commencer avec Azure, il est important de comprendre certaines de ses structures
de base. La compréhension de ces concepts permettra d'éclairer les décisions stratégiques
à l’avenir :
Locataire
Un locataire Azure est une représentation Azure Active Directory (AAD) d’une
organisation. Vous connaissez probablement déjà les services Active Directory (AD)
traditionnels. Lorsqu’une organisation crée un compte Microsoft, une instance AAD
est créée.
Toutes les instances AAD sont complètement séparées les unes des autres, de même
que toutes les identités (comptes utilisateur ou service). Si vous disposez d’un locataire
Microsoft existant (par exemple, pour Office  365), vous pouvez l’utiliser pour votre
empreinte Azure. Vous pouvez également créer votre propre locataire AAD via le portail
Azure. Pensez à activer l’authentification multifacteur (MFA)2 sur votre locataire.

2 La MFA ajoute une couche de protection au processus de connexion. Lorsqu'ils accèdent aux comptes
ou aux applications, les utilisateurs procèdent à une vérification d’identité supplémentaire, telle que le
balayage d’une empreinte digitale ou la saisie d’un code qu’ils reçoivent sur leur téléphone.

Mise en route avec Azure et configuration de l’environnement 11


Abonnements
Les abonnements Azure sont essentiellement un conteneur de facturation pour vos
ressources. Vous pouvez également définir des stratégies Azure spécifiques au niveau
de l’abonnement.
Groupes de gestion
Les groupes de gestion Azure permettent d’organiser et de gérer les abonnements.
Vous pouvez utiliser des groupes de gestion pour appliquer une stratégie/gouvernance
à toutes les ressources (y compris les abonnements) au sein du groupe de gestion. Tous
les abonnements au sein du groupe de gestion hériteront de la stratégie de groupe de
gestion par défaut.
Groupes de ressources
Un groupe de ressources est un conteneur qui contient des ressources associées pour une
solution Azure. Le groupe de ressources inclut les ressources que vous souhaitez gérer en tant
que groupe. Vous déterminez les ressources qui appartiennent à un groupe de ressources
en fonction de ce qui est le plus logique pour votre organisation. En outre, les ressources
peuvent être situées dans des régions différentes de celle du groupe de ressources.
Régions
Une région Azure contient au moins deux zones de disponibilité. Une zone de
disponibilité est un datacenter physiquement isolé dans une région géographique.
La figure 2-3 illustre l'association de ces structures basiques entre elles.

Figure 2-3. Structures basiques d'Azure

Création d'un compte Azure


Vous pouvez créer un compte Azure en naviguant vers https://azure.microsoft.com et en
utilisant l'option gratuite. Vous devrez vous connecter avec un compte Microsoft. Vous
pourrez également utiliser un compte Hotmail, Outlook ou Office  365. Toutefois, nous
recommandons vivement d’utiliser la MFA. Suivez les instructions sur le site Web Azure

12 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


pour créer votre compte. Pour continuer, cliquez sur le lien Mon compte dans le coin
supérieur droit ou accédez directement au portail Microsoft Azure.
Abordons désormais l'utilisation d'Azure CLI.

Installation d’Azure CLI


Azure CLI est un ensemble de commandes qui peuvent être utilisées pour créer et gérer
des ressources Azure. Vous pouvez utiliser Azure CLI sur le navigateur Web dans le portail
Azure, ou vous pouvez télécharger une version appropriée pour l'installer localement sur
votre ordinateur. Par exemple, sur MacOS, vous pouvez installer Azure CLI à l’aide de la
commande brew install azure-cli.
Vous pouvez interagir avec Azure via le portail à l’aide de la commande az, comme suit :
$ az account show
{
"environmentName": "AzureCloud",
"id": "b5627140-9189-4305-a94c-3b199afe86791",
"isDefault": true,
"name": "Visual Studio Enterprise",
"state": "Enabled",
"tenantId": "baeb0498-d28a-41cd-a20d-d9409814a06k",
"user": {
"name": "cloudnative@xyz.com",
"type": "user"
}
}

Vous pouvez même utiliser Azure CLI pour scripter de façon déclarative votre infrastructure
Cloud. Ces scripts peuvent être exécutés dans PowerShell ou Bash. Bien que les scripts
Azure s'avèrent performants pour des tâches telles que la création, le démantèlement et le
redéploiement récent de l’infrastructure, ils le sont un peu moins pour des tâches telles que
la mise à jour d’un environnement existant, en raison du manque de idempotence dans les
commandes Azure CLI.
En plus d’Azure, d’autres outils sont disponibles pour la création et la gestion des
environnements Cloud. Nous allons examiner les outils les plus importants.

Outils IaC de premier plan


Comme nous l’avons mentionné précédemment, des outils supplémentaires peuvent être
utilisés pour créer une infrastructure Cloud native en appliquant l’approche IaC. Ici, nous
allons nous concentrer sur trois des outils open source les plus populaires qui peuvent être
utilisés avec une variété de fournisseurs Cloud :

• Terraform, un outil IaC basé sur CLI


• Packer, un outil CLI conçu pour créer des images de machine
• Ansible, un outil de gestion de configuration

Outils IaC de premier plan 13


Vous pouvez également utiliser tous ces outils dans les configurations d’intégration
continue/déploiement continu (CI/CD). Dans ce scénario, dès que vous écrivez une nouvelle
configuration, celle-ci peut être intégrée en continu dans votre pipeline de déploiement.

Terraform
Terraform est un outil IaC open source d'HashiCorp, écrit dans le langage de programmation
Go. Le mot terraform signifie transformer l’environnement d’une planète de manière à ce
qu'il ressemble étroitement à l’atmosphère de la terre, propice à la vie de l’espèce humaine.
Cette analogie s'avère véridique lorsqu'elle est appliquée aux fournisseurs Cloud qui sont
assimilés à des environnements qui doivent être transformés en une infrastructure gérable,
afin que votre application puisse tirer pleinement parti du Cloud et atteindre son véritable
potentiel.
Terraform vous aide à créer, modifier et versionner une infrastructure dans le Cloud de
manière sûre et efficace. Cet outil utilise une syntaxe déclarative. Ainsi, vous n’avez qu’à
lui indiquer l'élément que vous souhaitez créer au lieu de la façon dont vous souhaitez le
concevoir. L’un des principaux avantages de Terraform est la prise en charge d’un langage
spécifique au domaine (DSL) facile à comprendre, appelé HashiCorp Configuration
Language (HCL), qui est relativement facile à interpréter pour les humains. Vous pouvez
également utiliser JSON aux mêmes fins. En outre, Terraform est indépendant du Cloud et
prend donc en charge plusieurs fournisseurs Cloud. Il est donc très facile à prendre en main
car vous n’avez pas besoin d’apprendre le langage (HCL/JSON) une nouvelle fois pour un
autre fournisseur Cloud. Cette section traite de l'utilisation de Terraform pour concevoir
une infrastructure sur Azure à l’aide de la philosophie IaC.
Pour que vous compreniez le fonctionnement de Terraform, nous devons d’abord expliquer
la terminologie de base :
Fournisseurs
Un fournisseur dans Terraform est généralement (mais pas toujours) un fournisseur
Cloud. Ce fournisseur est chargé d’exposer les ressources (par exemple, les ressources
Cloud) et d’interpréter l’API. Certains des fournisseurs pris en charge par Terraform
sont Azure, Amazon Web Services (AWS), Google Cloud Platform (GCP) et PagerDuty.
Plan
Lorsque vous créez votre infrastructure à l’aide de la commande terraform plan,
Terraform crée un plan d’exécution qui est basé sur le code que vous écrivez via les
fichiers de configuration Terraform (.tf). Le plan d’exécution inclut les étapes que
Terraform prendra pour concevoir l’infrastructure demandée.
Application
Lorsque vous êtes satisfait du plan que Terraform a créé, vous pouvez utiliser la
commande terraform apply pour créer l’infrastructure, qui finira par atteindre l’état
souhaité (en l'absence de conflits ou d'erreurs).

14 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


État
Terraform doit recourir à un mode de stockage de l’état de votre infrastructure afin
qu’il puisse le gérer. Les états sont stockés dans des fichiers d’état basés sur du texte,
qui détiennent le mappage de vos ressources Cloud à votre configuration. Terraform
procède généralement de deux façons pour stocker l’état de votre infrastructure  :
localement sur le système où vous l’initialisez, ou à distance. Le stockage d'état à distance
est la méthode recommandée, car il maintient l’état de votre infrastructure sauvegardé.
Ainsi, d'autres ingénieurs peuvent collaborer pour créer une infrastructure, car il existe
une source unique de vérité qui permet de connaître l’état actuel de l’infrastructure.
Les fichiers d’état ne doivent pas être enregistrés dans un référentiel Git, car ils peuvent
contenir des secrets. Vous pouvez utiliser le stockage blob Azure à la place. Terraform
prend également en charge le verrouillage par défaut des fichiers d’état.
Module
Un module Terraform est un groupe de nombreuses ressources qui sont utilisées
ensemble. Les modules permettent de créer une infrastructure réutilisable en faisant
abstraction des ressources qui sont configurées une fois et utilisées plusieurs fois. L’un des
principaux avantages de l’utilisation de modules est qu’ils vous permettent de réutiliser
le code qui peut être partagé ultérieurement entre les équipes pour différents projets.
Back-end
Un back-end décrit le chargement d'un état. C’est ici que votre fichier d’état sera stocké
à terme. Vous pouvez utiliser le stockage de fichiers local ou le stockage blob Azure
à cette fin.
La figure 2-4 illustre les blocs fournisseur et ressources. Le bloc fournisseur mentionne la
version du gestionnaire de ressources Azure, tandis que les deux blocs de ressources créent
le groupe de ressources Azure et le réseau virtuel.

Figure 2-4. Un fichier Terraform simple représentant la création de ressources et le fournisseur Azure

Outils IaC de premier plan 15


Maintenant que nous avons introduit les notions de base de Terraform, découvrons
comment elles se regroupent pour configurer l’infrastructure Cloud native dans Azure. La
figure 2-5 représente la charge de travail générale.

Figure 2-5. Charge de travail Terraform

Pour commencer à utiliser Terraform, procédez comme suit :

1. Initialisez Terraform à l’aide de la commande terraform init. Vous ne pouvez pas


exécuter les commandes plan et apply si vous n’avez pas initialisé le répertoire.
2. Une fois que vous avez correctement initialisé Terraform, créez le plan d’exécution ou
réalisez un essai à l’aide de la commande terraform plan.
3. Lorsque vous êtes satisfait de la sortie du plan, créez votre infrastructure à l’aide de la
commande terraform apply.

Selon le fournisseur que vous utilisez, l’infrastructure sera mise en service dans
l’environnement du fournisseur Cloud correspondant (dans notre cas, il s’agit de Microsoft
Azure). Si vous souhaitez supprimer votre infrastructure, vous pouvez simplement saisir
la commande terraform destroy, qui supprimera l’infrastructure correspondant au
répertoire actif. Veuillez faire très attention lorsque vous supprimez votre infrastructure
avec Terraform.

Veuillez faire très attention lorsque vous supprimez votre


infrastructure avec Terraform. Une fois qu’une infrastructure est
supprimée, le fichier d’état est mis à jour avec les valeurs mises à jour.
Avant de supprimer votre infrastructure, vérifiez visuellement la
sortie de la commande terraform destroy afin de ne pas supprimer
accidentellement des informations importantes.

Maintenant que vous disposez d'une compréhension générale de la charge de travail et des
détails opérationnels de Terraform, intéressons-nous à la pratique.

16 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Installation de Terraform
Pour installer Terraform sur votre ordinateur,3 vous devez d’abord trouver le package
binaire adapté à votre système d’exploitation. Si vous utilisez Azure Cloud Shell, la dernière
version de Terraform sera présente par défaut. Au moment de la rédaction du présent texte,
la version actuelle de Terraform est 0.12.28. Pour trouver le package binaire approprié :
# For Linux-based systems:
$ wget https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_linux_amd64.zip

# For macOS-based systems:


$ wget https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_darwin_amd64.zip

Une fois que vous avez saisi le fichier ZIP, décompressez le fichier binaire et déplacez le
binaire unique vers /usr/local/bin. Vous pouvez vérifier que Terraform est installé en
recourant à l’aide de Terraform à partir du shell :
$ ~ terraform --version
Terraform v0.12.28
$ ~ terraform --help
Usage: terraform [-version] [-help] <command> [args]

The available commands for execution are listed below.


The most common, useful commands are shown first, followed by
less common or more advanced commands. If you're just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.

Common commands:
apply Builds or changes infrastructure
console Interactive console for Terraform interpolations
destroy Destroy Terraform-managed infrastructure
env Workspace management
fmt Rewrites config files to canonical format
get Download and install modules for the configuration
graph Create a visual graph of Terraform resources
import Import existing infrastructure into Terraform
init Initialize a Terraform working directory
login Obtain and save credentials for a remote host
logout Remove locally-stored credentials for a remote host
output Read an output from a state file
plan Generate and show an execution plan
providers Prints a tree of the providers used in the configuration
refresh Update local state file against real resources
show Inspect Terraform state or plan
taint Manually mark a resource for recreation
untaint Manually unmark a resource as tainted
validate Validates the Terraform files
version Prints the Terraform version
workspace Workspace management

All other commands:

3 Azure Cloud Shell est livré avec la dernière version de Cloud Shell. Pour en savoir plus sur la prise en main de
Terraform sur Cloud Shell, visitez https://docs.microsoft.com/azure/developer/terraform/get-started-cloud-shell.

Outils IaC de premier plan 17


0.12upgrade Rewrites pre-0.12 module source code for v0.12
debug Debug output management (experimental)
force-unlock Manually unlock the terraform state
push Obsolete command for Terraform Enterprise legacy (v1)
state Advanced state management
$ ~

Configuration de l’accès Terraform à un compte Microsoft Azure


Maintenant que vous avez configuré votre compte Azure et configuré Terraform localement,
vous pouvez configurer Terraform pour accéder à votre compte Azure :

1. Cliquez sur l’icône Cloud Shell située dans le coin supérieur droit de votre compte dans
le portail Azure, puis cliquez sur « Créer un compte de stockage ». Vous bénéficierez
ainsi d'un nouveau compte de stockage en quelques minutes.
2. Après avoir sélectionné Cloud Shell, vous pouvez choisir Bash ou PowerShell. Vous
pouvez également modifier le shell ultérieurement si vous le souhaitez. Tout au long
de cet ouvrage, nous allons utiliser le shell Bash. Une fois l’installation terminée, vous
recevez les notifications suivantes :
Requesting a Cloud Shell.Succeeded.
Connecting terminal...

Welcome to Azure Cloud Shell

Type "az" to use Azure CLI


Type "help" to learn about Cloud Shell
$

3. Vous devez maintenant obtenir la liste de valeurs d’ID d’abonnement et d’ID de locataire.
Pour ce faire, exécutez la commande suivante dans Cloud Shell pour répertorier tous
les noms de compte Azure, les ID d’abonnement et les ID de locataire :
$ az account list --query "[].{name:name, subscriptionId:id, tenantId:tenantId}"

[
{
"name": "Visual Studio Enterprise",
"subscriptionId": "b1234567-89017-6135-v94s-3v16ifk86912",
"tenantId": "ba0198-d28a-41ck-a2od-d8419714a098"
}
]
$

4. Notez la valeur subscriptionId dans la sortie JSON renvoyée. Ensuite, tout en restant
dans Cloud Shell, remplacez subscriptionId par la commande suivante :
$ az account set --subscription="b1234567-89017-6135-v94s-3v16ifk86912"

La commande précédente doit s’exécuter sans qu'aucune sortie ne soit renvoyée.

18 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


5. Créez un principal de service4 à utiliser avec Terraform à l’aide de la commande
suivante sur Cloud Shell :
$ az ad sp create-for-rbac --role="Contributor" \
--scopes="/subscriptions/b1234567-89017-6135-v94s-3v16ifk86912"

Creating a role assignment under the scope of \


"/subscriptions/b1234567-89017-6135-v94s-3v16ifk86912"
Retrying role assignment creation: 1/36
{
"appId": "b0b88757-6ab2-3v41-1234-23ss224vd41e",
"displayName": "azure-cli-2020-07-03-18-24-17",
"name": "http://azure-cli-2020-07-03-18-24-17",
"password": "aH2cK.asfbbashfbjafsADNknvsaklvQQ",
"tenant": "ba0198-d28a-41ck-a2od-d8419714a098"
}
$

Cette commande renvoie les valeurs appId, displayName, name, password, et tenant.
6. Utilisez les données suivantes pour configurer les variables d’environnement Terraform
sur l’ordinateur local :
ARM_SUBSCRIPTION_ID=<subscription>
ARM_CLIENT_ID=<appId>
ARM_CLIENT_SECRET=<password>
ARM_TENANT_ID=<tenant>
7. Dans votre .bash_profile (ou, si vous préférez, envvar.sh), remplacez les variables dans
le code précédent comme suit :
#!/bin/sh
echo "Setting environment variables for Terraform"
export ARM_SUBSCRIPTION_ID=b1234567-89017-6135-v94s-3v16ifk86912
export ARM_CLIENT_ID=b0b88757-6ab2-3v41-1234-23ss224vd41e
export ARM_CLIENT_SECRET=aH2cK.asfbbashfbjafsADNknvsaklvQQ
export ARM_TENANT_ID=ba0198-d28a-41ck-a2od-d8419714a098

# Not needed for public, required for usgovernment, german, china


export ARM_ENVIRONMENT=public

8. Effectuez un source .bash_profile sur votre terminal pour lire et exécuter des
commandes depuis le fichier vers l’environnement shell actuel.

Vous pouvez désormais utiliser Cloud Shell pour interagir avec les ressources Azure ou, si
vous préférez, utilisez Azure CLI sur votre ordinateur local pour interagir avec Azure.

4 Un principal de service Azure est une identité créée pour être utilisée avec des applications, des services
hébergés et des outils automatisés pour accéder aux ressources Azure. Cet accès est limité par les rôles
attribués au principal de service, afin que vous puissiez contrôler les ressources accessibles et à quel niveau.

Outils IaC de premier plan 19


À ce stade, vous devez vous connecter à Azure à l’aide de la ligne de commande, qui vous
redirigera vers le navigateur pour vous authentifier. Une fois l’authentification réussie, vous
serez en mesure de vous connecter :
$ ~ az login
You have logged in. Now let us find all the subscriptions to which you have access...
[
{
"cloudName": "AzureCloud",
"id": "b1234567-89017-6135-v94s-3v16ifk86912",
"isDefault": true,
"name": "Visual Studio Enterprise",
"state": "Enabled",
"tenantId": "ba0198-d28a-41ck-a2od-d8419714a098",
"user": {
"name": "nishant7@hotmail.com",
"type": "user"
}
}
]

Maintenant que vous avez réussi à vous connecter et à vous authentifier avec Azure, vous
pouvez consulter le répertoire ~/.azure, qui contient les informations d’authentification et
d’autorisation :
$ ls -al
total 44
drwxr-xr-x 1 nsingh nsingh 270 Apr 13 21:13 .
drwxr-xr-x 1 nsingh nsingh 624 Apr 13 21:13 ..
-rw------- 1 nsingh nsingh 7842 Apr 13 21:13 accessTokens.json
-rw-r--r-- 1 nsingh nsingh 5 Apr 13 21:13 az.json
-rw-r--r-- 1 nsingh nsingh 5 Apr 13 21:13 az.sess
-rw-r--r-- 1 nsingh nsingh 420 Apr 13 21:21 azureProfile.json
-rw-r--r-- 1 nsingh nsingh 66 Apr 13 21:21 clouds.config
-rw-r--r-- 1 nsingh nsingh 5053 Apr 13 21:13 commandIndex.json
drwxr-xr-x 1 nsingh nsingh 318 Apr 13 21:21 commands
-rw------- 1 nsingh nsingh 51 Apr 13 21:13 config
drwxr-xr-x 1 nsingh nsingh 26 Apr 13 21:13 logs
drwxr-xr-x 1 nsingh nsingh 10 Apr 13 21:13 telemetry
-rw-r--r-- 1 nsingh nsingh 16 Apr 13 21:13 telemetry.txt
-rw-r--r-- 1 nsingh nsingh 211 Apr 13 21:13 versionCheck.json

Utilisation de base et configuration de l’infrastructure avec Terraform


Maintenant que vous avez configuré avec succès Terraform pour communiquer avec Azure,
vous pouvez créer une infrastructure de base. À mesure que vous franchirez ces étapes,
vous obtiendrez une vue d'ensemble et pourrez utiliser efficacement Terraform.
Tout le code Terraform relatif à ce chapitre est disponible dans le référentiel GitHub, que
vous pouvez cloner sur votre ordinateur local.
Accédez au répertoire Test, et recherchez le fichier nommé test.tf. L'exemple 2-1 indique le
contenu de ce fichier. Dans cet exemple, nous utilisons un fichier d’état local pour stocker
l’état actuel de l’infrastructure.

20 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Exemple 2-1. Le fichier test.tf

provider "azurerm" {
}
resource "azurerm_resource_group" "rg" {
name = "testResourceGroup"
location = "westus"
}

Le code créera un groupe de ressources nommé testResourceGroup dans westus.


Pour exécuter ce fichier, procédez comme suit :

1. Exécutez terraform init pour initialiser Terraform et télécharger le fournisseur


AzureRM :
$ Test git:(master) terraform init

Initializing the backend...

Initializing provider plugins...


- Checking for available provider plugins...
- Downloading plugin for provider "azurerm" (hashicorp/azurerm) 2.17.0...

The following providers do not have any version constraints in configuration,


so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.azurerm: version = "~> 2.17"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,


rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

2. Une fois l’initialisation réussie, exécutez terraform plan :


$ Test git:(master) ✗ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

An execution plan has been generated and is shown below.


Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:

Outils IaC de premier plan 21


# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "westus"
+ name = "testResourceGroup"
}

Plan: 1 to add, 0 to change, 0 to destroy.


------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

3. Comme vous pouvez le voir dans la sortie précédente, nous essayons de créer un groupe
de ressources intitulé testResourceGroup. Maintenant, vous pouvez enfin créer la
ressource en exécutant terraform apply. N’oubliez pas que vous devez explicitement
saisir yes pour continuer :
$ Test git:(master) ✗ terraform apply

An execution plan has been generated and is shown below.


Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# azurerm_resource_group.rg will be created


+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "westus"
+ name = "testResourceGroup"
}

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?


Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg: Creation complete after 3s
[id=/subscriptions/b5627140-9087-4305-a94c-3b16afe86791/resourceGroups/ \
testResourceGroup]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.


$ Test git:(master) ✗

Un groupe de ressources est créé, comme illustré à la figure 2-6. Vous pouvez le vérifier en
accédant au portail Azure.

22 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Figure 2-6. Création d’un groupe de ressources simple avec Terraform

Vous devriez également le voir dans la CLI. Sachez que l’opération shell n’est pas spécifique
à la région. Les ressources sont affichées sur plusieurs emplacements. Pour répertorier le
groupe de ressources, utilisez la commande suivante :
$ az group list --output table
Name Location Status
-------------------------- ---------- ---------
testResourceGroup westus Succeeded

Dans votre répertoire Test, vous remarquerez peut-être un fichier nommé terraform.
tfstate qui a été créé une fois le groupe de ressources configuré. Il s’agit du fichier d’état qui
maintient l’état actuel de l’infrastructure.

Exploration de l’infrastructure Azure avec Terraform


Maintenant que nous avons créé une infrastructure de base avec Terraform, continuons en
créant une infrastructure supplémentaire.
Dans l'exemple  2-1, nous vous avons montré comment stocker votre état localement.
Bien qu’il ne s’agisse pas d’un processus recommandé, il suffit pour une utilisation de
base, comme dans le cas de l’exemple de création de groupe de ressources (test.tf). Il est
préférable d’utiliser le stockage à distance, comme le stockage blob Azure, qui confère à la
fois durabilité et sécurité à vos configurations d’infrastructure.

Les états Terraform constituent une source de la vérité et doivent


toujours être stockés dans un back-end tel que Consul ou
Amazon DynamoDB Azure Storage. Vous pourrez ainsi stocker
en toute sécurité votre état d’infrastructure ainsi que le contrôle
de versions pour faciliter la gestion ou la restauration en cas de
sinistre. Les états ne doivent pas être enregistrés dans le système
de contrôle de version Git, car Git contient des secrets.

Outils IaC de premier plan 23


Dans les sous-sections suivantes, nous allons créer un stockage blob dans notre compte
Azure pour stocker tous nos états d’infrastructure. Nous allons également suivre le
principe (« Don’t Repeat Yourself », Ne vous répétez pas) en veillant à utiliser le module de
Terraform. Nous allons procéder à la création de réseaux virtuels et de machines virtuelles.
Ces modules sont disponibles dans le référentiel GitHub de ce chapitre.

Création d'un stockage blob Azure. Pour créer un stockage blob Azure, vous devez utiliser le
module suivant : https://bit.ly/3bM6jCx.

Suivez maintenant les étapes décrites dans «  Utilisation de base et configuration de


l’infrastructure avec Terraform » à la page 20 pour déployer le compte de stockage. Une
fois que vous exécutez la commande terraform plan, vous obtiendrez de nombreuses
informations sur le plan lié aux modules. Vous remarquerez également que les modules
(par exemple, Storage_accounts) sont abstraits, tandis que le fichier de ressources se
présente comme suit :
module "CloudNativeAzure-strg-backend" {
source = "../Modules/Services/Storage_accounts"
resource-grp-name = "CloudNativeAzure-group"
storage-account-name = "cnabookprod"
azure-dc = "westus"
storage-account-tier = "Standard"
storage-replication-type = "LRS"
storage-container-name = "cloud-native-devs"
storage-container-access = "private"
blob-name = "cloud-native-with-azure"
}

Cela démontre la puissance de l’abstraction de l’infrastructure répétable derrière les


modules. Une fois que vous utilisez terraform apply, le programme s’exécute, en créant
en quelques minutes un compte de stockage nommé cnabookprod et un blob nommé
cloud-native-with-azure :
.
.
module.CloudNativeAzure-strg-backend.azurerm_storage_container.generic-container: Creation
complete after 3s [id=https://cnabookprod.blob.core.windows.net/cloud-native-devs]
module.CloudNativeAzure-strg-backend.azurerm_storage_blob.generic-blob: Creating...
module.CloudNativeAzure-strg-backend.azurerm_storage_blob.generic-blob: Creation complete
after 4s [id=https://cnabookprod.blob.core.windows.net/cloud-native-devs/cloud-native-with-
azure]
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
Blob-ID = https://cnabookprod.blob.core.windows.net/cloud-native-devs/cloud-native-with-
azure
Blob-URL = https://cnabookprod.blob.core.windows.net/cloud-native-devs/cloud-native-
with-azure
Primary-Access-Key =
Bfc9g/piV3XkJbGosJkjsjn13iLHevR3y1cuPyM8giGT4J0vXeAKAKvjsduTI1GZ45ALACLAWPAXA==

La figure 2-7 illustre le compte de stockage Azure et le blob.

24 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Figure 2-7. Compte de stockage et blob Azure

Notez la sortie de l'opération terraform apply effectuée dans l'étape précédente et copiez
la valeur de la clé d’accès primaire. Vous devrez mettre à jour la valeur de la clé primaire
dans le même .bash_profile dans votre ordinateur local, comme suit :
#!/bin/sh
echo "Setting environment variables for Terraform"
export ARM_SUBSCRIPTION_ID=b1234567-89017-6135-v94s-3v16ifk86912
export ARM_CLIENT_ID=b0b88757-6ab2-3v41-1234-23ss224vd41e
export ARM_CLIENT_SECRET=aH2cK.asfbbashfbjafsADNknvsaklvQQ
export ARM_TENANT_ID=ba0198-d28a-41ck-a2od-d8419714a098

# Not needed for public, required for usgovernment, german, china


export ARM_ENVIRONMENT=public
export
ARM_ACCESS_KEY=Bfc9g/piV3XkJbGosJkjsjn13iLHevR3y1cuPyM8giGT4J0vXeAKAKvjsduTI1GZ45AL
ACLAWPAXA==

Nous pouvons désormais créer en toute sécurité une infrastructure supplémentaire


et stocker l’état de l’infrastructure dans le stockage blob.

Création d'un réseau virtuel et d'instances Azure.. Nous allons à présent créer un réseau
virtuel sur Azure en suivant la configuration Terraform à l'adresse https://bit.ly/3bHE342.
Nous allons également expliquer le flux syntaxique du module de réseau virtuel, afin de
mieux appréhender la simplicité du code.

Outils IaC de premier plan 25


Examinez de plus près le module Virtual_network dans le répertoire /Modules/Services/
Virtual_network, afin d'accéder au processus de création de réseau virtuel étape par étape :

1. Créez un groupe de sécurité réseau à l’aide de azurerm_network_security_group.


2. Ajoutez la règle de sécurité et associez-la au groupe de sécurité lors de l'étape 1.
3. Créez un plan de protection DDoS.
4. Créez un réseau virtuel.
5. Créez quatre sous-réseaux différents à l’intérieur du réseau virtuel.
6. Créez une interface réseau et associez-la à un sous-réseau (privé).
7. Créez des adresses IP publiques.
8. Créez un sous-réseau public en l'associant à une adresse IP publique.

Le résultat final de la commande apply de Terraform ressemble à ceci :

Apply complete! Resources: 12 added, 0 changed, 0 destroyed.


Releasing state lock. This may take a few moments...
Outputs:

private-nic-id = /subscriptions/b5627140-9087-4305-a94c-3b16afe86791/resourceGroups/
CloudNativeAzure-group/providers/Microsoft.Network/networkInterfaces/private-nic
private-subnet-a-id = /subscriptions/b5627140-9087-4305-a94c-3b16afe86791/resourceGroups/
CloudNativeAzure-group/providers/Microsoft.Network/virtualNetworks/cna-prod/subnets/
linkedin-private-a
private-subnet-b-id = /subscriptions/b5627140-9087-4305-a94c-3b16afe86791/resourceGroups/
CloudNativeAzure-group/providers/Microsoft.Network/virtualNetworks/cna-prod/subnets/
linkedin-private-b
pub-nic-id = /subscriptions/b5627140-9087-4305-a94c-3b16afe86791/resourceGroups/
CloudNativeAzure-group/providers/Microsoft.Network/networkInterfaces/public-nic
public-subnet-a-id = /subscriptions/b5627140-9087-4305-a94c-3b16afe86791/resourceGroups/
CloudNativeAzure-group/providers/Microsoft.Network/virtualNetworks/cna-prod/subnets/
linkedin-public-a
public-subnet-b-id = /subscriptions/b5627140-9087-4305-a94c-
3b16afe86791/resourceGroups/CloudNativeAzure-group/providers/Microsoft.Network/
virtualNetworks/cna-prod/subnets/linkedin-private-b
vpc-id = /subscriptions/b5627140-9087-4305-a94c-3b16afe86791/resourceGroups/
CloudNativeAzure-group/providers/Microsoft.Network/virtualNetworks/cna-prod

La figure 2-8 illustre la création du réseau virtuel dans le portail Azure.

26 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Figure 2-8. Création d'un réseau virtuel

Comme illustré à la figure 2-9, Terraform a stocké l’état du réseau virtuel sur le compte de
stockage blob que nous avons créé précédemment.

Figure 2-9. État du réseau virtuel téléchargé par Terraform sur le compte de stockage

De même, vous pouvez suivre les étapes précédentes et lancer une machine virtuelle à l’intérieur
de l’un des sous-réseaux à l’aide du module Terraform à l’adresse https://bit.ly/3bQjTEV.

Une fois que vous avez fini de créer les ressources dans cet exemple,
veillez à les supprimer à l’aide de la commande terraform
destroy à l’intérieur de chaque répertoire de ressources, si la
facturation est activée dans votre compte Azure.

Outils IaC de premier plan 27


Modèles Terraform et ARM
Avant de passer à la section suivante, étudions les offres natives d'Azure dédiées à la mise en
œuvre de l’infrastructure en tant que code. Comme nous l’avons mentionné précédemment,
Azure utilise des modèles ARM pour mettre en œuvre l’infrastructure en tant que code pour
les charges de travail basées sur Azure. Les modèles ARM sont livrés dans un fichier JSON5 qui
définit l’infrastructure et la configuration de votre projet. Le fichier utilise une syntaxe déclarative
similaire à Terraform, qui vous permet d’indiquer la configuration d’infrastructure prévue. En
plus de la syntaxe déclarative, les modèles ARM héritent également de l'idempotence. Vous
pouvez donc déployer le même modèle plusieurs fois et obtenir les mêmes types de ressources
dans le même état. Le gestionnaire de ressources est chargé de convertir le modèle ARM en une
opération API REST. Par exemple, si vous déployez le modèle ARM suivant :
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "teststorageaccount",
"location": "eastus",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
}
]

le gestionnaire de ressources convertira le fichier JSON en une opération API REST, qui
sera envoyée au fournisseur de ressources Microsoft.Storage comme suit :
PUT
https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/ \
{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/ \
teststorageaccount?api-version=2019-04-01
REQUEST BODY
{
"location": "eastus",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
}

Vous pouvez également utiliser Terraform pour déployer des modèles ARM à l’aide de la
ressource azurerm_ resource_group_template_deployment pour le fournisseur AzureRM.

5 Azure propose JSON (JavaScript Object Notation) par défaut pour l’écriture de modèles ARM. Azure
propose également un langage spécifique au domaine appelé Bicep qui utilise également la syntaxe
déclarative pour déployer des ressources Azure. Vous pouvez utiliser Bicep au lieu de JSON pour réduire la
complexité et améliorer l’expérience globale de développement/gestion.

28 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Packer
Au début de ce chapitre, nous avons introduit le terme infrastructure immuable, qui
représente le changement de mise en service et de déploiement d'applications Cloud natives.
Packer est l'un des outils les plus utilisés pour créer des images de machine préparées6.
Les images de machine sont principalement des ressources de calcul qui ont toutes les
configurations, métadonnées, artefacts et fichiers associés préinstallés/configurés. Packer
est un outil open source d'HashiCorp qui est utilisé pour créer des images de machine
à partir d’une configuration définie. Il automatise l’ensemble du processus de création
d’images de machine, en vue d'accélérer le déploiement de l’infrastructure. Tout comme
Terraform est utilisé pour créer une infrastructure, Packer contribue à la création d’images
de machine en intégrant tous les logiciels essentiels, les binaires, etc., dans l’image de la
machine.
En outre, étant donné que tous les logiciels de l’image de machine sont installés et configurés
avant la création de cette dernière, Packer contribue à améliorer la stabilité globale de
l’infrastructure. Il prend également en charge les différents fournisseurs Cloud, qui finissent
par créer des images de machine identiques sur plusieurs plateformes. Étudions d'un peu
plus près comment utiliser Packer pour créer des images dans Azure.

Installation de Packer
Pour démarrer avec Packer, téléchargez le binaire Packer approprié pour votre ordinateur
local7. Décompressez le binaire et déplacez-le vers /usr/local/bin. Packer devrait ainsi être
configuré sur votre ordinateur. Pour le vérifier, exécutez packer --help sur votre shell :
$ ~ packer --help
Usage: packer [--version] [--help] <command> [<args>]

Available commands are:


build build image(s) from template
console creates a console for testing variable interpolation
fix fixes templates from old versions of packer
inspect see components of a template
validate check that a template is valid
version Prints the Packer version

Création d’une image Linux sur Azure


Packer utilise un fichier de configuration qui définit l’image de machine. Le fichier
de configuration est appelé modèle Packer et est écrit dans JSON. Le modèle inclut des
concepteurs et des provisionneurs. Les concepteurs créent une image de machine pour une
plateforme en lisant les clés de configuration et d’authentification. Les provisionneurs sont
responsables de l’installation et de

Préparé est un terme utilisé pour désigner la préinstallation d’un logiciel sur une image de machine, qui
6 
peut être utilisée ultérieurement pour faire tourner plus d’instances ou de conteneurs.
7 Packer est disponible sur Azure Cloud Shell par défaut.

Outils IaC de premier plan 29


la configuration d’un logiciel sur l’image de machine avant que l’image ne devienne immuable.
La figure 2-10 représente le processus de création d’images Packer.

Figure 2-10. Processus de création d’images à l’aide de Packer

Le code relatif à la création d’images de machine Packer est disponible à l’adresse


https://bit.ly/ 3GRgswf. Pour exécuter ce code, vous devez d’abord mettre à jour le code avec
les détails d’authentification dans le fichier example.json. Vous pouvez utiliser les détails
d’authentification pour votre compte Azure dans le fichier bash_profile. Le résultat de
l'exécution doit ressembler à ceci :
$ 1.2.2.2 git:(master) ✗ packer build example.json
azure-arm: output will be in this color.

==> azure-arm: Running builder ...


==> azure-arm: Getting tokens using client secret
==> azure-arm: Getting tokens using client secret
azure-arm: Creating Azure Resource Manager (ARM) client ...
==> azure-arm: WARNING: Zone resiliency may not be supported in East US, checkout the
docs at https://docs.microsoft.com/en-us/azure/availability-zones/
==> azure-arm: Creating resource group ...
==> azure-arm: -> ResourceGroupName : 'pkr-Resource-Group-k9831ev1uk'
==> azure-arm: -> Location : 'East US'
==> azure-arm:
.
.
.
.
.
==> azure-arm:
Build 'azure-arm' finished.

==> Builds finished. The artifacts of successful builds are:


--> azure-arm: Azure.ResourceManagement.VMImage:

OSType: Linux
ManagedImageResourceGroupName: CloudNativeAzure-group
ManagedImageName: myfirstPackerImage
ManagedImageId: /subscriptions/b1234567-89017-6135-v94s-3v16ifk86912/resourceGroups/
CloudNativeAzure-group/providers/Microsoft.Compute/images/myfirstPackerImage
ManagedImageLocation: East US

La figure 2-11 illustre l’image que nous avons créée dans le portail Azure. Vous pouvez
vérifier que l’image dispose du serveur Web Nginx et de l’équilibreur de charge HAProxy et
que le serveur proxy est intégré en faisant tourner des machines virtuelles à l’aide de l’image.

30 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Figure 2-11. Création d’images de machine avec Packer

Maintenant que vous comprenez le rôle de l'outil Packer dans la création d’images,
intéressons-nous à Ansible, qui contribue énormément à la gestion des environnements
Cloud natifs complexes.

Ansible
Ansible est un outil de gestion de configuration simple qui vous permet de mettre en service,
de configurer et de gérer des serveurs. Ansible facilite la gestion des serveurs sur lesquels aucun
agent préinstallé n'est exécuté. Vous pouvez toutefois configurer Ansible en tant que système
basé sur des agents. En outre, vous pouvez utiliser Ansible en tant qu’outil une fois que vous
avez créé l’infrastructure à l’aide de Terraform et d’images de machine préparées via Packer.
Voici les concepts de base d'Ansible :

Playbooks
Les playbooks Ansible sont une liste de tâches ou de pièces qui doivent être exécutées
sur une instance. En voici un exemple simple :
• Ajoutez le référentiel Nginx.
• Installez Nginx.
• Créez le répertoire racine.
Cette liste ordonnée peut être convertie en playbook Ansible comme suit :
---
- hosts: local
vars:
- docroot: /var/www/serversforhackers.com/public

Outils IaC de premier plan 31


tasks:
- name: Add Nginx Repository
apt_repository: repo='ppa:nginx/stable' state=present

- name: Install Nginx


apt: pkg=nginx state=installed update_cache=true

- name: Create Web Root


file: dest=/etc/nginx
mode=775
state=directory
owner=www-data
group=www-data
notify:
- Reload Nginx

Nœud de contrôle
Il s’agit généralement du nœud à partir duquel vous allez exécuter le playbook Ansible.
Il peut s’agir de n’importe quel nœud où Ansible est installé. Pour exécuter un playbook
Ansible, vous pouvez exécuter la commande suivante dans votre terminal :
ansible-playbook -vi path_to_host_inventory_file playbook.yaml

Nœuds gérés
Les nœuds ou les hôtes que vous souhaitez gérer sont appelés nœuds gérés. Ces nœuds
sont généralement les serveurs distants. Ansible présente un avantage important : vous
n’avez pas besoin d’installer d'agent sur les instances distantes pour les gérer. Tout ce
dont vous avez besoin, c’est d’un démon Secure Shell (SSH) qui écoute et d'installer
Python sur les hôtes. Ansible crée une connexion SSH pour exécuter les playbooks.

Fichier d’inventaire
Le fichier d’inventaire contient la liste de tous les hôtes gérés par Ansible. L’inventaire
contient généralement des adresses IP, des noms d’hôte, le nom d’utilisateur SSH et des
clés pour se connecter à la machine distante. Voici un exemple d'inventaire :

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

[midtierrservers]
10.0.1.2
10.2.3.4

Étant donné qu'Ansible n'entraîne pas beaucoup de frais généraux et qu'il est écrit dans
YAML, il s’agit d’un excellent outil post-configuration. Nous utiliserons parfois Ansible
dans cet ouvrage pour configurer des parties des services.

32 Chapitre 2 : infrastructure en tant que code : configuration de la passerelle


Avant de conclure ce chapitre, jetons un coup d’œil à Azure DevOps, qui nous permet de
combiner tous ces outils de façon automatisée pour créer un pipeline CI/CD.

Azure DevOps et infrastructure en tant que code


Azure propose Azure DevOps, qui fournit un chaîne d'outils DevOps end-to-end pour
le développement et le déploiement des applications. Il s'agit principalement d'un
service hébergé pour le déploiement de pipelines CI/CD. Vous pouvez également utiliser
conjointement Terraform, Ansible et Packer, et créer des pipelines sur plusieurs niveaux
pour vos projets. Azure DevOps propose de nombreux services, notamment Azure Pipeline,
qui est principalement utilisé pour la création, le test et le déploiement des pipelines CI/
CD. Nous n'étudierons pas en détail Azure DevOps, car ce n'est pas le sujet de cet ouvrage,
mais vous pouvez en savoir plus sur la configuration d’Azure DevOps en consultant la
documentation officielle.

Résumé
Dans ce chapitre, nous avons présenté les tremplins qui permettent de concevoir un
environnement Cloud natif moderne à l’aide d’Azure. Vous avez créé un compte Azure,
que vous utiliserez dans les chapitres à venir, tout en acquérant une compréhension plus
approfondie des technologies Cloud natives. Vous avez également découvert Terraform,
Packer et Ansible : trois orchestrateurs Cloud natifs qui vous aideront à déployer et à gérer
votre infrastructure Cloud. Terraform est un outil indépendant du Cloud, conçu pour
le développement de l’infrastructure en tant que code à partir de zéro. Packer est utilisé
pour créer des images de machine pour le développement d’artefacts immuables, tandis
qu'Ansible est un outil de gestion de configuration.
Grâce à cette compréhension de base d’Azure et des technologies associées, nous pouvons
passer au chapitre suivant, lequel porte sur les conteneurs, le registre de conteneurs et la
conteneurisation de votre application.

Azure DevOps et infrastructure en tant que code 33


CHAPITRE 3
Conteneurisation de votre application :
Plus que des boîtes

Au cours des dernières années, les conteneurs ont gagné en popularité. Ils offrent non
seulement de faibles frais généraux, ainsi qu'une sécurité et une portabilité importantes, tout
en respectant également les principes des bonnes pratiques du Cloud, tels que l’immuabilité,
l'éphémérité et la mise à l’échelle automatique.
Ce chapitre présente les conteneurs et les plateformes de conteneurisation populaires.
Nous étudierons également les conteneurs au-delà de l'effet de mode qu'ils suscitent dans le
secteur en général, en expliquant l'intérêt de leur utilisation. Nous présenterons également
certains de leurs avantages, en particulier en ce qui concerne le Cloud.

Pourquoi opter pour les conteneurs ?


Les conteneurs s’exécutent sur un système d’exploitation hôte et sont généralement
orchestrés à l’aide de logiciels comme Docker (comme nous le verrons plus loin dans ce
chapitre) ou Kubernetes (voir figure 3-1).

Figure 3-1. Le paradigme de l’exploitation des conteneurs

35
L’un des avantages les plus importants des conteneurs réside dans le fait qu’ils fournissent
une isolation de type machine virtuelle, sans nécessiter l'exécution d’une instance du
système d’exploitation hôte pour chaque conteneur. À grande échelle, cet avantage permet
d'économiser considérablement les ressources système.

Isolation
Les conteneurs fournissent une isolation grâce à un certain nombre de méthodes d’une
manière flexible qui est configurée par l’administrateur. Les conteneurs peuvent être isolés
de la manière suivante :
Ressources système
L’isolation est réalisée via le processeur, la mémoire et le cgroups de disque.
Espaces de noms
L’isolation est réalisée en autorisant des espaces de noms distincts pour chaque conteneur.
Limites POSIX
L’isolation avec cette méthode vous permet de définir des limites sur votre conteneur.
Ces mécanismes vous permettent d’exécuter en toute sécurité de nombreuses charges de
travail ensemble sans vous soucier de la contention de ressources ou sans qu'un système
en perturbe un autre. Un orchestrateur de conteneurs sensible aux charges de travail vous
permet d'utiliser efficacement toutes les ressources sur un ordinateur hôte sans dégrader
les performances (dénommé bin packing). Si elle est effectuée avec succès à grande échelle,
cette opération permet de réaliser des économies significatives à mesure que vous optimisez
l'efficacité de votre infrastructure.

Sécurité
Fondé sur les fonctions d’isolation des conteneurs, la spécification de runtime du conteneur
OCI (abordée plus loin dans ce chapitre) fournit un certain nombre de fonctions de sécurité
de runtime. Celles-ci garantissent que si une application est compromise au sein d'un
conteneur, le risque de déplacement latéral est considérablement réduit sur tout le réseau.

Voici certaines de ces fonctions :


Fonctionnalités Linux
Celles-ci réduisent l’accès du conteneur aux API du noyau.
Attributs d'utilisateur/de groupe
Ceux-ci définissent les attributs d’utilisateur et de groupe du conteneur, en vue de
limiter l’accès aux ressources du système de fichiers.
Appareils
Ceux-ci contrôlent les appareils auxquels un conteneur a accès.

36 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Étiquettes SELinux
À l’instar d’autres composants d’un système Linux (fichiers, processus, canaux), les
étiquettes SELinux peuvent être appliquées aux conteneurs.
Sysctl
Cela ajoute des contrôles supplémentaires (limités) au conteneur.
Ces couches de fonctions de sécurité de runtime permettent de déployer des applications
avec un risque considérablement réduit pour l’infrastructure hôte et les applications
voisines. Au sein de l’écosystème de conteneurs, il existe également une suite de paradigmes
de sécurité d'artefacts, que nous aborderons plus loin dans ce chapitre.
Notez que si vous souhaitez appliquer la stratégie système à l’aide d’un mécanisme
d’orchestration (Puppet/Chef, etc.), sa mise en œuvre par application est beaucoup plus
fastidieuse que si vous utilisez simplement les fonctions proposées par le conteneur.

Conditionnement et déploiement
Comme nous le verrons plus loin dans cet ouvrage, la normalisation des images de
conteneurs et la portabilité de ces images sont extrêmement utiles. Il est relativement
simple de prendre un conteneur en cours d’exécution sur votre bureau et de le déployer
en production sans surcharge opérationnelle importante ou d’effectuer des étapes de
configuration et d’administration supplémentaires. Le mantra de Docker est le suivant
« créer, fournir et exécuter ». Il met en évidence la mentalité « du bureau à la production »
que la conteneurisation apporte au cycle de vie du développement logiciel.
En outre, compte tenu de la spécification des images OCI, comme nous le verrons plus
loin dans ce chapitre, vous pouvez être certain que le système de déploiement fonctionnera
parfaitement tant que votre registre de conteneurs est disponible.

Primitives de conteneur basique


Vous serez peut-être surpris d'apprendre que les conteneurs ne sont pas un concept de
première classe au sein de Linux. En effet, aucune définition formelle ne concernait
les conteneurs avant la création de la spécification OCI en 2017 (nous aborderons les
spécifications en détail plus loin dans ce chapitre). Un conteneur de base est généralement
composé des primitives suivantes :
• Cgroups
• Espaces de noms
• Copie en écriture (CoW)
• Seccomp-BPF

Primitives de conteneur basique 37


Les conteneurs Linux sont une évolution naturelle de technologies similaires telles que les
FreeBSD jails, les zones Solaris et les machines virtuelles. À bien des égards, les conteneurs
rassemblent le meilleur de ces technologies.
Étant donné que les conteneurs ne sont pas un concept primitif, un certain nombre de
plateformes logicielles de conteneur sont disponibles. Nous couvrirons ce sujet en détail plus
tard dans ce chapitre Pour l’instant, étudions chacune des primitives de conteneur de base.

Cgroups
Un groupe de contrôle (cgroup) est une primitive Linux qui offre la possibilité de limiter
les ressources accessibles/attribuables par un groupe de programmes au sein d’un cgroup.
Cette approche permet de remédier au problème de « voisinage bruyant ». Voici la liste des
contrôles effectués par les cgroups :
CPU
Le contrôle du sous-système CPU vous permet d’allouer une certaine quantité de temps
CPU (cpuacct) ou un certain nombre de cœurs (cpuset) à un cgroup. Vous pouvez
ainsi garantir qu’aucun processus ne submerge toutes les ressources du CPU sur une
machine physique.
Mémoire
Le sous-système de mémoire vous permet de signaler et de définir des limites pour
l’utilisation de la mémoire dans un cgroup. Cette méthode fonctionne à la fois pour la
mémoire utilisateur et la mémoire swap.
Blkio
Le sous-système de contrôleur blkio (prononcé Block-i-o) fournit un mécanisme
permettant de limiter les opérations d’entrée/sortie par seconde (IOPS) ) ou de bande
passante (bps) d’un cgroup vers un périphérique de bloc.
PID
Le contrôleur PID (identificateur de processus) définit le nombre maximal de processus
pouvant être exécutés dans le cgroup.
Appareils
Applicable uniquement aux cgroups  v1, ce contrôle permet à l’administrateur de
définir des listes d'autorisation/de refus concernant l'accès aux appareils par un cgroup
sur l'ordinateur hôte.
Réseau
Le cgroup classificateur de réseau (net_cls) fournit une interface pour étiqueter les
paquets réseau avec un identificateur de classe (classid).
Le cgroup de priorité réseau (net_prio) fournit une interface pour définir dynamiquement
la priorité du trafic réseau généré par diverses applications.
Dans la plupart des cas, la fonctionnalité de contrôle réseau est mieux assurée par eBPF dans
les versions plus récentes du noyau Linux.

38 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Espaces de noms
Les espaces de noms sont une fonction Linux qui vous permet de limiter les ressources
hors calcul afin qu’un groupe de processus voit un ensemble de ressources et qu’un
deuxième groupe de processus voit un ensemble de ressources différent. Au moment de la
rédaction de ce chapitre, Linux contient un ensemble de types d’espaces de noms similaire
à ce qui suit :
Mount (mnt)
Contrôle les montages de systèmes de fichiers disponibles pour le conteneur. Ainsi, les
processus dans différents espaces de noms de montage accèdent à des vues différentes
d’une hiérarchie de répertoires.

PID (pid)
Fournit le conteneur avec un ensemble de PID numérotés de manière indépendante.
Le premier processus au sein d’un conteneur avec son propre espace de noms PID aura
un PID de 1. Tous les descendants du PID 1 agiront comme un système Unix normal.

Network (net)
Une fonctionnalité extrêmement utile qui vous permet de virtualiser votre infrastructure
réseau. Chaque espace de noms disposera d’un ensemble privé d’adresses IP et de sa
propre table de routage, d'une liste de sockets, de la table de suivi des connexions, d'un
pare-feu et d’autres ressources liées au réseau.

Interprocess communication (ipc)


Isole les processus de la communication interprocessus Linux (IPC). Parmi les exemples
citons les conduites et les signaux.

Unix Time Sharing (uts)


Permet à un système unique d'avoir en apparence différents noms d’hôte et de domaine
pour différents processus.

User ID (user)
Similaire à l’espace de noms PID. Il vous permet d’obtenir des privilèges élevés dans le
conteneur, mais pas dans l’ensemble du système. L’espace de noms utilisateur fournit
une séparation d’identification des utilisateurs entre les espaces de noms. L' espace de
noms utilisateur contient une table de mappage qui convertit les ID utilisateur du
point de vue du conteneur au point de vue du système. Ainsi, l’utilisateur racine peut
par exemple présenter l’ID utilisateur 0 dans le conteneur, mais être traité comme l'ID
utilisateur 1 400 000 par le système pour les contrôles de propriété.

Control group (cgroup)


Dissimule l'identité du groupe de contrôle dont le processus est un membre (distinct
de la primitive du cgroup).

Primitives de conteneur basique 39


Time (time)
Permet aux processus de voir différents temps système d’une manière similaire
à l’espace de noms UTS.

Copie en écriture
La copie en écriture (CoW) est une technique de gestion de la mémoire qui copie uniquement
la mémoire lorsqu’une ressource est modifiée (ou écrite). Cette technique réduit la quantité
de mémoire nécessaire.

Capacités
Les fonctionnalités Linux permettent à l’administrateur de contrôler plus étroitement les
fonctionnalités du processus ou du cgroup sur le système hôte. La liste des fonctionnalités
est disponible sur la page de manuel des fonctionnalités (7). Par exemple, si vous souhaitez
exécuter un serveur Web sur le port 80, vous n’avez qu’à donner au cgroup/processus la
fonctionnalité CAP_NET_BIND_SERVICE. Si le serveur Web est compromis, les actions de
l’attaquant sont limitées car le processus ne dispose pas des autorisations système pour
effectuer d’autres commandes administratives.

Seccomp-BPF
Seccomp (SECure COMPuting) est utile pour créer des restrictions absolues sur l’ensemble
des systèmes. Seccomp-BPF permet d'effectuer un contrôle granulaire application par
application. Seccomp-BPF offre une granularité par thread (en mode strict). Il a été ajouté
au noyau dans la version 3.5 (2012).
Seccomp-BPF permet aux programmes Berkeley Packet Filter (BPF) (programmes qui
s’exécutent dans l’espace de noyau) de filtrer les appels système (et leurs arguments) et
de retourner une valeur sur l'événement qui se déroule après la sortie du programme
Seccomp-BPF.

Composants de l’exécution d’un conteneur


Comme nous l’avons mentionné plus tôt dans ce chapitre, les conteneurs ne sont pas un
concept de premier ordre sous Linux (il n’y a pas de primitive de conteneur Linux). Ils sont
donc légèrement difficiles à définir et auparavant, il était difficile de rendre les conteneurs
portables. Néanmoins, pour exécuter un conteneur, une sorte d’interface doit être définie
entre la structure du conteneur et le système d’exploitation (ou le logiciel hôte).
Cette section couvre en détail le logiciel de conteneur, y compris les runtimes de conteneur
(le logiciel qui exécute le conteneur), les plateformes de conteneurs et les orchestrateurs de
conteneurs.
Tout d’abord, examinons rapidement les couches d’abstraction dans l’écosystème de
conteneurs (figure 3-2).

40 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Figure 3-2. Les couches d'abstraction de conteneur

Cette section porte sur ces couches d’abstraction de haut en bas, en expliquant comment
travailler à partir de conteneurs soigneusement orchestrés vers les concepts Linux de bas
niveau qui permettent aux conteneurs de fonctionner.

Orchestrateurs de conteneur
Situés sur la couche supérieure, les orchestrateurs de conteneurs traitent la gestion de la mise
à l’échelle automatique, du remplacement d’instance et de la surveillance d’un écosystème
de conteneurs qui rendent les conteneurs en tant que concept si attrayants. Comme nous
le verrons au chapitre 4 et au Chapitre 5, Kubernetes est la plateforme de planification et
d’orchestration graduée de la Cloud Native Computing Foundation (CNCF).
Les orchestrateurs de conteneurs fournissent généralement les fonctionnalités suivantes :

• Mise à l’échelle automatique des instances de cluster en fonction de la charge d’instance


• Mise en service et déploiement d’instances
• Fonctionnalité de pilotage de base
• Découverte de services

Parmi les autres orchestrateurs de conteneurs CNCF, citons :

• Azure Kubernetes Service (AKS)


• Azure Service Fabric
• Amazon Elastic Container Service (ECS)
• Docker Swarm
• Apache Mesos
• HashiCorp Nomad

Les orchestrateurs de conteneurs de qualité facilitent la création et la maintenance des


clusters de calcul. Au moment de la rédaction de ce chapitre, il n’existe aucune configuration
standard pour les orchestrateurs de conteneurs.

Logiciel de conteneur
Les démons de conteneur, situés tout en dessous des orchestrateurs, fournissent le logiciel pour
exécuter un démon. Selon l’orchestrateur que vous utilisez, ce processus peut être transparent
pour vous. Essentiellement, les démons de conteneur gèrent le cycle de vie du runtime

Composants de l’exécution d’un conteneur 41


du conteneur. Dans de nombreux cas, ce processus sera aussi simple que de démarrer ou
d’arrêter le conteneur ou aussi compliqué que d’interagir avec des plug-ins comme Cilium
(dont nous allons discuter au chapitre 8).
Voici les plateformes logicielles de conteneurs courantes :

• Docker
• Agent Mesos (Mesos)
• Kubelet (Kubernetes)
• LXD (LXC)
• Rkt

Comme nous le verrons plus loin dans ce chapitre, Docker vous permet de prendre une image,
puis de créer des instances de conteneur de celui-ci, en fournissant essentiellement l’interface
utilisateur en tant qu’action non orchestrée. Les agents Mesos ou kubelets sont légèrement
différents car ils sont axés sur des interfaces de ligne de commande ou des API REST.

Runtimes de conteneur
Les runtimes de conteneur fournissent une interface afin d’exécuter des instances de
conteneur. Il existedeux catégories d’interfaces standard à l’échelle du secteur :

• Runtime de conteneur (runtime-spec)


• Spécification d’image (image-spec)

Le runtime du conteneur spécifie la configuration du conteneur (par exemple, les


fonctionnalités, les montages et la configuration réseau). La spécification d’image contient
des informations sur la disposition du système de fichiers et le contenu de l’image.
Bien que chaque démon ou orchestrateur de conteneur ait sa propre spécification de
configuration, le runtime de conteneur est un domaine qui offre une normalisation
à travers l’image.
Voici les trois runtimes de conteneurs les plus importants :

• Containerd
• CRI-O
• Docker (jusqu’à Kubernetes v 1.2)

Containerd
Containerd est un runtime de conteneur conforme à l'OCI qui est utilisé dans Docker.
Containerd agit comme une couche d’abstraction entre tous les appels système Linux, qui assure
le fonctionnement d'un conteneur et la configuration OCI standard qui configure un conteneur.

42 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Containerd présente un sous-système d’événements qui permet à d’autres systèmes comme
etcd de s’abonner aux modifications du système et d’agir en conséquence. Containerd prend
notamment en charge les formats d’image OCI et Docker. « Qu’est-ce que containerd ? » est
un excellent article de blog contenant des informations supplémentaires.

CRI-O
CRI-O est une mise en œuvre de Kubernetes Container Runtime Interface (CRI) qui
active les runtimes compatibles avec OCI. Elle est considérée comme une alternative légère
à l’utilisation de Docker et de containerd. CRI-O prend également en charge l’exécution
de Kata Containers (un conteneur de type machine virtuelle créé par VMware), en étant
extensible à tout autre runtime conforme à l'OCI. Il s'agit donc d'un runtime attractif qui
peut être utilisé pour un certain nombre de cas d’utilisation.

Docker
À ne pas confondre avec la plateforme Docker complète, Docker contient en réalité un
moteur d’exécution qui est ironiquement containerd. Kubernetes a pris en charge Docker
en tant que runtime de conteneur jusqu’à la version v1.20 de Kubernetes.

Conteneurs
L’instance de conteneur est le déploiement et le fonctionnement d’un logiciel tel que défini
par une spécification de conteneur. Le conteneur exécute le logiciel défini (généralement
fourni par une image) selon un certain nombre de paramètres et de limites.

Système d’exploitation
Le système d’exploitation correspond à la structure la plus basse, sur laquelle nos conteneurs
s’exécutent. Dans la plupart des runtimes de conteneur, le noyau est partagé entre toutes les
instances de conteneur.

Spécification Open Container Initiative (OCI)


Comme nous l’avons mentionné précédemment, les conteneurs ne sont pas un concept bien
défini dès le début. L'Open Container Initiative (OCI) a été établie en 2015 pour formaliser
les spécifications de runtime et d’image susmentionnées.
La configuration d’un runtime de conteneur est spécifiée dans un fichier config.json
utilisé par le runtime du conteneur pour configurer les paramètres opérationnels du
conteneur. L’utilisation d’un conteneur conforme à l'OCI permet d’exécuter la même image/
configuration de conteneur sur plusieurs orchestrateurs de conteneurs (par exemple,
Docker et rkt) sans avoir à modifier quoi que ce soit.

Spécification Open Container Initiative (OCI) 43


Spécification d’image OCI
La spécification d’image OCI décrit la façon dont les couches du conteneur sont définies.
Voici certaines des propriétés de l'image :

• Auteur
• Architecture et système d’exploitation
• Utilisateur/groupe pour exécuter le conteneur
• Ports exposés à l’extérieur du conteneur
• Variables d’environnement
• Points d’entrée et commandes
• Répertoire de travail
• Étiquettes d’image

Voici un exemple d’une image de conteneur :


{
"created": "2021-01-31T22:22:56.015925234Z",
"author": "Michael Kehoe <michaelk@example.com>",
"architecture": "x86_64",
"os": "linux",
"config": {
"User": "alice",
"ExposedPorts": {
"5000/tcp": {}
},
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
],
"Entrypoint": [
"/usr/bin/python"
],
"Cmd": [
"app.py"
],
"Volumes": {
"/var/job-result-data": {},
"/var/log/my-app-logs": {}
},
"WorkingDir": "/app,
"Labels": {
"com.example.project.git.url": "https://example.com/project.git",
"com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
}
},
"rootfs": {
"diff_ids": [
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
],
"type": "layers"
},

44 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


"history": [
{
"created": "2021-01-31T22:22:54.690851953Z",
"created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e28
1fe3f282c65fb853ee171c5 in /"
},
{
"created": "2021-01-31T22:22:55.613815829Z",
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
"empty_layer": true
}
]
}

Spécification de runtime OCI


À un niveau élevé, la spécification de configuration du runtime contrôle les attributs de
conteneur suivants :

• Systèmes de fichiers par défaut


• Espaces de noms
• Mappages d’ID utilisateur et d’ID de groupe
• Appareils
• Configuration du groupe de contrôle
• Sysctl
• Seccomp
• Montage Rootfs
• Chemins masqués et en lecture seule
• Montage d'étiquettes SELinux
• Commandes Hook
• Autres attributs spécialisés (p. ex., personnalité, unifié et Intel RDT)

La spécification OCI couvre également la façon dont l’état d’un conteneur est défini et le
cycle de vie du conteneur. L’état du conteneur peut être interrogé par une fonction d’état
(dans containerd, in et runc) et renvoie les attributs suivants :
{
"ociVersion": "0.2.0",
"id": "oci-container1",
"status": "running",
"pid": 4422,
"bundle": "/containers/redis",
"annotations": {
"myKey": "myValue"
}
}

Spécification Open Container Initiative (OCI) 45


Le cycle de vie du conteneur OCI rassemble toutes les spécifications de runtime, y compris
la façon dont la spécification de runtime interagit avec la spécification d’image. Il définit
le démarrage et la configuration du conteneur, puis comment il interagit avec les hooks de
démarrage/arrêt de l’image.

Docker
Docker fournit une plateforme en tant que service (PaaS) qui permet aux utilisateurs
d’exécuter des applications conteneurisées sur un bureau, de manière simplifiée et
standardisée. Docker exécute des images conformes à l'OCI et est souvent un moyen de
tester un déploiement de code avant le déploiement dans un environnement de production.
En outre, la disponibilité générale des logiciels de bureau de Docker offre aux développeurs
un mécanisme facile pour tester le conditionnement et le déploiement de leur code. Il s'agit
donc d'un système attractif pour les développeurs.

Création de votre première image Docker


Les images Docker sont des images non conformes à l'OCI (leur structure est différente
de la spécification OCI) qui s’exécutent sur Docker et sur les systèmes qui prennent en
charge le runtime Docker (containerd). Les images Docker sont très intéressantes de par
la rapidité et la simplicité de leur création. Chaque image de conteneur est définie par un
Dockerfile. Ce fichier se compose de propriétés et de commandes permettant de créer
l’image, d’exécuter l’application, de l’exposer au réseau et d’effectuer des contrôles d’intégrité.
Il définit les dépendances et les fichiers qui sont copiés dans l’image du conteneur, ainsi que
les commandes qui sont exécutées pour démarrer les applications au sein du conteneur.
Cette approche de création d'image Docker avec un format simple (impératif) de type script
Shell (exécution de la commande après la commande) a été privilégiée par les développeurs
qui souhaitaient remédier à la gestion de packages fastidieuse ou au regroupement
d'applications dans RPM ou DEB (ou archive tarball).
Docker fournit une référence de configuration complète qui peut vous aider à créer votre
propre conteneur.

Création de votre propre conteneur


Pour bien appréhender le fonctionnement des conteneurs, vous
pouvez créer votre propre conteneur de base. Nous vous conseillons
d’essayer l'atelier rubber-docker. Celui-ci couvre les composants de
base d’un conteneur, en expliquant leur mise en œuvre.

Bien que le Dockerfile permette des configurations complexes, il est exceptionnellement


facile de créer une image en seulement quelques lignes. Dans l’exemple suivant, nous allons
exécuter une application Web Python Flask et exposer le port 5000 :

46 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


1. Créez un fichier requirements.txt :
Flask >2.0.0

2. Créez un fichier app.py :


from Flask import flask

app = Flask(__name__)

@app.route('/')
def index():
return "Hello, World"

3. Créez votre Dockerfile, appelé Dockerfile :


FROM ubuntu:18.04 
COPY . /app 
WORKDIR /app 
RUN pip3 install -r requirements.txt 
ENTRYPOINT ["python3"] 
CMD ["app.py"] 
EXPOSE 5000 

Nous allons utiliser une image de base. Dans ce cas, nous allons utiliser Ubun-
 
tu 18.04 dans le registre Docker Hub.

 Nous copions le répertoire actuel dans un nouveau répertoire d’images appelé app.

 Nous définissons le répertoire de travail actuel sur /app.

 Nous utilisons pip pour installer les dépendances définies de l’application.

 Nous définissons l’application entrypoint en tant que Python3 (il s’agit d’une
bonne pratique Docker/Python).

 Nous démarrons l’application en exécutant app.py.

 Nous exposons les services de port 5000 à l’extérieur du conteneur.

Dans son intégralité, cette image de conteneur copie sur notre application, puis installe
les exigences de l’application, démarre elle-même et autorise les connexions réseau sur le
port 5000 (le port Flask par défaut).
Pour permettre aux utilisateurs ou à d’autres applications d’accéder à votre conteneur, le
Dockerfile contient une directive, EXPOSE, qui permet à la pile du réseau de conteneurs
d'écouter sur le port spécifié lors du runtime. Nous reviendrons plus en détail sur la
connectivité réseau dans le chapitre 8.

Docker 47
Un Dockerfile est techniquement un mélange d’un runtime
de conteneur et d’une spécification d’image, car il fournit une
sémantique sur le contenu de l’image, ainsi que sur son exécution
(par exemple, les limitations du CPU ou de la mémoire du
cgroup).

Une fois que vous avez créé votre Dockerfile, vous pouvez créer votre image de conteneur à
l’aide de la commande docker build. Cette étape est généralement effectuée avec le chemin
d’accès au fichier actuel (par exemple, docker build), mais vous pouvez également
utiliser une URL (par exemple, docker build https:// github.com/abcd/docker-
example). Une référence complète sur l'utilisation de docker build est disponible dans
la documentation.
Vous devez créer une balise pour votre image afin que vous puissiez la référencer rapidement
et facilement à l’avenir lorsque vous créerez l’image. Pour ce faire, vous pouvez ajouter
l’option--tag lors de la création :
docker build --tag example-flask-container

Lorsque vous exécutez docker build, une sortie semblable à celle-ci sera générée :
[internal] load build definition from Dockerfile
=> transferring dockerfile: 203B
[internal] load .dockerignore
=> transferring context: 2B
[internal] load metadata for docker.io/library/ubuntu:18.04
[1/6] FROM docker.io/libraryubuntu:18.04
[internal] load build context
=> transferring context: 953B
CACHED [2/6] WORKDIR /app
[3/6] COPY requirements.txt requirements.txt
[4/6] RUN pip3 install -r requirements.txt
[5/6] COPY . .
[6/6] CMD [ "python3", "app.py"]
exporting to image
=> exporting layers
=> writing image sha256:8cae92a8fbd6d091ce687b71b31252056944b09760438905b726625831564c4c
=> naming to docker.io/library/example-flask-container

Bonnes pratiques appliquées à l’utilisation de Docker


L’application douze facteurs suit un certain nombre de caractéristiques d'applications
Dockerisée et basées dans le Cloud. L'application doit donc pouvoir être déployée,
démarrée, arrêtée et supprimée sans provoquer de problèmes. Bien que nous aborderons
les solutions de stockage de conteneurs plus loin dans ce livre, nous vous recommandons
d'éviter les données persistantes autant que possible, à moins que vous ne lisiez les données
d’un volume persistant ou que vous utilisiez un service de données Cloud natif (consultez
le chapitre 9 pour en savoir plus à ce sujet).

48 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Voici quelques-unes des bonnes pratiques à suivre :

• Utilisez les options docker build. Ces options vous permettent de personnaliser votre
image Docker pour ajouter des fonctions telles que les contrôles cgroup, les balises, les
configurations réseau, etc.
• Utilisez un fichier .dockerignore. Similaire à un fichier .gitignore, un fichier .dockerignore
vérifiera que certains fichiers ou dossiers ne sont pas inclus dans la version de l’image.
• Pour réduire la complexité, les dépendances, les tailles de fichiers et les délais de
création, évitez d’installer des packages supplémentaires ou inutiles.
• U
 tilisez des applications multiniveaux. Si vous devez installer des dépendances pour
compiler votre application, vous pouvez le faire pendant le processus de création de
votre image, en copiant uniquement la sortie dans l’image du conteneur. La taille de
votre version d'image est ainsi réduite.

Des informations supplémentaires concernant les bonnes pratiques de Dockerfile sont


disponibles sur le site Web de Docker.

Autres plateformes de conteneurs


Bien que les images de la plateforme Docker et d'OCI demeurent des leaders du secteur, de
nombreuses autres offres de conteneurs sont néanmoins disponibles.

Conteneurs Kata
Les conteneurs Kata sont des conteneurs conformes à l'OCI, qui visent à renforcer la
sécurité. Les conteneurs Kata présentent les fonctions de conteneur léger suivantes :

• Cgroups
• Espaces de noms
• Filtres de capacité
• Filtrage Seccomp
• Contrôle d’accès obligatoire

Les conteneurs Kata présentent également certains concepts de machine virtuelle, en


particulier un noyau invité séparé par conteneur et une meilleure isolation matérielle. Le
processus kata-runtime contrôle l’instanciation du conteneur conforme à l'OCI. La plus
grande différence réside dans le fait qu’il existe un noyau invité distinct par conteneur au
lieu d’un noyau partagé entre les instances de conteneur.

LXC et LXD
LXC (conteneurs Linux) est l’un des premiers mécanismes de conteneur. LXC utilise princi-
palement les fonctionnalités de cgroup et d’espace de noms du noyau, ainsi qu’un ensemble
standard d’API de bibliothèque pour contrôler les conteneurs.

Autres plateformes de conteneurs 49


LXD est le logiciel et les outils de gestion de conteneurs qui orchestrent les API LXC. Le
démon LXD fournit une API REST sur un socket/réseau Unix qui vous permet d’utiliser les
outils de ligne de commande fournis ou de créer les vôtres.
L’écosystème de LXC et de LXD est plus orienté vers l’infrastructure exécutée que le dévelop-
pement d’applications en raison d’un manque d’outils de développement et de déploiement.

Registres de conteneurs
Après avoir créé vos images de conteneur, vous aurez besoin d’emplacements pour les
stocker et les servir. Ceux-ci sont appelés registres de conteneurs. Les registres de conteneurs
ont gagné en popularité en raison de leurs mécanismes de déploiement faciles et de leurs
mécanismes de réplication intégrés, qui remplacent les configurations de rsync difficiles
requises pour les référentiels de packages DEB et RPM.
Lors du déploiement d’un conteneur, le système de déploiement de conteneurs téléchargera
l’image à partir du registre vers l'ordinateur hôte. Essentiellement, un registre de conteneurs
agit comme un serveur de fichiers d'image de conteneur hautement disponible (avec
certaines protections). Toutes les plateformes de conteneurisation courantes vous permettent
de configurer un référentiel public ou privé en tant que source pour les images de conteneur.
Bien que ces responsabilités semblent raisonnables, de nombreuses fonctionnalités de
registres de conteneurs sont négligées, alors qu'elles jouent un rôle important dans les
déploiements de conteneurs sécurisés.
Ces fonctions sont les suivantes :
Réplication d'images
Réplication des images vers d’autres registres de conteneurs (globalement)
Authentification
Seuls les utilisateurs autorisés peuvent accéder au registre et à son contenu
Contrôle basé sur les rôles
Restriction des personnes qui peuvent modifier des images et des données au sein du
registre
Analyse des vulnérabilités
Vérification des images pour identifier les vulnérabilités connues
Collecte de déchets
Suppression régulière d’anciennes images
Audit
Fournir des informations fiables sur les modifications apportées au registre de
conteneurs afin de protéger les informations
Voici les produits Cloud natifs actuels (consultez le paysage CNCF pour en savoir plus) :

• Harbor

50 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


• Dragonfly
• Alibaba Container Registry
• Amazon Elastic Container Registry (Amazon ECR)
• Azure Registry
• Docker Registry
• Google Container Registry
• IBM Cloud Container Registry
• JFrog Artifactory
• Kraken
• Portus
• Quay

Bon nombre de ces systèmes sont liés à des fournisseurs spécifiques. Nous allons toutefois
étudier Harbor en détail, car c'est l’un des systèmes de registre de conteneurs open source
les plus populaires.

Stockage d'images sécurisé avec Harbor


Harbor est la plateforme de registre de conteneurs Cloud natif open source. Créé en 2016,
Harbor stocke des images de conteneurs et fournit des fonctionnalités d’analyse de signature
et de sécurité. En outre, Harbor prend en charge la haute disponibilité et la fonctionnalité de
réplication, ainsi qu’un certain nombre de fonctions de sécurité, notamment la gestion des
utilisateurs, le contrôle d’accès en fonction du rôle et l’audit d’activité. Harbor constitue une
plateforme intéressante car elle propose des fonctions de premier ordre malgré sa nature
open source.

Installation d'Harbor
Lorsque vous utilisez Harbor, il devient essentiellement un composant critique de vos
pipelines de création et de déploiement. Vous devez l’exécuter sur un hôte qui respecte ces
spécifications recommandées :

• CPU quatre cœurs


• 8 Go de mémoire
• Disque de 160 Go minimum
• Ubuntu 18.04/CentOS7 ou version ultérieure

Registres de conteneurs 51
Nous vous recommandons d'évaluer les performances de la référence
de disque ou de la configuration afin qu'elles correspondent aux
performances souhaitées. Harbor est essentiellement un serveur de
fichiers, et les performances du disque affectent considérablement
les temps de création et de déploiement.

Harbor prend en charge plusieurs méthodes d’installation, notamment Kubernetes et


Helm. Étant donné que nous n’avons pas encore exploré Kubernetes, nous allons installer
Harbor manuellement.
Harbor présente deux versions (ou types d’installations)  : un installateur en ligne et
un installateur hors ligne. L'installateur en ligne est de plus petite taille et utilise une
connexion Internet pour télécharger l’image complète de Docker Hub (directement à partir
d’Internet). L'installateur hors ligne présente une taille supérieure et ne nécessite pas de
connexion Internet. Si vos conditions de sécurité sont restrictives, l’installateur hors ligne
est probablement la meilleure option.
Les versions Harbor sont répertoriées sur Github.

Vérification de l'installateur
Il est recommandé de vérifier l’intégrité du package téléchargé. Vous devrez ouvrir
l’accès Internet sortant au port TCP/11371 afin d’atteindre le serveur de clés GPG. Pour
vérifier l’installateur :

1. Téléchargez le fichier *.asc correspondant à votre version d’installateur.


2. Obtenez la clé publique pour les versions Harbor :
gpg --keyserver. hkps://keyserver.ubuntu.com --receive-keys 644FF454C0B4115C

3. Vérifiez le package en exécutant :


gpg -v –keyserver hkps://keyserver.ubuntu.com –verify <asc-file-name>

Vous devriez recevoir le message gpg : Good signature from "Harbor-sign


(The key for signing Harbor build) dans la sortie de la commande précédente.

Configuration d'Harbor pour l’installation


Avant de configurer Harbor, vous devez activer HTTPS en installant un certificat Secure
Sockets Layer (SSL) via votre propre autorité de certification (CA) ou via une CA publique
comme Let’s Encrypt. Vous trouverez plus d’informations sur la procédure à suivre dans la
documentation Harbor.

Tous les paramètres d’installation pour Harbor sont définis dans un fichier appelé harbor.
yml. Les paramètres définis dans ce fichier configurent Harbor pour sa première utilisation
ou lorsqu’il est reconfiguré via le fichier install.sh. Toutes les options de configuration
sont disponibles sur GitHub. Pour commencer, nous allons utiliser les paramètres requis
suivants :

52 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


hostname: container-registry.example.com

http:
# port for http, default is 80. If https enabled, this port will redirect to https port
port: 80

https:
port: 443
certificate: /your/certificate/path
private_key: /your/private/key/path

harbor_admin_password: Harbor12345

# DB configuration
database:
password: root123
max_idle_conns: 50
max_open_conns: 100

# The default data volume


data_volume: /data

clair:
updaters_interval: 12

jobservice:
max_job_workers: 10

notification:
webhook_job_max_retry: 10

chart:
absolute_url: disabled

log:
level: info
local:
rotate_count: 50
rotate_size: 200M
location: /var/log/harbor

proxy:
http_proxy:
https_proxy:
no_proxy: 127.0.0.1,localhost,.local,.internal,log,db,redis,nginx,core,portal, \
postgresql,jobservice,registry,registryctl,clair

Création d’une image Packer


Grâce aux connaissances acquises au cours du chapitre  2, nous allons créer une image
Packer pour déployer Harbor. Vous pouvez utiliser le fichier harbor.json suivant :
{
"builders": [{
"type": "azure-arm",

"client_id": "<place-your-client_id-here>",
"client_secret": "<place-your-client_secret-here>",
"tenant_id": "<place-your-tenant-id-here>",

Registres de conteneurs 53
"subscription_id": "<place-your-subscription-here>",

"managed_image_resource_group_name": "CloudNativeAzure-group",
"managed_image_name": "harborImage",

"os_type": "Linux",
"image_publisher": "Canonical",
"image_offer": "UbuntuServer",
"image_sku": "16.04-LTS",

"azure_tags": {
"env": "Production",
"task": "Image deployment"
},

"location": "East US",


"vm_size": "Standard_DS1_v2"
}],
"provisioners": [{
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'",
"inline": [
"apt-get update",
"apt-get upgrade -y",
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
],
"inline_shebang": "/bin/sh -x",
"type": "shell"
},
{
{
"type": "shell",
"inline": [
"wget https://github.com/goharbor/harbor/releases/download/v2.1.3/ \
harbor-online-installer-v2.1.3.tgz -O /tmp/harbor-online-installer-v2.1.3.tgz",
"tar -xvf /tmp/harbor-online-installer-v2.1.3.tgz"
]
},
{
"type": "file",
"source": "{{template_dir}}/harbor.yml",
"destination": "/tmp/harbor"
},
{
"type": "shell",
"inline": [
"sudo chmod 0777 /tmp/harbor/install.sh",
"sudo /tmp/harbor/install.sh"
]
}
]
}

Lorsque vous exécutez le fichier packer build harbor.json, votre image s’intégrera avec
Harbor installé et configuré. Si vous utilisez HTTPS (nous vous le recommandons), vous devrez
modifier le fichier Packer pour y inclure la copie du certificat et des fichiers de clé privée.

54 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Stockage d'images sécurisé avec Azure Container Registry
Azure fournit son propre registre de conteneurs, connu sous le nom d'Azure Container
Registry. Étant donné que le registre est fourni par Azure, la configuration initiale et les
exigences de maintenance continue sont beaucoup plus aisées que celles d'une exécution de
logiciels comme Harbor.

Installation d'Azure Container Registry


Le déploiement d’une instance de conteneur est exceptionnellement simple :

1. Connectez-vous au portail Azure et cliquez sur « Créer une ressource », comme illustré
à figure 3-3.

Figure 3-3. Page d’accueil du portail Azure

2. Recherchez « registre de conteneur » dans la barre de recherche, puis cliquez sur l’offre
de registre de conteneurs de Microsoft (voir la figure 3-4).

Figure 3-4. Recherche d’Azure Container Registry

Vous accéderez à un formulaire où vous devrez renseigner des informations de base sur
le registre que vous allez créer (figure 3-5).

Registres de conteneurs 55
Figure 3-5. Première page de la création d'un registre de conteneurs

3. Renseignez les détails de base suivants du registre de conteneurs :


Abonnement
Choisissez l’abonnement dans lequel vous souhaitez effectuer le déploiement.
Groupe de ressources
Vous pouvez utiliser un groupe de ressources existant ou en créer un nouveau.
Dans ce cas, nous allons en créer un nouveau appelé rg-container-registry.
Nom du registre
Il permet de configurer l’URL utilisée pour accéder au registre. Nous allons utiliser
cloudnativeinfra.azurecr.io. Ce nom est unique au monde. Vous devrez donc
choisir le vôtre et vérifier que vous n’obtenez pas de message d’erreur indiquant
qu’il est déjà utilisé.
Région
Choisissez votre région. Nous allons effectuer notre déploiement dans la région est
des États-Unis 2.

56 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Référence
Trois niveaux sont disponibles. Une comparaison entre eux est disponible sur
la page de tarification du registre du conteneur. Nous allons utiliser le niveau
Standard.
4. Choisissez votre configuration réseau (figure 3-6). Étant donné que nous utilisons le
niveau Standard, nous ne pourrons pas apporter de modifications de configuration.
Par conséquent, le registre de conteneurs qui sera créé est disponible sur Internet et pas
seulement dans notre réseau Azure.

Figure 3-6. Configuration de la mise en réseau du registre de conteneurs

5. Choisissez votre configuration de chiffrement (figure 3-7). Le niveau Standard assure le


chiffrement au repos des données stockées, tandis que le niveau Premium permet aux
utilisateurs de définir leurs propres clés de chiffrement.

Registres de conteneurs 57
Figure 3-7. Chiffrement du registre de la mise en réseau du conteneur

6. Cliquez sur « Examiner + créer » pour créer votre registre de conteneurs. Ce déploiement
sera effectué en moins de deux minutes.

Points de terminaison privés


Les points de terminaison privés sont un concept Azure qui
permet aux ressources Cloud d’être disponibles via l’espace
d’adressage  RFC1918 au lieu d’une adresse IP publique. Ainsi,
votre ressource n’est disponible uniquement via un réseau
connecté localement et non sur Internet.

Si vous souhaitez déployer votre registre de conteneurs via Terraform, utilisez le code
suivant :
resource "azurerm_resource_group" "rg" {
name = "example-resources" location = "West Europe"
}

resource "azurerm_container_registry" "acr" {


name = "containerRegistry1"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
sku = "Premium"

identity {
type = "UserAssigned"
identity_ids = [ azurerm_user_assigned_identity.example.id ]
}

58 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


encryption {
enabled = true
key_vault_key_id = data.azurerm_key_vault_key.example.id
identity_client_id = azurerm_user_assigned_identity.example.client_id
}
}

resource "azurerm_user_assigned_identity" "example" {


resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location name = "registry-uai"
}

data "azurerm_key_vault_key" "example" {


name = "super-secret"
key_vault_id = data.azurerm_key_vault.existing.id
}

Stockage d'images Docker dans un registre


Comme nous l’avons mentionné précédemment, vous devez être en mesure de stocker et
de servir des images de conteneur de manière fiable. Une fois que vous avez créé votre
image personnalisée, vous devez la déplacer vers le registre. En outre, et comme nous
l’avons mentionné précédemment, il existe de nombreuses options pour stocker des images,
notamment Docker Registry et les registres de conteneurs de différents fournisseurs Cloud
(Azure Container Registry par exemple). Dans ce cas, nous allons utiliser le registre Harbor
que nous avons créé plus tôt dans ce chapitre.
Commençons par nous connecter au registre :
$ docker login <harbor_address>

Par exemple :
$ docker login https://myharborinstallation.com

Nous allons maintenant marquer l’image :


$ docker tag example-flask-container <harbor_address>/demo/example-flask-container

Nous allons la mettre en ligne :


$ docker push <harbor_address>/demo/example-flask-container

Une fois cette opération terminée, Harbor sera en mesure de servir notre image sur
n’importe quel logiciel de conteneur qui la télécharge.

Stockage d'images Docker dans un registre 59


Exécution de Docker sur Azure
Azure vous permet d'exécuter votre image de conteneur Docker de deux façons : en utilisant
Azure Container Instances ou en exécutant votre propre machine virtuelle si Docker est
installé. Cette section décrit ces deux méthodes.

Azure Container Instances
Azure Container Instances (ACI) vous permet de lancer rapidement une instance de
conteneur dans Azure sans avoir à gérer l’infrastructure sous-jacente. Vous obtenez ainsi un
service semblable à Docker, mais dans le Cloud public. ACI permet le déploiement d’images
Azure Container Registry ou d’images Docker à partir de référentiels privés ou publics.
ACI permet également de placer ces instances dans des réseaux privés ou publics en fonction
de l’utilisation des images de conteneurs.
Voici certaines des fonctions supplémentaires proposées par ACI :

• Inutile de configurer une spécification de runtime OCI


• La capacité de transmettre des variables d’environnement au conteneur
• Gestion de pare-feu basique
• Stratégies de redémarrage des conteneurs géré

En bref, ACI constitue la solution la plus simple pour commencer à exécuter des conteneurs
dans Azure ! ACI vous permet également de tester de nouvelles images de conteneurs sans
créer un environnement totalement distinct.
Quelques mises en garde cependant concernant l'exécution d'ACI :

• Une limite de quatre cœurs/16 Go est appliquée à toutes les instances de conteneur uniques.
• Les paramètres ACI relatifs au CPU et à la mémoire remplaceront tout paramètre
d’exécution que vous avez appliqué à votre image de conteneur.

60 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Déploiement d’Azure Container Instance
Le déploiement d’une instance de conteneur Azure est exceptionnellement simple.
Commencez par vous connecter au portail Azure et cliquez sur « Créer une ressource »,
comme illustré à figure 3-8.

Figure 3-8. Page d’accueil du portail Azure

Cliquez en bas à gauche sur Conteneurs, afin d'accéder à une offre de produits liés aux
conteneurs (voir la figure 3-9). Cliquez sur le lien Instances de conteneurs.

Figure 3-9. Recherche de produits de conteneurs

Vous accéderez à un formulaire où vous devrez renseigner des informations de base sur
l'instance de conteneur que vous allez créer (figure 3-10).

Exécution de Docker sur Azure 61


Figure 3-10. Configuration de base pour une instance de conteneur Azure

Remplissez les champs suivants :


Abonnement
Choisissez l’abonnement dans lequel vous souhaitez effectuer le déploiement.
Groupe de ressources
Vous pouvez utiliser un groupe de ressources existant ou en créer un nouveau. Dans ce
cas, nous allons en créer un nouveau appelé rg-aci-test.
Nom du conteneur
Nommez votre instance de conteneur. Nous allons appeler le nôtre aci-demo.
Région
Choisissez votre région. Nous allons effectuer notre déploiement dans la région est des
États-Unis.

62 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Source d'image
C'est ici que vous choisissez votre image de conteneur. Trois options vous sont
proposées : 
• Utilisez une image de démarrage rapide de démonstration fournie par Azure.
• Utilisez une image qui a été téléchargée dans Azure Container Registry.
• Utilisez une image téléchargée vers Docker Hub ou un autre registre. Si vous avez
déployé Harbor, vous pouvez le configurer ici. Dans ce cas, nous allons sélectionner
l'image de démarrage rapide Nginx.
Taille
Cette option vous permet de choisir le type de machine virtuelle souhaité. Sachez que la
taille de la machine virtuelle est proportionnelle au prix de son exécution. Dans ce cas,
nous allons exécuter une configuration « 1 vCPU, 1,5 Gio de mémoire, 0 gpus ». Vous
disposez donc d'un cœur virtuel, de 1 Go de mémoire et d'aucune carte graphique.
Une fois cette configuration réalisée, cliquez sur le bouton Suivant : Mise en réseau pour
configurer la mise en réseau (figure 3-11).

Figure 3-11. Configuration du réseau pour une instance de conteneur Azure

Dans cette démo, nous allons attribuer une adresse IP publique à notre instance de conteneur
(afin de pouvoir y accéder via Internet), puis attribuer un nom DNS à l’adresse IP : aci-
demo.eastus.azurecontainer.io (ce nom doit être unique). Nous avons ensuite la possibilité
de rendre les ports TCP ou UDP disponibles à l’extérieur du conteneur.

Exécution de Docker sur Azure 63


Comme nous utilisons un conteneur Nginx, le port 80 est automatiquement ouvert. Vous
pouvez également créer un réseau virtuel (ou en utiliser un existant) et un sous-réseau si
vous utilisez un adressage IP privé.
Si vous cliquez sur le bouton Suivant : Avancé, vous accéderez à d’autres paramètres que vous
pouvez appliquer pour gérer les stratégies de défaillances et les variables d’environnement.
Nous n'apporterons aucune modification pour cette instance (figure 3-12).

Figure 3-12. Configuration avancée du réseau pour une instance de conteneur Azure

Enfin, cliquez sur « Examiner + créer » afin de créer l’image de conteneur. Le déploiement
d'une image de démarrage rapide nécessitera environ deux minutes.
Une fois que votre conteneur est déployé, vous pourrez accéder au nom DNS de votre conteneur
dans un navigateur Internet et voir la bannière « Bienvenue dans Nginx ! » (voir la figure 3-13).
Vous pouvez également obtenir une connexion de console à l'instance sur le portail Azure.

Figure 3-13. Page d'accueil par défaut de Nginx

Des informations supplémentaires sur le déploiement d’ACI sont disponibles dans la


documentation Microsoft.

64 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


Exécution de Docker Container Engine
Si vous souhaitez affiner le contrôle de votre environnement de conteneur, vous pouvez
exécuter votre propre moteur de conteneur Docker. Cette approche présente néanmoins le
compromis de gérer la machine virtuelle sous-jacente sur laquelle Docker s’exécute.

1. Installez Docker CE sur une machine virtuelle Azure :


Centos/Red Hat :
$ sudo yum install -y yum-utils

$ sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum install docker-ce docker-ce-cli containerd.io

Debian/Ubuntu :
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"

$ sudo apt-get update


$ sudo apt-get install docker-ce docker-ce-cli containerd.io

Vous pouvez également installer Docker CE via une image Packer. Voici un exemple
de configuration Packer :
{
"builders": [{
"type": "azure-arm",
"client_id": "<place-your-client_id-here>",
"client_secret": "<place-your-client_secret-here>",
"tenant_id": "<place-your-tenant-id-here>",
"subscription_id": "<place-your-subscription-here>",
"managed_image_resource_group_name": "CloudNativeAzure-group",
"managed_image_name": "DockerCEEngine",

"os_type": "Linux",
"image_publisher": "Canonical",
"image_offer": "UbuntuServer",
"image_sku": "16.04-LTS",

"azure_tags": {
"env": "Production",
"task": "Image deployment"
},

"location": "East US",


"vm_size": "Standard_DS1_v2"
}],
"provisioners": [{
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'",
"inline": [
"apt-get update",
"apt-get upgrade -y",
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"

Exécution de Docker sur Azure 65


],
"inline_shebang": "/bin/sh -x",
"type": "shell"
},
{
"type": "shell",
"inline": [
"sudo apt-get remove docker docker-engine",
"sudo apt-get install apt-transport-https ca-certificates curl \
software-properties-common",
"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add \
-sudo apt-key fingerprint 0EBFCD88",
"sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/ \
linux/ubuntu $(lsb_release -cs) stable",
"sudo apt-get update",
"sudo apt-get -y upgrade",
"sudo apt-get install -y docker-ce",

"sudo groupadd docker",


"sudo usermod -aG docker ubuntu",
"sudo systemctl enable docker"
]
}]
}

2. Une fois que votre Docker CE s'exécute, démarrez votre conteneur en exécutant :
$ docker run -d <container-name>

3. À l’aide de notre exemple Flask précédant, vous pouvez démarrer votre conteneur en
exécutant :
$ docker run -d -p 5000:5000 http://myharborinstallation.com/demo/ \
example-flask-container

4. Si vous exécutez curl localhost:5000/, vous recevrez un message « Hello, World ».

Résumé
Dans ce chapitre, nous avons brièvement discuté des couches d’abstraction qui composent
l’écosystème de conteneurs. Étant donné que l’écosystème de conteneurs est désormais
bien centré sur les spécifications OCI et Kubernetes, il connaît un essor important. Nous
constatons par ailleurs une prolifération de produits de sécurité et de réseau conçus selon la
norme OCI. Dans le chapitre 8, nous étudierons une autre norme de conteneur : Container
Network Interface (CNI). Nous découvrirons comment tirer parti de celle-ci pour configurer
la mise en réseau de votre conteneur.

66 Chapitre 3 : Conteneurisation de votre application : plus que des boîtes


CHAPITRE 4
Kubernetes : le grand Orchestrator

À mesure que les applications monolithiques sont divisées en microservices, les conteneurs
deviennent les hébergements de référence pour ces microservices. Les microservices
constituent une approche architecturale Cloud native, dans laquelle une application unique est
composée de nombreux services ou composants plus petits, associés librement et déployables
de façon indépendante. Les conteneurs veillent au bon fonctionnement du logiciel lorsqu’il est
déplacé entre différents environnements. Grâce aux conteneurs, les microservices fonctionnent
à l’unisson avec d’autres microservices pour former une application entièrement opérationnelle.
Même si la division des applications monolithiques en services plus petits résout un problème,
elle en crée d'autres plus importants en termes de gestion et de maintenance des applications
sans provoquer de temps d’arrêt significatifs, de mise en réseau des différents microservices, de
stockage distribué, etc. Les conteneurs sont utiles car ils découplent les applications dans des
codes base rapides et plus petits, en se concentrant sur le développement des fonctionnalités.
Toutefois, bien que ce découplage soit propre et facile au début (car il y a moins de conteneurs
à gérer), il devient presque impossible de déboguer, de mettre à jour ou même de déployer des
microservices conteneurisés dans la pile en toute sécurité sans provoquer de défaillance ou
de temps d'arrêt à mesure que le nombre de microservices augmentent dans une application.
La conteneurisation des applications a été la première grande étape vers la création
d’environnements auto-réparables sans aucun temps d’arrêt. Cette pratique devait
néanmoins évoluer davantage, surtout en termes de développement logiciel et de livraison
dans les environnements Cloud natifs. Des planificateurs et des moteurs d'orchestrateur
tels que Mesos, Docker Swarm, Nomad et Kubernetes ont donc été développés par la suite.
Dans ce chapitre et le suivant, nous nous concentrerons sur Kubernetes en raison de sa
maturité et de son adoption étendue dans le secteur.
Google a introduit Kubernetes en 2014, après avoir passé presque une décennie à perfectionner
et à apprendre de son système de gestion de cluster interne, Borg. En termes simples,
Kubernetes est un système d’orchestration de conteneurs open source. Le terme orchestration
de conteneurs désigne principalement le cycle de vie complet de la gestion des conteneurs dans

67
des environnements dynamiques comme le Cloud, où les machines (serveurs) vont et viennent
en fonction des besoins. Un orchestrateur de conteneurs automatise et gère une variété de
tâches, telles que la mise en service, le déploiement, la planification, l’allocation des ressources,
la mise à l’échelle, l’équilibrage de charge et la surveillance de l’intégrité des conteneurs. En
2015, Google a fait don de Kubernetes à la Cloud Native Computing Foundation (CNCF).
Kubernetes (alias K8s) est l’un des éléments les plus largement adoptés d’une solution Cloud
native, car il prend en charge le déploiement, l’évolutivité et la maintenance des applications
conteneurisées. Kubernetes aide les ingénieurs de fiabilité de site (SRE) et les ingénieurs DevOps
à exécuter leur charge de travail dans le Cloud avec résilience, tout en prenant en charge la mise
à l’échelle et le basculement des applications couvrant plusieurs conteneurs entre les clusters.

Pourquoi Kubernetes est-il appelé K8s ?


Kubernetes dérive d'un mot grec qui signifie timonier ou capitaine
de navire. Kubernetes est également appelé K8s. Ce numéronyme
dérive du remplacement des huit lettres entre K et S (« ubernete »)
par un 8.

Voici quelques-unes des fonctions clés prêtes à l'emploi offertes par Kubernetes :
Auto-régénération
L’une des fonctions les plus importantes de Kubernetes est le processus de création de
nouveaux conteneurs lorsqu’un conteneur est défaillant.
Découverte de services
Dans un environnement Cloud natif, les conteneurs passent d’un hôte à un autre. Le
processus qui consiste à déterminer comment se connecter à un service/une application
exécuté(e) dans un conteneur est appelé découverte de services. Kubernetes expose
automatiquement les conteneurs à l’aide de DNS ou de l’adresse IP des conteneurs.
Équilibrage de charge
Pour maintenir l’application déployée dans un état stable, Kubernetes équilibre les
charges et distribue automatiquement le trafic entrant.
Déploiements automatiques
Kubernetes fonctionne avec une syntaxe déclarative, ainsi, vous n’avez pas à vous soucier
du déploiement des applications. Il vous suffit d'indiquer les éléments à déployer et
Kubernetes s'en charge.
Bin packing
Pour tirer le meilleur parti des ressources de calcul, Kubernetes déploie automatiquement
les conteneurs sur le meilleur hôte possible sans gaspiller ou compromettre la
disponibilité globale des autres conteneurs.

68 Chapitre 4 : Kubernetes : le grand Orchestrator


Ce chapitre couvre les principaux composants et les concepts sous-jacents de Kubernetes.
Bien que ce chapitre ne vise pas à explorer Kubernetes dans son intégralité (il existe en effet
un large éventail d'ouvrages1 à ce sujet), nous souhaitons vous proposer des connaissances
solides en la matière. Cette approche pratique vous aidera à mieux comprendre l'essentiel
de l’environnement global. Commençons par aborder le fonctionnement d'un cluster
Kubernetes.

Composants de Kubernetes
Le cluster Kubernetes contient deux types de composants de nœud :
Plan de contrôle
Il s’agit du composant de gouvernance du cluster Kubernetes. Il garantit que plusieurs
services importants (par exemple, la planification, le démarrage de nouveaux pods2)
sont toujours en cours d’exécution. L’objectif principal du nœud de plan de contrôle
consiste à garantir l'intégrité et le bon fonctionnement continus du cluster.
Nœuds de travail
Il s’agit des instances de calcul qui exécutent votre charge de travail sur votre cluster
Kubernetes, qui héberge tous vos conteneurs.

La figure 4-1 représente les composants globaux de Kubernetes. Les lignes désignent les
connexions, telles que les nœuds de travail acceptant une connexion parlant à l’équilibreur
de charge, qui distribue le trafic.

Figure 4-1. Composants de Kubernetes

1 Managing Kubernetes de Brendan Burns et Craig Tracey (O’Reilly, 2019) ; Kubernetes in Action de Marko
Lukša (Manning, 2018).
2 Les pods sont les plus petites unités de calcul déployables que vous pouvez créer et gérer dans Kubernetes.

Composants de Kubernetes 69
Examinons à présent plus en détails chacun des composants.

Plan de contrôle
Le plan de contrôle est principalement responsable de la prise de décisions globales sur le
cluster Kubernetes, tels que la détection de l’état du cluster, la planification des pods sur
les nœuds et la gestion du cycle de vie des pods. Le plan de contrôle Kubernetes comporte
plusieurs composants, que nous décrirons dans les sous-sections suivantes.

kube-apiserver (serveur API)


Le serveur API est le front-end du plan de contrôle Kubernetes et le seul composant doté
d'un accès direct à l’ensemble du cluster Kubernetes. Comme vous l’avez vu dans la figure 4-1,
le serveur API sert de point central pour toutes les interactions entre les nœuds de travail
et les nœuds de contrôleur. Les services exécutés dans un cluster Kubernetes utilisent le
serveur d’API pour communiquer entre eux. Vous pouvez exécuter plusieurs instances du
serveur API car elles sont conçues pour une mise à l'échelle horizontale.

Planificateur Kube
Le planificateur Kube est chargé de déterminer quel nœud de travail exécutera un pod (ou
une unité basique de travail). Nous couvrirons les pods plus en détail plus loin dans ce
chapitre). Le planificateur Kube parle au serveur API, qui détermine quels nœuds de travail
sont disponibles pour exécuter le pod planifié d'une manière optimale. Le planificateur
recherche les pods nouvellement créés qui ne sont pas encore attribués à un nœud, puis
identifie les nœuds possibles comme candidats potentiels. Il évalue chacun d'entre eux en
fonction de différents facteurs, tels que la capacité des ressources de nœud et l’exigence
matérielle, afin de garantir une prise de décision appropriée en matière de planification.
Le nœud présentant le score le plus élevé est choisi pour exécuter le pod. Le planificateur
notifie également le serveur API de cette décision dans un processus appelé liaison.

Gestionnaire de contrôleur Kube


Kubernetes dispose d’une fonction intégrée qui met en œuvre les capacités de réparation
spontanée dans le cluster. Cette fonction est appelée gestionnaire de contrôleur Kube et
s’exécute en tant que démon. Le gestionnaire de contrôleur exécute une boucle de contrôle
appelée boucle de rapprochement, qui est une boucle qui ne se termine pas et qui est
responsable des éléments suivants :

• Déterminer si un nœud est défaillant et, le cas échéant, prendre des mesures appropriées.
Ce processus est effectué par le contrôleur de nœud.
• Maintenir le nombre adéquat de pods. Ce processus est effectué par le contrôleur de
réplication.
• Joindre les objets de point de terminaison (c.-à-d., les services et les pods). Ce processus
est effectué par le contrôleur de point de terminaison.

70 Chapitre 4 : Kubernetes : le grand Orchestrator


• S’assurer que les comptes par défaut et les points de terminaison sont créés pour de
nouveaux espaces de noms. Ce processus est effectué par le compte de service et les
contrôleurs de jetons.

C'est la boucle de rapprochement qui génère la capacité de réparation spontanée de


Kubernetes. Kubernetes détermine l’état du cluster et de ses objets en exécutant en continu
les étapes suivantes dans une boucle :

1. Récupérer l’état déclaré par l’utilisateur (l'état souhaité).


2. Observer l’état du cluster.
3. Comparer les états observés et souhaités pour trouver des différences.
4. Prendre des mesures en fonction de l’état observé.

etcd
Kubernetes utilise etcd comme une banque de données. etcd est un magasin clé/valeur qui est
responsable de la persistance de tous les objets Kubernetes. Il a été créé à l’origine par l’équipe
de CoreOS et est désormais géré par CNCF. Il est généralement lancé dans une configuration
hautement disponible, et les nœuds etcd sont hébergés sur des instances distinctes.

Nœuds de travail
Un cluster Kubernetes contient un ensemble d'ordinateurs Kubernetes appelées nœuds de
travail qui exécutent les applications conteneurisées. Le plan de contrôle gère les nœuds de
travail et des pods dans le cluster. Certains composants s’exécutent sur tous les nœuds de
travail Kubernetes ; ils sont abordés dans les sous-sections suivantes.

Kubelet
Kubelet est l’agent de démon qui s’exécute sur chaque nœud pour s’assurer que les conteneurs
sont toujours dans un état d’exécution dans un pod, en veillant à leur bon fonctionnement.
Kubelet signale au serveur API les ressources actuellement disponibles (CPU, mémoire,
disque) sur les nœuds de travail afin que le serveur API puisse utiliser le gestionnaire de
contrôleur pour observer l’état des pods. Étant donné que kubelet est l’agent qui s’exécute sur
les nœuds de travail, les nœuds de travail gèrent les tâches d'entretien basiques, telles que le
redémarrage des conteneurs si nécessaire, et effectuent des contrôles d’intégrité systématiques.

Kube-proxy
Kube-proxy est le composant de mise en réseau qui s’exécute sur chaque nœud. KUBE-proxy
surveille tous les services Kubernetes3 du cluster et garantit que lorsqu’une requête à un service

3 Dans Kubernetes, les services sont une façon d’exposer vos pods afin qu’ils puissent être découverts au sein du
cluster Kubernetes.

Composants de Kubernetes 71
particulier est effectuée, elle est acheminée vers le point de terminaison IP virtuel particulier.
Kube-proxy est responsable de la mise en œuvre d’un type d'adresse IP virtuelle pour les services
Maintenant que vous acquis les connaissances de bases sur les composants Kubernetes, nous
allons approfondir ce sujet et nous intéresser d'un peu plus près au serveur API Kubernetes.

Objets de serveur d'API Kubernetes


Le serveur d’API est responsable de toutes les communications à l’intérieur et à l’extérieur
d’un cluster Kubernetes, et il expose une API HTTP RESTful. Le serveur API, à un niveau
fondamental, vous permet d’interroger et de manipuler des objets Kubernetes. En termes
simples, les objets Kubernetes sont des entités avec état qui représentent l’état global de votre
cluster (figure 4-2). Pour commencer à travailler avec ces objets, nous devons comprendre
les principes fondamentaux de chacun.

Figure 4-2. Interaction de serveur d’API avec des objets de cluster

Pods
Dans Kubernetes, les pods sont la plus petite unité atomique de base. Un pod est un groupe
d’un ou de plusieurs conteneurs qui sont déployés sur les nœuds de travail. Kubernetes est
responsable de la gestion des conteneurs qui s’exécutent au sein d'un pod. Les conteneurs au
sein d’un pod se retrouvent toujours sur le même nœud de travail et sont étroitement couplés.
Étant donné que les conteneurs au sein d’un pod sont co-localisés, ils s’exécutent dans le même
contexte (c.-à-d. qu’ils partagent le réseau et le stockage). Ce contexte partagé est caractéristique
des espaces de noms Linux, cgroups et d’autres aspects qui maintiennent l’isolement (comme
nous l’avons expliqué au chapitre 3). Les pods obtiennent également une adresse IP unique.
Dans les scénarios typiques, un conteneur unique est exécuté au sein d’un pod, mais dans
certains cas, plusieurs conteneurs doivent travailler ensemble dans un pod. Cette dernière
configuration est généralement appelée conteneur sidecar. L’un des exemples les plus
courants d’exécution d’un conteneur sidecar est l’exécution d’un conteneur de journalisation
pour votre application. Celui-ci expédiera vos journaux vers un stockage externe, tel qu’un
serveur ELK (Elasticsearch, Logstash et Kibana), au cas où votre pod d’application défaille
ou si un pod est supprimé. Les pods constituent également une solution intelligente, car
si un processus plante dans un conteneur, Kubernetes le redémarre instantanément en
fonction des contrôles d’intégrité définis au niveau de l’application.

72 Chapitre 4 : Kubernetes : le grand Orchestrator


Les pods autorisent par ailleurs la mise à l’échelle horizontale par réplication mise en œuvre
via ReplicaSets. Ainsi, si vous souhaitez mettre votre application à l'échelle horizontalement,
vous devez créer davantage de pods à l’aide de ReplicaSets.
Les pods sont également éphémères. Si un pod plante, il sera donc déplacé et redémarré sur
un autre hôte. Cette opération est également effectuée à l’aide de ReplicaSets.

ReplicaSets
La fiabilité est une caractéristique principale de Kubernetes, et comme personne n’exécute
une seule instance d’un pod, la redondance devient importante. Un ReplicaSet est un objet
Kubernetes qui garantit qu’un ensemble stable de pods de réplica s’exécute pour maintenir
un cluster autorégénérateur. Tout cela est réalisé par la boucle de rapprochement, qui
continue à s’exécuter en arrière-plan pour observer l’état global du cluster Kubernetes.
ReplicaSets utilise la boucle de rapprochement et veille à ce qu'un nouveau pod soit démarré
si l’un des pods plante ou se redémarre, afin de conserver l’état de réplication souhaité.
En général, vous n'interagissez pas directement avec ReplicaSets. Vous utiliserez à la place
l’objet Deployment, qui garantit des mises à jour sans interruption de votre application et
une approche déclarative en matière de gestion et d'exploitation de Kubernetes.

Déploiements
Kubernetes est principalement un orchestrateur axé sur la syntaxe déclarative. Ainsi, pour
déployer de nouvelles fonctions, vous devez indiquer les actions souhaitées à Kubernetes,
afin qu'il détermine comment effectuer ces opérations en toute sécurité. Deployment est
l'un des objets proposés par Kubernetes pour fluidifier la publication de nouvelles versions.
Si vous mettez à jour manuellement les pods, vous devrez les redémarrer et cette opération
provoquera des temps d’arrêt. Bien qu’un ReplicaSet sache comment maintenir le nombre
de pods souhaité, il ne procédera pas à une mise à niveau sans interruption. C'est dans ce
cas que l'objet Deployment s'avère utile, car il permet de déployer des modifications sur des
pods sans interruption, en gardant un nombre prédéfini de pods actif en permanence avant
le déploiement d'un nouveau pod mis à jour.

Services
Pour exposer une application exécutée à l’intérieur d’un pod, Kubernetes propose un
objet appelé Service. Étant donné que Kubernetes est un système très dynamique, il est
nécessaire de s’assurer que les applications s’adressent au back-ends appropriés. Les pods
sont des processus éphémères dans le monde Kubernetes, car ils sont régulièrement
créés ou supprimés. Les pods sont associés à une adresse IP unique. Ainsi, si vous vous
fiez uniquement à l’adresse IP des pods, vous serez certainement confronté à un service
défectueux lorsqu’un pod plante, car celui-ci obtiendra une adresse IP différente après son
redémarrage, même si vous exécutez ReplicaSets. L'objet Service offre une abstraction
en définissant un ensemble logique de pods ainsi qu'une stratégie qui régit l'accès à ces

Objets de serveur d'API Kubernetes 73


derniers. Chaque Service obtient une adresse IP stable et un nom DNS qui peuvent être
utilisés pour accéder aux pods. Vous pouvez définir de façon déclarative les services qui
précèdent vos pods et utiliser le Label-Selector pour accéder au service.

Espaces de noms
Étant donné que plusieurs équipes et projets sont déployés dans un environnement de
production, il devient nécessaire d’organiser les objets Kubernetes. Pour faire simple, les
espaces de noms sont des clusters virtuels séparés par un partitionnement logique, c’est-à-
dire que vous pouvez regrouper vos ressources, telles que les déploiements, les pods, etc.,
en fonction des partitions logiques. Certaines personnes assimilent les espaces de noms
à des répertoires pour séparer les noms. Chaque objet de votre cluster présente un nom
unique à ce type de ressource particulier, et de même, chaque objet possède un UID unique
sur l’ensemble du cluster. Les espaces de noms vous permettent également de diviser les
ressources de cluster entre plusieurs utilisateurs en définissant des quotas de ressources.

Étiquettes et sélecteurs
À mesure que vous commencez à utiliser Kubernetes et à créer des objets, vous vous
rendrez compte de la nécessité d’identifier ou de marquer vos ressources Kubernetes afin
de les regrouper en entités logiques. Kubernetes propose des étiquettes pour identifier
les métadonnées des objets, ce qui vous permet facilement de regrouper et d’exploiter les
ressources. Les étiquettes sont des paires clé-valeur qui peuvent être associées directement à
des objets tels que les pods, les espaces de noms, les DaemonSets, etc. Vous pouvez ajouter
des étiquettes à tout moment et les modifier à votre guise. Pour rechercher ou identifier vos
ressources Kubernetes, vous pouvez interroger les étiquettes à l’aide de sélecteurs d’étiquettes.
Voici l'exemple d'une étiquette pour un type de niveau d’application :
"tier" : "frontend", "tier" : "backend", "tier" : "midtier"

Annotations
Les annotations sont également des paires clé-valeur, mais contrairement aux étiquettes, qui
sont utilisées pour identifier des objets, les annotations visent à contenir des informations non
identifiantes sur l’objet lui-même. Par exemple, les informations de création, de publication
ou d’image, telles que les horodatages, les ID de publication, la branche Git, les numéros PR,
les hachages d’image et l’adresse des registres peuvent être enregistrées dans une annotation.

Contrôleur d'entrées
Pour que vos services reçoivent du trafic depuis Internet, vous devez exposer les points
de terminaison HTTP et HTTPS de l’extérieur aux services Kubernetes exécutés sur les
pods. Une entrée vous permet d’exposer vos services s’exécutant au sein du cluster vers
le monde extérieur en proposant un équilibrage de charge avec des terminaisons Secure
Sockets Layer/Transport Layer Security (SSL/TLS) à l’aide d’un hébergement virtuel basé
sur le nom. Pour prendre en charge une entrée, vous devez d’abord choisir un contrôleur

74 Chapitre 4 : Kubernetes : le grand Orchestrator


d'entrées, qui est similaire à un proxy inversé, pour accepter les connexions entrantes pour
HTTP et HTTPS.

StatefulSets
Pour gérer et mettre à l’échelle vos charges de travail avec état sur Kubernetes, vous devez vous
assurer que les pods sont stables (par exemple, un réseau et un stockage stables). StatefulSets
assure la commande des pods et maintient leur unicité (contrairement à ReplicaSets). Un
StatefulSet est un contrôleur qui vous aide à déployer des groupes de pods qui demeurent
résilients pour les redémarrages et les replanifications. Chaque pod d’un StatefulSet dispose
de conventions de nommage uniques dont la valeur ordinale commence à 0. Celles-ci sont
associées à un ID de réseau stable (contrairement à un ReplicaSet, dans lequel la convention
de nommage est aléatoire).

DaemonSets
Dans les environnements ordinaires, nous exécutons un certain nombre de services et d’agents de
démons sur l’hôte, y compris les agents de journalisation et les agents de surveillance. Kubernetes
vous permet d’installer ces agents en exécutant une copie d’un pod sur un ensemble de nœuds
au sein d’un cluster à l’aide de DaemonSets. Tout comme ReplicaSets, les DaemonSets sont de
longs processus qui garantissent que l’état souhaité et l’état observé demeurent identiques. La
suppression d’un DaemonSet supprime également les pods qu’il a précédemment créés.

Tâches
Les tâches sont des entités éphémères dans le monde Kubernetes. Il peut par exemple
s'agir de petites tâches telles que l’exécution d’un script autonome. Les tâches créent des
pods. Les tâches sont exécutées jusqu’à ce qu’elles soient correctement terminées. Il s'agit
de la principale différence entre un pod qui contrôle une tâche et un pod ordinaire qui
continuera à être redémarré et replanifié s’il est terminé. Si un pod de tâche échoue avant
son achèvement, le contrôleur créera un nouveau pod en fonction du modèle.

Ce chapitre est un cours accéléré sur Kubernetes et il n'offre qu’un


aperçu de l’orchestration de conteneurs. Vous pouvez accéder
à d’autres ressources pour en savoir plus sur Kubernetes. Voici
certaines ressources que nous vous recommandons :

• Managing Kubernetes de Brendan Burns et Craig Tracey


(O’Reilly, 2018)
• Kubernetes: Up and Running, 2nd Edition de Brendan Burns,
Joe Beda, et Kelsey Hightower (O’Reilly 2019)
• «  Introduction to Kubernetes  », un cours gratuit de
LinuxFoundationX
• Cloud Native DevOps with Kubernetes, 2nd Edition de John
Arundel et Justin Domingus (O’Reilly, 2022)

Objets de serveur d'API Kubernetes 75


Maintenant que nous avons abordé la terminologie de base du monde Kubernetes, jetons
un coup d’œil aux détails opérationnels relatifs à la gestion du cluster.

Observer, exploiter et gérer des clusters Kubernetes avec


kubectl
L’une des façons les plus courantes d’interagir avec un orchestrateur de conteneurs consiste
à utiliser un outil de ligne de commande ou un outil graphique. Vous pouvez interagir
avec le cluster dans les deux sens dans Kubernetes, mêle s'il vaut mieux utiliser la ligne
de commande. Kubernetes propose la CLI kubectl. kubectl est largement utilisé pour
administrer le cluster. kubectl peut être assimilé au couteau suisse de diverses fonctions
Kubernetes qui vous permettent de déployer et de gérer des applications. Si vous êtes
un administrateur de votre cluster Kubernetes, la commande kubectl vous permettra
principalement de gérer le cluster. kubectl offre une variété de commandes, notamment :

• Commandes de configuration des ressources, qui sont de nature déclaratives


• Commandes de débogage pour obtenir des informations sur les charges de travail
• Commandes de débogage pour manipuler et interagir avec des pods
• Commandes de gestion de cluster générales

Cette section couvre Kubernetes en détail, en se concentrant sur les commandes de cluster
de base pour gérer les clusters Kubernetes, les pods et d’autres objets à l’aide de kubectl.

Informations générales et commandes liées aux clusters


La première étape pour interagir avec le cluster Kubernetes consiste à obtenir des
informations sur le cluster, l’infrastructure et les composants Kubernetes avec lesquels vous
travaillerez. Pour voir les nœuds de travail qui exécutent les charges de travail dans votre
cluster, exécutez la commande kubectl suivante :
$ ~ kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker0 Ready <none> 11d v1.17.3
worker1 Ready <none> 11d v1.17.3
worker2 Ready <none> 11d v1.17.3

Vous obtiendrez ainsi la liste des ressources de votre nœud et leur statut, ainsi que les
informations de version. Vous pouvez obtenir davantage d’informations à l’aide de -o wide
flag dans la commande get nodes comme suit :
$ ~ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE
worker0 Ready <none> 11d v1.17.3 10.240.0.20 <none>
worker1 Ready <none> 11d v1.17.3 10.240.0.21 <none>
worker2 Ready <none> 11d v1.17.3 10.240.0.22 <none>

76 Chapitre 4 : Kubernetes : le grand Orchestrator


KERNEL-VERSION CONTAINER-RUNTIME
Ubuntu 16.04.7 LTS 4.15.0-1092-azure containerd://1.3.2
Ubuntu 16.04.7 LTS 4.15.0-1092-azure containerd://1.3.2
Ubuntu 16.04.7 LTS 4.15.0-1092-azure containerd://1.3.2

Pour obtenir encore plus de détails spécifiques sur une ressource ou un collaborateur, vous
pouvez utiliser la commande describe comme suit :
$ ~ kubectl describe nodes worker0

La commande kubectl describe est une commande de débogage très utile. Vous pouvez
éventuellement l’utiliser pour obtenir des informations détaillées sur les pods et d’autres
ressources.
La commande get peut être utilisée pour obtenir des informations supplémentaires sur
les pods, les services, les contrôleurs de réplication, etc. Par exemple, pour obtenir des
informations sur tous les pods, vous pouvez exécuter :
$ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox-56d8458597-xpjcg 0/1 ContainerCreating 0 1m

Pour obtenir la liste de tous les espaces de noms dans votre cluster, vous pouvez utiliser get
comme suit :
$ ~ kubectl get namespace
NAME STATUS AGE
default Active 12d
kube-node-lease Active 12d
kube-public Active 12d
kube-system Active 12d
kubernetes-dashboard Active 46h

Par défaut, kubectl interagit avec l'espace de nom par défaut. Pour utiliser un espace
de nom différent, vous pouvez passer l'indicateur --namespace pour référencer des objets
dans un espace de noms :
$ ~ kubectl get pods --namespace=default
NAME READY STATUS RESTARTS AGE
busybox-56d8458597-xpjcg 1/1 Running 0 47h

$ ~ kubectl get pods --namespace=kube-public


No resources found in kube-public namespace.

$ ~ kubectl get pods --namespace=kubernetes-dashboard


NAME READY STATUS RESTARTS AGE
dashboard-metrics-scraper-779f5454cb-gq82q 1/1 Running 0 2m
kubernetes-dashboard-857bb4c778-qxsnj 1/1 Running 0 46h

Pour afficher les détails globaux du cluster, vous pouvez utiliser cluster-info comme suit :
$ ~ kubectl cluster-info
Kubernetes master is running at https://40.70.3.6:6443
CoreDNS is running at https://40.70.3.6:6443/api/v1/namespaces/kube-system/services/ \
kube-dns:dns/proxy

La commande cluster-info vous permet d’obtenir les détails de l’équilibreur de charge


API où le plan de contrôle réside, ainsi que d’autres composants.

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 77


Pour maintenir une connexion avec un cluster spécifique, vous devez utiliser un contexte
dans Kubernetes. Un contexte permet au groupe d'accéder aux paramètres sous un seul
nom. Chaque contexte contient un cluster Kubernetes, un utilisateur et un espace de noms.
Le contexte actuel est le cluster qui est actuellement la valeur par défaut de kubectl, et toutes
les commandes émises par kubectl s’exécutent sur ce cluster. Vous pouvez afficher votre
contexte actuel comme suit :
$ ~ kubectl config current-context
cloud-native-azure

Pour modifier l’espace de noms par défaut, vous pouvez utiliser un contexte qui est enregistré
dans le fichier kubectl kubeconfig de votre environnement. Le fichier kubeconfig est le
fichier réel qui indique à kubectl comment trouver votre cluster Kubernetes et s’authentifier
en fonction des secrets qui ont été configurés. Le fichier est généralement stocké dans votre
répertoire personnel sous .kube/. Pour créer et utiliser un nouveau contexte avec un espace
de noms différent comme sa valeur par défaut, procédez comme suit :
$ ~ kubectl config set-context test --namespace=mystuff
Context "test" created.
$ ~ kubectl config use-context test
Switched to context "test"

Assurez-vous que vous disposez réellement du contexte (le cluster Kubernetes test ), sinon
vous ne pourrez pas l’utiliser.
Comme nous l’avons mentionné précédemment, les étiquettes sont utilisées pour organiser
vos objets. Par exemple, si vous souhaitez étiqueter le pod busybox avec une valeur appelée
production, procédez comme suit, en attribuant à l'étiquette le nom environment :
$ ~ kubectl label pods busybox-56d8458597-xpjcg environment=production
pod/busybox-56d8458597-xpjcg labeled

Il peut arriver que vous souhaitiez identifier les problèmes relatifs à vos pods, en essayant
de les déboguer. Pour afficher le journal d'un pod, vous pouvez exécuter la commande logs
sur un nom de pod :
$ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox-56d8458597-xpjcg 0/1 ContainerCreating 0 2d
$ ~ kubectl logs busybox-56d8458597-xpjcg
Error from server (BadRequest): container "busybox" in pod "busybox-56d8458597-xpjcg" is
waiting to start: ContainerCreating

Il peut arrive que plusieurs conteneurs s'exécutent simultanément au sein d'un pod. Pour
choisir les conteneurs à l’intérieur de celui-ci, vous pouvez utiliser l'indicateur -c.

Gestion des pods


Comme nous l’avons mentionné plus tôt, les pods sont les plus petits artefacts déployables
dans Kubernetes. En tant qu’utilisateur ou administrateur, vous traitez directement avec des
pods et non avec des conteneurs. Les conteneurs sont gérés par Kubernetes en interne, et
cette logique est abstraite. Il est également important de se rappeler que tous les conteneurs

78 Chapitre 4 : Kubernetes : le grand Orchestrator


au sein d’un pod sont placés sur le même nœud. Les pods présentent également un cycle de
vie défini, présentant les états suivants : Pending, Running, Succeeded ou Failed.
L’une des façons dont vous pouvez créer un pod consiste à utiliser la commande kubectl
run comme suit :
$ kubectl run <name of pod> --image=<name of the image from registry>

La commande kubectl run extrait une image publique d’un référentiel de conteneurs et
crée un pod. Vous pouvez par exemple exécuter une image hazelcast comme suit et exposer
également le port du conteneur :
$ ~ kubectl run hazelcast --image=hazelcast/hazelcast --port=5701
deployment.apps/hazelcast created

Dans les environnements de production, vous ne devez jamais


exécuter ni créer de pods, car ces derniers ne sont pas gérés
directement par Kubernetes et ne seront pas redémarrés ou
replanifiés en cas de défaillance. Les déploiements constituent la
méthode recommandée pour utiliser les pods.

La commande kubectl run contient de nombreux jeux de fonctionnalités et vous pouvez


contrôler de nombreux comportements de pod. Par exemple, si vous souhaitez exécuter un pod
au premier plan (c.-à-d., avec un terminal interactif à l’intérieur du pod) et si vous ne souhaitez
pas le redémarrer en cas de défaillance, vous pouvez effectuer les opérations suivantes :
$ ~ kubectl run -i -t busybox --image=busybox --restart=Never
If you don't see a command prompt, try pressing enter.
/ #

Cette commande vous permet de vous connecter directement à l’intérieur du conteneur.


Etpuisque vous exécutez le pod en mode interactif, vous pouvez vérifier l’état de busybox,
qui passe de Running à Completed, comme ci-dessous :
$ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 52s

$ ~ kubectl get pods


NAME READY STATUS RESTARTS AGE
busybox 0/1 Completed 0 61s

Vous pouvez également utiliser la syntaxe déclarative dans des manifestes de pod pour
créer des pods. Les manifestes de pod, qui relèvent de la même importance que votre code
d’application, peuvent être écrits à l’aide de YAML ou de JSON. Vous pouvez créer un
manifeste de pod pour exécuter un pod Nginx comme suit :
apiVersion: v1
kind: pod
metadata:
name: nginx
spec:
containers:

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 79


- image: nginx
name: nginx
ports:
- containerPort: 80
name: http

Le pod manifeste des informations telles que kind, spec, et d’autres informations en-
voyées au serveur API Kubernetes concerné. Vous pouvez enregistrer le manifeste de pod
avec une extension YAML et utiliser apply comme suit :
$ kubectl apply -f nginx_pod.yaml
pod/nginx created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 7s

Lorsque vous exécutez kubectl apply, le manifeste de pod est envoyé au serveur API
Kubernetes, qui planifie instantanément le pod pour qu’il s’exécute sur un nœud sain dans
le cluster. Le pod est surveillé par le processus de démon kubelet, et si le pod plante, il est
replanifié de façon à s'exécuter sur un autre nœud sain.
Continuons à présent et étudions comment mettre en œuvre des contrôles d’intégrité sur
vos services dans Kubernetes.

Contrôles d'intégrité
Kubernetes propose trois types de contrôles d’intégrité HTTP, appelés sondes, pour garantir
le bon fonctionnement de l’application : sondes de disponibilité, sondes de préparation et
sondes de démarrage.

Sonde de disponibilité. Cette sonde est chargée de veiller au bon fonctionnement de


l’application. Après le déploiement, un délai de quelques secondes peut être nécessaire pour
que votre application soit prête. Vous pouvez donc configurer cette sonde pour vérifier un
certain point de terminaison dans votre application. Par exemple, dans le manifeste de pod
suivant, une sonde de disponibilité a été utilisée pour effectuer une opération httpGet sur
le chemin / sur le port 80. initialDelaySeconds est défini sur 2, ce qui signifie que le
point de terminaison / ne sera pas atteint tant que cette période n'aura pas été écoulée. En
outre, nous avons défini le délai d’attente à 1 seconde et le seuil d’échec à 3 échecs de sonde
consécutifs. periodSeconds est défini comme la fréquence à laquelle Kubernetes appellera
les pods. Dans le cas présent, cette période correspond à 15 secondes.
apiVersion: v1
kind: Pod
metadata:
name: mytest-pod
spec:
containers:
- image: test_image
imagePullPolicy: IfNotPresent
name: mytest-container
command: ['sh', '-c', 'echo Container 1 is Running ; sleep 3600']
ports:
- name: liveness-port

80 Chapitre 4 : Kubernetes : le grand Orchestrator


containerPort: 80
hostPort: 8080
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
timeoutSeconds: 1
periodSeconds: 15
failureThreshold: 3

Sonde de préparation. La responsabilité de la sonde de préparation consiste à identifier quand


un conteneur est prêt à répondre à la demande de l’utilisateur. Une sonde de préparation aide
Kubernetes en évitant d'ajouter les points de terminaison de pod qui ne sont pas prêts à un
équilibreur de charge. Une sonde de préparation peut être configurée simultanément avec un
bloc de sonde de disponibilité dans le manifeste de pod, comme suit :
containers:
- name: test_image
image: test_image
command: ["/bin/sh"]
args: ['sh', '-c', 'echo Container 1 is Running ; sleep 3600']
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5

Sonde de démarrage. Parfois, un certain temps de démarrage supplémentaire est


nécessaire lors de la première initialisation d'une application. Dans ce cas, vous pouvez
configurer une sonde de démarrage à l’aide d’une vérification HTTP ou TCP associée à un
failureThreshold * periodSeconds, qui est suffisamment long pour couvrir le temps
de démarrage le plus pessimiste :
startupProbe:
httpGet:
path: /healthapi
port: liveness-port
failureThreshold: 30
periodSeconds: 10

Limites des ressources


Lorsque vous traitez avec des pods, vous pouvez indiquer les ressources dont votre application
aura besoin. Le CPU et la mémoire font partie des exigences de ressources les plus élémentaires
pour l'exécution d'un pod. Bien que Kubernetes puisse gérer davantage de types de ressources,
nous allons simplifier notre explication en discutant uniquement du CPU et de la mémoire.
Vous pouvez déclarer deux paramètres dans le manifeste de pod : requête et limite. Dans le bloc
de requête, vous indiquez à Kubernetes l’exigence de ressources minimale pour l'exécution de
votre application et, dans le bloc limite, vous informez Kubernetes du seuil maximal. Si votre
application enfreint le seuil dans le bloc limite, elle sera interrompue ou redémarrée et le pod

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 81


sera principalement supprimé. Par exemple, dans le manifeste de pod suivant, nous plaçons
une limite de CPU maximale de 500 m4 et une limite de mémoire maximale de 206 Mi,5. Si ces
valeurs sont atteintes, le pod sera supprimé et replanifié sur un autre nœud :
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
name: http
resources:
requests:
cpu: "100m"
memory: "108Mi"
limits:
cpu: "500m"
memory: "206Mi"

Volumes
Certaines applications nécessitent que les données soient stockées de façon permanente,
mais parce que les pods sont des entités éphémères et sont fréquemment redémarrées
ou supprimées à la volée, toutes les données associées à un pod peuvent également être
supprimées. Les volumes résolvent ce problème avec une couche d’abstraction de disques de
stockage dans Azure. Un volume est un mode de stockage, de récupération et de conservation
des données sur l’ensemble des pods tout au long du cycle de vie de l’application. Si votre
application présente un état, vous devez utiliser un volume pour rendre vos données
persistantes. Azure propose Azure Disk et Azure Files pour créer les volumes de données
qui fournissent cette fonctionnalité.

Requête de volume persistant (PVC). Le PVC sert de couche d’abstraction entre le pod et le
stockage. Dans Kubernetes, le pod monte les volumes à l’aide de PVC et le PVC parle aux
ressources sous-jacentes. Il est important de noter qu’un PVC vous permet de consommer
la ressource de stockage sous-jacente abstraite. Vous pouvez ainsi demander un stockage
préconfiguré. Le PVC définit la taille et le type de disque, puis monte le stockage réel sur
le pod. Ce processus de liaison peut être statique, comme dans un volume persistant (PV)
ou dynamique, dans une classe de stockage par exemple. Par exemple, dans le manifeste
suivant, nous demandons qu'un PersistentVolumeClaim présente un stockage de 1 Gi :

4 Les demandes fractionnaires sont autorisées. Par exemple, un CPU peut être divisé en deux 0,5 s.
L’expression 0,1 équivaut à 100 m.
5 L
 a limite et la requête sont mesurées en octets. La mémoire peut être exprimée sous la forme d’un entier simple ou d’un
nombre à virgule fixe à l’aide des suffixes E, P, T, G, M, K ou de leur puissance de deux équivalents Ei, Pi, Ti, Gi, Mi, Ki.

82 Chapitre 4 : Kubernetes : le grand Orchestrator


apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: persistent-volume-claim-app1
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: azurefilestorage

Volume persistant : statique. Un administrateur de cluster, généralement l’équipe SRE ou


DevOps, peut créer manuellement un nombre prédéfini de volumes persistants, qui peuvent
ensuite être utilisés par les utilisateurs du cluster selon leurs besoins. Le volume persistant
est la méthode de provisionnement statique. Par exemple, dans le manifeste suivant, nous
créons un PersistentVolume avec une capacité de stockage de 2 Gi :
apiVersion: v1
kind: PersistentVolume
metadata:
name: static-persistent-volume-app1
labels:
storage: azurefile
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
storageClassName: azurefilestorage
azureFile:
secretName: static-persistence-secret
shareName: user-app1
readOnly: false

Classe de stockage : dynamique. Les classes de stockage sont des volumes configurés de
manière dynamique pour le PVC (c’est-à-dire qu’elles permettent de créer des volumes
de stockage à la demande). Les classes de stockage fournissent essentiellement aux
administrateurs de cluster un mode de description des classes de stockage proposées.
Chaque classe de stockage dispose d’un provisionneur qui détermine le plug-in de volume
utilisé pour la mise en service des volumes persistants.
Dans Azure, deux types de provisionneurs déterminent le type de stockage qui sera utilisé :
AzureFile et AzureDisk.
AzureFile peut être utilisé avec le mode d’accès ReadWriteMany :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile
provisioner: kubernetes.io/azure-file
parameters:
skuName: Standard_LRS

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 83


location: eastus
storageAccount: azure_storage_account_name

AzureDisk ne peut être utilisé qu’avec le mode d’accès ReadWriteOnce :


apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/azure-disk
parameters:
storageaccounttype: Standard_LRS
kind: Shared

La figure 4-3 présente la relation logique entre les différents objets de stockage offerts par
Kubernetes.

Figure 4-3. Relation entre les pods, le PVC, le PV et la classe de stockage

Enfin, vous pouvez supprimer un pod à l’aide de kubectl delete pod comme suit :
$ ~ kubectl delete pod nginx
pod "nginx" deleted

Lorsque vous supprimez un pod, vous devez vérifier qu'il n’est pas contrôlé par un
déploiement, car si c'est le cas, il réapparaîtra. Par exemple :
$ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
hazelcast-84bb5bb976-vrk97 1/1 Running 2 2d7h
nginx-76df748b9-rdbqq 1/1 Running 2 2d7h
$ ~ kubectl delete pod hazelcast-84bb5bb976-vrk97
pod "hazelcast-84bb5bb976-vrk97" deleted
$ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
hazelcast-84bb5bb976-rpcfh 1/1 Running 0 13s
nginx-76df748b9-rdbqq 1/1 Running 2 2d7h

Un nouveau pod a été dérivé car Kubernetes a observé qu’un état du cluster était
perturbé là où les états souhaités et observés ne correspondaient pas. La boucle de
rapprochement est donc intervenue pour équilibrer les pods. Cet événement peut se
produire car le pod a été déployé via un déploiement, en étant associé à ReplicaSets.

84 Chapitre 4 : Kubernetes : le grand Orchestrator


Donc, pour supprimer un pod, vous devez supprimer le déploiement, qui à son retour
supprime automatiquement le pod. Ceci est illustré ci-dessous :
$ ~ kubectl get deployments --all-namespaces
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
default hazelcast 1/1 1 1 2d21h
default nginx 1/1 1 1 2d21h
kube-system coredns 2/2 2 2 3d7h
$ ~ kubectl delete -n default deployment hazelcast
deployment.apps "hazelcast" deleted
$ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
hazelcast-84bb5bb976-rpcfh 0/1 Terminating 0 21m
nginx-76df748b9-rdbqq 1/1 Running 2 2d7h

Kubernetes en production
Maintenant que nous avons abordé les notions de base des pods, des concepts Kubernetes et
du fonctionnement du cluster Kubernetes, découvrons comment relier les pods et préparer
un cluster Kubernetes à la production. Cette section explique comment les concepts
abordés dans la section précédente prennent en charge les charges de travail de production
et le déploiement des applications sur Kubernetes.

ReplicaSets
Comme nous l’avons vu, ReplicaSets confère à Kubernetes une capacité de réparation
spontanée au niveau de l’infrastructure en maintenant un nombre stable de pods. En cas de
défaillance au niveau de l’infrastructure (c’est-à-dire les nœuds qui détiennent les pods), le
ReplicaSet replanifiera les pods sur un autre nœud sain. Un ReplicaSet inclut les éléments
suivants :
Sélecteur
ReplicaSets utilise des étiquettes de pod pour rechercher et répertorier les pods
exécutés dans le cluster pour créer des réplicas en cas de défaillance.
Nombre de réplicas à créer
Ce chiffre correspond au nombre de pods à créer.
Modèle
Il spécifie les données associées des nouveaux pods qu’un ReplicaSet doit créer pour
répondre au nombre de pods souhaité.
Dans les cas d’utilisation de production réels, vous aurez besoin d’un ReplicaSet pour
maintenir un ensemble stable de pods exécutés en introduisant une redondance. Vous
ne devez toutefois pas interagir directement avec le ReplicaSet, car il s’agit d’une couche
d’abstraction. Vous devez à la place utiliser des déploiements, qui constituent une option
plus judicieuse pour déployer et gérer les pods.

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 85


Un ReplicaSet ressemble à ceci :
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-cnf-replicaset
labels:
app: cnfbook
spec:
replicas: 3
selector:
matchLabels:
app: cnfbook
template:
metadata:
labels:
app: cnfbook
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

Pour créer le ReplicaSet à partir de la configuration précédente, enregistrez le manifeste


sous nginx_Replicaset.yaml et appliquez-le. Vous créerez ainsi un ReplicaSet et trois pods,
illustrés comme suit :
$ kubectl apply -f nginx_Replicaset.yaml
replicaset.apps/myapp-cnf-replicaset created

$ kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-cnf-replicaset 3 3 3 6s

$ kubectl get pods


NAME READY STATUS RESTARTS AGE
myapp-cnf-replicaset-drwp9 1/1 Running 0 11s
myapp-cnf-replicaset-pgwj8 1/1 Running 0 11s
myapp-cnf-replicaset-wqkll 1/1 Running 0 11s

Déploiements
Dans les environnements de production, le changement est un élément essentiel en
permanence. Vous continuerez à mettre à jour/modifier les applications dans votre
environnement de production à un rythme très rapide, car la tâche la plus courante que
vous effectuerez en production consistera à déployer de nouvelles fonctions de votre
application. Kubernetes propose par défaut l'objet Deployment pour effectuer les mises
à jour propagées, en offrant une expérience transparente à l’administrateur du cluster
et aux utilisateurs. Vous pouvez donc envoyer des mises à jour à vos applications sans
interruption, car Deployment garantit que seul un certain nombre de pods sont arrêtés
pendant la mise à jour. Cette approche est appelée déploiements sans interruption. Par
défaut, les déploiements garantissent qu’au moins 75 % du nombre de pods souhaité sont
fonctionnels. Les déploiements sont la manière fiable, sûre et appropriée de déployer de
nouvelles versions d’applications sans interruption dans Kubernetes.

86 Chapitre 4 : Kubernetes : le grand Orchestrator


Vous pouvez créer un objet Deployment comme suit :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

Pour appliquer la configuration précédente, enregistrez-la dans un fichier nommé nginx_


Deployment.yaml, puis utilisez kubectl apply comme suit :
$ kubectl apply -f nginx_Deployment.yaml
deployment.apps/nginx-deployment created

Fait intéressant, le déploiement gère également les ReplicaSets, car nous avons déclaré avoir
besoin de trois réplicas pour le pod de déploiement. Nous pouvons vérifier ceci comme suit :
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 18s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-d46f5678b 3 3 3 29s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-d46f5678b-dpc7p 1/1 Running 0 36s
nginx-deployment-d46f5678b-kdjxv 1/1 Running 0 36s
nginx-deployment-d46f5678b-kj8zz 1/1 Running 0 36s

Ainsi, à partir du fichier manifeste, nous avons créé trois réplicas dans le fichier .spec.rep-
licas. Pour que l'objet Deployment puisse trouver les pods gérés par nginx-deployment,
nous utilisons le champ spec.selector. Les pods sont étiquetés à l’aide du champ de
modèle via metadata.labels.
Déployons maintenant une nouvelle version de Nginx. Supposons que nous souhaitions
épingler la version de Nginx sur 1.14.2. Il nous suffit de changer le manifeste de déploiement
en modifiant le fichier. Nous devons donc modifier la version, puis enregistrer le fichier
manifeste, comme suit :
$ kubectl edit deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment edited

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 87


L'objet Deployment sera ainsi mis à jour, et vous pouvez le vérifier comme suit :
$ kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have
been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have
been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have
been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have
been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have
been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 old replicas are pending
termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending
termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending
termination...
deployment "nginx-deployment" successfully rolled out

L'objet Deployment garantit qu’un certain nombre de pods sont toujours disponibles et en
service, pendant que les anciens pods sont mis à jour. Comme nous l’avons déjà mentionné,
par défaut, un maximum de 25 % des pods sont indisponibles pendant l’exécution d’une
mise à jour. Le déploiement garantit un pourcentage de pic maximal de 25 %, de manière
à ce que seul un certain nombre de pods est créé par rapport au nombre de pods souhaité.
Ainsi, grâce au rollout status, vous pouvez clairement voir qu’au moins deux pods sont
disponibles en permanence, lors du déploiement d'une modification. Vous pouvez à nouveau
obtenir les détails de votre déploiement à l’aide de kubectl describe deployment.

Nous avons directement publié une commande de déploiement


à l’aide de kubectl edit, mais l’approche privilégiée consiste
toujours à mettre à jour le fichier manifeste réel, avant d’appliquer
kubectl. Cela vous permet également de conserver vos manifestes
de déploiement dans le contrôle de version. Vous pouvez également
utiliser la commande --record ou set pour la mise à jour.

Échelle horizontale de Pod


Kubernetes prend en charge la mise à l’échelle dynamique grâce à l’utilisation de l’échelle
horizontale de Pod (HPA), où les pods sont mis à l’échelle horizontalement. Autrement dit,
vous pouvez créer n nombre de pods en fonction des métriques observées pour votre pod si,
par exemple, vous souhaitez augmenter ou diminuer le nombre de pods de manière dynamique
en fonction de la mesure de CPU observée. La HPA fonctionne via une boucle de contrôle
qui vérifie l’utilisation des ressources spécifiée par rapport aux métriques à un intervalle de
15 secondes par défaut (voir la figure 4-4).

88 Chapitre 4 : Kubernetes : le grand Orchestrator


Figure 4-4. Échelle horizontale de Pod à l’œuvre

Il est important de noter que pour utiliser la HPA, metrics-server est nécessaire pour collecter
les mesures de kubelets et les exposer dans le serveur API Kubernetes via l’API de métriques,
afin qu'elles soient utilisées par la HPA. Nous commençons par créer une mise à  l’échelle
automatique à l’aide de kubectl autoscale pour notre déploiement, comme suit :
$ ~ kubectl autoscale deployment nginx-deployment --cpu-percent=50 --min=3 --max=10
horizontalpodautoscaler.autoscaling/nginx-deployment autoscaled

La commande kubectl précédente créera une HPA qui veillera à ce qu'un nombre minimal
de trois et un nombre maximal de 10 pods soient utilisés dans notre nginx-deployment.
La HPA augmentera ou diminuera le nombre de réplicas pour maintenir une utilisation
moyenne du CPU dans tous les pods à 50 % maximum.
Vous pouvez afficher votre HPA à l’aide des éléments suivants :
$ ~ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-deployment Deployment/nginx-deployment 0%/50% 3 10 3 6m59s

Nous pouvons ajouter le facteur d’échelle sur lequel nous pouvons créer la mise à l'échelle
automatique à l’aide d'un fichier YAML manifeste HPA qui est lié au déploiement.

Service
Comme nous l’avons mentionné précédemment, l’environnement Kubernetes est un
système très dynamique où les pods sont créés, supprimés et déplacés à un rythme
variable. Cet environnement dynamique ouvre également la voie à un problème
bien connu  : trouver les pods de réplicas sur lesquels réside une application, car
plusieurs pods s’exécutent pour un déploiement. Les pods doivent également pouvoir
rechercher les autres pods afin de communiquer avec eux. Kubernetes propose
l'objet Service en tant qu’abstraction pour un point d’entrée unique dans le groupe
de pods. L'objet Service présente une adresse IP, un nom DNS et un port qui ne

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 89


changent jamais tant que l’objet existe. Officiellement connu sous le nom de de service,cette
fonction nom de découverte de service, cette fonction aide essentiellement d’autres pods/
services à accéder à d’autres services dans Kubernetes sans induire de complexité sous-jacente.
Nous étudierons de plus près l’approche de découverte de service Cloud native au chapitre 6.
Pour utiliser un service, vous pouvez utiliser le manifeste de service YAML. Imaginons
qu'une simple application «  hello  world  » s’exécute déjà en tant que déploiement. Une
technique par défaut pour exposer ce service consiste à indiquer ClusterIP comme type
de service. Ce service sera exposé sur l’IP interne du cluster et ne sera accessible qu’à partir
du cluster, comme suit :
---
apiVersion: v1
kind: Service
metadata:
name: hello-world-service
spec:
type: ClusterIP
selector:
app: hello-world
ports:
- port: 8080
targetPort: 8080

Le port représente le port où le service sera disponible et le targetPort est le port de
conteneur réel où le service sera transféré. Dans ce cas, nous avons exposé le port 8080 de
l'application hello-world sur le port cible 8080 sur l’IP de pod :
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-world-service ClusterIP 10.32.0.40 <none> 8080/TCP 6s

L'adresse IP indiquée est l’IP du cluster et non l’adresse IP réelle de la machine. Si vous
appliquez le SSH sur un nœud de travail, vous pouvez vérifier si le service a été exposé sur le
port 8080 en effectuant simplement une boucle comme suit :
ubuntu@worker0:~$ curl http://10.32.0.40:8080
Hello World

Toute tentative d'accès à cette adresse IP à partir de l’extérieur du cluster (c.-à-d., tout autre nœud
en dehors des nœuds de travail), se soldera par un échec. Vous pouvez donc utiliser NodePort,
qui expose un service sur l'IP de chaque nœud au niveau d'un port défini comme suit :
---
apiVersion: v1
kind: Service
metadata:
name: hello-world-node-service
spec:
type: NodePort
selector:
app: hello-world
ports:
- port: 8080
targetPort: 8080
nodePort: 30767

90 Chapitre 4 : Kubernetes : le grand Orchestrator


Dans le manifeste de service, nous avons mappé le port de pod 8080 à NodePort (c’est-à-
dire le port d'instance physique) 30767. De cette manière, vous pouvez exposer l’adresse IP
directement ou placer l'équilibreur de charge de votre choix. Si vous exécutez désormais get
svc, vous pouvez voir le mappage des ports comme suit :
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-world-node-service NodePort 10.32.0.208 <none> 8080:30767/TCP 8s
hello-world-service ClusterIP 10.32.0.40 <none> 8080/TCP 41m

Nous pouvons à présent accéder au service sur le nœud au port 30767 :

ubuntu@controller0:~$ curl http://10.240.0.21:30767


Hello World

L’IP dans la commande curl est l’adresse IP physique du nœud de travail (pas l’IP du cluster),
et le port qui est exposé est 30767. Vous pouvez même accéder directement à l’adresse IP
publique du nœud pour le port 30767. La figure 4-5 montre comment l’IP du cluster, le port
de nœud et l’équilibreur de charge interagissent les uns avec les autres.

Figure 4-5. IP de cluster, port de nœud et équilibreur de charge dans un nœud Kubernetes

Parmi les autres types de services, citons LoadBalancer et ExternalName. LoadBalancer


expose le service en externe à l’aide de l’équilibreur de charge d’un fournisseur Cloud. Les
services NodePort et ClusterIP, qui sont les destinations de l’équilibreur de charge
externe, sont automatiquement créés, tandis que ExternalName mappe un service à un
nom DNS comme my.redisdb.inter nal.com. La figure 4-6 illustre les interactions entre
les différents types de services.

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 91


Figure 4-6. Service externe et LoadBalancer dans un environnement Cloud

Entrée
L'objet Service permet d’exposer l’application à l’intérieur et à l’extérieur du cluster.
Toutefois, dans les systèmes de production, nous ne pouvons pas nous permettre d’ouvrir des
ports nouveaux et uniques pour tous les services que nous déployons à l’aide de NodePort, ni
de créer un nouvel équilibreur de charge chaque fois que nous sélectionnons LoadBalancer
comme type de service. Parfois, nous devons déployer un service basé sur HTTP et effectuer
également un déchargement SSL. L' objet Service ne s'avère donc pas utile dans ce cas.
Dans Kubernetes, l’équilibrage de charge HTTP (ou, officiellement l’équilibrage de charge de
couche 7) est effectué par l'objet Ingress.
Pour travailler avec Ingress, nous devons d’abord configurer un contrôleur d’entrées.6
Nous allons configurer Application Gateway Ingress Controller pour Azure Kubernetes
Service (AKS) afin de mieux appréhender ce sujet et découvrir son comportement, et le plus
simple pour y parvenir consiste à étudier ce qui suit :

6 Pour que la ressource Ingress fonctionne, un contrôleur d’entrée doit s'exécuter dans le cluster.
Contrairement aux autres types de contrôleur qui s'exécutent dans le cadre du binaire kube-controller-
manager, les contrôleurs d’entrée ne sont pas démarrés automatiquement avec un cluster. Il existe différents
types de contrôleurs d’entrées. Azure propose notamment AKS Application Gateway Ingress Controller pour
configurer Azure Application Gateway.

92 Chapitre 4 : Kubernetes : le grand Orchestrator


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wildcard-host
spec:
rules:
- host: "foo.bar.com"
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: hello-world-node-service
port:
number: 8080
- host: "*.foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: service2
port:
number: 80

Dans le manifeste d’entrées, nous avons créé deux règles et mappé foo.bar.com en tant
qu’hôte à un sous-domaine /bar, dont la destination est notre service précédent hello-
world-node-service.
De même, plusieurs chemins d’accès peuvent être définis pour un domaine et acheminés
vers d’autres services. Les entrées peuvent être configurées de plusieurs façons, en fonction
de vos besoins. Il peut s'agir d’un routage de domaine unique, de plusieurs services ou de
plusieurs domaines acheminés vers plusieurs domaines (voir la figure 4-7).

Figure 4-7. Entrée avec plusieurs chemins pour un domaine

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 93


Enfin, vous pouvez spécifier la prise en charge TLS en créant un objet Secret, puis en
utilisant ce secret dans votre spécification d’entrée, comme suit :
apiVersion: v1
kind: Secret
metadata:
name: my_tls_secret
namespace: default
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
type: kubernetes.io/tls

Vous pouvez sécuriser l'entrée en spécifiant uniquement le certificat TLS encodé en base64 et
la clé. Lorsque vous référencez ce secret dans votre manifeste d’entrée, il s’affiche comme suit :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- https.mywebsite.example.come
secretName: my_tls_secret
rules:
- host: https.mywebsite.example.come
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 80

Les contrôleurs d’entrées traitent de nombreuses fonctions et complexités, et Azure Gateway


Ingress Controller gère un grand nombre d’entre elles. Il peut s'agit de tirer parti de l’équilibreur
de charge de la passerelle d’application native de couche 7 d’Azure pour exposer vos services
à Internet. Nous en reparlerons plus en détail lorsque nous présenterons AKS au chapitre 5.

DaemonSet
Comme nous l’avons mentionné plus tôt, DaemonSets est généralement utilisé pour exécuter
un agent sur un nombre de nœuds dans le cluster Kubernetes. L’agent est exécuté à l’intérieur
d’un conteneur extrait par des pods. La plupart du temps, les ingénieurs SRE et DevOps
préfèrent exécuter un agent de journal ou un agent de surveillance sur chaque nœud pour
obtenir la télémétrie et les événements d’application. Par défaut, un DaemonSet crée une
copie d’un pod sur chaque nœud, bien que cela puisse être limité également à l’aide d’un
sélecteur de nœuds. ReplicaSets et DaemonSets présentent de nombreuses similitudes. La
différence principale entre ces deux éléments est l’exigence (avec DaemonSets) de l’exécution
d’une application d’agent unique (par exemple un pod) sur l’ensemble de vos nœuds.

94 Chapitre 4 : Kubernetes : le grand Orchestrator


Pour exécuter un conteneur de journalisation, vous pouvez notamment déployer un pod
dans chaque nœud à l’aide d’un DaemonSet. Fluentd est une solution de journalisation open
source largement utilisée pour collecter des journaux à partir de systèmes. Cet exemple
illustre l’une des façons de déployer Fluentd à l'aide d’un DaemonSet :
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
central-log-k8s: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers

Dans la configuration DaemonSet précédente, nous avons créé un DaemonSet qui déploie
le conteneur Fluentd sur chaque nœud. Nous avons également créé une tolérance, qui ne
planifie pas le nœud Fluentd sur les nœuds maîtres (plan de contrôle).

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 95


 ubernetes propose des fonctions de planification appelées altérations
K
et tolérances :
• Les altérations dans Kubernetes permettent à un nœud de re-
pousser un ensemble de pods (c.-à-d., si vous souhaitez que cer-
tains nœuds ne planifient pas un type de pod).
• Les tolérances sont appliquées aux pods et permettent (sans ob-
ligation) de planifier des pods sur des nœuds avec les altérations
correspondantes.
 es altérations et les tolérances fonctionnent ensemble pour garantir
L
que les pods ne sont pas planifiés sur des nœuds inappropriés. Une ou
plusieurs altérations sont appliquées à un nœud ; cela indique que le
nœud ne doit pas accepter les pods qui ne tolèrent pas les altérations.
 ous pouvez vérifier les pods qui ont été créés automatiquement pour chaque nœud de
V
travail, comme suit :
$ kubectl apply -f fluentd.yaml
daemonset.apps/fluentd-elasticsearch created
$ kubectl get ds --all-namespaces
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE
kube-system fluentd-elasticsearch 3 3 3 3 3
NODE SELECTOR AGE
<none> 5m12s

$ kubectl get pods --namespace=kube-system -o wide


NAME READY STATUS RESTARTS AGE IP NODE
fluentd-elasticsearch-5jxg7 1/1 Running 1 45m 10.200.1.53 worker1
fluentd-elasticsearch-c5s4c 1/1 Running 1 45m 10.200.2.32 worker2
fluentd-elasticsearch-r4pqz 1/1 Running 1 45m 10.200.0.43 worker0
NOMINATED NODE READINESS GATES
<none> <none>
<none> <none>
<none> <none>

Tâches
Il se peut que nous devions exécuter un petit script jusqu’à son achèvement. Kubernetes
vous permet de le faire avec l'objet Job. L’objet Job crée et gère les pods qui s’exécutent
jusqu’à leur achèvement, et contrairement aux pods ordinaires, une fois la tâche donnée
terminée, ces pods créés par l'objet Job ne sont pas redémarrés. Vous pouvez utiliser un
YAML Job pour décrire un Jobsimple , comme suit :
apiVersion: batch/v1
kind: Job
metadata:
name: examplejob
spec:
template:
metadata:
name: examplejob
spec:
containers:
- name: examplejob

96 Chapitre 4 : Kubernetes : le grand Orchestrator


image: busybox
command: ["echo", "Cloud Native with Azure"]
restartPolicy: Never

Ici, nous avons créé un Job pour imprimer une commande shell. Pour créer un Job, vous
pouvez enregistrer le YAML précédent et l’appliquer à l’aide de kubectl apply. Une fois
que vous avez appliqué le manifeste de tâche, Kubernetes créera la tâche et l’exécutera
immédiatement. Vous pouvez vérifier l’état du Job à l’aide de kubectl describe
comme suit :
$ kubectl apply -f job.yaml
job.batch/examplejob created
$ kubectl get jobs
NAME COMPLETIONS DURATION AGE
examplejob 1/1 3s 9s
$ kubectl describe job examplejob
Name: examplejob
Namespace: default
Selector: controller-uid=f6887706-85ef-4752-8911-79cc7ab33886
Labels: controller-uid=f6887706-85ef-4752-8911-79cc7ab33886
job-name=examplejob
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"batch/v1","kind":"Job","metadata":
{"annotations":{},"name":"examplejob","namespace":"default"},
"spec":{"template":{"metadat...
Parallelism: 1
Completions: 1
Start Time: Mon, 07 Sep 2020 01:08:53 +0530
Completed At: Mon, 07 Sep 2020 01:08:56 +0530
Duration: 3s
Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=f6887706-85ef-4752-8911-79cc7ab33886
job-name=examplejob
Containers:
examplejob:
Image: busybox
Port: <none>
Host Port: <none>
Command:
echo
Cloud Native with Azure
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 61s job-controller Created pod: examplejob-mcqzc
Normal Completed 58s job-controller Job completed

Observer, exploiter et gérer des clusters Kubernetes avec kubectl 97


Résumé
Kubernetes est une plateforme puissante qui a été créée après une décennie d’expérience
acquise grâce à la conteneurisation des applications à grande échelle chez Google.
Kubernetes a essentiellement conduit à la création de la Cloud Native Computing
Foundation, en devenant le premier projet diplômé. Par la suite, une rationalisation
importante de l’écosystème des microservices a été réalisée concernant la prise en charge
et l’adoption d’un environnement Cloud natif. Dans ce chapitre, nous avons examiné les
différents composants et concepts qui permettent à Kubernetes de fonctionner à grande
échelle. Ce chapitre établit les bases des prochains chapitres dans lesquels nous utiliserons
la plateforme Kubernetes pour servir des applications Cloud native de production.

Compte tenu de la complexité sous-jacente liée à la gestion d’un cluster Kubernetes, nous
étudierons comment créer et utiliser ce type de cluster au chapitre 5. Nous découvrirons
également Azure Kubernetes Service, entre autre.

98 Chapitre 4 : Kubernetes : le grand Orchestrator


CHAPITRE 5
Création d'un cluster Kubernetes dans Azure

Maintenant que vous avez acquis les notions de base du fonctionnement d'un cluster
Kubernetes, il est temps de mettre ces connaissances en pratique, afin de créer et d’utiliser
un cluster Kubernetes. Plusieurs outils vous permettent de créer et d'exécuter un cluster
Kubernetes dans un environnement de production à haute disponibilité. Certains des outils
les plus courants sont kops, kubeadm, Kubespray et Rancher. Ces outils sont associés à des
playbooks préécrits dédiés à la création d’un cluster, afin qu'il puisse être exécuté dans un
environnement de production de manière fluide grâce à une combinaison de technologies.
Nous n’utiliserons pas d’outil prédéfinis dans ce chapitre. Nous adopterons à la place
une approche plus pratique pour créer un cluster que vous pourrez utiliser dans un
environnement de production.

Création d'un cluster Kubernetes à partir de zéro


Dans cette section, nous allons créer un cluster Kubernetes à partir de zéro à l’aide d'Ansible,
de Terraform et de Packer. Nous allons vous montrer comment créer manuellement le
cluster afin que vous puissiez mieux comprendre les différents composants de ce dernier.
Bien que ce ne soit pas la meilleure façon de créer ou d’exécuter un cluster Kubernetes de
production, cette méthode vous procurera des connaissances fondamentales solides.
Pour commencer, vous devrez cloner le GitHub pour ce livre et ouvrir le dossier Chapitre 5.
Ce chapitre suppose que vous avez déjà configuré votre compte Azure en suivant les étapes
du chapitre 2, et que vous avez procédé à la création de l’infrastructure à l’aide des playbooks
Terraform, Packer et Ansible.
Nous avons organisé le code dans le référentiel Git au chapitre 5 comme suit :
$ Chapter5 git:(master)
.
├── AKS - Azure Kubernetes Service Cluster
├── Ansible-Playbooks - Ansible playbooks for creating k8s cluster
├── Deployments

99
├── K8S_Packer_Image - Machine images for worker and controller
├── Kubernetes_Cluster- Terraform initialization directory for k8s
├── README.md
└── terraform_modules - Terraform modules for k8s cluster

Dans cette section, nous utiliserons uniquement K8S_Packer_Image, Ansible-Playbooks,


Kubernetes_cluster, et terraform_modules.

Création d'un groupe de ressources


La première consiste à créer un groupe de ressources pour tous les composants de
l’infrastructure afin de pouvoir les regrouper. Pour ce faire, accédez au répertoire Chapter5/
Kubernetes_Cluster/A-Resource_Group et exécutez la commande suivante :
$ terraform init

Après l’initialisation, exécutez la commande suivante pour créer un groupe de ressources :


$terraform apply

An execution plan has been generated and is shown below.


Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# module.CNA-Terraform-Resource-Grp.azurerm_resource_group.generic-resource-gp
# will be created
+ resource "azurerm_resource_group" "generic-resource-gp" {
+ id = (known after apply)
+ location = "eastus2"
+ name = "K8Scluster"
+ tags = {
+ "cluster" = "k8s-experments"
+ "environment" = "dev"
}
}

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?


Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

Pour continuer, saisissez Oui. Cette étape aboutit à la création d'un groupe de ressources
nommé K8Scluster dans la région eastus2 d’Azure.

Création des images de machine pour les machines de travail


et de contrôleur
La deuxième étape consiste à créer les images Packer du contrôleur et des images des
instances de travail. Dans le répertoire Chapter5/K8S_Packer_Image, mettez à jour controller.
json et worker.json avec les informations d’identification Azure pour client_id, client_
secret, tenant_id, et subscription_id. Exécutez les commandes packer comme suit :

100 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


$ packer build worker.json
==> Builds finished. The artifacts of successful builds are:
--> azure-arm: Azure.ResourceManagement.VMImage:

OSType: Linux
ManagedImageResourceGroupName: K8Scluster
ManagedImageName: k8s_worker
ManagedImageId: /subscriptions/b5624140-9087-4311-a94a-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Compute/images/k8s_worker
ManagedImageLocation: eastus2

$ packer build controller.json


==> Builds finished. The artifacts of successful builds are:
--> azure-arm: Azure.ResourceManagement.VMImage:

OSType: Linux
ManagedImageResourceGroupName: K8Scluster
ManagedImageName: k8s_controller
ManagedImageId: /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Compute/images/k8s_controller
ManagedImageLocation: eastus2

Notez le ManagedImageId, que nous utiliserons plus loin dans le chapitre.

Création d’un Backend de compte de stockage


Nous allons ensuite créer le compte de stockage pour stocker tous les mappages
d’infrastructure qui seront utilisés par Terraform. Encore une fois, nous allons d’abord
initialiser le référentiel avec terraform init dans Kubernetes_Cluster/B-Storage_Account_
backend, puis appliquer la commande comme suit :
$ terraform apply
.
.

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

Blob-ID = https://cnabookprod.blob.core.windows.net/k8s-cluster-dev/test
Blob-URL = https://cnabookprod.blob.core.windows.net/k8s-cluster-dev/test
Primary-Access-Key = ymMDE1pUQgtuxh1AOJyUvlvfXnmjAeJEHl2XvMmQ38AZp1O8Z0Xk4Hrw4N/ \
d8yovb8FQ5VzqtREH94gzPCzAWCA==

Cette étape aboutit à la création d'un compte de stockage nommé cnabookprod. Mettez à
jour votre bash_pro file avec une valeur de clé d’accès principal à l’aide du nom de variable
ARM_ACCESS_KEY :
export ARM_ACCESS_KEY=ymMDE1pUQgtuxh1AOJyUvlvfXnmjAeJEHl2XvMmQ38AZp1O8Z0Xk4Hrw4N/ \
d8yovb8FQ5VzqtREH94gzPCzAWCA==

Création d'un cluster Kubernetes à partir de zéro 101


Création d'un réseau virtuel Azure
L’étape suivante consiste à créer le réseau virtuel dans lequel nous allons héberger notre
cluster Kubernetes. Accédez au répertoire Chapter5/Kubernetes_Cluster/C-Virtual_Network
et initialisez à nouveau Terraform à l’aide de terraform init. Une fois initialisé, appliquez
la configuration suivante pour créer un réseau virtuel Azure :
$ terraform apply
.
.
.
Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
Releasing state lock. This may take a few moments...
Outputs:

subnet_id = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/virtualNetworks/cna-k8s-vnet/subnets/cna-k8s-subnet
vnet_id = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/virtualNetworks/cna-k8s-vnet

Cette étape aboutit à la création d'un réseau virtuel avec un CIDR de 10.240.0.0/24 nommé
cnak-8s-vnet.

Création d’adresses IP publiques pour l’équilibreur de charge


Nous allons maintenant créer des adresses IP publiques pour l’équilibreur de charge, ainsi
que tous les nœuds de travail et de plan de contrôle. Une fois que nous avons créé les
adresses IP publiques pour toutes les ressources, nous allons créer l’équilibreur de charge
pour les nœuds de plan de contrôle en vue d'exposer le serveur API à des clients distants.
Pour créer des adresses IP publiques, accédez au répertoire Chapter5/Kubernetes_Cluster/
D-K8S_PublicIP et initialisez Terraform à l’aide de terraform init. Le code sera organisé
comme suit :
module "K8S-API-Server-Public-IP" {
source = "../../terraform_modules/Azure_PublicIP"
name_of_ip = "k8s_master_lb"
resource-grp-name = "K8Scluster"
azure-dc = "eastus2"
env = "dev"
type-of-cluster = "k8s-experments"
}

module "Worker0" {
source = "../../terraform_modules/Azure_PublicIP"
name_of_ip = "Worker0"
resource-grp-name = "K8Scluster"
azure-dc = "eastus2"
env = "dev"
type-of-cluster = "k8s-experments"
}
module "Worker1" {
source = "../../terraform_modules/Azure_PublicIP"
name_of_ip = "Worker1"

102 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


resource-grp-name = "K8Scluster"
azure-dc = "eastus2"
env = "dev"
type-of-cluster = "k8s-experments"
}
module "Worker2" {
source = "../../terraform_modules/Azure_PublicIP"
name_of_ip = "Worker2"
resource-grp-name = "K8Scluster"
azure-dc = "eastus2"
env = "dev"
type-of-cluster = "k8s-experments"
}

module "Controller0" {
source = "../../terraform_modules/Azure_PublicIP"
name_of_ip = "Controller0"
resource-grp-name = "K8Scluster"
azure-dc = "eastus2"
env = "dev"
type-of-cluster = "k8s-experments"
}
module "Controller1" {
source = "../../terraform_modules/Azure_PublicIP"
name_of_ip = "Controller1"
resource-grp-name = "K8Scluster"
azure-dc = "eastus2"
env = "dev"
type-of-cluster = "k8s-experments"
}
module "Controller2" {
source = "../../terraform_modules/Azure_PublicIP"
name_of_ip = "Controller2"
resource-grp-name = "K8Scluster"
azure-dc = "eastus2"
env = "dev"
type-of-cluster = "k8s-experments"
}

Une fois Terraform initialisé, vous pouvez appliquer la configuration à l’aide de terraform
apply comme suit, afin de créer les adresses IP publiques :
$ terraform apply
.
.
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
Releasing state lock. This may take a few moments...
Outputs:
Controller0_IP = 52.252.6.89
Controller0_IP_ID = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/publicIPAddresses/Controller0
Controller1_IP = 52.254.50.7
Controller1_IP_ID = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/publicIPAddresses/Controller1
Controller2_IP = 52.251.58.212
Controller2_IP_ID = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/publicIPAddresses/Controller2
PublicIP_ID = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \

Création d'un cluster Kubernetes à partir de zéro 103


K8Scluster/providers/Microsoft.Network/publicIPAddresses/k8s_master_lb
Public_IP = 52.254.73.23
Worker0_IP = 52.251.59.169
Worker0_IP_ID = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/publicIPAddresses/Worker0
Worker1_IP = 52.251.59.78
Worker1_IP_ID = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/publicIPAddresses/Worker1
Worker2_IP = 52.254.50.15
Worker2_IP_ID = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/publicIPAddresses/Worker2

Le code précédent créera sept adresses IP publiques : six pour les instances de nœud (de
travail et de contrôleur) et une pour l’équilibreur de charge. Nous pouvons désormais créer
l’équilibreur de charge et l'associer à l’IP de l’équilibreur de charge en accédant au répertoire
Chapter5/Kubernetes_Cluster/EK8S-API-Public-loadbalancer et en initialisation Terraform.
Nous pouvons ensuite utiliser terraform apply une nouvelle fois, comme suit :
$ terraform apply
.
.
.
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Releasing state lock. This may take a few moments...

Outputs:

lb_backend_pool = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/loadBalancers/k8s_master_lb/backendAddressPools/ \
k8s-control-plane
load_balancer_frontend_id = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/ \
resourceGroups/K8Scluster/providers/Microsoft.Network/loadBalancers/k8s_master_lb/ \
frontendIPConfigurations/K8S-frontend-config
load_balancer_id = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/loadBalancers/k8s_master_lb
load_balancer_private_ip =
load_balancer_public_ip = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/ \
resourceGroups/K8Scluster/providers/Microsoft.Network/publicIPAddresses/k8s_master_lb

Cette procédure crée un équilibreur de charge et appliquer les règles d’équilibreur de charge
que nous avons prédéfinies dans le fichier de configuration Terraform.

Création d’instances de travail et de contrôleur


À ce stade, nous sommes prêts à créer les nœuds d’instance qui constitueront notre cluster
Kubernetes. Nous allons créer un cluster à six nœuds dans lequel trois nœuds seront des
nœuds de travail qui exécuteront la charge de travail réelle (pods, etc.) et les trois autres
constitueront le plan de contrôle.
Avant de réaliser ces étapes, nous devons mettre à jour le fichier main.tf dans le répertoire
/Chapter5/Kubernetes_  Cluster/F-K8S-Nodes/main.tf avec l’ID d’image pour le nœud de
travail et le nœud de contrôleur. Nous devons également mettre à jour le chemin d’accès à
l’emplacement de la clé Secure Shell (SSH) qui sera utilisée pour se connecter en SSH sur
les machines :

104 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


module "master" {
source = "../../terraform_modules/Azure_VMs-Master"
azure-dc = "eastus2"
resource-grp-name = "K8Scluster"
private_ip_addresses = ["10.240.0.10", "10.240.0.11", "10.240.0.12"]
vm_prefix = "controller"
username = "ubuntu"
public_ip_address_id = [data.terraform_remote_state.k8s_public_ip.outputs. \
Controller0_IP_ID,data.terraform_remote_state.k8s_public_ip.outputs. \
Controller1_IP_ID,data.terraform_remote_state.k8s_public_ip.outputs.Controller2_IP_ID]
vm_size = "Standard_D1_v2"
env = "dev"
type-of-cluster = "k8s-experments"
vm_count = 3
subnet_id = data.terraform_remote_state.k8s_vnet.outputs.subnet_id
image_id = "/subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/ \
resourceGroups/K8Scluster/providers/Microsoft.Compute/images/k8s_controller"
ssh_key = "${file("/Users/nissingh/TESTING/SSH_KEYS/id_rsa.pub")}"
lb_backend_pool = data.terraform_remote_state.k8s_loadbalancer.outputs.lb_backend_pool
}

Dans la configuration précédente (main.tf), l’ID d’image a été mis à jour pour le nœud
du contrôleur (maître), ainsi que le chemin d’accès à la clé SSH. Vous pouvez maintenant
générer une paire de clés SSH à l’aide de la commande ssh-keygen pour ssh_key. De même,
vous pouvez mettre à jour le fichier de configuration du nœud de travail, puis initialiser
Terraform comme avant. Une fois l’initialisation terminée, vous pouvez utiliser terraform
apply pour créer les instances de travail et de contrôleur :
$ terraform apply
.
.
.
.
Apply complete! Resources: 17 added, 0 changed, 0 destroyed.
Releasing state lock. This may take a few moments...

Outputs:

master_machine_id = [
[
"/subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/ \
providers/Microsoft.Compute/virtualMachines/controller0",
"/subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/ \
providers/Microsoft.Compute/virtualMachines/controller1",
"/subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/ \
providers/Microsoft.Compute/virtualMachines/controller2",
],
]
master_machine_name = [
[
"controller0",
"controller1",
"controller2",
],
]
master_machine_private_ips = [
[
"10.240.0.10",
"10.240.0.11",

Création d'un cluster Kubernetes à partir de zéro 105


"10.240.0.12",
],
]
worker_machine_id = [
[
"/subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/ \
providers/Microsoft.Compute/virtualMachines/worker0",
"/subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/ \
providers/Microsoft.Compute/virtualMachines/worker1",
"/subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/ \
providers/Microsoft.Compute/virtualMachines/worker2",
],
]
worker_machine_name = [
[
"worker0",
"worker1",
"worker2",
],
]
worker_machine_private_ips = [
[
"10.240.0.20",
"10.240.0.21",
"10.240.0.22",
],
]

Une fois la commande terraform apply exécutée, les nœuds seront configurés pour votre
cluster Kubernetes (voir figure 5-1).

Figure 5-1. Les nœuds de travail et de contrôleur sont opérationnels

Utilisation d'Ansible pour déployer et configurer des nœuds


de contrôleur Kubernetes
Étant donné que l’infrastructure s'exécute, il est temps de configurer le cluster, car nous
n'avons préparé que le minimum pour le moment. Pour déployer et configurer Kubernetes
et travailler sur le nœud, nous allons utiliser les playbooks Ansible.

106 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


Tout d’abord, mettez à jour le fichier hosts dans Chapter5/Ansible-Playbooks/hosts avec les
adresses IP des instances de travail et de contrôleur, ainsi que le nom d’utilisateur SSH et le
chemin d’accès de la clé SSH privée qui a été générée avec ssh-keygen :
[controllers]
inventory_hostname=controller0 ansible_host=52.252.6.89
inventory_hostname=controller1 ansible_host=52.254.50.7
inventory_hostname=controller2 ansible_host=52.251.58.212
[workers]
inventory_hostname=worker0 ansible_host=52.251.59.169
inventory_hostname=worker1 ansible_host=52.251.59.78
inventory_hostname=worker2 ansible_host=52.254.50.15

[all:vars]
ansible_user = ubuntu
ansible_ssh_private_key_file = /Users/SSH_KEYS/id_rsa

Une fois le fichier hosts mis à jour, nous devons mettre à jour le groupvars (situé dans le
fichier all du répertoire Chapter5/Ansible-Playbooks/group_vars) avec loadbalancer_
public_ip et la clé de chiffrement. Étant donné que Kubernetes stocke une variété de
données, notamment l’état du cluster, les configurations d'application et les secrets, nous
devons chiffrer ces données au repos.   Kubernetes prend en charge le chiffrement des
données de cluster au repos.
Pour créer une clé de chiffrement, vous pouvez simplement émettre la commande suivante
sur votre terminal et coller le secret généré dans le fichier group_vars/all comme suit :
$ head -c 32 /dev/urandom | base64
Uvivn+4ONy9yqRf0ynRVOpsEE7WsfyvYnM7VNakiNeA=

Vous devrez également mettre à jour l’IP de l’équilibreur de charge. Voici à quoi devrait
ressembler le group_vars définitif :
---
#Change loadbalancer_public_ip
loadbalancer_public_ip : 52.254.73.23
controller_private_ips_list : 10.240.0.10,10.240.0.11,10.240.0.12
# Keep k8s_internal_virtual_ip as it is
k8s_internal_virtual_ip: 10.32.0.1
k8s_cluster_cidr: "10.200.0.0/16"
k8s_cluster_name: "cloud-native-azure"
# Generate your own encryption_key : "head -c 32 /dev/urandom | base64"
encryption_key: Uvivn+4ONy9yqRf0ynRVOpsEE7WsfyvYnM7VNakiNeA=
#ETC config below for systemd file, place private ip's. No need to change
controller_0_ip: 10.240.0.10
controller_1_ip: 10.240.0.11
controller_2_ip: 10.240.0.12

Une fois que la mise à jour des fichiers est terminée, nous devons installer les outils côté
client sur le système local, principalement kubectl, cfssl et cfssljson. CFSSL est le
kit de ressources de l'infrastructure à clé publique/Transport Layer Security (PKI/TLS) de
Cloudflare pour la signature, la vérification et le regroupement des certificats TLS en général.
Nous utiliserons cette boîte à outils pour générer le certificat TLS pour les nœuds Kubernetes.

Création d'un cluster Kubernetes à partir de zéro 107


Vous pouvez installer les programmes cfssl et cfssljson sur MacOS comme suit :
$ brew install cfssl

Sous Linux, vous pouvez effectuer les opérations suivantes :


$ wget -q --show-progress --https-only --timestamping \
https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssl_1.4.1_linux_amd64 \
https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssljson_1.4.1_linux_amd64
$ chmod +x cfssl_1.4.1_linux_amd64 cfssljson_1.4.1_linux_amd64
$ sudo mv cfssl_1.4.1_linux_amd64 /usr/local/bin/cfssl
$ sudo mv cfssljson_1.4.1_linux_amd64 /usr/local/bin/cfssljson

Exécutez la commande cfssl version pour garantir une installation adéquate :


$ ~ cfssl version
Version: dev
Runtime: go1.14

Pour installer le binaire kubectl sur OS X, nous pouvons effectuer les opérations suivantes :
$ curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https:// \
storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl"
$ chmod +x ./kubectl
$ sudo mv ./kubectl /usr/local/bin/kubectl

Voici la commande dédiée à l’installation sous Linux :


$ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https:// \
storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
$ chmod +x ./kubectl
$ sudo mv ./kubectl /usr/local/bin/kubectl

Vous pouvez vérifier le bon fonctionnement du client kubectl en exécutant les commandes
suivantes :
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3",
GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean",
BuildDate:"2020-02-11T18:14:22Z", GoVersion:"go1.13.6", Compiler:"gc",
Platform:"darwin/amd64"}

Nous sommes maintenant prêts à créer nos nœuds de contrôleur avec Ansible. Accédez
au répertoire du playbook Ansible (Chapter5/Ansible-Playbooks) et exécutez la commande
suivante :
$ ansible-playbook -vi hosts controllers.yaml

L'exécution de cette commande peut nécessiter un certain temps. Une fois terminée, la sortie
suivante doit s'afficher :
PLAY RECAP *********************************************************************************
inventory_hostname=controller0 : ok=43 changed=39 unreachable=0 failed=0 skipped=4
rescued=0 ignored=0
inventory_hostname=controller1 : ok=22 changed=19 unreachable=0 failed=0 skipped=7
rescued=0 ignored=0
inventory_hostname=controller2 : ok=22 changed=19 unreachable=0 failed=0 skipped=7
rescued=0 ignored=0

108 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


Cette étape clôture l’installation des nœuds de contrôleur. Grâce au playbook controllers.
yaml , nous avons essentiellement amorcé les nœuds etcds et de contrôleur. Le playbook
Ansible contient des instructions pas à pas, qui sont explicites, pour l’amorçage des nœuds
de contrôleur pour le cluster. Nous vous conseillons vivement de suivre les directives du
playbook concernant le chemin d’exécution afin de comprendre les détails essentiels.

Utilisation d'Ansible pour déployer et configurer des nœuds de travail


Kubernetes
Maintenant que les nœuds de contrôleur ont été déployés, nous pouvons en faire de même
avec les nœuds de travail à l’aide du fichier workers.yaml :
$ ansible-playbook -vi hosts workers.yaml
.
.
.

PLAY RECAP *********************************************************************************


inventory_hostname=worker0 : ok=31 changed=29 unreachable=0 failed=0 skipped=20
rescued=0 ignored=0
inventory_hostname=worker1 : ok=23 changed=21 unreachable=0 failed=0 skipped=23
rescued=0 ignored=0
inventory_hostname=worker2 : ok=23 changed=21 unreachable=0 failed=0 skipped=23
rescued=0 ignored=0

Cette étape permet de déployer et de configurer les nœuds de travail avec les binaires
Kubernetes nécessaires.

Configuration de la mise en réseau et du routage des pods


Il nous reste à configurer la mise en réseau et le routage des pods pour notre cluster. Pour ce
faire, nous devons d’abord configurer la mise en réseau entre les pods en accédant au répertoire
Chapter5/Kubernetes_Cluster/G-K8S-PodsNetwork et en exécutant terraform init :
$ terraform init
Une fois Terraform initialisé dans le répertoire, nous pouvons exécuter terraform apply :
$ terraform apply
.
.
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

route_table_id = /subscriptions/b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/ \
K8Scluster/providers/Microsoft.Network/routeTables/k8s-pod-router

Cette étape permet de configurer la table de route des pods dans Azure. Nous pouvons enfin
créer les routes en accédant au répertoire final, Chapter5/Kubernetes_Cluster/H-k8S_Route-
creation, et en initialisation Terraform comme auparavant. Une fois initialisé, nous pouvons
exécuter terraform apply :

Création d'un cluster Kubernetes à partir de zéro 109


$ terraform apply
.
.
.
Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?


Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

module.route0.azurerm_route.generic-routes: Creating...
module.route1.azurerm_route.generic-routes: Creating...
module.route0.azurerm_route.generic-routes: Creation complete after 4s [id=/subscriptions/
b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/providers/Microsoft.Network/
routeTables/k8s-pod-router/routes/k8s-pod-router0]
module.route2.azurerm_route.generic-routes: Creating...
module.route1.azurerm_route.generic-routes: Creation complete after 5s [id=/subscriptions/
b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/providers/Microsoft.Network/
routeTables/k8s-pod-router/routes/k8s-pod-router1]
module.route2.azurerm_route.generic-routes: Creation complete after 4s [id=/subscriptions/
b5624140-9087-4311-a04b-3b16a2e84792/resourceGroups/K8Scluster/providers/Microsoft.Network/
routeTables/k8s-pod-router/routes/k8s-pod-router2]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Cette étape clôture la configuration de notre réseau pour le cluster Kubernetes.

Génération du fichier kubeconfig pour l’accès à distance


et la validation de cluster
Enfin, nous devons télécharger le fichier kubeconfig pour le cluster que nous venons de créer
et tester si le cluster a été configuré correctement. Revenez au répertoire Ansible, Chapter5/
Ansible-Playbooks, et exécutez les commandes suivantes :
$ ansible-playbook -vi hosts remote_access_k8s.yaml
.
.
.

PLAY RECAP *********************************************************************************


inventory_hostname=controller0 : ok=6 changed=5 unreachable=0 failed=0 skipped=0
rescued=0 ignored=0
inventory_hostname=controller1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0
rescued=0 ignored=0
inventory_hostname=controller2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0
rescued=0 ignored=0

Le fichier kubeconfig sera alors téléchargé et stocké localement afin que vous puissiez accéder
et interagir avec le cluster. Vérifiez si vous pouvez le faire en exécutant kubectl get nodes
sur votre ordinateur local comme suit :
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
worker0 Ready <none> 27m v1.17.3

110 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


worker1 Ready <none> 27m v1.17.3
worker1 Ready <none> 27m v1.17.3

La sortie répertorie les nœuds de travail de votre cluster. Vous pouvez maintenant utiliser ce
cluster pour expérimenter et explorer les fonctions abordées dans ce chapitre. Même s'il ne
s’agit pas d’un cluster prêt à la production, vous savez désormais comment créer, exécuter et
gérer un cluster Kubernetes à partir de zéro.
Maintenant que vous connaissez la procédure la plus complexe (semi-automatisée), jetons
un coup d’œil à la méthode privilégiée la plus simple pour exécuter un cluster Kubernetes
de production dans Azure.

Azure Kubernetes Service


Comme vous l’avez vu, le processus de gestion et de maintenance d’un cluster Kubernetes
est complexe. Du point de vue opérationnel, il ne s’agit pas seulement d’exécuter un cluster.
Ce processus implique la sécurité, les performances, la journalisation, la mise à niveau du
cluster, l’application des correctifs, etc. Pour vous soulager de ces tâches fastidieuses, Azure
propose une solution gérée appelée Azure Kubernetes Service ou AKS.
AKS simplifie énormément le processus de déploiement et de gestion d’un cluster
Kubernetes dans Azure. Une solution Kubernetes gérée par Azure élimine une grande
partie du processus de gestion Kubernetes. Il ne vous reste plus qu’à gérer votre application.
AKS offre plusieurs avantages, notamment :
• La surveillance de l'intégrité des clusters est entièrement gérée par Azure.
• Les tâches de maintenance des nœuds sous-jacents relèvent de la responsabilité
d’Azure.
• Le plan de contrôle est géré et maintenu par Azure, et vous, en tant qu’utilisateur, n’avez
qu'à gérer les nœuds agents.
• AKS vous permet d’intégrer Azure Active Directory et l’API Kubernetes RBAC pour la
sécurité et le contrôle d’accès.
• Tous les journaux de votre application et de votre cluster sont stockés dans l’espace de
travail Azure log Analytics.
• L'échelle horizontale de Pod (HPA) et la mise à l’échelle automatique du cluster sont
fluides à utiliser concernant la configuration d’un cluster Kubernetes à partir de zéro et
la prise en charge des autorisations de ressources sous-jacentes.
• AKS offre une prise en charge aisée du volume de stockage.

Maintenant que vous comprenez les avantages d’AKS, créons un cluster AKS. Vous disposez
de deux méthodes simples pour y parvenir : utilisez le portail Azure (l’approche manuelle)
ou utilisez Terraform (l’approche automatisée).

Azure Kubernetes Service 111


Vous pouvez accéder à la console Azure et commencer à déployer votre cluster AKS en
cliquant sur le bouton Ajouter (figure 5-2), puis appliquer les instructions suivantes.

Figure 5-2. Cliquez sur le bouton Ajouter pour créer un cluster AKS

Par ailleurs, dans Terraform, vous pouvez accéder au répertoire AKS du référentiel /cloud_
native_azure/Chapter5/AKS et exécuter la commande suivante :
$ terraform apply

Cliquez sur Appliquer, afin qu'un cluster Kubernetes de trois nœuds soit exécuté en quelques
minutes (figure 5-3).

Figure 5-3. Cluster AKS créé via Terraform

Pour télécharger le fichier kubeconfig, vous pouvez utiliser la commande Azure CLI de
votre système local pour obtenir des informations d’identification d’accès pour votre cluster
Kubernetes géré comme suit :
az aks get-credentials --resource-group azure-k8stest --name akstest --file config

112 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


Votre kubeconfig sera téléchargé avec le nom config ; déplacez-le vers le dossier. kube. Vous
pouvez désormais utiliser ce cluster de la même manière que vous utiliseriez le cluster que
vous avez créé via la méthode la plus complexe :
$ ~ kubectl get nodes
NAME STATUS ROLES AGE VERSION
aks-agentpool-42554519-vmss000000 Ready agent 29m v1.17.9
aks-agentpool-42554519-vmss000001 Ready agent 29m v1.17.9
aks-agentpool-42554519-vmss000002 Ready agent 29m v1.17.9

Bien que vous puissiez créer à peu près n’importe quelle ressource et déployer des applica-
tions à ce stade, il ne s'agit pas d’un cluster de production. Il existe de nombreuses ressourc-
es en ligne1 susceptibles de vous guider dans la création de clusters de production.
Grâce à ces connaissances, nous sommes prêts à passer à la suite et à découvrir Helm, qui
sert principalement d’outil pour rationaliser l’installation et la gestion des applications
Kubernetes.

Déploiement d’applications et de services avec Helm :


un gestionnaire de package pour Kubernetes
Vous devez probablement savoir à présent que la gestion Kubernetes est extrêmement
complexe. Même si vous pouvez utiliser un service géré comme Microsoft AKS, il est
difficile de gérer les déploiements et les ReplicaSets à mesure que vos besoins augmentent.
Helm est un gestionnaire de packages pour Kubernetes qui vous permet d’extraire, de
déployer et de gérer les applications. Fondamentalement, Helm vous soulage de la gestion
fastidieuse des fichiers YAML importants, qui contiennent des informations sur les pods,
les ReplicaSets, les services, les paramètres RBAC, etc. Helm propose également des
restaurations, des informations sur l’historique des versions et des hooks de test intégrés,
lesquels facilitent considérablement la gestion du cycle de vie complet de vos applications
sur un cluster Kubernetes.
Dans cet ouvrage, nous utilisons la version stable Helm v 3.3.1. Les anciennes versions (par
exemple, v2) utilisaient un composant côté serveur appelé Tiller, qui devait être déployé sur
le cluster Kubernetes. Tiller présentait un risque de sécurité potentiel à cause des privilèges
d’utilisateur et de frais de gestion plus importants, en compromettant l'utilisation d'Helm
dans la communauté des utilisateurs. Avec l’introduction de l’API RBAC, Helm ne contient
désormais que des binaires côté client et Tiller a été complètement supprimé. La figure 5-4
représente le fonctionnement de base du gestionnaire de package Helm.

1 Consultez https://kubernetes.io/docs/setup/production-environment, https://kubernetes.io/docs/setup/production-


environment/tools, et l'adresse https://github.com/kubernetes/kops.

Déploiement d’applications et de services avec Helm : un gestionnaire de package pour Kubernetes 113
Figure 5-4. Référentiel de tableau Helm et interaction avec les clients

Notions de base d'Helm


Le client Helm est installé en tant qu’utilitaire de ligne de commande pour l’utilisateur final.
La principale responsabilité du client est d’aider au développement de tableaux et de gérer
les référentiels et les versions. Helm se compose des éléments suivants :
Tableau
Les tableaux sont les packages Kubernetes gérés par Helm. Les tableaux se composent de
toutes les informations requises pour créer une application dans Kubernetes, y compris
les définitions de ressources requises pour exécuter une application ou un service dans
un cluster Kubernetes.
Référentiel
Il s’agit de la base de données où tous les tableaux Helm sont stockés.
Config
Il s’agit de la configuration qui peut être fusionnée dans un tableau packagé pour créer
un objet éditable.
Version
Les versions permettent de suivre toutes les applications installées par Helm sur
le cluster Kubernetes. Un tableau unique peut être installé plusieurs fois, et chaque
nouvelle installation crée une nouvelle version.

Installation et gestion d'Helm


Vous pouvez télécharger la dernière version du client Helm pour votre ordinateur à partir
de ses référentiels officiels à l’adresse https://github.com/helm/helm/releases.
Après avoir installé Helm, nous devons ajouter un référentiel de tableaux à partir duquel les
packages peuvent être téléchargés. Google propose le référentiel de tableau Kubernetes, qui
peut être ajouté comme suit :
$helm repo add stable https://kubernetes-charts.storage.googleapis.com/

Essentiellement, Helm extrait des tableaux ou des packages à partir d’un référentiel central,
puis les installe/publie sur le cluster Kubernetes.

114 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


Recherche de référentiels Helm
Vous pouvez rechercher des référentiels Helm à l’aide de la commande helm search selon
deux méthodes différentes. La première façon consiste à rechercher le référentiel que vous
avez ajouté localement (comme lors de l'étape précédente, dans laquelle nous avons ajouté
un référentiel stable) :
$ helm search repo stable
NAME CHART VERSION APP VERSION
DESCRIPTION
stable/acs-engine-autoscaler 2.2.2 2.1.1
DEPRECATED Scales worker nodes within agent pools
stable/aerospike 0.3.3 v4.5.0.5
A Helm chart for Aerospike in Kubernetes
stable/airflow 7.6.0 1.10.10
Airflow is a platform to programmatically autho...
stable/ambassador 5.3.2 0.86.1 DEPRECATED A Helm chart for...
stable/anchore-engine 1.6.9 0.7.2 Anchore container analysis...
stable/apm-server 2.1.5 7.0.0 The server receives data...
stable/ark 4.2.2 0.10.2 DEPRECATED A Helm...
.
.
.

Cette recherche est effectuée parmi les données locales sur votre ordinateur.
L’autre méthode de recherche des référentiels Helm consiste à effectuer une recherche dans
le hub Helm. Le hub Helm se compose de plusieurs référentiels publics. Ainsi, si vous ne
trouvez pas de tableau dans votre référentiel local, vous pouvez toujours le rechercher dans
le hub. Dans l'exemple suivant, nous avons cherché tous les tableaux « kafka » :
$ ~ helm search hub kafka
URL CHART VERSION APP VERSION
DESCRIPTION
https://hub.helm.sh/charts/kafkaesque/imagepuller 1.0.0 1.0
Pull container images to your nodes so that the...
https://hub.helm.sh/charts/kafkaesque/pulsar 1.0.26 1.0
Apache Pulsar Helm chart for Kubernetes
https://hub.helm.sh/charts/kafkaesque/pulsar-mo... 0.1.5 1.0
A Helm chart for the Pulsar Monitor application
https://hub.helm.sh/charts/kafkaesque/teleport 1.0.0
Teleport Community
https://hub.helm.sh/charts/bitnami/kafka 11.8.4 2.6.0
Apache Kafka is a distributed streaming platform.
https://hub.helm.sh/charts/stable/kafka-manager 2.3.1 1.3.3.22
A tool for managing Apache Kafka.
https://hub.helm.sh/charts/touk/hermes 0.3.1 1.5.2
A Helm chart for Kubernetes of Hermes, a reliab...

Installation d’un tableau Helm sur Kubernetes


Pour installer un tableau, vous pouvez exécuter la commande helm install comme suit :
$ ~ helm install my-first-server stable/tomcat
NAME: my-first-server
LAST DEPLOYED: Thu Sep 10 00:04:17 2020
NAMESPACE: default
STATUS: deployed

Déploiement d’applications et de services avec Helm : un gestionnaire de package pour Kubernetes 115
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w my-first-server-tomcat'
export SERVICE_IP=$(kubectl get svc --namespace default my-first-server-tomcat -o \
jsonpath='{.status.loadBalancer.ingress[0].hostname}')
echo http://$SERVICE_IP:

Ici, nous avons essayé de déployer un serveur Tomcat, en sélectionnant le nom de version
my-first-server. À l’aide de kubectl, vous pouvez vérifier les ressources créées pour le tableau
Tomcat :
$ ~ kubectl get svc --namespace default my-first-server-tomcat
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-first-server-tomcat LoadBalancer 10.32.0.3 <pending> 80:31495/TCP 56s

Modification des valeurs par défaut du tableau


Parfois, le tableau doit être personnalisé avant son installation. Pour modifier la valeur d’un
tableau par défaut, vous devez d’abord afficher la valeur à l’aide de la commande helm show
values :
$ helm show values stable/tomcat
# Default values for the chart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1

image:
webarchive:
repository: ananwaresystems/webarchive
tag: "1.0"
tomcat:
.
.
.
resources: {}
# limits:
# cpu: 100m
# memory: 256Mi
# requests:
# cpu: 100m
# memory: 256Mi

nodeSelector: {}

tolerations: []

affinity: {}

Pour modifier la valeur, vous pouvez utiliser l'argument --values ou --set. Le premier
présente l'avantage de pouvoir indiquer un fichier YAML que vous pouvez transmettre lors
de l’installation d’un tableau. Les valeurs du tableau sont ainsi remplacées. Par exemple,
dans le tableau précédent, si nous voulons modifier les valeurs par défaut, nous pouvons
créer un fichier YAML comme suit et l’enregistrer sous custom_vals.yaml :

116 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 256Mi

Pour installer les valeurs modifiées, procédez comme suit :


$ helm install --values custom_vals.yaml new-v2-install stable/tomcat

La section suivante explique comment gérer les versions dans Helm.

Gestion des versions Helm


Dans la terminologie Helm, une version est une instance d’un tableau exécuté dans un
cluster Kubernetes.Un tableau peut être installé plusieurs fois dans le même cluster, et
chaque fois qu’il est installé, une nouvelle version est créée. Cette section porte sur la gestion
des versions Helm.

Vérification d’une version


Pour déterminer l’état de la version, vous pouvez utiliser helm status, qui vous donnera
des détails tels que le numéro de révision et la date de son dernier déploiement :
$ helm status my-first-server
NAME: my-first-server
LAST DEPLOYED: Thu Sep 10 00:04:17 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w my-first-server-tomcat'
export SERVICE_IP=$(kubectl get svc --namespace default my-first-server-tomcat -o \
jsonpath='{.status.loadBalancer.ingress[0].hostname}')
echo http://$SERVICE_IP:
$ ~ helm show values stable/tomcat
# Default values for the chart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1

Mise à niveau d’une version


Vous pouvez utiliser helm upgrade pour mettre à niveau vos tableaux installés lorsqu’une
nouvelle version est lancée ou si vous avez modifié la configuration :
$ helm upgrade --values new_vals.yaml new-v2-install stable/tomcat

Vous pourrez ainsi mettre à niveau new-v2-install avec le même tableau (stable/tomcat),
tout en mettant à jour de nouvelles valeurs dans le fichier new_vals.yaml.

Déploiement d’applications et de services avec Helm : un gestionnaire de package pour Kubernetes 117
Restaurer une version
Si vous avez besoin de restaurer un tableau déployé (version), vous pouvez simplement
utiliser l'option helm rollback comme suit :
$ ~ helm rollback my-first-server 1
Rollback was a success! Happy Helming!
$ ~

Ici, nous avons restauré my-first-server à la version 1.

Désinstallation d’une version


Pour supprimer définitivement une version du cluster, vous pouvez utiliser helm uninstall :
$ ~ helm uninstall my-first-server
release "my-first-server" uninstalled

Création de tableaux pour vos applications


Pour créer un tableau pour votre application, suivez le format de package par défaut fourni
par Helm. Vous pouvez utiliser les tableaux pour déployer un pod simple ou une pile
d’application Web complète contenant des serveurs HTTP, des bases de données, etc. Les
tableaux contiennent une collection de fichiers dans un répertoire. La structure du répertoire
est la suivante, où le nom du répertoire principal (nginx dans ce cas) est le nom du tableau :
~ nginx/
Chart.yaml
LICENSE
README.md
values.yaml
values.schema.json
charts/
crds/
templates/

Dans le répertoire, Helm s’attend à ce que le fichier présente la même structure et les mêmes
noms, car Helm les réserve. Chaque élément du répertoire sert un objectif spécifique :

• Chart.yaml contient toutes les informations sur le tableau au format YAML.


• LICENSE est un fichier en texte brut facultatif qui contient la licence du tableau.
• README.md est un fichier README lisible par l’homme.
• values.yaml contient toutes les valeurs de configuration par défaut pour le tableau.
alues.schema.json est un fichier de schéma JSON facultatif qui impose une structure
• 
sur values.yaml.
• charts/ inclut tous les tableaux dépendants sur lesquels repose le tableau actuel.
• crds/ contient des définitions de ressources personnalisées.

118 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


• 
templates/ est le répertoire qui contient des modèles, lesquels peuvent être combinés
avec des valeurs pour générer des fichiers manifeste Kubernetes.

Le fichier Chart.yaml est nécessaire pour créer le tableau, et il contient les champs suivants :
apiVersion: The chart API version (required)
name: The name of the chart (required)
version: A SemVer 2 version (required)
kubeVersion: A SemVer range of compatible Kubernetes versions (optional)
description: A single-sentence description of this project (optional)
type: The type of the chart (optional)
keywords:
- A list of keywords about this project (optional)
home: The URL of this projects home page (optional)
sources:
- A list of URLs to source code for this project (optional)
dependencies: # A list of the chart requirements (optional)
- name: The name of the chart (nginx)
version: The version of the chart ("1.2.3")
repository: The repository URL ("https://example.com/charts") or alias ("@repo-name")
condition: (optional) A yaml path that resolves to a boolean, used for
enabling/disabling charts (e.g. subchart1.enabled )
tags: # (optional)
- Tags can be used to group charts for enabling/disabling together
enabled: (optional) Enabled bool determines if chart should be loaded
import-values: # (optional)
- ImportValues holds the mapping of source values to the parent key to be imported.
Each item can be a string or pair of child/parent sublist items.
alias: (optional) Alias to be used for the chart. Useful when you have to add the same
chart multiple times
maintainers: # (optional)
- name: The maintainers name (required for each maintainer)
email: The maintainers email (optional for each maintainer)
url: A URL for the maintainer (optional for each maintainer)
icon: A URL to an SVG or PNG image to be used as an icon (optional).
appVersion: The version of the app that this contains (optional). This needn't be SemVer.
deprecated: Whether this chart is deprecated (optional, boolean)
annotations:
example: A list of annotations keyed by name (optional).

Utilisation de Helm pour créer et gérer des tableaux


Vous pouvez créer chaque fichier dans votre répertoire de tableau manuellement, ou vous
pouvez utiliser Helm pour créer un plan, comme suit :
$ helm create examplechart
Creating examplechart

Vous pouvez ensuite modifier les fichiers dans le répertoire du tableau et les conditionner
dans une archive de tableau :
$ helm package examplechart
Archived examplechart.1.-.tgz

Une fois que vous avez conditionné votre tableau, vous pouvez l’utiliser avec helm install
et créer une version.

Déploiement d’applications et de services avec Helm : un gestionnaire de package pour Kubernetes 119
Résumé
Dans ce chapitre, vous avez appris à créer un cluster Kubernetes, à la fois manuellement
à partir de zéro à l'aide d'Azure et avec Azure AKS en tant que service géré. Nous avons
également découvert Helm, en tant que gestionnaire de package Kubernetes qui vous
permet de créer et de gérer vos applications avec un seul modèle.
Avant de passer à l’étape suivante, nous tenons à souligner que Kubernetes est difficile
à gérer à lui seul(en particulier dans la production). Il ne s'agit certainement pas d'une
solution miracle capable de faciliter la gestion des applications dans les environnements
Cloud natifs. Compte tenu de la complexité sous-jacente de la gestion d’un cluster
Kubernetes, il est évident qu’une approche telle qu'AKS constitue une meilleure solution
pour gérer les services exécutés sur des clusters Kubernetes. AKS vous soulage de la gestion
de l’infrastructure sous-jacente ou des parties mobiles de l’écosystème, car la plupart des
points critiques, comme la haute disponibilité et la redondance du cluster Kubernetes, sont
pris en charge.
Ceci étant dit, passons au chapitre 6, qui traite de l’observabilité des systèmes distribués et
de la fiabilité des applications Cloud natives.

120 Chapitre 5 : Création d'un cluster Kubernetes dans Azure


CHAPITRE 6
Observabilité : suivre les miettes de pain

L’évolution rapide des systèmes Cloud natifs a introduit des complexités en matière de
configuration d’infrastructure, de déploiement d’infrastructure et de gestion de logiciels.
La conception des applications est axée sur la résilience, en nécessitant une compréhension
plus approfondie de l’infrastructure sous-jacente de l’environnement Cloud, y compris les
modes de défaillance et les goulots d’étranglement. Il est plus essentiel que jamais d'obtenir
la visibilité nécessaire sur la pile d’applications en raison de la multitude de nouveaux
éléments mobiles et des changements de conception des applications actuelles.
Dans ce chapitre, nous allons introduire le concept d’observabilité et expliquer pourquoi il est
nécessaire dans le monde Cloud natif d’aujourd’hui. Vous en apprendrez davantage sur les trois
piliers de l’observabilité (journaux, métriques et traces), en découvrant comment ils interagissent
pour créer des systèmes observables. Nous allons également examiner divers outils Cloud natifs
bien connus qui peuvent être utilisés pour créer un système observable et démontrer comment
l’observabilité est un sur-ensemble de la surveillance. Enfin, vous découvrirez comment tirer
parti d’Azure en tant que plateforme Cloud pour optimiser l’observabilité de vos applications,
de l’infrastructure sous-jacente et de la pile de mise en réseau.

Introduction à l’observabilité
La première chose à comprendre au sujet de l’observabilité c'est sa définition elle-même.
Le terme observabilité découle du monde de la théorie du contrôle en mathématiques, qui
traite principalement de la création d’un moyen de contrôler les systèmes dynamiques à
l’aide d’un contrôleur à l’aide de retours du système lui-même. Dans la théorie du contrôle,
l’observabilité est la capacité de mesurer l’état interne d’un système à partir des connaissances
des sorties externes. En ce qui concerne l’ingénierie logicielle, l’observabilité implique la
détermination de l'état exact d’une application à partir de ses sorties. En concevant votre
application et votre infrastructure afin qu’elles soient observables, vous serez essentiellement
en mesure de déterminer pourquoi un problème survient et comment vous pourrez le

121
résoudre. L’objectif sous-jacent de rendre un système observable est de permettre à un
ingénieur d'identifier clairement un problème, de sa cause à son effet.
L’observabilité est encore plus importante dans l’infrastructure Cloud native actuelle en raison
de l’environnement dynamique et distribué où les logiciels et les systèmes sont susceptibles
de planter. L’essor des microservices, des conteneurs et de l’informatique sans serveur a
entraîné un maillage complexe de services interconnectés qui effectuent de nombreux appels
entre eux. Lorsque ces réseaux de services entrelacés se comportent mal, il est presque
impossible d'identifier la cause profonde du problème sans perturber les activités. Il devient
essentiel d'identifier rapidement un problème dans l’environnement sous-jacent de services
complexes, afin de ne pas compromettre les activités. L’observabilité résout ce problème, en
vous permettant d'interroger vos systèmes et d’obtenir rapidement des réponses.
Avant de discuter de l'utilité de l’observabilité au sein d'environnements distribués modernes,
expliquons la définition d'un système observable et discutons du principe sous-jacent et de
la philosophie qui sous-tend l’idée de développer l’observabilité dans les systèmes.

Observabilité : plus que trois piliers


L’idée fondamentale sur laquelle repose l’observabilité est d’obtenir une meilleure
compréhension d’une application grâce à des informations pertinentes. À mesure que les
applications deviennent plus éphémères et plus distribuées, il devient essentiel de définir
les principes directeurs d’un système observable. Les utilisateurs du Cloud ont défini les
journaux, les métriques et les traces comme trois verticaux ou piliers majeurs, sur lesquels
repose la création d’un système observable moderne. Jetons un coup d’œil à ces piliers et
découvrons en quoi ils sont importants.

Métriques
Les métriques représentent les données numériques mesurées sur un intervalle de temps.
Les métriques sont généralement collectées en exécutant un agent sur un hôte, qui envoie
périodiquement les données dans une banque de données centrale, avant de les regrouper
et de les représenter sur les tableaux de bord. Les métriques sont ensuite utilisées pour
créer des systèmes d’alerte, car elles sont stockées dans une base de données de séries
chronologiques. Par exemple, une métrique est une requête par seconde (qps).

Journaux
Les journaux représentent les événements qui se produisent dans un système (application
et infrastructure). Les journaux contiennent des détails sur l’état interne et les événements.
Ils sont généralement écrits et stockés sur le système d’application dans un format structuré
qui peut être analysé facilement. Une architecture de journalisation performante est l’outil
le plus fondamental pour obtenir la plupart des informations dans un système. Il s'agit
également du premier outil de débogage utilisé par les développeurs de logiciels. Voici un
exemple de journaux d’accès simples au serveur Apache :
10.185.248.71 - - [09/Jan/2015:19:12:06 +0000] 808840 "GET /inventoryService/inventory/ \
purchaseItem?userId=20253471&itemId=23434300 HTTP/1.1" 500 17 "-" \
"Apache-HttpClient/4.2.6 (java 1.5)"

122 Chapitre 6 : Observabilité : suivre les miettes de pain


Traces
Enfin et surtout, les traces constituent la solution moderne qui permet d'identifier le
comportement d’un événement unique dans un système distribué. Les traces vous
permettent d’identifier la quantité de traitement (c.-à-d., la latence) qui est exécutée à
chaque couche de la pile. Dans le monde des microservices et des systèmes distribués,
les traces fournissent des informations précieuses sur les événements en créant un visuel
d’appels distribués corrélés entre les services.

La figure 6-1 illustre des traces simples.

Figure 6-1. Un exemple d’application montrant des traces

Un système comprenant des journaux, des métriques et des traces ne constitue pas
nécessairement un système observable pour autant. Les trois piliers présentent des
avantages et des inconvénients, que nous allons aborder dans le reste du chapitre. Ils ne
constituent individuellement qu'un point de départ puisqu’ils vous permettent d’identifier
un problème, sans toutefois répondre directement au cas d’utilisation ou aux besoins
de votre entreprise. Pour tirer parti des piliers, vous devez tous les utiliser ensemble, en
tenant compte des facteurs qui influent sur votre entreprise. Si vous collectez des données
des métriques séparément à partir des journaux et des traces, vous risquez de perdre du
contexte et de n'observer qu'un seul système sous-jacent. L’observabilité appliquée à une
approche unifiée implique la collecte et la préservation du contexte sous-jacent, ainsi que
toute sa richesse et sa dimension.

Observabilité : un sur-ensemble de surveillance


La surveillance se concentre principalement sur la collecte passive des événements, des
journaux et des métriques d’un système. En général, vous surveillez un service critique qui,
selon vous, est susceptible de planter en raison d'erreurs antérieures ou de modes d'échec
connus. La surveillance adopte une approche proactive en vous permettant de planifier les
alertes et la capacité, tandis que l’observabilité adopte une approche plus réactive en vous aidant
à examiner la pile d'applications de plus près pour identifier les problèmes et leurs sources.
Examinons un exemple. Imaginez qu’un développeur de votre entreprise modifie
un chemin de code critique en essayant d’ajouter une nouvelle fonction dans une
application phare. Malheureusement, il introduit accidentellement un bug, qui

Introduction à l’observabilité 123


provoque alors une panne. Dans une telle situation, vos systèmes de surveillance
seront en mesure d'indiquer clairement l’augmentation du taux d’erreur d’un
service, sans toutefois aller plus loin. En intégrant l’observabilité à votre pile, vous
pouvez néanmoins approfondir rapidement le problème (par exemple, un problème
avec un cluster Redis), puis utiliser les traces pour déterminer la cause profonde.
Il est important de répéter que vos systèmes de surveillance fonctionnent très bien dans
ce scénario. Ils font ce qu’ils sont conçus pour faire  : vous alerter en cas de problème.
L’observabilité vous permet de déterminer la raison des défaillances.
Il est également important de noter que l’observabilité ne remplace pas la surveillance. Il s’agit
plutôt d’un sur-ensemble de surveillance, car elle fournit à la fois une présentation générale
des services ainsi que des informations détaillées sur divers événements au sein d’un système
distribué. En outre, dans un environnement Cloud distribué où plusieurs services s’exécutent et
interagissent les uns avec les autres, il est extrêmement fastidieux de trouver l’erreur si le système
n’est pas instrumenté pour identifier la défaillance pertinente. L’observabilité gère ce type de
scénario en identifiant la chaîne de problèmes à mesure que la requête parcourt les services.

Développement axé sur l’observabilité


Comme nous l’avons vu, les applications Cloud natives sont principalement distribuées et
basées sur les microservices. Elles sont donc intrinsèquement difficiles à maintenir et à
gérer. Toute personne qui exécute une application de production réalisera également que
lorsque celle-ci démontre un comportement incorrect, il devient presque impossible de
déterminer quel élément mobile spécifique est la cause principale de cette erreur. Parfois,
les échecs sont interceptés à l’aide de configurations de journalisation et de surveillance
traditionnelles, lesquelles peuvent vous orienter dans la bonne direction, compte tenu de la
complexité de votre pile d’applications. Toutefois, à mesure que vos services évoluent, vous
ne pouvez pas déterminer avec certitude tous les modes de défaillance de votre application
ou de l’infrastructure sous-jacente.
Le développement axé sur l’observabilité vise à résoudre l'identification des paramètres
inconnus dans les environnements de production en encourageant l’ajout de l’observabilité
au début du cycle de vie du développement logiciel. En outre, il préconise l’instrumentation
du code d’application au cours de la phase de conception logicielle, de sorte qu’il devient plus
facile d’ajouter des instruments en un seul endroit plutôt que dans de nombreux emplacements
à un stade ultérieur. Il vise également à combler le fossé entre les responsables opérationnels
et les développeurs, en permettant à ces derniers de détenir le code et de déboguer facilement
les environnements de production. Non seulement les équipes d’ingénierie sont en mesure
de diagnostiquer les pannes et les échecs au début de la production, tout en étant capables de
mesurer et d’analyser les performances opérationnelles et commerciales.
Les sections suivantes expliquent comment rendre l’infrastructure et les applications Cloud
natives moderne observables. Nous étudierons chacun des trois piliers, en découvrant
la meilleure façon de créer vos systèmes dans des environnements Cloud natifs, en
commençant par les métriques.

124 Chapitre 6 : Observabilité : suivre les miettes de pain


Surveillance des métriques avec
Prometheus dans un monde Cloud natif
Comme nous l’avons expliqué au chapitre  4, Kubernetes a révolutionné la création et le
déploiement des applications modernes. Les microservices et les architectures orientés
service ont transformé le paysage des environnements Cloud natifs à mesure que l'ajout des
fonctions et les déploiements sont se accélérés. En raison de l'adoption de ces architectures
modernes, l’approche traditionnelle de la surveillance ne permet pas de suivre le rythme
actuel de ces architectures. Ces dernières s’articulent autour d’une infrastructure éphémère,
de plusieurs régions et de zones de disponibilité, en s'étendant parfois même sur différents
Clouds. Toutes ces couches de complexité ajoutées induisent également des modes de
défaillance nouveaux et inédits, qui échappent à l’approche de surveillance traditionnelle.
Pour relever le problème de la surveillance des applications et de l’infrastructure Cloud
natives modernes, nous devons revisiter l’un des principaux piliers de l’observabilité  :
les métriques. Comme nous l’avons vu, les métriques sont l’un des domaines clés qui
fournissent une quantité considérable d’informations sur l’état de votre application.
L'ensemble traditionnel de métriques ne permet généralement pas de dresser une image
complète de vos applications Cloud natives. Un jeu de métriques personnalisé doit donc
être collecté pour mieux évaluer les performances et de la santé globales de l’application.
Ces métriques sont généralement configurées pour être émises par l’application elle-même.
Elles fournissent donc des informations plus précises. C’est ici que Prometheus se révèle
très utile.
Prometheus est un système de surveillance et d’alerte open source conçu à l'origine par
SoundCloud. Il a ensuite été introduit en tant que deuxième projet, après Kubernetes,
par la Cloud Native Computing Foundation (CNCF). Il est principalement conçu pour
les applications Cloud natives modernes, spécifiquement axées sur l’espace métrique. Avec
l’avènement de Kubernetes, Prometheus est devenu l’une des solutions incontournables
pour surveiller les nouvelles applications Cloud natives qui exposent un large éventail
de métriques personnalisées. Prometheus est également plus léger que son prédécesseur
dans l’espace de surveillance, car il fonctionne dans des modèles d’exportateurs basés sur
l'attraction, qui récupèrent essentiellement les données des applications et de l’infrastructure.

Composants et architecture de Prometheus


Prometheus a été principalement conçu en se concentrant sur la surveillance des
applications basées sur des conteneurs qui s’exécutent à grande échelle. Les applications
Cloud natives modernes exposent la variété des données qui doivent être collectées, ainsi
que la complexité des éléments mobiles sous-jacents. Par ailleurs, Prometheus a été conçu
à partir de zéro pour gérer cet environnement dynamique.

Surveillance des métriques avec Prometheus dans un monde Cloud natif 125
Pour prendre en charge la nouvelle ère des applications Cloud natives, l’écosystème
Prometheus se compose des cinq principaux composants suivants :

Serveur Prometheus
Chargé du scraping des métriques et de leur stockage dans une base de données de
séries chronologiques.

Bibliothèques client
Utilisées pour instrumenter le code de l’application et envoyer les données de
métriques extraites au serveur Prometheus. Les bibliothèques client sont disponibles
dans différents langages, notamment C#, Python, Java et Ruby.

Passerelle Push
Utilisées pour prendre en charge des tâches éphémères.

Exportateurs
Exécutés à côté des applications qui ne peuvent pas être instrumentées directement,
telles que HAP- roxy et statsd, pour extraire des métriques qui peuvent être envoyées
au serveur Prometheus dans le format souhaité.

Alertmanager
Reçoit des alertes du serveur Prometheus en envoyant les notifications de manière
efficace. Alertmanager exécute cette tâche de plusieurs façons cohérentes et efficaces :

• Il regroupe les alertes d’un type similaire afin de réduire le nombre d’alertes
envoyées lors d’une panne importante.
• Il peut limiter les notifications envoyées au système de réception.
• Il permet la suppression des notifications des alertes sélectionnées si des alertes
associées ont déjà été déclenchées.
• Il peut envoyer des alertes à un certain nombre de systèmes, tels qu'OpsGenie et
PagerDuty, entre autres.

La figure 6-2 illustre l'aspect de ces principaux composants dans l’architecture Prometheus.

Prometheus stocke toutes les données sous forme de séries chronologiques, c’est-à-dire des
flux de valeurs horodatées qui appartiennent à la même métrique et au même ensemble de
dimensions étiquetées. Chaque série chronologique est identifiée de manière unique par
son nom de métrique et par une paire clé-valeur facultative appelée étiquettes. En outre, les
bibliothèques client proposent des types de métriques de base quatre cœurs  : compteur,
jauge, histogramme et résumé. Nous allons parler de ces quatre types plus loin dans le
chapitre, en fournissant des exemples d’instrumentation.

126 Chapitre 6 : Observabilité : suivre les miettes de pain


Figure 6-2. Architecture Prometheus

Tâches et instances
Un point de terminaison que vous pouvez récupérer est appelé une
instance, dans Prometheus et il correspond généralement à un seul
processus. Une collection d’instances ayant le même objectif (un
processus répliqué pour l’évolutivité ou la fiabilité, par exemple) est
appelée une tâche.
Voici l'exemple d'une tâche de serveur API avec quatre instances
répliquées :
job: api-server
instance 1: 1.2.3.4:5670
instance 2: 1.2.3.4:5671
instance 3: 5.6.7.8:5670
instance 4: 5.6.7.8:5671

Nous allons installer et configurer Prometheus pour comprendre quelques concepts clés
supplémentaires autour de l’écosystème Prometheus.

Installation et configuration de Prometheus


Pour installer et exécuter Prometheus localement, vous devez commencer par télécharger
la version appropriée pour votre plateforme. Au moment de la rédaction de ce chapitre, la
dernière version de Prometheus est 2.23. Une fois que vous avez téléchargé l’archive tarball,
accédez au répertoire et extrayez-la à l’aide de la commande suivante :
$ tar xvfz prometheus-2.23.0.darwin-amd64.tar.gz
$ cd prometheus-2.23.0.darwin-amd64

Surveillance des métriques avec Prometheus dans un monde Cloud natif 127
Une fois que vous avez accédé au répertoire Prometheus, vous pouvez configurer Prometheus
pour qu'il récupère son propre point de terminaison HTTP à l’aide du fichier prometheus.
yaml, qui se trouve dans le répertoire Prometheus. Le fichier par défaut contient déjà le port
TCP 9090 sur le localhost sur lequel s’exécute le serveur Prometheus. Vous pouvez exécuter
Prometheus en exécutant le binaire à l’aide de la commande suivante :
$ ./prometheus
level=info ts=2020-11-29T06:51:43.519Z caller=head.go:659 component=tsdb msg="On-disk
memory mappable chunks replay completed" duration=7.944μs
level=info ts=2020-11-29T06:51:43.519Z caller=head.go:665 component=tsdb msg="Replaying WAL,
this may take a while"
level=info ts=2020-11-29T06:51:43.520Z caller=head.go:717 component=tsdb msg="WAL segment
loaded" segment=0 maxSegment=0
level=info ts=2020-11-29T06:51:43.520Z caller=head.go:722 component=tsdb msg="WAL replay
completed" checkpoint_replay_duration=93.857μs wal_replay_duration=695.77μs
total_replay_duration=812.641μs
level=info ts=2020-11-29T06:51:43.521Z caller=main.go:742 fs_type=19
level=info ts=2020-11-29T06:51:43.521Z caller=main.go:745 msg="TSDB started"
level=info ts=2020-11-29T06:51:43.521Z caller=main.go:871 msg="Loading configuration file"
filename=prometheus.yml
level=info ts=2020-11-29T06:51:43.712Z caller=main.go:902 msg="Completed loading of
configuration file" filename=prometheus.yml totalDuration=190.710981ms
remote_storage=6.638μs web_handler=591ns query_engine=807ns scrape=189.731297ms
scrape_sd=42.312μs notify=322.934μs notify_sd=20.022μs rules=3.538μs
level=info ts=2020-11-29T06:51:43.712Z caller=main.go:694 msg="Server is ready to receive
web requests."

Vous pouvez accéder à l’interface utilisateur de Prometheus sur votre navigateur à l'adresse
http://localhost:9090, et consulter les différentes métriques proposées par le serveur
Prometheus à son sujet à l'adresse http://localhost:9090/metrics.
Vous pouvez désormais utiliser le navigateur d’expression pour exécuter des requêtes ou
écrire des expressions PromQL.
PromQL est le langage de requête fonctionnel de Prometheus. Il vous permet de sélectionner
et d’agréger des données de séries chronologiques en temps réel. Les données peuvent être
visualisées sur l’interface utilisateur de Prometheus au format tableau ou graphique.
Pour utiliser le navigateur d’expression, utilisez n’importe quelle métrique exposée à partir
de localhost:9090/metrics, telles que prometheus_http_requests_total (qui est un
compteur pour toutes les requêtes HTTP vers le serveur Prometheus). Cliquez ensuite sur
l’onglet graphique et saisissez la métrique dans la console d'expression, comme illustré à la
figure 6-3.

128 Chapitre 6 : Observabilité : suivre les miettes de pain


Figure 6-3. Résultat du navigateur d’expression pour prometheus_http_requests_total

Le langage de requête est très étendu et prend en charge un certain nombre de fonctions et
d’opérateurs sur les métriques. Par exemple, si vous souhaitez compter le nombre de séries
chronologiques renvoyées pour la métrique prometheus_target_interval_length_
seconds, vous pouvez émettre la commande suivante dans le navigateur d'expression :
count(prometheus_target_interval_length_seconds)

De même, si vous souhaitez déterminer la vitesse de l'augmentation d'une métrique


par seconde, vous pouvez utiliser rate dans le navigateur d’expression :
rate(prometheus_tsdb_head_samples_appended_total[1m])

node_exporter
Le node_exporter expose un tableau de métriques au niveau de l’hôte, telles que le CPU,
la mémoire, l’espace disque, les E-S et la bande passante réseau, ainsi qu’une variété de
métriques au niveau du noyau. Vous pouvez télécharger le node_exporter à partir de
https://prometheus.io/download. Une fois que vous avez téléchargé et extrait l’archive tarball,
vous pouvez exécuter le binaire directement sans apporter de modifications :
$ cd node_exporter-1.0.1.darwin-amd64
$ ./node_exporter
level=info ts=2020-12-01T13:20:29.510Z caller=node_exporter.go:177 msg="Starting
 

node_exporter" version="(version=1.0.1, branch=HEAD,


revision=3715be6ae899f2a9b9dbfd9c39f3e09a7bd4559f)"
level=info ts=2020-12-01T13:20:29.510Z caller=node_exporter.go:178 msg="Build context"
build_context="(go=go1.14.4, user=root@4c8e5c628328, date=20200616-12:52:07)"
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:105 msg="Enabled collectors"
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=boottime
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=cpu
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=diskstats
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=filesystem

Surveillance des métriques avec Prometheus dans un monde Cloud natif 129
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=loadavg
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=meminfo
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=netdev
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=textfile
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=time
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:112 collector=uname
level=info ts=2020-12-01T13:20:29.511Z caller=node_exporter.go:191 msg="Listening on"
address=:9100
level=info ts=2020-12-01T13:20:29.511Z caller=tls_config.go:170 msg="TLS is disabled and it
cannot be enabled on the fly." http2=false

Le node_exporter s’exécute à l'adresse http://localhost:9100 par défaut. Le fichier scrape_


configs dans prometheus.yml doit être mis à jour avec un peu plus d’informations afin qu’il
puisse surveiller le node_exporter en récupérant les données de celui-ci :
scrape_configs:
- job_name: node
static_configs:
- targets:
- localhost:9100

Une fois que vous avez redémarré Prometheus afin d'appliquer la nouvelle modification,
Prometheus aura deux cibles de point de terminaison pour récupérer les données de métriques.
Vous pouvez également consulter le point de terminaison http://localhost:9090/targets.
Imaginons maintenant que vous deviez déterminer combien de secondes chaque CPU consacre
à la réalisation de différents types de tâches (par exemple, utilisateur, système, iowait, etc.).
Pour ce faire, vous pouvez exécuter la requête suivante dans le navigateur d’expression, à l’aide
de laXfonction irate  :
irate(node_cpu_seconds_total{job="node"}[5m])

Le job="node" est l’étiquette qui a été mise en correspondance et qui filtre les métriques.
Examinons maintenant le fonctionnement de l’instrumentation des applications avec
Prometheus.

Instrumentation des applications


Pour surveiller les services personnalisés, nous devons ajouter l'instrumentation, qui
implique l’ajout d’une bibliothèque de traçage au code de l’application pour nous permettre
de mettre en œuvre les types de métriques de Prometheus. Cette section décrit la procédure
à suivre pour y parvenir à l’aide des bibliothèques client, que vous pouvez utiliser pour
instrumenter votre code d’application. La bibliothèque client vous permet principalement
de définir et d’exposer des métriques internes à l’aide de points de terminaison HTTP sur
votre application. Lorsque Prometheus récupère votre instance d’application, la bibliothèque
client envoie l’état des métriques actuelles à celle-ci. Nous allons utiliser python comme
bibliothèque d’instrumentation, même si d'autres mises en œuvre de bibliothèques client
soient disponibles à l’adresse https://prometheus.io/docs/instrumenting/clientlibs.
Vous devez commencer par installer la bibliothèque cliente sur votre hôte à l’aide de pip
comme suit :
$ pip install prometheus_client

130 Chapitre 6 : Observabilité : suivre les miettes de pain


D’après les quatre types de métriques que nous avons mentionnés précédemment, il existe
principalement quatre solutions différentes pour instrumenter votre code. Nous les verrons
plus en détail dans les sections suivantes.

Compteurs
Comme leur nom l’indique, les compteurs sont principalement utilisés pour compter
les valeurs qui augmentent et ils peuvent être réinitialisés à zéro lors du redémarrage au
moment où l'application redémarre. Par exemple, lorsqu’une requête GET est envoyée au
serveur HTTP Python, le code suivant incrémente la valeur de compteur de 1 :
import http.server
from prometheus_client import Counter, start_http_server

http_requests = Counter('my_app_http_request','Description: Num of HTTP request')

class SampleServer(http.server.BaseHTTPRequestHandler):
def do_GET(self):
http_requests.inc()
self.send_response(200)
self.end_headers()
self.wfile.write(b"Simple Counter Example")

if __name__ == "__main__":
start_http_server(5555)
server = http.server.HTTPServer(('localhost', 5551), SampleServer)
server.serve_forever()

Vous pouvez exécuter ce bloc de code en tant que `python counter.py` dans votre
terminal, afin qu'il démarrer un serveur HTTP simple. Nous utilisons start_http_server
comme point de terminaison à partir duquel les métriques seront récupérées. Vous pouvez
accéder aux métriques à l'adresse http://localhost:5555. Pour ingérer ces métriques dans
Prometheus, vous devez à nouveau configurer le fichier scrape_configs dans le fichier
prometheus.yml comme suit :
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: node
static_configs:
- targets:
- localhost:9100
- job_name: my_application
static_configs:
- targets:
- localhost:5555

Vous pourrez accéder à la métrique à l'aide du navigateur d’expression via le taux


d’expression PromQL (my_app_http_request_total[1m]), une fois que vous aurez
redémarré Prometheus. Le compteur augmente chaque fois que vous cliquez
sur l’URL de l’application à l'adresse http://localhost: 5551. Le graphique de la
figure 6-4 illustre l'augmentation du taux de requêtes HTTP entrantes.

Surveillance des métriques avec Prometheus dans un monde Cloud natif 131
Figure 6-4. Métrique de compteur pour une application instrumentée

Comme vous pouvez le voir, il est assez facile d’instrumenter du code avec des compteurs.
Vous pouvez également utiliser des compteurs pour comptabiliser des exceptions et des
erreurs dans le code, et même avoir une valeur personnalisée pour votre compteur au lieu
de l’augmenter d’un.

Jauges
Les jauges indiquent principalement l’état actuel d’une entité et peuvent augmenter ou
diminuer. Les jauges peuvent être utilisées dans des scénarios différents. Par exemple,
supposons que vous ayez besoin de connaître le nombre de threads actifs ou le nombre
d’éléments dans une file d’attente, ou que vous souhaitez connaître le nombre d’éléments
dans un cache.
Les jauges utilisent trois méthodes principales : inc, dec, et set. L’exemple suivant illustre
comment utiliser les trois méthodes de manière indépendante pour suivre la métrique
souhaitée :
from prometheus_client import Gauge
sample_gauge_1 = Gauge('my_increment_example_requests', 'Description of increment gauge')
sample_gauge_2 = Gauge('my_decrement_example_requests', 'Description of decrement gauge')
sample_gauge_3 = Gauge('my_set_example_requests', 'Description of set gauge')

sample_gauge_1.inc() # This will increment by 1


sample_gauge_2.dec(10) # This will decrement by given value of 10
sample_gauge_3.set(48) # This will set to the given value of 48

Dans le code précédent, la méthode inc() incrémentera la valeur de 'my_incre ment_


example_requests' de 1, diminuera la valeur de 'my_decrement_example_ requests'
de 10 et définira la valeur de 'my_set_example_requests' à 48.

132 Chapitre 6 : Observabilité : suivre les miettes de pain


Résumé
Un résumé suit généralement la durée de la requête ou la taille de la réponse. Il est
généralement utilisé pour déterminer la taille et le nombre d’événements. Il utilise une
méthode d'observation qui accepte la taille de l’événement. Par exemple, dans le code
suivant, nous essayons de trouver le temps nécessaire à la réalisation d’une requête :
import http.server
import time
from prometheus_client import Summary, start_http_server

LATENCY = Summary('latency_in_seconds','Time for a request')

class SampleServer(http.server.BaseHTTPRequestHandler):
def do_GET(self):
start_time = time.time()
self.send_response(200)
self.end_headers()
self.wfile.write(b"My application with a Summary metric")
LATENCY.observe(time.time() - start_time)

if __name__ == "__main__":
start_http_server(5555)
server = http.server.HTTPServer(('localhost', 5551), SampleServer)
server.serve_forever()

Pour exécuter le code précédent, vous pouvez appliquer la même approche que celle
que nous avons adopté dans « Compteurs » à la page 131. Le point de terminaison de la
métrique à l'adresse http://localhost:5555/metrics présentera deux séries chronologiques  :
latency_in_seconds_count et latency_in_seconds_sum. Le premier représente le
nombre d’appels d'observation qui ont été effectués et le dernier correspond à la somme
des valeurs transmises à l'observation. Si vous divisez le taux de ces deux métriques
(taux[latency_in_seconds_count[1m])/taux (latency_in_seconds_sum[1m]), vous
obtiendrez la latence moyenne de la dernière minute.

Histogrammes
Les histogrammes vous permettent de suivre la taille et le nombre d’événements dans
des compartiments, tout en vous permettant de regrouper les calculs de quantiles. Les
histogrammes peuvent être utilisés afin de mesurer les durées des requêtes pour un appel
de requête HTTP spécifique.
L’instrumentation de code des histogrammes utilise également la méthode d'observation,
et vous pouvez la combiner avec le temps nécessaire pour suivre la latence, comme dans
notre exemple précédent. Le code suivant ajoute 10 secondes à la latence de la requête :
from prometheus_client import Histogram
req_latency = Histogram('request_latency_seconds', 'Description of histogram')
req_latency.observe(10) # Observe 10 (seconds in this case)

L’instrumentation est généralement appliquée au niveau du service ou aux bibliothèques


client qui sont utilisées dans les services. Il est important de noter que des limites sont

Surveillance des métriques avec Prometheus dans un monde Cloud natif 133
appliquées au niveau d’instrumentation que vous pouvez/devez ajouter, même si Prometheus
permet de gérer efficacement plusieurs métriques.1
Maintenant que vous comprenez comment fonctionne l’instrumentation de code et
comment les hôtes sont ajoutés à Prometheus et collectés, voyons comment ajouter des
hôtes à Prometheus afin qu'ils soient collectés dans un environnement de production.

Recherche d'hôtes
Prometheus utilise le fichier static_configs sous scrape_configs dans le fichier prometheus.yml
pour trouver des cibles d’hôte à récupérer. Les environnements Cloud sont de nature très
dynamique et ils évoluent souvent, en particulier dans les environnements en conteneurs.
Dans ces environnements, l’ajout manuel d’hôtes au fichier prometheus.yml ne permettra
pas une mise à l'échelle efficace. Prometheus offre une grande variété de moyens pour gérer
ces situations en prenant en charge de nombreuses sources, y compris Kubernetes, Azure et
des plateformes de découverte de service personnalisées. La découverte de services permet
de facilement trouver tous les services exécutés dans un environnement Cloud distribué.
Nous aborderons en détail la découverte de services au chapitre 7. Ici, nous allons étudier
les différentes façons nous permettant de rechercher les hôtes en production qui seront
récupérés par Prometheus.

Utiliser Ansible
Si les hôtes exécutent des exportateurs de nœuds, l’un des moyens les plus simples de trouver
les hôtes consiste à effectuer une mise à jour statique des hôtes d’inventaire dans le fichier
prometheus.yml. Globalement, chaque fois que vous ajoutez une machine, vous devriez
également avoir une provision pour l’ajouter au fichier de configuration prometheus.yml.
Voici un extrait de code pour mettre à jour le fichier prometheus.yml à l’aide d’Ansible :
scrape_configs:
- job_name: {{ hostname }}
static_configs:
- targets:
- {{ hostname }}:9100

Vous pouvez également faire une boucle sur les hôtes sous n’importe quel groupe dans un
inventaire Ansible et les ajouter directement.

Utiliser des fichiers


Vous pouvez également fournir une liste de cibles qui peuvent être lues directement à partir
d’un fichier YAML ou JSON sans aucune surcharge réseau. Il s’agit là encore d’une autre
méthode statique de découverte de service dans laquelle Prometheus peut lire des hôtes
directement à partir d’un fichier statique. Par exemple, si vous avez un fichier inventory.json :

1 L’équipe de Prometheus a compilé les bonnes pratiques concernant l’utilisation de l’instrumentation à l’adresse
https://prometheus.io/docs/practices/instrumentation/#counter-vs-gauge-summary-vs-histogram.

134 Chapitre 6 : Observabilité : suivre les miettes de pain


[
{
"targets": [
"192.168.101.11:9100"
],
"labels": {
"job": "node",
"Team": "dev"
}
},
{
"targets": [
"192.168.101.155:9200"
],
"labels": {
"job": "node",
"Team": "sre"
}
}
]

À présent, vous pouvez mettre à jour le fichier prometheus.yml en utilisant file_sd_configs


sous scrape_configs comme suit :
scrape_configs:
- job_name: file
file_sd_configs:
- files:
- 'inventory.json'

Vous pouvez trouver les cibles collectées à l’adresse http://localhost:9090/service-discovery.

Utiliser azure_sd_config
Prometheus permet une prise en charge automatique pour collecter les données des machines
virtuelles Azure. Les configurations de découverte de services (SD) Azure vous permettent
d’extraire des cibles collectées à partir de machines virtuelles Azure. Vous pouvez utiliser la
découverte de services Azure en définissant les informations d’identification Azure dans le
fichier prometheus.yml comme suit :
- job_name: 'azure-nodes'
azure_sd_configs:
- subscription_id: '$SUBSCRIPTION_ID'
tenant_id: '$TENANT_ID'
client_id: '$CLIENT_ID'
client_secret: '$CLIENT_SECRET'
port: 9100
relabel_configs:
- source_labels: [__meta_azure_machine_tag_cloudnative]
regex: true.*
action: keep
- source_labels: [__meta_azure_machine_name]
target_label: web_instance
- source_labels: [__meta_azure_machine_tag_public_ip]
regex: (.+)
replacement: ${1}:9100
target_label: __address__

Surveillance des métriques avec Prometheus dans un monde Cloud natif 135
Dans cette configuration, nous utilisons également le réétiquetage, qui réécrit dynamiquement
l’ensemble d’étiquettes d’une cible avant qu’elle ne soit collectée. Le réétiquetage permet de
manipuler les métriques afin de garder votre stockage en ordre et de ne pas l’encombrer avec
des données non requises. Plusieurs réétiquetages sont appliqués à l’ensemble d’étiquettes
de chaque cible par ordre d’occurrence dans le fichier de configuration.

Examinons plus en détail les principaux éléments de relabel_configs dans l’exemple


précédent :
__meta_azure_machine_tag_cloudnative
Prometheus examine toutes les instances de machines virtuelles Azure et si l’instance
possède une balise appelée cloudnative, Prometheus conserve le service. Globalement,
cette instruction dit à Prometheus : « Si une machine virtuelle dans le compte Azure
possède une balise cloudnative: true, collecte ses informations. Sinon, ignore-la. »
__meta_azure_machine_name
Ici, Prometheus cherche machine_name et le place dans une étiquette web_instance.
__meta_azure_machine_tag_public_ip
Ici, Prometheus ajoute :9100 à l’adresse IP publique. Cela remplace également le
contenu de l’étiquette __address__.
Lorsque vous avez des machines virtuelles Azure en cours d’exécution, vous pouvez
trouver la cible découverte à l’adresse http://localhost:9090/service-discovery, ainsi que les
métadonnées extraites de la machine virtuelle.
Maintenant que nous avons vu comment fonctionne la découverte de services classiques
dans Prometheus, voyons comment Prometheus fonctionne avec Kubernetes et contribue à
pallier les lacunes en matière de surveillance pour les applications natives du cloud.

Prometheus sur Kubernetes


La prise en charge de la découverte de services Kubernetes est intégrée directement à
Prometheus et prête à l’emploi, ce qui permet d’extraire les cibles à collecter à partir de
l’API REST Kubernetes. Pour utiliser cette fonctionnalité, Prometheus doit déjà être exécuté
à l’intérieur du cluster Kubernetes. Vous pouvez utiliser des diagrammes Helm pour
configurer et déployer Prometheus facilement sur un cluster AKS.
Cinq types de rôles peuvent être configurés pour découvrir des cibles dans les clusters
Kubernetes  : nœud, service, Pod, points de terminaison et entrée. Étudions ces rôles de
découverte de services plus en détail.

Le rôle de nœud
Le rôle de nœud découvre tous les nœuds du cluster Kubernetes. Étant donné que kubelet
s’exécute sur chaque nœud d’un cluster Kubernetes, le rôle de nœud découvre les nœuds

136 Chapitre 6 : Observabilité : suivre les miettes de pain


cibles du cluster dont l’adresse par défaut est celle du port HTTP de kubelet. Vous pouvez
extraire les nœuds kubelet à l’aide de la commande Prometheus scrape_configs suivante :
scrape_configs:
- job_name: 'kubelet'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: ca.crt

Dans la configuration prometheus.yml précédente, l’étiquette est fournie sous forme de


kubelet et le rôle est défini en tant que nœud. Nous utilisons le schéma HTTPS, puisque les
métriques kubelet ne sont offertes que sur HTTPS, ce qui nécessite également un certificat
TLS (protocole de sécurité de la couche de transport).

Le rôle de service
Le rôle de service vous aide principalement à surveiller les systèmes opaques pour vérifier si
tous les services répondent. Le rôle de service trouve une cible pour chaque port de service
de chaque service. Vous pouvez collecter un objet de service comme suit :
scrape_configs:
- job_name: prometheus-pushgateway
kubernetes_sd_configs:
- role: service

Le rôle de Pod
Le rôle de Pod est responsable de la découverte de tous les Pods en cours d’exécution dans
le cluster et d’exposer les conteneurs qui y sont en cours d’exécution en tant que cibles. Il
renvoie également toutes les métadonnées liées aux Pods. Vous pouvez collecter les Pods
comme suit :
scrape_configs:
- job_name: 'pods'
kubernetes_sd_configs:
- role: pod

Le rôle de points de terminaison


Le rôle de points de terminaison permet de trouver les cibles dans les points de terminaison
de service d’un cluster Kubernetes. Pour chaque adresse de point de terminaison, une cible
est découverte par port. Vous pouvez collecter les points de terminaison comme suit :
scrape_configs:
- job_name: 'k8apiserver'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: cert.crt

Surveillance des métriques avec Prometheus dans un monde Cloud natif 137
Le rôle d’entrée
Enfin, le rôle d’entrée permet de découvrir une cible pour chaque chemin de chaque entrée.
Ce rôle est également un moyen d’effectuer une surveillance opaque de l’entrée. Vous pouvez
collecter une entrée comme suit :
scrape_configs:
- job_name: 'gateway'
kubernetes_sd_configs:
- role: ingress
scheme: https
tls_config:
ca_file: cert.crt

Maintenant que nous avons examiné les métriques en détail, voyons comment la
journalisation est mise en œuvre dans les applications natives du cloud.

La journalisation dans l’écosystème natif du cloud


Peu importe l’environnement dont il est question, les journaux constituent une information
cruciale. Ils sont principalement utilisés par les ingénieurs lors du dépannage d’un
problème ou de l’acquisition d’informations sur un système. Mais la journalisation en
général est complexe. Cette complexité est due aux différentes façons d’utiliser les journaux,
aux différences entre les formats de données et à la nécessité de s’assurer que l’application
sous-jacente responsable du stockage des journaux évolue également de façon appropriée à
mesure que le nombre de journaux augmente. Dans les environnements distribués natifs du
cloud, la journalisation devient encore plus importante. Non seulement ces environnements
possèdent de nombreux éléments mobiles, mais ils sont également plus complexes, car
désormais les systèmes sont distribués et éphémères, comme les Pods de Kubernetes. Cela
signifie que les journaux doivent être écartés et stockés de manière centralisée pour être
consultés plus tard.
Pour résoudre les problèmes de journalisation à grande échelle dans un environnement
natif du cloud, nous avons besoin d’une solution qui peut être facilement introduite dans
un environnement et s’intégrer de manière transparente. C’est là que Fluentd entre en scène.

La journalisation avec Fluentd


Fluentd est un projet en code source libre de CNCF qui permet de collecter, d’analyser, de
transformer et d’analyser les journaux. Fluentd agit essentiellement comme une couche de
journalisation unifiée en structurant les données au format JSON (voir la figure 6-5). Ceci
l’aide ensuite à collecter, filtrer, mettre en mémoire tampon et produire les journaux sur
plusieurs sources et destinations.
Fluentd est écrit en CRuby, une implémentation C du langage de programmation Ruby qui
occupe peu d’espace mémoire (30 à 40 Mo) et traite environ 13 000 événements par seconde
et par cœur. Voyons certains des concepts de base de Fluentd et comment ce dernier peut
être utilisé pour permettre la journalisation dans les environnements natifs du cloud.

138 Chapitre 6 : Observabilité : suivre les miettes de pain


Figure 6-5. Fluentd agissant en tant que couche de journalisation

Pour commencer, installez l’agent, en fonction de la distribution de votre système d’exploitation,


à partir du site web officiel de Fluentd.

Le paquet de distribution Fluentd stable officiel est appelé td-agent. Il


assure la stabilité et est préconfiguré avec les paramètres recommandés
par l’auteur original, Treasure Data Inc.

Une fois que vous avez téléchargé et installé td-agent pour votre distribution, assurez-vous
qu’il est opérationnel. Par exemple, sur macOS, vous pouvez le faire avec launchctl :
$ ~ sudo launchctl load /Library/LaunchDaemons/td-agent.plist
Password:
$

Vous pouvez confirmer que l’agent s’exécute en consultant les journaux qui se trouvent à
l’emplacement suivant.
$ ~ tail -f /var/log/td-agent/td-agent.log
2020-12-17 22:57:46 +0530 [info]: adding match pattern="td.*.*" type="tdlog"
2020-12-17 22:57:46 +0530 [warn]: #0 [output_td] secondary type should be same with primary
one primary="Fluent::Plugin::TreasureDataLogOutput" secondary="Fluent::Plugin::FileOutput"
2020-12-17 22:57:46 +0530 [info]: adding match pattern="debug.**" type="stdout"
2020-12-17 22:57:46 +0530 [info]: adding source type="forward"
2020-12-17 22:57:46 +0530 [info]: adding source type="http"
2020-12-17 22:57:46 +0530 [info]: adding source type="debug_agent"
2020-12-17 22:57:46 +0530 [info]: #0 starting fluentd worker pid=96320 ppid=95841 worker=0
2020-12-17 22:57:47 +0530 [info]: #0 [input_debug_agent] listening dRuby
uri="druby://127.0.0.1:24230" object="Fluent::Engine"
2020-12-17 22:57:47 +0530 [info]: #0 [input_forward] listening port port=24224
bind="0.0.0.0"
2020-12-17 22:57:47 +0530 [info]: #0 fluentd worker is now running worker=0

La journalisation dans l’écosystème natif du cloud 139


Le fichier de configuration aide à mieux comprendre Fluentd. Vous pouvez trouver le fichier
de configuration par défaut pour Fluentd à l’adresse /etc/td-agent/td-agent.conf. Le fichier
de configuration contient un certain nombre d’instructions, comme indiqué dans les sous-
sections suivantes.

Instruction source (où toutes les données sont assimilées)


L’instruction source détermine la source d’entrée de données qui doit être activée (c’est-à-
dire l’emplacement à partir duquel les données doivent être ingérées). L’instruction source
envoie un événement au système de routage de Fluentd. Par défaut, Fluentd propose deux
modules d’extension d’entrée standard. Le module d’extension http fournit un point
de terminaison HTTP pour écouter les messages HTTP entrants, tandis que le module
d’extension forward fournit un point de terminaison TCP pour accepter des paquets TCP :
<source>
@type forward
port 24224
</source>

<source>
@type http
port 9999
bind 0.0.0.0
</source>

La définition précédente définit deux sources  : dans la première, nous recevons des
événements du port TCP 24224, alors que dans la deuxième, nous avons un serveur HTTP
à l’écoute sur le port TCP 9999.
L’événement soumis par la source au moteur de routage de Fluentd contient trois entités :
balise, heure et enregistrement. Par exemple, dans la sortie de journal suivante :
2020-12-16 15:38:27 +0900 test.root: {"action":"logout","user":3}

2020-12-16 15:38:27 +0900 est l’heure, qui est déterminée par les modules d’extension
d’entrée et doit être au format d’heure Unix. test.root est la balise (selon Fluentd, il est
fortement recommandé que l’étiquette soit en minuscules et comporte un mélange de chiffres
et de traits de soulignement). Enfin, {"action":"logout","user":3} est un objet JSON.
Les modules d’extension d’entrée sont responsables de la récupération des journaux
d’événements à partir de sources externes. En règle générale, un module d’extension d’entrée
crée un thread, un socket et un socket d’écoute. Les plug-ins d’entrée ne sont pas seulement
limités à http et à forward. Voici quelques plug-ins supplémentaires :
in_syslog
Pour récupérer des événements de journal au moyen du protocole de Syslog sur UDP
ou TCP. Fluentd crée un socket sur le port 5140. Ensuite, vous n’avez plus qu’à installer
votre démon Syslog pour envoyer des événements de journal sur ce socket :
<source>
@type syslog
port 5140
bind 0.0.0.0

140 Chapitre 6 : Observabilité : suivre les miettes de pain


tag system
</source>

in_tail
Permet à Fluentd de lire les événements à partir de la fin des fichiers journaux, tout
comme la commande Unix tail -f  :
<source>
@type tail
path /var/log/httpd-access.log
pos_file /var/log/td-agent/httpd-access.log.pos
tag apache.access
<parse>
@type apache2
</parse>
</source>

Fluentd offre des plug-ins d’entrée supplémentaires, comme in_unix pour récupérer les
enregistrements dans un socket de domaine Unix et in_exec pour exécuter les programmes
externes et extraire les journaux d’événements. Vous pouvez même rédiger vos propres
plug-ins d’entrée personnalisés.

Instruction match (que faire avec les données d’entrée)


L’instruction match détermine la destination de sortie en recherchant les événements ayant
des balises correspondantes. Les plug-ins de sortie standard pour Fluentd comprennent file
et forward. L’instruction match doit inclure un modèle de correspondance et un paramètre
@type qui définit le plug-in de sortie à utiliser. Lorsque l’une des balises d’un événement
correspond au modèle, l’événement est envoyé à la destination de sortie. Voici un exemple
d’utilisation de l’instruction match :
<match mywebapp.access>
@type file
path /var/log/mywebapp/access
</match>

Dans la configuration précédente, nous faisons correspondre les événements balisés avec
mywebapp.access et nous les stockons dans file à l’adresse /var/log/mywebapp/access.
En plus de file et forward, vous pouvez avoir d’autres types de plug-ins de sortie qui
déterminent essentiellement où vous souhaitez écrire les données. Par exemple, out_
elasticsearch est un plug-in de sortie qui écrit les enregistrements sur un point de
terminaison de cluster Elasticsearch. Vous devrez d’abord installer le plug-in en utilisant :
fluent-gem install fluent-plugin-elasticsearch

Ensuite, vous pouvez utiliser la configuration suivante pour envoyer les données de sortie
à un point de terminaison Elasticsearch :
<match my.logs>
@type elasticsearch
host 192.168.23.13
port 9200
logstash_format true
</match>

La journalisation dans l’écosystème natif du cloud 141


De même, vous pouvez utiliser out_copy pour copier des événements dans plusieurs
sorties, out_kafka2 pour envoyer des enregistrements à un cluster Kafka, out_mongo pour
écrire des enregistrements dans une base de données MongoDB et stdout pour simplement
écrire des événements dans une sortie standard.

Instruction filter (le pipeline de traitement des événements)


L’instruction filter se comporte comme une règle qui approuve ou rejette un événement
basé sur une condition. filter a la même syntaxe que match, mais filter peut être liée à un
pipeline de traitement. Vous pouvez ajouter une instruction filter comme suit :
<source>
@type http
port 9999
bind 0.0.0.0
</source>

<filter test.session>
@type grep
<exclude>
key action
pattern ^logout_session$
</exclude>
</filter>

<match test.session>
@type stdout
</match>

Dans la configuration précédente, une fois que les données sont sourcées, elles passent par
la section de filter, puis par celle de match. L’instruction filter acceptera ou rejettera
l’événement en fonction de son type et de sa règle. Ici, nous avons rejeté l’action logout_
session et utilisé le type grep à l’intérieur de filter pour exclure tout message sur lequel
la clé action a la chaîne logout.
Voici quelques plug-ins filter supplémentaires :
filter_record_transformer
Permet de modifier un flux d’événements entrant. Vous pouvez utiliser l’exemple de
configuration suivant pour record_transformer :
<filter myweb.access>
@type record_transformer
<record>
host_param "#{Socket.gethostname}"
</record>
</filter>

Dans la configuration précédente, record_transformer ajoutera le champ host_


param à l’événement de journal.

142 Chapitre 6 : Observabilité : suivre les miettes de pain


filter_geoip
Ajoute des informations sur l’emplacement géographique aux journaux à l’aide de la
base de données GeoIP de MaxMind.
filter_stdout
Imprime les événements dans une sortie standard et est principalement utilisé à des
fins de débogage.

Instruction system (définit la configuration à l’échelle du système)


Vous pouvez définir diverses configurations à l’échelle du système à l’aide de l’instruction
system. Voici quelques-unes des options de configuration offertes dans l’instruction system :

• log_level
• suppress_repeated_stacktrace
• emit_error_log_interval
• suppress_config_dump
• without_source
• process_name

Vous pouvez utiliser la configuration system comme suit :


<system>
process_name my_app
log_level error
without_source
</system>

Ici, nous définissons d’abord les noms de processus de superviseur et de travailleur sur
my_app, puis nous définissons le niveau de journal par défaut sur error. Nous demandons
également à Fluentd de démarrer sans aucun plug-in d’entrée.

Instruction label (pour le regroupement et l’acheminement des sorties)


Les étiquettes permettent principalement de résoudre le problème de la tenue et de la gestion
du fichier de configuration en autorisant les sections de routage qui ne suivent pas l’ordre
d’analyse de haut en bas dans le fichier de configuration. Vous pouvez également considérer
les étiquettes comme des déclarations de référence. Voici un exemple de configuration
simple pour utiliser les étiquettes :
<source>
@type http
bind 0.0.0.0
port 9999
@label @MYCLUE
</source>

<filter test.session>
@type grep
<exclude>

La journalisation dans l’écosystème natif du cloud 143


key action
pattern ^login$
</exclude>
</filter>

<label @MYCLUE>
<filter test.session>
@type grep
<exclude>
key action
pattern ^logout$
</exclude>
</filter>

<match test.session>
@type stdout
</match>
</label>

Dans la configuration précédente, le paramètre @label sous source est la redirection vers
la section de l’étiquette @MYCLUE. Le contrôle ignorera la définition filter pour login et
passera directement à l’étiquette @MYCLUE.

Instruction @include (pour importer des fichiers de configuration)


À l’aide de l’instruction @include, vous pouvez importer des fichiers de configuration à
partir d’un répertoire distinct. L’instruction @include prend en charge les conventions
standard pour le chemin de fichier, le modèle glob et l’URL HTTP comme suit :
# absolute path
@include /path/to/config.conf

# if using a relative path, the directive will use


# the dirname of this config file to expand the path
@include extra.conf

# glob match pattern


@include config.d/*.conf

# http
@include http://example.com/fluent.conf

Avant de poursuivre, voyons brièvement comment exécuter l’agent pour voir vos
journaux. Pour exécuter une configuration simple comme la suivante :
<source>
@type http
port 9999
bind 0.0.0.0
</source>

<filter myapp.test>
@type grep
<exclude>
key action
pattern ^logout$
</exclude>

144 Chapitre 6 : Observabilité : suivre les miettes de pain


</filter>

<match myapp.test>
@type stdout
</mat ch>

vous remplaceriez la configuration précédente à l’emplacement par défaut /etc/td-agent/


td-agent.conf. Vous pouvez maintenant recharger l’agent td-agent dans macOS comme suit :
$ sudo launchctl load /Library/LaunchDaemons/td-agent.plist
#check logs
$ tail -f /var/log/td-agent/td-agent.log
2020-12-21 12:21:57 +0530 [info]: parsing config file is succeeded \
path="/etc/td-agent/td-agent.conf"
2020-12-21 12:21:58 +0530 [info]: using configuration file: <ROOT>
<source>
@type http
port 9999
bind "0.0.0.0"
</source>
<filter myapp.test>
@type grep
<exclude>
key "action"
pattern ^logout$
</exclude>
</filter>
<match myapp.test>
@type stdout
</match>
</ROOT>
2020-12-21 12:21:58 +0530 [info]: starting fluentd-1.0.2 pid=43215 ruby="2.4.2"
2020-12-21 12:21:58 +0530 [info]: spawn command to main: \
cmdline=["/opt/td-agent/embedded/bin/ruby", "-Eascii-8bit:ascii-8bit", \
"/opt/td-agent/usr/sbin/td-agent", "--log", "/var/log/td-agent/td-agent.log", \
"--use-v1-config", "--under-supervisor"]
2020-12-21 12:21:58 +0530 [info]: gem 'fluent-plugin-elasticsearch' version '2.4.0'
2020-12-21 12:21:58 +0530 [info]: gem 'fluent-plugin-kafka' version '0.6.5'
2020-12-21 12:21:58 +0530 [info]: gem 'fluent-plugin-rewrite-tag-filter' version '2.0.1'
2020-12-21 12:21:58 +0530 [info]: gem 'fluent-plugin-s3' version '1.1.0'
2020-12-21 12:21:58 +0530 [info]: gem 'fluent-plugin-td' version '1.0.0'
2020-12-21 12:21:58 +0530 [info]: gem 'fluent-plugin-td-monitoring' version '0.2.3'
2020-12-21 12:21:58 +0530 [info]: gem 'fluent-plugin-webhdfs' version '1.2.2'
2020-12-21 12:21:58 +0530 [info]: gem 'fluentd' version '1.0.2'
2020-12-21 12:21:58 +0530 [info]: adding filter pattern="myapp.test" type="grep"
2020-12-21 12:21:58 +0530 [info]: adding match pattern="myapp.test" type="stdout"
2020-12-21 12:21:58 +0530 [info]: adding source type="http"
2020-12-21 12:21:58 +0530 [info]: #0 starting fluentd worker pid=43221 ppid=43215 worker=0
2020-12-21 12:21:58 +0530 [info]: #0 fluentd worker is now running worker=0

Vous pouvez maintenant exécuter les commandes curl suivantes pour simuler différentes
actions :
$ curl -i -X POST -d 'json={"action":"login","user":2}' http://localhost:9999/myapp.test

HTTP/1.1 200 OK
Content-Type: text/plain
Connection: Keep-Alive
Content-Length: 0

La journalisation dans l’écosystème natif du cloud 145


Puisque nous passons actuellement l’action en tant que login, nous pouvons vérifier les
journaux à l’emplacement pour confirmer qu’ils ont été imprimés :
2020-12-21 12:31:31.529967000 +0530 myapp.test: {"action":"login","user":2}

Si vous essayez de publier une action en tant que logout dans la commande curl suivante,
elle sera supprimée en fonction de la règle de filtre que nous avons déterminée et vous ne
pourrez voir aucun journal en cours d’impression :
$ curl -i -X POST -d 'json={"action":"logout","user":2}' http://localhost:9999/myapp.test
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: Keep-Alive
Content-Length: 0

Maintenant que vous comprenez comment fonctionne Fluentd, examinons comment


l’intégrer dans des environnements natifs du cloud et le déployer sur des orchestrateurs de
conteneurs comme Kubernetes.

Fluentd sur Kubernetes


Par défaut, dans Kubernetes, les journaux des conteneurs sont écrits dans la sortie standard
et le flux d’erreurs standard. Il s’agit de la journalisation d’E/S de base, que Kubernetes offre
systématiquement par défaut. Le moteur de conteneur redirige les flux vers un pilote de
journalisation configuré dans Kubernetes pour écrire les journaux dans un fichier au format
JSON. En tant qu’utilisateur, si vous souhaitez voir les journaux, vous pouvez utiliser la
commande kubectl logs. Cette approche présente cependant un problème évident : si
un conteneur se bloque, si un Pod est expulsé ou si le nœud lui-même expire, vous ne
serez plus en mesure d’accéder aux journaux d’application. Par conséquent, les journaux
doivent avoir un stockage indépendant des conteneurs, des Pods et des nœuds. Ce concept
est officiellement appelé journalisation au niveau du cluster et il nécessite un système dorsal
distinct pour stocker, analyser et interroger les journaux. Bien que Kubernetes ne fournisse
pas de solution native pour la journalisation au niveau du cluster, nous pouvons combiner
Fluentd avec une solution de stockage comme Elasticsearch pour créer une solution
évolutive.
Avant de passer à la méthode préférée de déploiement de Fluentd sur Kubernetes, examinons
les différentes approches par lesquelles la journalisation au niveau du cluster peut être mise
en œuvre, ainsi que leurs avantages et inconvénients. Il existe globalement deux façons de
créer une solution de journalisation au niveau du cluster :

• Utiliser un conteneur de sidecar dans chaque Pod d’application.


• Utiliser un agent de journalisation au niveau du nœud qui s’exécute sur chaque nœud.

Étudions chaque solution en détail.

146 Chapitre 6 : Observabilité : suivre les miettes de pain


Approche du conteneur sidecar
En règle générale, dans Kubernetes, vous exécutez vos applications dans un Pod, comme
décrit au chapitre 5. Dans le cadre de l’approche sidecar, vous pouvez créer un ou plusieurs
conteneurs sidecar à l’intérieur du Pod d’application. L’approche du conteneur sidecar peut
être utilisée de deux manières différentes :

• Vous pouvez utiliser le conteneur sidecar pour diffuser les journaux d’application vers
le stdout du conteneur.
• Vous pouvez exécuter un agent de journalisation à l’intérieur d’un conteneur sidecar,
qui sélectionne essentiellement les journaux des conteneurs d’application et les expédie
à un back-end de journalisation.

Voici certains avantages de l’approche du conteneur sidecar :

• Vous pouvez expédier des journaux d’application produits dans différents formats de
journal (structurés ou non structurés) en ayant deux conteneurs sidecar pour diffuser
un fichier journal particulier vers différents flux de journaux.
• Les conteneurs sidecar peuvent lire les journaux à partir de fichiers, de sockets ou de
journaux, puis chaque conteneur sidecar imprimera le journal dans les flux stdout et stderr.
• Les conteneurs sidecar peuvent également être utilisés pour effectuer la rotation des
fichiers journaux lorsque l’application elle-même ne peut pas le faire.

Il existe également des inconvénients à l’exécution d’un conteneur sidecar :

• Le disque d’E/S peut augmenter de manière considérable dans un scénario où vous
écrivez les journaux dans un fichier, puis où vous les diffusez sur stdout.
• Étant donné que plusieurs Pods exécutent votre application, vous devrez également
déployer plusieurs conteneurs sidecar dans chaque Pod.

Approche de l’agent de journalisation au niveau du nœud


Une autre façon de gérer la journalisation dans Kubernetes consiste à déployer un agent
de journalisation sur chaque nœud du cluster Kubernetes. L’agent peut être exécuté en tant
que conteneur vers un démon qui peut avoir accès à tous les journaux produits par les Pods
d’application. C’est également la manière privilégiée d’exécuter les agents Fluentd.
Un DaemonSet de Kubernetes est utilisé pour mettre en œuvre une telle stratégie où chaque nœud
peut exécuter une copie d’un Pod d’agent de journalisation Fluentd. Fluentd fournit officiellement
un DaemonSet avec les règles adéquates à déployer sur les clusters Kubernetes. Vous pouvez
récupérer une copie du DaemonSet par clonage à partir du référentiel Fluentd officiel :
$ git clone https://github.com/fluent/fluentd-kubernetes-daemonset

Avant de commencer à utiliser le DaemonSet mentionné ci-dessus, vous avez besoin


d’un cluster Kubernetes.  Si vous exécutez déjà un cluster Azure Kubernetes (comme

La journalisation dans l’écosystème natif du cloud 147


Azure Kubernetes Service [AKS]), vous pouvez commencer par déployer un back-end
de journalisation pour stocker les fichiers journaux. Elasticsearch est l’un des back-ends
de journalisation qui peut être utilisé pour stocker les journaux générés. Vous pouvez
déployer Elasticsearch à l’aide de Helm sur Kubernetes comme suit :
$ helm repo add elastic https://helm.elastic.co
$ helm install elasticsearch elastic/elasticsearch

Vous pouvez utiliser le transfert de port pour vérifier que le cluster Elasticsearch a été
déployé avec succès :
$ kubectl port-forward svc/elasticsearch-master 9200
Forwarding from 127.0.0.1:9200 -> 9200
Forwarding from [::1]:9200 -> 9200
Handling connection for 9200
Handling connection for 9200

Elasticsearch devrait être disponible sur http://localhost:9200. Pour déployer le DaemonSet de


Fluentd, vous devez d’abord modifier les variables d’environnement suivantes dans le fichier
fluentd-daemonset-elasticsearch-rbac.yaml :

• FLUENT_ELASTICSEARCH_HOST (le point de terminaison d’Elasticsearch)


• FLUENT_ELASTICSEARCH_PORT (le port d’Elasticsearch, habituellement 9200)

Une fois que vous avez mis à jour le fichier YAML avec les détails d’Elasticsearch, vous
pouvez déployer le DaemonSet de Fluentd sur AKS en passant au référentiel GitHub et en
appliquant le DaemonSet comme suit :
$ kubectl apply -f fluentd-daemonset-elasticsearch-rbac.yaml

Cela permettra de déployer le DaemonSet de Fluentd sur le cluster Kubernetes. Désormais,


chaque nœud aura au moins un Pod Fluentd en cours d’exécution qui transférera vos
journaux à Elasticsearch :

$ ~ kubectl get pods --namespace=kube-system -o wide


NAME READY STATUS RESTARTS AGE IP
NODE NOMINATED NODE READINESS GATES
fluentd-6pttr 1/1 Running 2 3h37m 10.240.0.27
aks-agentpool-25245360-vmss000000 <none> <none>
fluentd-9fj59 1/1 Running 1 3h37m 10.240.0.147
aks-agentpool-25245360-vmss000001 <none> <none>
fluentd-dh7pn 1/1 Running 1 139m 10.240.1.84
aks-agentpool-25245360-vmss000003 <none> <none>
fluentd-g5cnv 1/1 Running 2 3h37m 10.240.1.25
aks-agentpool-25245360-vmss000002 <none> <none>
fluentd-mzw2j 1/1 Running 2 139m 10.240.2.0
aks-agentpool-25245360-vmss000004 <none> <none>
fluentd-z52r2 1/1 Running 2 139m 10.240.2.143
aks-agentpool-25245360-vmss000005 <none> <none>
kube-proxy-56bv2 1/1 Running 0 139m 10.240.2.47
aks-agentpool-25245360-vmss000005 <none> <none>

148 Chapitre 6 : Observabilité : suivre les miettes de pain


Vous devez également afficher ces journaux, ce qu’il est possible de faire en utilisant Kibana.2
Vous pouvez déployer Kibana de la même manière avec l’aide de Helm et le transfert de port
localement comme suit :
$ helm repo add elastic https://helm.elastic.co
$ helm install kibana elastic/kibana
$ kubectl port-forward deployment/kibana-kibana 5601
Forwarding from 127.0.0.1:5601 -> 5601
Forwarding from [::1]:5601 -> 5601

Kibana (figure 6-6) devrait maintenant être opérationnel sur votre http://localhost:5601.

Figure 6-6. Présentation des journaux avec Kibana

Un autre nouvel outil d’agrégation de journaux est Loki de Grafana Labs. Loki enregistre
efficacement des étiquettes de journal déterminées au lieu d’un flux de journaux entier et
peut facilement s’intégrer à Fluentd. Nous n’étudierons pas ce sujet en détail, car il s’étend
au-delà de la portée de ce livre.
À présent que vous comprenez comment fonctionne la journalisation dans les
environnements natifs du cloud, concluons le chapitre par le suivi distribué.

2 Kibana est une interface utilisateur en code source libre qui est généralement utilisée pour visualiser les
données (journaux). Vous trouverez des informations supplémentaires sur https://elastic.co/kibana.

La journalisation dans l’écosystème natif du cloud 149


Suivi distribué dans l’écosystème natif du cloud natif
Le suivi distribué est l’un des piliers essentiels pour obtenir et renforcer l’observabilité dans
les applications modernes natives du cloud. Le suivi distribué vous permet de suivre le
flux de demandes dans un système distribué lorsqu’elles passent par plusieurs services et
émettent des informations sous forme de métadonnées, telles que le temps passé à chaque
service et les détails d’appels sous-jacents à d’autres points de terminaison de service. Ces
métadonnées peuvent être réassemblées ultérieurement pour brosser un tableau plus
complet du comportement de l’application au moment de l’exécution, ce qui peut vous aider
à résoudre des problèmes tels que le repérage des problèmes de latence de l’application et la
localisation des goulots d’étranglement.
Le suivi distribué répond à des questions importantes, telles que :

• Quels sont les goulots d’étranglement dans un service ?


• Combien de temps passe-t-on à chaque étape ou service d’un système distribué ?
• Quels sont les services en amont et en aval et points de terminaison concernés par une
demande ?

Pour mieux comprendre les principes fondamentaux du suivi distribué, jetez un coup d’œil
à l’application web simple illustrée à la figure 6-7.

Figure 6-7. Une application web simple présentant la propagation des requêtes via différents
niveaux de services

Prenons la figure 6-7 et supposons que vous disposiez d’un exemple de service qui prévoit des
ressources sur un cloud d’après le plan de l’utilisateur. Le flux normal d’exécution commence
par le client faisant une demande à l’équilibreur de charge. Celui-ci communique avec le
service d’authentification et le service de facturation, puis alloue des ressources au client
en effectuant un appel final au service d’allocation de ressources. Sur la figure  6-8, vous
pouvez voir les identifiants de demande uniques propagés dans le flux, ce qui aide ensuite à
regrouper les traces.

150 Chapitre 6 : Observabilité : suivre les miettes de pain


Ce flux de transactions peut être affiché à l’aide d’un diagramme de Gantt
(voir la figure 6-8).

Figure 6-8. Périodes et traces dans une application web simple

Suivi : concepts clés


Le diagramme de Gantt de la figure 6-8 résume les concepts clés importants suivants d’un
système de suivi distribué.

Périodes
Une période représente un appel opérationnel simple et constitue le bloc de construction
principal d’un suivi distribué, qui représente quant à lui une unité individuelle de travail
effectuée dans un système distribué. Les périodes contiennent des références à d’autres
périodes, ce qui permet à plusieurs périodes d’être regroupées dans une seule trace
complète. Par exemple, sur la figure 6-8, la période « transaction d’équilibreur de charge
du début à la fin » comprend d’autres périodes, comme l’équilibreur de charge interagissant
avec le service d’authentification ou l’équilibreur de charge interagissant avec le service de
facturation.
Les périodes sont créées sous la forme d’une relation parent-enfant dans laquelle chaque
période a un enfant, à l’exception de la racine et du service de début (figure 6-9).

Suivi distribué dans l’écosystème natif du cloud 151


Figure 6-9. Anatomie d’une période

Traces
Une trace représente le parcours complet d’une demande dans l’ensemble des services par
lesquels elle passe. Une trace complète simple est habituellement constituée de plusieurs
périodes pour la demande. Par exemple, la transaction complète du client du début à la fin
est constituée de plusieurs périodes.

Propagation du contexte
Pour chaque demande d’un service vers un service en aval, un identificateur d’exécution de
demande global est transmis en tant que métadonnées, lequel demeure unique pour chaque
demande. Cet identificateur d’exécution de demande global est utilisé ultérieurement pour
reconstruire l’exécution complète de la demande en regroupant les enregistrements sur la base
de l’identificateur de demande. Le processus de transmission de l’identificateur d’exécution
en tant que métadonnées de demande est appelé propagation de contexte distribué.
Les métadonnées qui sont propagées dans l’ensemble des périodes comprennent également
l’identifiant de trace et l’identifiant de période en plus de deux autres détails : les balises et
les journaux. Les balises sont des paires clé-valeur qui peuvent être ajoutées aux périodes
pour fournir des informations supplémentaires, telles que les codes d’erreur et les détails
de l’hôte. Les journaux sont également des paires clé-valeur qui capturent des messages de
journalisation propres à la période et aident au débogage.

152 Chapitre 6 : Observabilité : suivre les miettes de pain


Échantillonnage
La collecte de toutes les données en mémoire, puis leur envoi à un back-end de suivi,
peuvent avoir un impact grave sur le réseau, la latence des applications et le coût. Pour éviter
ce problème, le système de traçage applique l’échantillonnage. L’échantillonnage désigne le
processus de collecte et de stockage d’un sous-ensemble de données de trace pour éviter les
coûts de stockage et les frais généraux de performance. Il existe divers types de méthodes
d’échantillonnage, principalement classés comme des échantillonnages à la tête ou à la
queue. Par exemple, si vous souhaitez collecter x nombre de traces par minute, vous pouvez
utiliser un type d’échantillonnage appelé limitation de débit. De même, si vous souhaitez
échantillonner des traces lorsqu’un certain type d’erreur devient important, vous pouvez
utiliser un échantillonnage contextuel.

Architecture générale du système de suivi et assemblage du suivi


Pour mettre en œuvre un système de suivi dans un environnement natif du cloud, vous
pouvez utiliser les bibliothèques en code source libre déjà existantes (nous en discuterons
plus en détail dans la section suivante), ce qui vous aidera à instrumenter votre application
sans pour autant réinventer la roue. En général, la plupart des systèmes de suivi ont une
architecture simple, comme illustré à la figure 6-10.

Figure 6-10. Architecture générale d’un système de suivi distribué

Pour utiliser le suivi distribué, vous devez d’abord instrumenter le code de votre application
à l’aide d’une bibliothèque cliente. Les bibliothèques clientes sont offertes dans de nombreux
langages, notamment Java, Go et Python. La bibliothèque envoie les données à un agent
qui se trouve généralement sur la même instance ou le même hôte. L’agent est responsable
du transfert des suivis vers le back-end (par exemple, vers la couche de stockage au moyen
d’une couche mise en mémoire tampon en mode asynchrone). Par la suite, les suivis sont
reconstruits et affichés dans l’interface utilisateur. Ils peuvent également être utilisés pour
effectuer d’autres analyses et agrégations à l’aide de techniques d’exploration de données.

Suivi distribué dans l’écosystème natif du cloud 153


Les suivis sont assemblés en examinant ce qui suit dans une demande :
Période de demande entrante
Lorsqu’une demande entre dans un service, le système de suivi vérifie si un en-tête de
suivi est associé à la demande. Si aucun en-tête de suivi n’est joint, une période racine
est créée. Dans le cas contraire, une période enfant doit être créée.
Période de demande sortante
Si une demande sort d’un service pour entrer dans un autre, une période est créée
d’abord, puis le service de réception continue le suivi tel que décrit dans la période de
demande entrante.
Il existe plusieurs manières de mettre en œuvre le suivi distribué dans les environnements
cloud. Nous en verrons quelques-unes dans la prochaine section.

Normes, outils et instrumentation de code de suivi


Au fil des ans, le suivi distribué a évolué et est devenu un moyen standard de profiler et de
surveiller les applications dans les environnements natifs du cloud. En 2016, OpenTracing
est devenu un projet de la CNCF dont l’unique objectif était de fournir des spécifications
indépendantes des fournisseurs pour le suivi distribué. Pendant ce temps, Google travaillait
sur un projet de communauté en code source libre appelé OpenCensus, qui a ensuite été
dirigé par Microsoft.
OpenTracing et OpenCensus étaient deux cadres concurrents. Bien que leurs architectures
aient été différentes, ils résolvaient fondamentalement le même problème. L’existence de
deux normes a entraîné quelque peu d’incertitude en ce qui concerne le soutien et les
contributions, d’où une faible adoption à grande échelle dans le secteur. En mai 2019, la
CNCF a annoncé la fusion d’OpenTracing et d’OpenCensus en une seule norme connue sous
le nom d’OpenTelemetry. OpenTelemetry combine les avantages des normes précédentes et
est prise en charge par la communauté. Dans cette section, nous examinerons comment le
code d’application de base peut être instrumenté avec OpenTracing et OpenTelemetry.
Aujourd’hui, les deux outils de suivi les plus populaires et les plus pratiques sont Zipkin
et Jaeger. Zipkin a été le premier système de suivi développé avec l’infrastructure de suivi
de Dapper de Google. Jaeger a été développé chez Uber et est pris en charge par la CNCF.
Jaeger implémente la spécification OpenTracing et sa méthode de déploiement préconisée
est Kubernetes. Nous nous limiterons à aborder Jaeger.
Jaeger se base sur l’architecture générale d’un système de suivi puisqu’il comporte trois
composants principaux : des collecteurs, un service de requête et une interface utilisateur.
Il déploie un agent sur chaque hôte, qui regroupe essentiellement les données de suivi et les
envoie au collecteur (service de mise en mémoire tampon). Les données de suivi peuvent
ensuite être stockées dans une base de données, de préférence Elasticsearch ou Cassandra.
Voyons maintenant quelques façons simples dont nous pouvons instrumenter le code avec
différentes normes de suivi et Jaeger.

154 Chapitre 6 : Observabilité : suivre les miettes de pain


Par exemple, nous pouvons utiliser un client Python avec Jaeger :
$ pip install jaeger-client

À partir de là, nous pouvons exécuter tous les composants de Jaeger dans une seule image
Docker :
$ docker run -d -p6831:6831/udp -p16686:16686 jaegertracing/all-in-one:latest

Nous pouvons également vérifier le conteneur Docker en utilisant docker ps :


$ ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
874b2a03ebaa jaegertracing/all-in-one:latest "/go/bin/all-in-one-…" About a minute ago
STATUS PORTS
Up About a minute 5775/udp, 5778/tcp, 14250/tcp, 6832/udp, 14268/tcp,
0.0.0.0:6831->6831/udp, 0.0.0.0:16686->16686/tcp
NAMES
jolly_mclean

Vous pouvez cliquer sur http://localhost:16686 localement pour accéder à l’interface


utilisateur de Jaeger.
Examinons maintenant comment instrumenter un code d’application simple à l’aide
d’OpenTracing. Dans l’exemple suivant, nous avons un programme de réservation de films
qui dispose de quelques méthodes responsables de vérifier si un film est disponible. Si c’est
le cas, il réserve les billets :
import sys
import time
import logging
import random
from jaeger_client import Config
from opentracing_instrumentation.request_context import get_current_span, span_in_context
def initialize_tracer(service):
logging.getLogger('').handlers = []
logging.basicConfig(format='%(message)s', level=logging.DEBUG)
config = Config(
config={
'sampler': {
'type': 'const',
'param': 1,
},
'logging': True,
},
service_name=service,
)
return config.initialize_tracer()
def booking_manager(movie):
with tracer.start_span('booking') as span:
span.set_tag('Movie', movie)
with span_in_context(span):
get_cinema_details = check_movie(movie)
get_showtime_details = check_movie_showtime(get_cinema_details)
book_movie_now(get_showtime_details)
def check_movie(movie):
with tracer.start_span('CheckCinema', child_of=get_current_span()) as span:
with span_in_context(span):

Suivi distribué dans l’écosystème natif du cloud 155


num = random.randint(1,30)
time.sleep(num)
cinema_details = "Cinema Details"
flags = ['false', 'true', 'false']
random_flag = random.choice(flags)
span.set_tag('error', random_flag)
span.log_kv({'event': 'CheckCinema' , 'value': cinema_details })
return cinema_details

def check_movie_showtime( cinema_details ):


with tracer.start_span('CheckShowtime', child_of=get_current_span()) as span:
with span_in_context(span):
num = random.randint(1,30)
time.sleep(num)
showtime_details = "Showtime Details"
flags = ['false', 'true', 'false']
random_flag = random.choice(flags)
span.set_tag('error', random_flag)
span.log_kv({'event': 'CheckCinema' , 'value': showtime_details })
return showtime_details

def book_movie_now(showtime_details):
with tracer.start_span('BookShow', child_of=get_current_span()) as span:
with span_in_context(span):
num = random.randint(1,30)
time.sleep(num)
Ticket_details = "Ticket Details"
flags = ['false', 'true', 'false']
random_flag = random.choice(flags)
span.set_tag('error', random_flag)
span.log_kv({'event': 'CheckCinema' , 'value': showtime_details })

assert len(sys.argv) == 2
tracer = initialize_tracer('movie_booking')
movie = sys.argv[1]
booking_manager(movie)
time.sleep(2)
tracer.close()

Dans le code précédent, nous initialisons d’abord le traceur à l’aide de la méthode initial
ize_tracer, qui définit la configuration pour la journalisation et l’échantillonnage. La
prochaine chose à noter dans le code est la façon dont nous utilisons l’instance de traceur
pour démarrer une nouvelle période avec start_span, et child_of pour démarrer de
nouvelles périodes enfants à la période racine. Par exemple, la période racine est générée
dans la méthode booking_manager, alors que le reste des méthodes (par exemple, check_
movie_showtime et check_movie) sont principalement des périodes enfants de la période
racine.
Nous définissons également des balises et des journaux dans les méthodes. Par exemple :
-> span.set_tag('error', random_flag)

-> span.log_kv({'event': 'CheckCinema' , 'value': showtime_details })

Vous pouvez maintenant simplement exécuter ce programme dans votre console Python en
transmettant un titre de film composé comme argument, comme suit :

156 Chapitre 6 : Observabilité : suivre les miettes de pain


$ python jaeger_opentracing.py godfather
Initializing Jaeger Tracer with UDP reporter
Using sampler ConstSampler(True)
opentracing.tracer initialized to <jaeger_client.tracer.Tracer object at 0x10eeae410> \
[app_name=movie_booking]
Reporting span f99b147babd58321:5ca566beb40e89b0:931be7dc309045fd:1 movie_booking. \
CheckCinema
Reporting span f99b147babd58321:1ad5d00d2acbd02:931be7dc309045fd:1 movie_booking. \
CheckShowtime
Reporting span f99b147babd58321:f36b9959f34f61dc:931be7dc309045fd:1 movie_booking.BookShow
Reporting span f99b147babd58321:931be7dc309045fd:0:1 movie_booking.booking

Vous pouvez maintenant accéder à l’interface utilisateur Jaeger et rechercher movie_


booking dans le menu déroulant du service. Vous pouvez également afficher les suivis en
cliquant sur le bouton Rechercher les suivis, comme illustré à la figure 6-11.

Figure 6-11. Suivis pour le service de réservation de films

Vous pouvez voir les quatre périodes à la figure 6-11, movie_booking étant la période et
les autres, les périodes enfants. Étant donné qu’il s’agit d’un exemple de programme, à des
fins de démonstration, nous avons généré de façon aléatoire des erreurs dans le code qui
sont propagées. En production, ces erreurs indiquent des problèmes qui surviennent dans
l’environnement (par exemple, la temporisation de Redis en raison d’un problème d’E/S).
Nous pouvons également instrumenter notre code à l’aide d’OpenTelemetry, car
OpenTracing est à présent en cours de suppression. Tout d’abord, nous devons installer
l’API OpenTelemetry et le kit de développement logiciel :
pip install opentelemetry-api
pip install opentelemetry-sdk

Pour voir comment fonctionne OpenTelemetry, examinez le code python suivant :


from opentelemetry import trace
from opentelemetry.exporter import jaeger
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchExportSpanProcessor

trace.set_tracer_provider(TracerProvider())

jaeger_exporter = jaeger.JaegerSpanExporter(
service_name="my-helloworld-service",
agent_host_name="localhost",
agent_port=6831,
)

trace.get_tracer_provider().add_span_processor(
BatchExportSpanProcessor(jaeger_exporter)

Suivi distribué dans l’écosystème natif du cloud 157


)

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("foo"):
with tracer.start_as_current_span("bar"):
with tracer.start_as_current_span("tango"):
print("Hello world from Opentelemetry! Happy Tracing")

Le code précédent est un script Python de style «  Hello World  » simple qui utilise
OpenTelemetry. Notez comment nous utilisons l’exportateur Jaeger pour nous indiquer
l’emplacement de l’agent Jaeger.
Vous avez besoin de Python3 pour exécuter le script précédent :
$ python3 opentelemetry_simple.py
Hello world from Opentelemetry! Happy Tracing

Là encore, vous pouvez afficher dans l’interface utilisateur les périodes qui sont remplies
au même point de terminaison de Jaeger (http://localhost:16686) sous le service
my-helloworld-service.
Vous pouvez également instrumenter n’importe quel code en fonction du langage de votre
application. Par exemple, vous pouvez instrumenter une application Python Flask comme suit :
import flask
import requests

from opentelemetry import trace


from opentelemetry.exporter import jaeger
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
ConsoleSpanExporter,
SimpleExportSpanProcessor,
)

trace.set_tracer_provider(TracerProvider())

jaeger_exporter = jaeger.JaegerSpanExporter(
service_name="flask_app_example",
agent_host_name="localhost",
agent_port=6831,
)

trace.get_tracer_provider().add_span_processor(
SimpleExportSpanProcessor(jaeger_exporter)
)

app = flask.Flask(__name__)
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()

@app.route("/")
def hello():
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("example-request"):
requests.get("http://www.example.com")

158 Chapitre 6 : Observabilité : suivre les miettes de pain


return "hello"

app.run(debug=True, port=5000)

Vous pouvez exécuter le script précédent comme suit et consulter http://localhost:5000 pour
générer les suivis dans l’interface utilisateur de Jaeger :
$ python3 opentelemetry_flask.py
* Serving Flask app "opentelemetry_flask" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 156-661-048
127.0.0.1 - - [30/Dec/2020 20:54:37] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [30/Dec/2020 20:54:37] "GET /favicon.ico HTTP/1.1" 404 -

Ce sont quelques façons dont vous pouvez utiliser le suivi distribué dans vos environnements
natifs du cloud pour les rendre observables. En dehors des approches mentionnées ici,
les maillages de services tels qu’Istio fournissent également une architecture de mise en
œuvre de type sidecar pour effectuer un suivi distribué. Nous présenterons les maillages
de services au chapitre 7. Pour obtenir de plus amples informations sur le suivi distribué,
veuillez consulter Mastering Distributed Tracing (Maîtriser le suivi distribué) de Yuri Shkuro
(Packt, 2019).
Voyons maintenant ce qu’Azure peut vous offrir en termes d’espace de pilotage.

Azure Monitor
En plus des solutions sur mesure mentionnées précédemment pour la création de systèmes
observables, Microsoft Azure propose également Azure Monitor, une solution standard qui
peut vous aider à surveiller une application et une infrastructure exécutées dans le cloud et
dans des environnements sur site. Azure Monitor utilise principalement des métriques et
des journaux pour connaître les performances de votre application et identifie de manière
proactive les problèmes potentiels, ainsi que toute dépendance des ressources.
Azure Monitor dispose de différentes fonctions qui peuvent vous aider à détecter divers
problèmes dans votre application et votre infrastructure, comme indiqué ici et illustré
à la figure 6-12 :
Application Insights
Vous pouvez détecter et diagnostiquer les problèmes entre les applications et les
dépendances en surveillant la disponibilité, les performances et l’utilisation de vos
applications web hébergées dans le cloud ou sur site.
Azure Monitor pour les conteneurs et les machines virtuelles
Vous pouvez surveiller les performances de vos conteneurs exécutés sur AKS, ainsi que
la machine virtuelle qui les héberge.

Azure Monitor 159


Analyses et requêtes des journaux
Vous pouvez utiliser les journaux d’Azure Monitor basés sur l’Explorateur de données
Azure pour interroger les journaux.
Classeurs et tableaux de bord
Vous pouvez créer des visualisations flexibles à l’aide de classeurs et de tableaux de bord
pour l’analyse des données, ce qui vous permettra d’exploiter plusieurs ressources de
données sur Azure.
Métriques Azure Monitor
Vous pouvez utiliser les métriques Azure Monitor pour collecter des données
numériques à partir de votre infrastructure et de votre application.

Figure 6-12. Azure Monitor

Azure Monitor vous permet également d’utiliser des données de suivi distribué. Il existe
essentiellement deux façons de procéder. La première consiste à utiliser la vue de diagnostic
de transactions, qui est similaire à une pile d’appels avec l’ajout d’une dimension temporelle.
La vue Diagnostics de transaction fournit une visibilité sur une seule transaction/requête.
Elle est utile pour trouver la cause profonde des problèmes de fiabilité et les goulots
d’étranglement de performances à la demande. La deuxième méthode consiste à utiliser
la vue cartographique de l’application, qui regroupe de nombreuses transactions pour
afficher une vue topologique de la manière dont les systèmes interagissent, ainsi que des
performances moyennes et des taux d’erreur.

160 Chapitre 6 : Observabilité : suivre les miettes de pain


En outre, vous pouvez intégrer Azure Monitor à d’autres systèmes et créer une plateforme
d’observation personnalisée qui utilise vos données de surveillance.

Résumé
Dans ce chapitre, nous avons présenté l’observabilité comme un besoin croissant dans
l’environnement natif du cloud. Nous avons abordé l’observabilité et la façon dont elle
complète la surveillance, puis nous avons présenté le développement axé sur l’observabilité
comme la nouvelle méthode par excellence pour obtenir des informations sur votre
application et votre infrastructure cloud. Nous avons vu les trois piliers de l’observabilité
et abordé les méthodes préconisées en matière de journalisation, de surveillance et de
suivi pour les systèmes distribués modernes sur Azure. Enfin, nous avons brièvement vu
comment Azure propose ses solutions intégrées pour effectuer des tâches similaires avec
Azure Monitor. Grâce à ces connaissances, vous pouvez désormais créer en toute confiance
des systèmes observables dans des environnements natifs du cloud et intégrer l’observabilité
dans votre application.
Dans le chapitre suivant, nous allons examiner comment le maillage et la découverte des
services sont utiles dans les environnements natifs du cloud.

Résumé 161
CHAPITRE 7
Découverte et maillage des services :
Rechercher de nouveaux territoires
et traverser les frontières

Lorsque les applications modernes sont passées à une architecture de microservices, elles
sont devenues évolutives et efficaces sur le cloud. Les microservices favorisent la division
d’une application en composants indépendants et découplés. Le découplage est généralement
réalisé en séparant les bases de code en applications autonomes connectées à l’aide du réseau
et d’API ou d’interfaces bien définies que les microservices utilisent pour communiquer.
Une fois vos applications déplacées vers une architecture de microservices, de nombreuses
possibilités s’offrent à vous  : vous pouvez faire évoluer chaque service individuellement,
découpler le cycle de publication de chaque service, choisir un différent langage pour écrire
vos composants d’application individuels et même organiser la structure de votre équipe en
fonction de ces services indépendants.
Cependant, maintenant que vous avez divisé votre application en applications plus petites,
ces binaires ont leur propre cycle de vie. Ce dernier est planifié indépendamment et se
termine indépendamment selon les besoins, tout en maintenant la communication sur le
réseau. Dans ce scénario, supposons que vous ayez un grand nombre de microservices en
cours d’exécution qui communiquent entre eux. Vous commenceriez probablement à poser
plusieurs questions, dont les suivantes :

• 
Comment les services communiquent-ils entre eux lorsqu’ils sont de nature
dynamique  ? (Autrement dit, puisque les services communiquent sur un réseau,
l’adresse IP ne fournit pas suffisamment d’informations pour joindre les services, car
ceux-ci peuvent être résiliés indépendamment et reprogrammés sur un autre hôte.)
• Comment puis-je contrôler et gérer le trafic ou le trafic d’itinéraire selon les besoins,
y compris les scénarios avancés tels que les versions Canary, les tests A/B et les ruptures
de circuits ?

163
• Comment puis-je assurer la sécurité entre les services et chiffrer le trafic entre eux ?

Pour résoudre ces problèmes, vous pouvez commencer à écrire et à créer une logique propre
à l’infrastructure supplémentaire dans votre application. Toutefois, cela augmenterait la
portée de l’application au-delà de vos besoins professionnels et vous auriez à mettre en
œuvre cette logique avec chaque pile de microservices que vous utilisez.
Heureusement, ces problèmes ont été résolus dans l’espace de l’environnement natif du
cloud à l’aide des technologies de découverte de services et de maillage de services, qui
offrent un moyen beaucoup plus flexible de gérer, de sécuriser et de fournir un contrôle
beaucoup plus important sur les services.

La découverte de services est un mécanisme par lequel les services se découvrent les uns les
autres afin de pouvoir communiquer. La découverte de services est principalement axée sur
la recherche de l’emplacement réseau d’un service dans le cloud. Dans les environnements
classiques, où vous avez affaire à des serveurs physiques, vous connaissez les adresses IP
statiques de vos serveurs et vous pouvez utiliser un fichier de configuration simple pour
stocker les adresses IP, qui peuvent ensuite être utilisées par vos services pour contacter
d’autres services. Cette configuration fonctionne très bien dans un environnement local
ou physique. Toutefois, pour les applications hautement évolutives dans le cloud, cette
approche échoue, car votre application est désormais de nature dynamique, ce qui signifie
que l’application peut être redémarrée, replanifiée ou terminée selon les besoins. Dans de
tels scénarios, il est impossible de coder l’IP car elle peut changer. La découverte des services
permet de stocker un mappage pour chaque nom de service à l’adresse IP dans le cloud.
Un maillage de services est axé sur la résolution de la complexité accrue qu’entraîne la
communication interservices. Il s’agit d’une infrastructure dédiée qui gère toutes les
communications réseau de service à service dans l’environnement cloud. Il offre les
avantages suivants : visibilité élevée, résilience, gestion du trafic et contrôle de sécurité, avec
peu ou pas de modification du code d’application existant.
Dans ce chapitre, nous examinerons CoreDNS, une plateforme de découverte de services de
premier plan dans le domaine natif du cloud qui est utilisée par Azure Kubernetes Service
(AKS) pour la gestion DNS de clusters et la résolution de noms. Nous aborderons également
Istio, un maillage de services important qui est largement utilisé dans les environnements
cloud comme Azure.

Découverte de services
La découverte de services est un composant fondamental d’un environnement natif du cloud
moderne. Elle doit s’assurer que les services peuvent se trouver et communiquer entre eux sur
un réseau. En règle générale, la découverte de services implique un registre de services auquel
tous les services/applications de l’environnement cloud doivent s’inscrire. Le registre des
services est le référentiel central qui contient l’emplacement des services. Dans un scénario
type, un nouveau service s’enregistre d’abord avec le registre de service lorsqu’il entre dans
l’environnement. Le registre des services met à jour le dernier emplacement réseau du service,

164 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
et lorsqu’un service client demande à se connecter au service, le registre des services permet
au consommateur d’avoir un emplacement pour ce service.
Dans cette section, nous allons explorer CoreDNS et voir comment il peut être utilisé en
tant qu’outil de découverte de service dans les environnements cloud.

Présentation de CoreDNS
CoreDNS est un serveur DNS extensible qui prend en charge les protocoles DNS, DNS sur
TLS et DNS standard sur gRPC. CoreDNS a été créé par Miek Gieben en 2016 en utilisant une
infrastructure de serveur développée dans le cadre du serveur web Caddy. Il dispose d’une
architecture de plug-in qui est hautement configurable, flexible et extensible. CoreDNS a été
plus largement adopté après avoir reçu le support de Cloud Native Computing Foundation
(CNCF). En  2019, CoreDNS a reçu le niveau de maturité «  graduation  » de CNCF.1
CoreDNS est maintenant le serveur DNS par défaut officiel avec Kubernetes 1.13 et versions
ultérieures. Il remplace Kube-DNS. CoreDNS résout les problèmes de fiabilité et de sécurité
dans la mise en œuvre originale de Kube-DNS grâce à plusieurs différences clés :

• 
CoreDNS exécute un conteneur unique, tandis que Kube-DNS exécutait trois
conteneurs : kubedns, dnsmasq et sidecar.
• CoreDNS est conçu pour être utilisé en tant que serveur  DNS à usage général
rétrocompatible avec Kubernetes.
• CoreDNS est principalement un processus Go qui améliore les fonctionnalités de
Kube-DNS.

L’architecture de plug-in CoreDNS garantit que la fonctionnalité du serveur DNS reste stable
et permet d’ajouter d’autres fonctionnalités à l’aide de plug-ins. L’utilisateur final peut définir
plusieurs serveurs en configurant des zones à servir sur un port particulier. Chaque serveur
transmet les requêtes via une chaîne de plug-in, qui détermine le type de réponse à envoyer :

• Si plusieurs serveurs sont configurés pour être écoutés sur le port interrogé, il vérifie
quelle est la zone la plus spécifique pour la requête à l’aide de la correspondance
de suffixe la plus longue.  Par exemple, si un utilisateur effectue une requête pour
www.health.abc.com et que deux serveurs sont configurés en tant que abc.com et health.
abc.com, la requête est acheminée vers ce dernier serveur.
• Une fois qu’un serveur approprié a été trouvé, il est acheminé via la chaîne de plug-in
configurée pour le serveur.

1 CNCF attribue différents niveaux de maturité aux projets : sandbox, incubés et gradués, qui correspondent aux
niveaux des Visionnaires, des Premiers adoptants et de la Majorité précoce du diagramme Franchir le gouffre.
Le niveau de maturité est un signal que CNCF attribue à un projet afin d’identifier les types d’entreprises qui
devraient adopter le projet.

Découverte de services 165


• Chaque plug-in inspectera la requête et, en fonction de cette dernière, ce qui suit pourra
se produire :
— Si la requête est traitée par le plug-in, une réponse appropriée sera renvoyée au
client. Le traitement des requêtes s’arrête ici et plus aucun plug-in n’est appelé.
— Si la requête n’est pas traitée par le plug-in, elle appellera le plug-in suivant dans la
chaîne. Si le dernier plug-in de la chaîne n’est pas en mesure de traiter la requête,
CoreDNS renverra un SERVFAIL au client appelant.
— Si la requête est traitée à l’aide d’une méthode de passage dans laquelle les autres
plug-ins de la chaîne sont également appelés pour jeter un coup d’œil à la requête, le
mot-clé fallthrough est utilisé pour l’activer.
— Si la requête est traitée à l’aide d’un indice, le prochain plug-in de la chaîne sera
toujours appelé. Toutefois, il fournit un indice qui lui permet de voir la réponse qui
sera renvoyée au client.

La figure 7-1 présente les étapes qui se produisent lorsque le CoreDNS traite une requête.

Figure 7-1. Processus de requête CoreDNS

Maintenant, nous allons voir comment installer CoreDNS et utiliser le fichier


de configuration, également connu sous le nom de Corefile, afin de le configurer.

166 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Installation et configuration de CoreDNS
Vous pouvez installer CoreDNS à l’aide du fichier binaire, disponible à l’emplacement
GitHub de ce livre. Une fois que vous avez saisi le binaire pour votre système d’exploitation,
vous pouvez le déplacer vers votre emplacement local. Par exemple, si vous avez téléchargé
le binaire pour macOS, coredns_1.8.1_darwin_amd64.tgz, vous pouvez décompresser le
fichier et le déplacer vers /usr/local/bin. Vérifiez que CoreDNS fonctionne correctement en
émettant la commande suivante :
$ coredns -version
CoreDNS-1.8.1
darwin/amd64, go1.15.7, 95622f4

CoreDNS offre de nombreuses options de configuration qui sont gérées par un Corefile.
Lorsque CoreDNS démarre, il recherche un fichier nommé Corefile dans le répertoire actif
si l’indicateur -conf n’est pas passé. Le fichier définit principalement les éléments suivants :

• Quel serveur écoute sur quel port


• Pour quelle zone chaque serveur fait autorité
• Quels plug-ins sont chargés dans un serveur

Un Corefile ressemble normalement à ceci :


ZONE:[PORT] {
[PLUGIN]...
}

où ZONE représente la zone DNS, PORT est l’endroit où la zone du serveur est exécutée (en
général, le port 53) et PLUGIN définit les plug-ins qui doivent être chargés.
Parmi les entrées les plus courantes d’un fichier Corefile figurent les différents blocs de
serveurs qui définissent un serveur dans CoreDNS (c’est-à-dire la façon dont les requêtes
pour un nom de domaine particulier sont traitées). Voici un exemple :
example.com {
}

Le bloc de serveurs précédent gérera toutes les requêtes qui se retrouvent dans
example.com, sauf si un bloc de serveurs plus précis, comme foo.example.com, est présent.
Pour un exemple plus concret, imaginons que vous ayez ce qui suit dans votre Corefile :
cloudnativeazure.io:53 {
log
errors
}

.:53 {
forward . 8.8.8.8
log
errors

Découverte de services 167


cache
}

Dans cet exemple, nous avons deux blocs de serveurs pour cloudnativeazure.io et la
racine (.). Tous deux écoutent sur le port 53. De plus, plusieurs plug-ins sont définis : log,
pour consigner chaque requête reçue par CoreDNS, errors, pour consigner les erreurs et
cache, pour activer le cache frontal (c’est-à-dire tous les enregistrement, sauf les transferts
de zone et les métadonnées qui seront mis en cache pendant un maximum de 3  600  s).
Vous pouvez exécuter le Corefile précédent en tapant simplement  coredns dans le même
répertoire que celui où se trouve Corefile, ou faire coredns -conf Corefile:
$ coredns -conf Corefile
.:53
cloudnativeazure.io.:53
CoreDNS-1.8.1
darwin/amd64, go1.15.7, 95622f4

En effectuant l’action dig pour cloudnativeazure.io sur un autre terminal, vous verrez
ce qui suit :
$ ~ dig @127.0.0.1 cloudnativeazure.io:53
; <<>> DiG 9.10.6 <<>> @127.0.0.1 cloudnativeazure.io:53
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 48995
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;cloudnativeazure.io:53. IN A

;; AUTHORITY SECTION:
.  1800 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2021021001 \
1800 900 604800 86400

;; Query time: 82 msec


;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Feb 10 23:45:28 IST 2021
;; MSG SIZE rcvd: 126

Vous pouvez voir les journaux sur le terminal CoreDNS comme suit :
$ coredns -conf Corefile
.:53
cloudnativeazure.io.:53
CoreDNS-1.8.1
darwin/amd64, go1.15.7, 95622f4

[INFO] 127.0.0.1:59741 - 48995 "A IN cloudnativeazure.io:53. udp 51 false 4096" NXDOMAIN \


qr,rd,ra,ad 115 0.05160016s

Nous n’avons couvert que les notions de base de CoreDNS ici. Pour obtenir de plus amples
informations, nous vous suggérons de lire Learning CoreDNS de John Belamaric et Cricket
Liu (O’Reilly, 2019).

168 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Voyons maintenant comment CoreDNS est utilisé dans la découverte de services sur Kubernetes.

Découverte de services Kubernetes avec CoreDNS


Comme nous l’avons mentionné précédemment, CoreDNS est le mécanisme de découverte
de services par défaut dans la version 1.13 de Kubernetes et les versions ultérieures, et vous
pouvez commencer à l’utiliser dès le départ. Comme vous pouvez le voir dans le bloc de
code suivant, les Pods exécutés dans l’espace de noms kube-system sont de type core-dns :
$ kubectl get pods --namespace=kube-system -o wide
NAME READY STATUS RESTARTS AGE IP
NODE NOMINATED NODE READINESS GATES
coredns-748cdb7bf4-9xggk 1/1 Running 0 4h31m 10.244.0.3
aks-agentpool-13363041-vmss000002 <none> <none>
coredns-748cdb7bf4-f6glb 1/1 Running 0 4h30m 10.244.0.5
aks-agentpool-13363041-vmss000002 <none> <none>
coredns-autoscaler-868b684fd4-7ck6z 1/1 Running 0 4h30m 10.244.0.4
aks-agentpool-13363041-vmss000002 <none> <none>

$ kubectl get deployments --namespace=kube-system


NAME READY UP-TO-DATE AVAILABLE AGE
coredns 2/2 2 2 4h31m
coredns-autoscaler 1/1 1 1 4h31m

Le plug-in CoreDNS de Kubernetes surveille les services Kubernetes et les ressources de


points de terminaison, en plus de mettre ces données en cache. Au moment où une requête
est effectuée, le plug-in CoreDNS vérifie si une ressource correspondant au nom demandé
existe, puis il renvoie les données appropriées. Les enregistrements de réponse sont
construits à la volée en fonction des demandes entrantes à l’aide de la fonction surveillance
du serveur API Kubernetes.
Voyons comment CoreDNS prend en charge la découverte de services sur un cluster
Kubernetes. Imaginons que vous exécutiez un simple Pod Nginx dans un cluster Kubernetes
comme suit :
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 3m20s

$ kubectl get services


NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 41m
nginx ClusterIP 10.0.148.122 <none> 80/TCP 3m26s

Pour accéder à ce serveur Nginx, vous devez exécuter un Pod infoblox/dnstools


possédant certains utilitaires DNS, comme dig et curl. Pour exécuter ce Pod, vous pouvez
utiliser la commande suivante :
$ kubectl run --restart=Never -it --image infoblox/dnstools dnstools
If you don't see a command prompt, try pressing enter.
dnstools#

La commande précédente vous connectera à l’intérieur du conteneur nouvellement créé et


vous présentera une invite de commande.

Découverte de services 169


Dans Kubernetes, la découverte de services DNS définit une spécification, ou un schéma DNS,
qui définit le nom DNS utilisé pour localiser les services en cours d’exécution dans le cluster.
Tous les enregistrements  DNS d’un cluster Kubernetes relèvent d’un seul domaine appelé
domaine de cluster, généralement défini sur cluster.local par défaut dans la plupart des
clusters Kubernetes. La spécification DNS indique que pour chaque adresse IP affectée à un
service (c’est-à-dire l’adresse IP du cluster), il existe un enregistrement A qui contient l’adresse IP
du cluster, ainsi qu’un nom dérivé du nom du service et de l’espace de noms, comme service.
namespace.svc.cluster-domain. Ainsi, pour accéder au Pod Nginx que nous avons créé,
vous pouvez entrer la commande suivante à partir du conteneur infoblox/dnstools  :
dnstools# curl nginx.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to


<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>


</body>
</html>
dnstools#

Vous pouvez également entrer une commande dig afin de voir l’enregistrement A pour le service :
dnstools# dig nginx.default.svc.cluster.local

; <<>> DiG 9.11.3 <<>> nginx.default.svc.cluster.local


;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52488
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 5086e790e261bf20 (echoed)
;; QUESTION SECTION:
;nginx.default.svc.cluster.local. IN A

170 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
;; ANSWER SECTION:
nginx.default.svc.cluster.local. 5 IN A 10.0.148.122

;; Query time: 1 msec


;; SERVER: 10.0.0.10#53(10.0.0.10)
;; WHEN: Sat Feb 27 07:37:10 UTC 2021
;; MSG SIZE rcvd: 119

Microsoft Azure fournit également DNS en tant que service, par le biais d’un service
d’hébergement connu sous le nom d’Azure DNS. C’est ce que nous allons voir maintenant.

Azure DNS
Azure  DNS fournit principalement l’infrastructure de Microsoft Azure afin de gérer les
résolutions de noms pour les domaines DNS. Vous pouvez utiliser Azure DNS pour héberger
votre domaine  DNS et gérer vos enregistrements  DNS. En hébergeant vos domaines
dans Azure, vous pouvez gérer vos enregistrements DNS en conservant les informations
d’identification, API, outils et données de facturation utilisés pour vos autres services Azure.
Azure  DNS peut être utilisé pour traduire les noms d’hôte dans les domaines public et
privé. La fonction de DNS public peut être utilisée pour héberger votre nom de domaine
préacheté sur Azure, tandis que le DNS privé vous permet de gérer et de traduire les noms
de domaine dans les réseaux virtuels.
CoreDNS fournit également un plug-in propre à Azure utilisé pour servir des zones à partir
d’Azure DNS. Le plug-in Azure peut être activé comme suit :
azure AZURE_RESOURCE_GROUP:ZONE... {
tenant <TENANT_ID>
client <CLIENT_ID>
secret <CLIENT_SECRET>
subscription <SUBSCRIPTION_ID>
environment <ENVIRONMENT>
fallthrough [ZONES...]
access private
}

Dans le code précédent :

• 
AZURE_RESOURCE_GROUP:ZONE est le groupe de ressources auquel appartiennent les
zones hébergées sur Azure, alors que ZONE est la zone qui contient des données.
• C LIENT_ID et CLIENT_SECRET sont les informations d’identification pour Azure,
tandis que tenant définit le TENANT_ID à utiliser. SUBSCRIPTION_ID est l’identifiant
d’abonnement.
• environment définit ENVIRONMENT dans Azure.

Découverte de services 171


• 
fallthrough précise que si la zone correspond et qu’aucun enregistrement ne peut
être généré, la requête doit être transmise au plug-in suivant. Si ZONES est omis, un
réacheminement se produit pour toutes les zones pour lesquelles le plug-in fait autorité.
• 
access détermine si la valeur de la zone DNS est public ou private. La valeur par
défaut est public.

Maintenant que nous avons couvert le rôle et l’importance de la découverte des services dans
les environnements distribués natifs du cloud, voyons ce qu’un maillage de services a à offrir.

Le maillage des services


Un maillage de services est un outil qui ajoute observabilité, fiabilité et sécurité au niveau
de la couche plateforme des applications natives du cloud. Un maillage de services est
généralement mis en œuvre en tant qu’ensemble évolutif de serveurs mandataires réseau
déployés en tant que sidecar à côté de l’application.  Ces serveurs mandataires sont
principalement responsables de la gestion de la communication entre les microservices et
ils sont exécutés à la couche 7 de la pile OSI. Outre les fonctions supplémentaires qu’il offre,
un maillage de services :

• Fournit la gestion du trafic par l’équilibrage de charge, la limitation de débit, le décalage


du trafic et les disjoncteurs
• Aide à la gestion du cycle de publication des applications avec les versions de Canary
• Ajoute des vérifications de fiabilité prêts à l’emploi, telles que des vérifications d’intégrité,
les nouvelles tentatives et les délais d’inactivité
• Aide à l’injection d’erreurs et au routage de débogage
• Garantit la sécurité via les stratégies TLS et ACL
• Fournit des métriques, des journaux et des suivis automatiques pour tout le trafic
au sein d’un cluster
• 
Sécurise la communication de service à service dans un cluster à l’aide d’une
authentification et d’une autorisation solides et basées sur l’identification

L’architecture générale d’un maillage de services comporte deux composants de haut niveau :
un plan de données et un plan de contrôle. La figure 7-2 représente quatre services (A – D)
déployés dans un maillage de services.
Comme vous pouvez le voir, chaque instance de service dispose d’une instance de
proxy sidecar qui est responsable de tous les flux et de la gestion du trafic. Cela signifie
fondamentalement que lorsque le service  A veut parler au service  C, par exemple, le
service A initie le flux de communication via son proxy sidecar local, qui accède ensuite au
proxy sidecar du service C. De cette façon, le service n’est pas au courant du proxy de réseau
local et reste isolé du réseau le plus important. Ainsi, chaque fois qu’un service doit parler à
un autre service, le proxy intercepte la requête et la transmet au destinataire.

172 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Figure 7-2. Architecture générale du maillage de services

La collection de tous les proxys de sidecar constitue le plan de données de l’architecture


du maillage de services. Le plan de données est responsable des éléments suivants :

• Transfert des demandes


• Découverte de services
• Vérification de l’intégrité
• Routage des demandes de service
• Équilibrage de charge
• Authentification et autorisation
• Garantir l’observabilité

Par conséquent, le plan de données touche fondamentalement chaque requête ou paquet


réseau du cluster.
En revanche, le plan de contrôle fournit les détails de la stratégie et de la configuration de
tous les proxys sidecar (plans de données) dans le maillage de services. Le plan de contrôle
vous permet de contrôler et de configurer les plans de données. Toute la configuration de
haut niveau est envoyée au plan de contrôle et elle est ensuite convertie en configuration
propre au plan de données. Le plan de contrôle aide le plan de données en :

• Acheminant entre les services


• Remplissant les données de découverte de services pour l’utilisation du plan de données
• Spécifiant des entrées pour les configurations responsables de l’équilibrage de charge,
de la rupture de circuit et des délais d’expiration
• 
Configurant des paramètres de validation, d’authentification et d’autorisation
dans le cluster

Le maillage des services 173


Dans la section suivante, nous allons examiner Istio, qui agit en tant que plan de contrôle
d’un maillage de services dans un environnement natif du cloud moderne.

Présentation d’Istio
Lorsque les applications monolithiques se sont transformées en microservices, les
développeurs et les équipes opérationnelles ont rencontré des difficultés avec la
communication d’un service à l’autre dans le maillage de services. Lorsque la taille d’un
maillage de services augmente, celui-ci devient difficile de gérer et à maintenir. Il devient
également difficile de comprendre comment les différents composants en évolution
fonctionnent ensemble.
Istio est un maillage de services open source qui simplifie la communication entre les
microservices dans les applications natives du cloud distribuées. Istio fournit des ensembles
de fonctionnalités riches et prédéfinis qui appliquent des modèles de résilience tels que
les nouvelles tentatives, les disjoncteurs, la mise en forme du trafic, le comportement de
routage, le déploiement canari, etc. En outre, Istio vous permet de créer facilement un
réseau de microservices déployés à l’aide de l’équilibrage de charge, de la surveillance et de
l’authentification, avec presque aucun changement de code d’application.
Comme nous l’avons mentionné dans la section précédente, une architecture de maillage
de services se compose d’un plan de données et d’un plan de contrôle. Istio utilise Envoy
comme plan de données et istiod comme plan de contrôle (comme illustré à la figure 7-3) :
Envoy (plan de données)
Envoy est un proxy haute performance écrit en C++ qui intercepte tous les trafics
entrants et sortants pour chaque application dans le maillage de services. Il est déployé
en tant que sidecar avec l’application et fournit un grand nombre de fonctionnalités, y
compris la terminaison TLS, l’équilibrage de charge, la rupture de circuit, les contrôles
d’intégrité, la découverte de service dynamique, l’injection d’erreur, etc. Ce modèle de
proxy sidecar vous permet d’utiliser Istio en tant que maillage de services sans aucune
modification de code.
Istiod (plan de contrôle)
Istiod gère et configure les proxys. Istiod comporte trois sous-composants :
• Pilot, responsable de la configuration des proxys Envoy au moment de l’exécution
• Citadel, responsable de l’émission et de la rotation des certificats
• 
Galley, responsable de la validation, de l’ingestion, de l’agrégation, de la
transformation et de la distribution de la configuration dans Istio
Istiod convertit les règles de routage de haut niveau qui contrôlent le trafic en configuration
spécifique à Envoy et se propage aux sidecars Envoy lors de l’exécution.

174 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Figure 7-3. Architecture Istio

Voyons maintenant comment vous pouvez utiliser Istio et Envoy dans un environnement
Azure Kubernetes.

Installation d’Istio sur Azure Kubernetes Service


Vous pouvez installer Istio sur un cluster Kubernetes de trois façons :

• Istio fournit un outil de ligne de commande appelé istioctl, qui peut être utilisé pour
fournir un niveau de personnalisation riche au plan de contrôle Istio et au plan de don-
nées proxy Envoy.
• Vous pouvez utiliser l’opérateur Istio Kubernetes pour gérer l’installation via les
définitions de ressources personnalisées (CRD) de l’API Kubernetes.
• Vous pouvez utiliser des tableaux Helm pour installer Istio directement.

Les tableaux Helm étant également utilisés indirectement dans les méthodes 1 et 2, nous les
utiliserons ici pour installer Istio.

Installer Istio à l’aide de Helm


Pour installer Istio à l’aide de Helm, vous avez besoin d’un cluster  AKS déjà en cours
d’exécution (consultez le chapitre  5 si vous souhaitez vérifier comment faire tourner un
cluster  AKS dans Azure). Les étapes suivantes vous permettront d’installer Istio sur un
cluster AKS :

Le maillage des services 175


1. Téléchargez Istio en exécutant les commandes suivantes à partir de votre machine locale :
$ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.9.2 TARGET_ARCH=x86_64 sh -

$ cd istio-1.9.2

2. Créez un nouvel espace de noms pour les composants Istio, appelé istio-system :
$ kubectl create namespace istio-system

namespace/istio-system created

3. Installez le graphique de base Istio qui contient les ressources à l’échelle du cluster
utilisées par le plan de contrôle istiod :
$ helm install istio-base manifests/charts/base -n istio-system
NAME: istio-base
LAST DEPLOYED: Sun Mar 28 15:59:16 2021
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

4. Installez le graphique de découverte Istio qui déploie le service istiod :


$ helm install istiod manifests/charts/istio-control/istio-discovery -n istio-system

NAME: istiod
LAST DEPLOYED: Sun Mar 28 16:02:23 2021
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

5. Vérifiez l’installation :
$ helm list -A
NAME NAMESPACE REVISION UPDATED STATUS
CHART APP VERSION
istio-base istio-system 1 2021-03-28 15:59:16.429126 +0530 IST deployed
base-1.9.2
istiod istio-system 1 2021-03-28 16:02:23.950541 +0530 IST deployed
istio-discovery-1.9.2
$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
istiod-6d68c86c8d-5nv2r 1/1 Running 0 10m

Il est important de s’assurer que chaque fois qu’un nouveau Pod est déployé dans le cluster,
un proxy Istio sidecar (proxy Envoy) est également injecté dans le même Pod afin que vous
puissiez tirer parti de toutes les fonctionnalités d’Istio. Nous verrons comment procéder à
la prochaine section.

176 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Injection automatique du proxy Sidecar (proxy Envoy)
Pour que vous puissiez tirer pleinement parti d’Istio en tant que maillage de services,
les Pods du cluster doivent également utiliser le proxy Istio sidecar. istio-proxy, qui
est essentiellement le proxy Envoy, peut être déployé de deux façons  : manuellement
ou automatiquement. La méthode manuelle vous permet de modifier la configuration
d’injection et de proxy. La méthode automatique, comme son nom l’indique, permet
une configuration de proxy automatique au moment de la création du Pod à l’aide d’un
contrôleur d’admission en mutation. Bien que la méthode manuelle soit utile lorsque vous
avez besoin d’une configuration spécifique, la méthode automatique est plus généralement
privilégiée. Nous allons maintenant voir comment utiliser la méthode automatique.

Contrôleurs d’admission Kubernetes


Un contrôleur d’admission est un élément de code qui intercepte
les demandes au serveur d’API  Kubernetes avant que l’objet ne
soit conservé, mais une fois la requête authentifiée et autorisée.
Kubernetes fournit une gamme de contrôleurs d’admission qui sont
compilés dans la bibliothèque kubeapiserver.

Un MutatingAdmissionController peut modifier les objets qu’il admet. En d’autres ter-


mes, il appelle n’importe quel hook web en mutation qui correspond à la demande.
Pour utiliser l’injection sidecar automatique, utilisez l’étiquette istio-injection sur
l’espace de noms souhaité et définissez-la sur enabled. Cela permet à tous les nouveaux
Pods créés dans cet espace de noms de déployer un sidecar proxy avec l’application. Pour
vous donner une meilleure compréhension de la façon dont cela se produit, nous allons
d’abord déployer un Pod Nginx sans injection de sidecar, puis nous activerons l’injection de
sidecar afin que le proxy sidecar soit automatiquement injecté lors des futurs déploiements.
Supposons que nous ayons déployé Nginx avec un seul Pod :
$ kubectl apply -f nginx_deploy.yaml
deployment.apps/nginx-deployment created

$ kubectl get deployment -o wide


NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx-deployment 1/1 1 1 105s nginx nginx:1.14.2 app=nginx

$ kubectl get pod


NAME READY STATUS RESTARTS AGE
nginx-deployment-6b474476c4-bqgh8 1/1 Running 0 4m5s

Nous allons étiqueter l’espace de noms par défaut istio-injection=enabled :


$ kubectl label namespace default istio-injection=enabled --overwrite
namespace/default labeled
$ kubectl get namespace -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 24m enabled
istio-system Active 18m

Le maillage des services 177


kube-node-lease Active 25m
kube-public Active 25m
kube-system Active 25m

Dans la mesure où l’injection se produit lorsque le Pod est créé, nous pouvons tuer le
pod Nginx et créer un nouveau Pod nginx, seulement cette fois, il aura un sidecar injecté
automatiquement :
$ kubectl delete pod -l app=nginx
pod "nginx-deployment-6b474476c4-bqgh8" deleted

$ kubectl get pod -l app=nginx


NAME READY STATUS RESTARTS AGE
nginx-deployment-6b474476c4-pfgqv 2/2 Running 0 57s

Notez que le déploiement créé deux conteneurs. Il s’agit du proxy sidecar en cours d’exécution
dans le Pod. Vous pouvez le vérifier à l’aide de describe comme suit :
$ kubectl describe pod -l app=nginx

Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully
assigned default/nginx-deployment-6b474476c4-pfgqv to aks-agentpool-20139558-vmss000001
Normal Pulling 2m35s kubelet, aks-agentpool-20139558-vmss000001 Pulling image
"docker.io/istio/proxyv2:1.9.2"
Normal Created 2m34s kubelet, aks-agentpool-20139558-vmss000001 Created
container nginx
Normal Created 2m34s kubelet, aks-agentpool-20139558-vmss000001 Created
container istio-init
Normal Started 2m34s kubelet, aks-agentpool-20139558-vmss000001 Started
container istio-init
Normal Pulled 2m34s kubelet, aks-agentpool-20139558-vmss000001 Container image
"nginx:1.14.2" already present on machine
Normal Pulled 2m34s kubelet, aks-agentpool-20139558-vmss000001 Successfully
pulled image "docker.io/istio/proxyv2:1.9.2"
Normal Started 2m34s kubelet, aks-agentpool-20139558-vmss000001 Started
container nginx
Normal Pulling 2m34s kubelet, aks-agentpool-20139558-vmss000001 Pulling image
"docker.io/istio/proxyv2:1.9.2"
Normal Pulled 2m33s kubelet, aks-agentpool-20139558-vmss000001 Successfully
pulled image "docker.io/istio/proxyv2:1.9.2"
Normal Created 2m33s kubelet, aks-agentpool-20139558-vmss000001 Created
container istio-proxy
Normal Started 2m33s kubelet, aks-agentpool-20139558-vmss000001 Started
container istio-proxy

Maintenant que vous savez comment déployer le proxy sidecar, vous pouvez utiliser Istio
pour la gestion du trafic, la sécurité, l’application des stratégies et l’observation. Nous
n’aborderons pas chacun de ces aspects, car cela dépasse le cadre de ce livre.
Lorsque vous utilisez un maillage de services, vous devez être en mesure de le visualiser et
de le gérer. Dans la section suivante, nous allons nous concentrer sur Kiali, une console de
gestion pour les maillages de services basés sur Istio.

178 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Gestion des maillages de services Istio à l’aide de Kiali
Kiali est une console de gestion pour Istio qui vous permet d’exécuter et de configurer
facilement un maillage de services avec une observabilité supplémentaire dans votre
environnement. Kiali fournit une image claire du maillage de services à travers les tableaux
de bord qui utilisent la topologie du trafic pour afficher sa structure et son intégrité. Dans
cette section, nous allons voir comment installer Kiali sur un cluster Kubernetes afin de
gagner en visibilité sur le maillage de services Istio.
Nous allons commencer par installer la passerelle d’entrée et de sortie pour Istio à partir du
répertoire istio-1.9.2 que nous avons utilisé lors de l’installation d’Istio :
Istio-1.9.2 $ helm install istio-ingress manifests/charts/gateways/istio-ingress -n \
istio-system
NAME: istio-ingress
LAST DEPLOYED: Mon Apr 5 15:19:01 2021
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

Istio-1.9.2 $ helm install istio-egress manifests/charts/gateways/istio-egress -n \


istio-system
NAME: istio-egress
LAST DEPLOYED: Mon Apr 5 15:19:31 2021
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

Maintenant, nous pouvons utiliser le tableau Helm pour installer Kiali :


$ kubectl create namespace kiali-operator
namespace/kiali-operator created
$ istio-1.9.2 helm install --set cr.create=true --set cr.namespace=istio-system \
--namespace kiali-operator --repo https://kiali.org/helm-charts \
kiali-operator kiali-operator

NAME: kiali-operator
LAST DEPLOYED: Mon Apr 5 15:27:03 2021
NAMESPACE: kiali-operator
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Welcome to Kiali! For more details on Kiali, see: https://kiali.io

The Kiali Operator [v1.32.0] has been installed in namespace [kiali-operator]. It will be
ready soon.
You have elected to install a Kiali CR in the namespace [istio-system]. You should be able
to access Kiali soon.

If you ever want to uninstall the Kiali Operator, remember to delete the Kiali CR first
before uninstalling the operator to give the operator a chance to uninstall and remove all
the Kiali Server resources.

(Helm: Chart=[kiali-operator], Release=[kiali-operator], Version=[1.32.0])

Le maillage des services 179


Cela va installer le dernier opérateur Kiali, ainsi que la ressource personnalisée Kiali (CR),
qui est essentiellement un fichier YAML contenant la configuration Kiali.
Pour accéder au tableau de bord Kiali, nous devons d’abord l’exposer correctement à
Internet. Pour ce faire, nous devons obtenir l’adresse de la passerelle d’entrée comme suit :
$ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o \
jsonpath='{.status.loadBalancer.ingress[0].ip}')
$ export INGRESS_DOMAIN=${INGRESS_HOST}.nip.io

Maintenant, nous devons créer un certificat autosigné :


$ CERT_DIR=/tmp/certs
$ mkdir -p ${CERT_DIR}
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj "/O=example Inc./ \
CN=*.${INGRESS_DOMAIN}" -keyout ${CERT_DIR}/ca.key -out ${CERT_DIR}/ca.crt
$ openssl req -out ${CERT_DIR}/cert.csr -newkey rsa:2048 -nodes -keyout \
${CERT_DIR}/tls.key -subj "/CN=*.${INGRESS_DOMAIN}/O=example organization"
$ openssl x509 -req -days 365 -CA ${CERT_DIR}/ca.crt -CAkey ${CERT_DIR}/ca.key \
-set_serial 0 -in ${CERT_DIR}/cert.csr -out ${CERT_DIR}/tls.crt
$ kubectl create -n istio-system secret tls telemetry-gw-cert --key=${CERT_DIR}/tls.key \
--cert=${CERT_DIR}/tls.crt

À ce stade, nous pouvons exposer Kiali à l’aide des configurations suivantes, qui vont créer
une passerelle, un service virtuel et une règle de destination :

• Gateway représente un plan de données qui achemine le trafic vers un cluster


Kubernetes en configurant un équilibreur de charge pour le trafic HTTP/TCP, quel
que soit l’endroit où il sera exécuté.
• 
VirtualService définit un ensemble de règles de routage de trafic à appliquer
pour un hôte. Une règle de routage peut correspondre à un certain type de trafic, et
lorsqu’une correspondance pour une règle se produit, le trafic est envoyé à un service
de destination nommé.
• 
DestinationRule configure l’ensemble de stratégies à appliquer lors du transfert du
trafic vers un service.
Voici le code :
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: kiali-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https-kiali
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: telemetry-gw-cert

180 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
hosts:
- "kiali.${INGRESS_DOMAIN}"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: kiali-vs
namespace: istio-system
spec:
hosts:
- "kiali.${INGRESS_DOMAIN}"
gateways:
- kiali-gateway
http:
- route:
- destination:
host: kiali
port:
number: 20001
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: kiali
namespace: istio-system
spec:
host: kiali
trafficPolicy:
tls:
mode: DISABLE
---
EOF

Vous pouvez maintenant accéder à l’interface utilisateur de Kiali à l’adresse https://kiali.$\


{INGRESS_DOMAIN\} (dans notre cas, il s’agit de https://kiali.40.118.246.227.nip.io).
Lorsque vous accédez à cette URL, vous voyez l’écran de connexion Kiali, où vous devez
saisir le jeton pour l’opérateur Kiali. Vous pouvez obtenir le jeton en procédant comme suit :
$ kubectl get secrets --namespace=kiali-operator -o wide
NAME TYPE DATA AGE
default-token-7ng8l kubernetes.io/service-account-token 3 142m
kiali-operator-token-jj88t kubernetes.io/service-account-token 3 141m
sh.helm.release.v1.kiali-operator.v1 helm.sh/release.v1 1 141m

$ ~ kubectl describe secret kiali-operator-token-jj88t --namespace=kiali-operator


Name: kiali-operator-token-jj88t
Namespace: kiali-operator
Labels: <none>
Annotations: kubernetes.io/service-account.name: kiali-operator
kubernetes.io/service-account.uid: 9a30ba7a-7d21-484d-9e7a-0a38458690a4

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1765 bytes
namespace: 14 bytes

Le maillage des services 181


token: eyJhbGciOiJSUzI1NiIsImtpZCI6InVvQXNpV01QRG1fRjhma0RRZjVIWXBNVHRnZDhJdHhoOXI2RmQ2
eGxDZDQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb
3VudC9uYW1lc3BhY2UiOiJraWFsaS1vcGVyYXRvciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0L
m5hbWUiOiJraWFsaS1vcGVyYXRvci10b2tlbi1qajg4dCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2Vyd
mljZS1hY2NvdW50Lm5hbWUiOiJraWFsaS1vcGVyYXRvciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2Vyd
mljZS1hY2NvdW50LnVpZCI6IjlhMzBiYTdhLTdkMjEtNDg0ZC05ZTdhLTBhMzg0NTg2OTBhNCIsInN1YiI6InN5c3Rlb
TpzZXJ2aWNlYWNjb3VudDpraWFsaS1vcGVyYXRvcjpraWFsaS1vcGVyYXRvciJ9.uJ6056HMQGvc0OQIK1OOMj1PCSrW
MpYZL0jKiG27EBurhUcZ73ngr1S0IfzcNnRwJLuWUkMPrzzKOzVvrUUMf6biPb0MieWn001HX6mfHOzNgXRAxdP-0Ape
hmEiCyENQS4DEQL2Eg8mUuaFsExuwzpx8LAibdLRpbI7sQa4P5B7he2Huol5FzJsoySjaKcP6eJGOaIbscIz-qtDcoM3
11EXhZr6xx8G7b5O7VEYuzs-LNKNyqCJL_iDyxV73WGgaPA2KHUjM-ESpBF-qkoWZDMy6oLqbe24Kcv-Mzmji_WlTMc2
8mXttjiMgVPllUeQfFTK4wYNvpnFEQCJ6ogYC4JJoRLSUuPslSC2JX_Bi4OaiGus1BH3JtFQsxxmL3f7ZnPa-XY66Zk_
ZyKZF1rqLKnzXCa85KkZZnCA83kpKT4ksM9MnqTBGBLNMW7OV4gTDTl9zUvQm--VgaoN2lnHZ-oqHPzeNym769OYHzSA
A_C_g1un1gtc8RlkUDM8u4F-DUSDGnJTBqjUQLiwXDyJS_epCpLuuA-6xCtx9DLkWTcGy886SPX1u3OLVeZUfRRlumjt
eEdId8Z884Iqx1T4V8Qo6AomdI0mLjbyFbpKoDLTfaTA98SuaEbko0oF5dzxo2xBgYXy-b1iE4KO9r_PnBZ-BLVQ0zfO
_1Bjym9JYWo

Vous pouvez maintenant copier le jeton et vous connecter avec celui-ci. Vous devez
également installer Prometheus avec Istio pour enregistrer des métriques qui assurent
le suivi de l’intégrité d’Istio et des applications au sein du maillage de services (repor-
tez-vous au chapitre 6 si vous avez besoin d’une actualisation sur la façon de procéder).
Utilisez le manifeste suivant pour installer et configurer Prometheus :
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.9/samples/ \
addons/prometheus.yaml

La figure 7-4 montre le tableau de bord Kiali obtenu. Vous remarquerez que la passerelle
d’entrée Istio envoie le trafic à Kiali, ainsi que d’autres déploiements dans l’espace de noms
istiosystem. Le tableau de bord présente différentes configurations et fonctionnalités
Istio qui peuvent être utilisées directement à partir de l’interface utilisateur. Par exemple,
si vous regardez l’espace de noms par défaut dans l’onglet Présentation, vous pouvez voir
que l’auto-injection de proxy sidecar est déjà activée pour tous les déploiements dans cet
espace de noms (voir la figure 7-5). Vous pouvez utiliser cette fonctionnalité à partir de
l’interface utilisateur pour activer ou désactiver l’injection automatique du proxy sidecar.

Figure 7-4. Tableau de bord Kiali montrant un trafic entrant simple

182 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Figure 7-5. Tableau de bord Kiali montrant l’état du proxy sidecar d’injection automatique
dans l’espace de noms par défaut

Exploration du tableau de bord Kiali


Dans cette section, nous vous expliquerons comment utiliser les fonctionnalités Istio
directement à partir du tableau de bord Kiali. Dans le cadre de cette démonstration, nous
allons utiliser un exemple de petit service d’application de voyage. Nous ne nous concentrerons
pas beaucoup sur l’application, mais nous mettrons en évidence les fonctionnalités de Kiali
et leur efficacité pour fournir tous les détails sur votre application native du cloud. Nous
présenterons également la logique métier de l’application de démonstration dans son
ensemble. Si vous souhaitez en savoir plus, reportez-vous à la documentation officielle Kiali
pour la démonstration de voyage.
Cette application simule deux domaines d’activité organisés dans différents espaces de
noms. Le premier espace de noms est appelé travel-portal et il dispose de plusieurs
agences de voyages, où les utilisateurs peuvent rechercher et réserver des vols, des hôtels,
des voitures et une assurance voyage. Les applications de l’agence peuvent se comporter
différemment en fonction des caractéristiques de la demande, telles que le canal (web ou
mobile) et l’utilisateur (nouveau ou existant). Tous les portails consomment un service
appelé travels qui est déployé dans l’espace de noms travel-agency.
Le deuxième espace de noms est appelé travel-agency et il héberge un ensemble de
services créés afin de fournir les devis pour les voyages. Un service de voyage principal sera
le point d’entrée pour l’agence de voyage. Il reçoit une ville de destination et un utilisateur
comme paramètres et calcule tous les éléments qui composent un budget de voyage : billet
d’avion, hébergement, réservation de voiture et assurance voyage.
Chaque service peut fournir un devis indépendant et le service de voyage doit ensuite les
regrouper dans une seule réponse.

Le maillage des services 183


Il existe également un troisième espace de noms, appelé travel-control, qui exécute le
tableau de bord principal avec différentes fonctions.
Nous avons déployé ces trois espaces de noms dans les services comme suit :
$ kubectl create namespace travel-agency
$ kubectl create namespace travel-portal
$ kubectl create namespace travel-control

$ kubectl apply -f <(curl -L https://raw.githubusercontent.com/kiali/demos/master/travels/ \


travel_agency.yaml) -n travel-agency
$ kubectl apply -f <(curl -L https://raw.githubusercontent.com/kiali/demos/master/travels/ \
travel_portal.yaml) -n travel-portal
$ kubectl apply -f <(curl -L https://raw.githubusercontent.com/kiali/demos/master/travels/ \
travel_control.yaml) -n travel-control

Après avoir déployé l’application de voyage de démonstration, vous devez commencer par
activer la prise en charge du proxy sidecar. Vous allez activer l’injection automatique pour
tous les espaces de noms nouvellement créés en cliquant sur les trois points dans la zone en
surbrillance à la figure 7-6.

Figure 7-6. Activation de l’injection automatique pour l’espace de noms travel-portal

Vous pouvez également activer l’injection automatique dans une charge de travail déjà
déployée (par exemple, des Pods) en cliquant sur le bouton Actions situé dans le coin
supérieur droit (voir la figure 7-7).

184 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Figure 7-7. Activation de l’injection automatique pour la charge de travail des voitures

Lorsque vous activez l’injection automatique pour les charges de travail via le tableau de
bord Kiali, le Pod d’application est redéployé avec un sidecar injecté automatiquement, ce
qui est similaire à la méthode que nous avons abordée précédemment pour le Pod Nginx.
Vous pouvez également utiliser la fonction de routage des demandes pour un service dans
l’onglet Services, comme illustré à la figure 7-8, afin de générer une règle de trafic qui crée
une configuration Istio pour acheminer une quantité de trafic souhaitée vers un hôte de
passerelle spécifique, ou qui utilise des disjoncteurs.

Figure 7-8. Actions de l’onglet Services

Le maillage des services 185


Vous pouvez utiliser la fonctionnalité de routage des demandes sur le service de contrôle pour
générer des règles de trafic en cliquant sur le bouton Ajouter une règle, afin d’ajouter une règle
par défaut selon laquelle toute demande sera acheminée vers la charge de travail du contrôle.
De même, vous pouvez ajouter une passerelle avec un hôte à votre équilibreur de charge et la
mettre à jour en cliquant sur le bouton Mettre à jour, comme illustré à la figure 7-9.

Figure 7-9. Ajout d’un itinéraire de requête pour un service dans une configuration Istio via Kiali

Kiali fournit également des journaux disponibles dès le départ, des métriques via
Prometheus et des suivis via Jaeger si vous les avez déployés dans votre environnement.
Nous vous conseillons vivement d’explorer l’interface utilisateur de Kiali pour découvrir
l’ensemble de ses fonctionnalités.

186 Chapitre 7 : Découverte des services et maillage de services : trouver de nouveaux territoires et franchir les frontières
Résumé
Dans ce chapitre, nous avons exploré la découverte des services et le maillage de services,
deux concepts importants qui vous permettent d’associer efficacement vos applications
à un environnement cloud comme Azure. Nous avons présenté CoreDNS comme DNS
par défaut remplaçant kube-dns en tant que mécanisme de découverte de services dans
l’environnement Kubernetes. Nous avons également vu le maillages de services Istio, qui
utilise Envoy comme plan de données pour obtenir des informations utiles sur les services
et fournit l’ensemble complet de fonctionnalités du maillage de services Istio. Vers la fin
du chapitre, nous avons présenté Kiali, qui fournit une console de gestion complète sur un
maillage de services basé sur Istio. Kiali est en mesure de fournir une observabilité dans un
service natif du cloud en offrant une vue unique de tout dans la pile.
Dans le chapitre suivant, nous verrons en détail comment le réseau de conteneurs fonctionne
dans Kubernetes et comment vous pouvez sécuriser votre infrastructure avec diverses
techniques de gestion des stratégies.

Résumé 187
CHAPITRE 8
Gestion des réseaux et des stratégies :
De véritables gardiens

Dans les chapitres précédents, nous avons créé une infrastructure dans Azure et nous avons
expliqué comment découvrir et surveiller nos applications. Maintenant, il est temps de
sécuriser ces applications ! Bien qu’il y ait eu de nombreuses violations de données dans
le cloud au fil du temps en raison de simples erreurs de configuration, la sécurisation de
l’infrastructure cloud est bien plus simple qu’il n’y paraît. La technologie de mise en réseau
dans le cloud a rapidement évolué et, aujourd’hui, un certain nombre de fournisseurs
proposent des logiciels natifs du cloud qui peuvent vous aider à améliorer votre configuration
réseau et à la sécuriser.
Azure est livré avec une offre, la stratégie Azure, qui vous permet de définir une stratégie sur
un locataire, un groupe de gestion ou un abonnement, afin de bénéficier d’une couche de
sécurité par défaut. Par exemple, dans Azure, vous pouvez définir une stratégie qui garantit
qu’aucun compte de stockage n’est accessible publiquement au sein d’un groupe de gestion
ou d’un abonnement.
Dans ce chapitre, nous allons explorer la puissance de la mise en réseau de conteneurs et
les nombreuses façons dont vous pouvez l’utiliser pour améliorer votre infrastructure. Nous
vous expliquerons également comment appliquer une stratégie à votre infrastructure pour
assurer sa sécurité.
Nous commencerons par une discussion sur la mise en réseau des conteneurs et les normes
sur lesquelles plusieurs projets sont conçus, puis nous nous concentrerons sur des produits
tels que Calico et Flannel, qui fournissent une connectivité réseau et une mise en œuvre
des stratégies réseau. Nous conclurons le chapitre par une discussion sur l’application de la
stratégie système avec Open Policy Agent (OPA).

189
Container Network Interface (CNI)
Comme nous l’avons mentionné dans les chapitres précédents, les conteneurs offrent un
vaste éventail de fonctionnalités de sécurité et de portabilité. La pile de mise en réseau est
l’une des capacités les plus intéressantes. Les fonctionnalités des espaces de noms cgroups
et réseau de Linux permettent d’accéder à diverses fonctionnalités réseau qui peuvent être
utilisées pour améliorer la sécurité et la télémétrie, ainsi que pour gérer les performances.
Cilium, un outil logiciel qui fournit l’équilibrage de charge, la sécurité, la télémétrie, la
gestion de la bande passante et d’autres fonctionnalités en tant que module complémentaire
pour Kubernetes, est un bon exemple de la flexibilité et de la puissance de la fonctionnalité
de réseau de conteneurs. Nous aborderons les Cilium en détail plus loin dans ce chapitre.
Au chapitre  3, nous avons brièvement mentionné comment le secteur dispose d’une
spécification standard pour le runtime de conteneur et l’image de conteneur. L’interface
de réseau de conteneurs (CNI) a été créée en  2015 (et a finalement été ajoutée à la liste
de projets d’incubation de la Cloud Native Computing Foundation [CNCF]) en tant que
norme pour la configuration des interfaces réseau de conteneurs et des paramètres réseau
associés. La spécification CNI permet de gérer les primitives suivantes :

• Noms d’interface et liaisons d’espace de noms réseau


• Adresses IP
• Routage
• Paramètres DNS
• Paramètres sysctl

Voici un exemple de configuration CNI :


{
"cniVersion": "1.0.0",
"name": "dbnet",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"args": {
"labels" : {
"appVersion" : "1.0"
}
},
"ipam": {
"type": "host-local",
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
},
{
"type": "tuning",

190 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


"sysctl": {
"net.core.somaxconn": "500"
}
}
]
}

Pourquoi utiliser un CNI ?


Vous vous demandez peut-être pourquoi le secteur a besoin d’une spécification uniquement
pour la mise en réseau. Comme nous l’avons mentionné au chapitre  3, l’écosystème de
conteneurs dispose d’un grand nombre de projets de mise en réseau qui se connectent à des
systèmes de conteneurs tels que Docker et Kubernetes. Plusieurs groupes créent des plug-
ins liés au réseau accessibles au public. Il est donc nécessaire de définir et de contrôler la
couche réseau du conteneur de manière standard.
La plateforme CNI permet une véritable virtualisation d’infrastructure, ce qui signifie que
vous n’avez pas besoin de vous soucier de la gestion de l’infrastructure réseau, y compris
dans le cloud ! L’utilisation de la plateforme de conteneurs en tant qu’infrastructure réseau
réduit considérablement le coût de la mise en œuvre et de l’exploitation d’une infrastructure
réseau complexe, et offre la possibilité d’évoluer et de mettre à niveau sans avoir à investir
dans des appareils réseau virtuels physiques ou dédiés.

Comment CNI fonctionne-t-il sur Azure ?


Azure prend en charge CNI, mais il dispose de deux plug-ins spéciaux qui connectent vos
conteneurs au réseau virtuel Azure (VNet) :

• azure-vnet, qui implémente le plug-in de réseau CNI


• azure-vnet-ipam, qui implémente le plug-in de gestion des adresses IP du CNI (IPAM)

Ces plug-ins garantissent que le conteneur peut s’interfacer correctement avec le plan de
contrôle Azure VNet. Vous pouvez utiliser ces plug-ins dans Azure Kubernetes service
(AKS) et des machines virtuelles Azure autonomes.

Mise en réseau Windows


Bien que les piles de réseau pour Linux et Windows soient
fondamentalement très différentes, les plug-ins Azure CNI disposent
d’une excellent prise en charge de Windows, ce qui signifie que vous
pouvez exécuter des conteneurs Windows et Linux dans la même
infrastructure sans avoir à créer une infrastructure réseau distincte.

Container Network Interface (CNI) 191


Voici un exemple de configuration Azure CNI :
{
"cniVersion": "1.0.0",
"name": "azure",
"plugins": [
{
"type":"azure-vnet",
"mode":"bridge",
"bridge":"azure0",
"ipam":{
"type":"azure-vnet-ipam"
}
},
"ipam": {
"type": "azure-vnet-ipam",
"Environment": "azure"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
},
{
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
]
}

Avec les plug-ins azure-vnet et azure-vnet-ipam, l’hôte n’a plus besoin de s’occuper de la
configuration ni de la gestion des IP, car c’est le plan de contrôle Azure VNet qui s’en charge.

Divers projets CNI
Le projet CNI contient un ensemble de plug-ins CNI par défaut appartenant à trois catégories :
Principal
Ces plug-ins créent des interfaces (bridge, ipvlan, macvlan, ptp, host-device).
IPAM
Ces plug-ins affectent des adresses IP aux interfaces (dhcp, host-local, statique).
Meta
Il s’agit d’autres plug-ins natifs qui permettent d’effectuer des actions telles que :
• Réglage de sysctl
• Limitation de la bande passante
• Pare-feu
• Flannel (que nous verrons en détails plus loin dans ce chapitre)
Vous trouverez plus d’informations sur ces plug-ins natifs sur la page des plug-ins CNI.

192 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


Outre les plug-ins CNI que nous avons mentionnés ici, il existe un vaste écosystème de
projets tiers qui écrivent des plug-ins compatibles avec CNI. Plus de 25 projets extérieurs
avaient tiré parti de la norme CNI au moment de la rédaction de ce document. Ces projets
fournissent différents types de fonctionnalités, notamment l’équilibrage de charge, IPAM,
les réseaux de superposition, la sécurité et la télémétrie réseau.
Parmi les plus petits projets, citons :

• Infoblox
• Juniper Contrail
• VMware NSX

Bien que certains plug-ins CNI soient conçus pour une infrastructure très spécifique (par
exemple, le CNI ACI de Cisco), bon nombre d’entre eux fonctionnent parfaitement bien avec
les services Azure et avec Azure. Dans les sections suivantes, nous allons explorer Calico,
Cilium, Flannel et OPA, puis nous aborderons la façon dont ces systèmes contribuent à la
création et à la sécurisation de l’infrastructure réseau.

Calico
Calico est une solution de sécurité et de stratégie de réseau open source pour les conteneurs,
les machines virtuelles et les installations bare metal sous Windows et Linux. Calico fournit
l’application de la stratégie de sécurité réseau, ainsi que la possibilité de mettre en œuvre
des réseaux Zero Trust. Calico prend en charge plusieurs plans de données, y compris un
plan de données de pointe Berkeley Packet Filter étendu (eBPF) Linux, le plan de données
de réseau Linux standard et le plan de données Windows HNS. Calico est exécuté au-dessus
du plan de données hôte pour effectuer l’application des stratégies réseau. Il prend en charge
la pile de réseau Linux standard.

Pourquoi utiliser Calico ?


La gestion de la connectivité et de la stratégie réseau devient exponentiellement plus
complexe à mesure que votre infrastructure se développe. En outre, il est particulièrement
difficile de s’assurer que la stratégie prévue est correcte, qu’elle est appliquée correctement et
que les commentaires sur la stratégie sont pris en compte. C’est là que Calico se distingue.
Il vous permet de définir votre stratégie réseau d’une manière abstraite et évolutive, puis
s’assure que la stratégie est appliquée sans sacrifier les performances du réseau.

Calico 193
Architecture de base
Calico est doté d’une architecture client/magasin de données qui simplifie le fonctionnement
de la plateforme. Il inclut les composants clés suivants (également représentés à la figure 8-1) :
Felix
Démon de programmation réseau qui installe des listes de contrôle d’accès réseau
(ACL), des informations de routage et la gestion des interfaces et des rapports sur l’état
BIRD
Obtient des informations de routage de Felix et redistribue le routage via le
protocole BGP (routes over Border Gateway Protocol)
confd
Écoute les modifications de routage depuis le magasin de données Calico et les transmet
à BIRD
Plug-in CNI Calico
Interface des interfaces réseau du conteneur
Banque de données
Contient des informations opérationnelles sur les stratégies, les charges de travail et les
allocations IPAM
Typha
Proxy de mise en cache pour le magasin de données utilisé pour mettre à l’échelle le
nombre de nœuds qui se connectent au magasin de données
calicoctl
Interface de ligne de commande pour la création, la lecture, la mise à jour et la
suppression d’objets Calico

194 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


Figure 8-1. Architecture Calico

Déploiement de Calico
Il existe deux options pour le déploiement de Calico : l’installer dans le cadre du moteur de
stratégie réseau dans une installation AKS ou le déployer manuellement sur Kubernetes.

Calico 195
Déploiement de Calico via l’installation AKS
Azure offre une excellente prise en charge de Calico en tant que moteur de stratégie
réseau. Il peut être installé dans le cadre de la création d’une instance AKS dans l’étape de
configuration réseau, comme illustré à la figure 8-2.

Figure 8-2. Configuration de la stratégie de réseau pour AKS

Dans cette étape, si vous cliquez sur Calico dans le cadre de l’installation d’AKS, un Pod
Calico Typha est installé sur chaque nœud de calcul Kubernetes.

Installation manuelle de Calico


Si vous souhaitez installer manuellement Calico sur votre cluster Kubernetes, vous pouvez
utiliser kubectl pour télécharger l’opérateur et installer les composants :

1. Tout d’abord, installez l’opérateur, puis les ressources Calico. Cela créera un espace
de noms d’opérateur tigera- :
$ kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
$ kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml

2. Vérifiez que tous les nœuds de Calico sont maintenant en cours d’exécution :
$ kubectl get pods -n calico-system

3. Attendez que tous les Pods aient l’état En cours d’exécution.

La configuration ipPools dans le fichier custom-resources.yaml


ne peut pas être modifiée après le déploiement. Cela signifie que
les ipPools (les adresses  IP qui peuvent être attribuées à chaque
Pod) sont définies une fois que vous avez appliqué le fichier
custom-resources.yaml.
 

Installation de calicoctl
Pour contrôler Calico, vous devrez avoir accès à son utilitaire d’interface de ligne de
commande. Il existe plusieurs façons d’accéder à l’utilitaire, comme décrit dans la
documentation d’installation de Calico. Dans le cadre de cet exemple, nous allons
simplement télécharger l’interface de ligne de commande sur notre machine et l’exécuter
en tant que plug-in kubectl :

196 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


1. Connectez-vous à un hôte et accédez à /usr/local/bin :
$ cd /usr/local/bin

2. Téléchargez le binaire :
$ curl -o kubectl-calico -O -L "https://github.com/projectcalico/calicoctl/releases/ \
download/v3.21.0/calicoctl-linux-ppc64le"

3. Définissez le binaire de façon à ce qu’il soit exécutable :


$ chmod +x kubectl-calico

4. Vérifiez que le plug-in fonctionne :


$ kubectl calico -h

Exploration de Calico
Nous avons vu les avantages généraux de l’utilisation de Calico, ainsi que quelques notions
de base sur son fonctionnement et son installation. Nous allons maintenant détailler son
utilisation. Dans le reste de cette section, nous allons utiliser eBPF comme plan de données
au lieu de la pile réseau Linux standard.
Azure n’autorise pas les IP inconnues dans son plan de données, de sorte que vous ne pouvez
utiliser qu’un réseau de superposition basé sur VXLAN entre les nœuds. Cela n’est possible
que si vous créez un cluster auto-géré (figure 8-3) et non AKS. Sur AKS, vous devez utiliser
 

Azure comme fournisseur de réseau, comme illustré à la figure 8-4.

Figure 8-3. Configuration auto-gérée (avec VXLAN)

Figure 8-4. Configuration du réseau AKS

Activation d’eBPF
La prochaine étape de la mise en place de notre infrastructure Calico consiste à activer
le plan de données eBPF.  L’activation d’eBPF vous permet de tirer parti des nouvelles
fonctionnalités du noyau et de supprimer l’utilisation de kube-proxy pour l’équilibrage de
charge. Lorsqu’eBPF est activé, l’utilisation de kube-proxy est désactivée. Pour en savoir plus
sur la mise en œuvre d’eBPF de Calico, lisez cet article de blog.

Calico 197
Pour activer eBPF, procédez comme suit :

1. Recherchez le serveur d’API en exécutant :


$ kubectl get configmap -n kube-system kube-proxy -o jsonpath='{.data.kubeconfig}' \
| grep server`
server: https://d881b853ae312e00302a84f1e346a77.hcp.us-east-1.azmk8s.io

2. Mettez à jour votre espace de noms tigera-operator :


kind: ConfigMap
apiVersion: v1
metadata:
name: kubernetes-services-endpoint
namespace: tigera-operator
data:
KUBERNETES_SERVICE_HOST: "<API server host>"
KUBERNETES_SERVICE_PORT: "<API server port>"

3. Redémarrez l’opérateur pour récupérer la modification :


$ kubectl delete pod -n tigera-operator -l k8s-app=tigera-operator

4. Désactivez le déploiement de kube-proxy (pour économiser les ressources) :


$ kubectl patch networks.operator.openshift.io cluster --type merge -p \
'{"spec":{"deployKubeProxy": false}}'

5. À l’aide de calicoctl, activez eBPF :


$ calicoctl patch felixconfiguration default --patch='{"spec": {"bpfEnabled": true}}'

Si vous rencontrez des problèmes en cours d’exécution d’eBPF, Calico a élaboré un


guide de dépannage.

Si vous utilisez eBPF et que vous disposez d’une version du noyau


ultérieure à  4.16, Calico essaiera automatiquement d’utiliser
XDP pour traiter les paquets, ce qui permettra d’améliorer les
performances. Ceci est utile pour les services à haut débit ou les
services DOS.

Mise en œuvre de la stratégie de sécurité Calico


Maintenant que notre réseau est configuré, nous sommes prêts à appliquer une stratégie de
sécurité. La stratégie de réseau de Calico a un plus grand ensemble de fonctionnalités que
Kubernetes, notamment :

• Ordre des stratégies


• Règles de refus
• Une plus grande flexibilité pour les règles de correspondance

La stratégie de réseau de Calico prend en charge la sécurisation des applications à l’aide des
critères OSI des couches 5 à 7, ainsi que de l’identité de chiffrement. Vous trouverez des
exemples sur la page Configuration de la stratégie réseau de Calico.

198 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


La stratégie réseau peut être appliquée de plusieurs façons :

• À l’échelle mondiale (s’applique à tous les Pods de tous les espaces de noms), connue
sous le nom de GlobalNetworkPolicy
• Par réseau ou hôte, connu sous le nom de HostEndpoint
• Par espace de noms, connu sous le nom NetworkPolicy

GlobalNetworkPolicy : autorisation du trafic ICMP


La première stratégie que nous allons créer est une stratégie de réseau mondial qui autorise
le trafic ICMP pour ping et traceroute à partir de tous les hôtes du cluster. Créez le fichier
global-policy.yaml comme suit :
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-ping-in-cluster
spec:
selector: all()
types:
- Ingress
ingress:
- action: Allow
protocol: ICMP
source:
selector: all()
icmp:
type: 8 # Ping request
- action: Allow
protocol: ICMPv6
source:
selector: all()
icmp:
type: 128 # Ping request

Appliquez la stratégie en exécutant :


$ calicoctl create global-policy.yaml

NetworkPolicy : autorisation du trafic au sein d’un espace de noms entre deux étiquettes
Dans cet exemple, nous allons autoriser le trafic de l’espace de noms de production avec
le sélecteur vert vers un Pod dans le même espace de noms avec le sélecteur bleu sur le
port TCP/1234. Créez un nouveau fichier appelé network-policy.yaml :
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-tcp-1234
namespace: production
spec:
selector: color == 'blue'
ingress:
- action: Allow
protocol: TCP

Calico 199
source:
selector: color == 'green'
destination:
ports:
- 1234

Appliquez la stratégie en exécutant :


$ calicoctl create network-policy.yaml

Une fois cette stratégie appliquée, le trafic sera en mesure de circuler vers le port TCP 1234
pour n’importe quel Pod vert.

Cilium
L’un des outils de mise en réseau les plus innovants qui a été lancé au cours des cinq
dernières années est le projet Cilium, qui utilise largement eBPF pour fournir une suite
de fonctionnalités de mise en réseau, d’observabilité et de sécurité qui sont orientées vers
l’infrastructure native du cloud.
Cilium utilise la technologie du noyau Linux eBPF qui permet de bénéficier d’une visibilité
réduite, d’une puissance élevée et d’une logique de contrôle pour le noyau et les applications
qui s’exécutent au-dessus de celle-ci. L’utilisation d’eBPF a contribué à la popularité de
Cilium pour les architectes d’infrastructure, car la plateforme fournit des fonctionnalités
autour de trois piliers clés :
Mise en réseau
Cilium implémente son propre CNI qui utilise Linux eBPF. Cilium est ainsi extrêmement
performant et peut effectuer un équilibrage de charge au lieu de s’appuyer sur kube-
proxy. Il permet également la connectivité multicluster, ce qui est important pour les
grandes installations.
Observabilité
Grâce à l’utilisation d’eBPF, Cilium est un logiciel d’observabilité unique. Cilium peut
effectuer une inspection approfondie des paquets sur toutes les couches de connectivité
et de données du réseau.
Sécurité
L’une des meilleures fonctionnalités de Cilium est qu’il peut effectuer un chiffrement
transparent entre les hôtes via IPSec. Cela signifie que vous obtenez un chiffrement de
bout en bout via un mécanisme très efficace, sans avoir à apporter de modifications
à votre application. Bien que Cilium fournisse sa propre stratégie de sécurité de
correspondance avec étiquette + CIDR, il effectue également un filtrage (OSI) de
couche  7 (par exemple, il filtre les requêtes  DNS sortantes ou les requêtes  HTTP
entrantes).
En dernier lieu, Cilium vous permet d’effectuer une introspection de la couche  7 pour
prendre des décisions stratégiques (consultez cet article de blog pour en savoir plus).

200 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


L’utilisation d’eBPF au cœur du fonctionnement de Cilium permet au logiciel d’approfondir
et de manipuler (si nécessaire) tout le trafic réseau sans affecter les performances du système.
L’agent Cilium (cilium-agent) s’exécute sur chaque nœud (voir la figure 8-5) et accepte la
configuration via l’API Kubernetes. L’agent transforme cette configuration en programme
eBPF qui s’exécute sur l’interface réseau du conteneur, sur le réseau de l’hôte ou sur l’une
des cartes réseau de l’hôte.

Figure 8-5. Architecture Cilium

Déploiement de Cilium
Cilium peut être installé sur un cluster Kubernetes auto-géré ou sur un cluster AKS géré.
Dans les deux cas, Cilium peut être déployé via Helm, ce qui facilite l’installation. En outre,
Cilium fournit un conteneur pour effectuer des contrôles de connectivité afin de vérifier
que le trafic réseau peut circuler librement entre les Pods de votre cluster.
Pour plus d’informations, consultez la documentation spécialisée sur le déploiement de
Cilium sur Azure.

Installation de Cilium auto-gérée


Nous allons installer Cilium à l’aide de son fichier d’installation rapide :

Cilium 201
$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/v1.9/install/ \
kubernetes/quick-install.yaml

Le code suivant va installer le contrôle de connectivité entre les hôtes :


$ kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/v1.9/examples/ \
kubernetes/connectivity-check/connectivity-check.yaml

Cela va déployer une série de Pods qui utiliseront différents chemins de réseau pour se
connecter les uns aux autres afin de vérifier la connectivité. Les chemins de connectivité
incluent ou non l’équilibrage de charge de service et diverses combinaisons de stratégies réseau.

Installation d’AKS Cilium


Pour configurer Cilium sur AKS, nous allons utiliser un tableau Helm :

1. Tout d’abord, nous allons créer un cluster AKS :


$ export RESOURCE_GROUP_NAME=aks-test
$ export CLUSTER_NAME=aks-test
$ export LOCATION=westus

$ az group create --name $RESOURCE_GROUP_NAME --location $LOCATION


$ az aks create \
--resource-group $RESOURCE_GROUP_NAME \
--name $CLUSTER_NAME \
--location $LOCATION \
--node-count 2 \
--network-plugin azure

2. Maintenant, nous allons créer un principal de service pour interagir avec les API Azure
et remplir les variables d’environnement à transmettre à Helm :
$ az ad sp create-for-rbac --name cilium-operator > azure-sp.json

$ AZURE_SUBSCRIPTION_ID="$(az account show | jq -r .id)"


$ AZURE_CLIENT_ID="$(jq -r .appId < azure-sp.json)"
$ AZURE_CLIENT_SECRET="$(jq -r .password < azure-sp.json)"
$ AZURE_TENANT_ID="$(jq -r .tenant < azure-sp.json)"
$ AZURE_NODE_RESOURCE_GROUP="$(az aks show --resource-group $RESOURCE_GROUP_NAME \
--name $CLUSTER_NAME | jq -r .nodeResourceGroup)"

3. Nous allons installer le référentiel Cilium localement, puis installer Cilium sur notre
cluster Kubernetes :
$ helm repo add cilium https://helm.cilium.io/
$ helm install cilium cilium/cilium --version 1.9.9 \
--namespace kube-system \
--set azure.enabled=true \
--set azure.resourceGroup=$AZURE_NODE_RESOURCE_GROUP \
--set azure.subscriptionID=$AZURE_SUBSCRIPTION_ID \
--set azure.tenantID=$AZURE_TENANT_ID \
--set azure.clientID=$AZURE_CLIENT_ID \
--set azure.clientSecret=$AZURE_CLIENT_SECRET \
--set tunneldisabled \
--set ipam.mode=azure \
--set masquerade=false \
--set nodeinit.enabled=true

202 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


--set kubeProxyReplacement=strict \
--set hostFirewall=true \
--set loadBalancer.algorithm=maglev \
--set k8sServiceHost=REPLACE_WITH_API_SERVER_IP \
--set k8sServicePort=REPLACE_WITH_API_SERVER_PORT

4. Nous pouvons contrôler l’installation en vérifiant que les quatre Pods initiaux ont été
créés et que des Pods cilium et cilium-operator sont en cours d’exécution :
$ kubectl -n kube-system get pods --watch
cilium-operator-ad4375ds5-2x2q3 1/1 Running 0 4m18s
cilium-s5x8xk 1/1 Running 0 4m19s

Installation de Hubble
Vous devez également installer Hubble, une interface utilisateur qui vous permet d’afficher
une carte de service représentant les dépendances entre les services et les statistiques sur
l’utilisation et les performances du réseau. L’interface de mise à jour en temps réel de Hubble
(voir la figure 8-6) vous permet de présenter un grand nombre d’informations sur la mise
en page et le flux des données entre les conteneurs. De plus, elle permet de modéliser la
stratégie Cilium.

Figure 8-6. Capture d’écran de l’interface utilisateur Hubble montrant une architecture simple

Vous pouvez installer Hubble à l’aide de kubectl :


kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/v1.9/install/kubernetes/ \
quick-hubble-install.yaml

Cilium 203
Intégration de Cilium à votre cloud
Comme nous l’avons déjà mentionné, Cilium dispose d’un riche ensemble de fonctionnalités
dont vous pouvez tirer parti. Dans cette section, nous allons utiliser Cilium en tant que
fournisseur de réseau et d’agent responsable de l’application des stratégies (pare-feu). Nous
verrons ensuite l’observabilité avec Cilium.

Pare-feu hôte
Comme nous l’avons fait dans le cadre de l’exploration approfondie de Calico, nous allons
mettre en œuvre un ensemble de stratégies réseau. L’avantage de Cilium est qu’il peut
appliquer la stratégie sur les couches L3, L4 ou L7. La mise en œuvre de la stratégie réseau
comporte trois modes, comme indiqué dans le tableau 8-1.

Tableau 8-1. Modes de mise en œuvre de la stratégie réseau


Mode Description
Par défaut Si une règle sélectionne des points de terminaison et qu’elle comporte une section d’entrée ou de sortie, le
point de terminaison sera refusé conformément à la stratégie.
Toujours L’application de la stratégie est activée sur tous les points de terminaison, même si aucune règle ne sélectionne
des points de terminaison spécifiques.
Jamais L’application de la stratégie est désactivée sur tous les points de terminaison et tout le trafic est autorisé.

Pour configurer le mode d’application de la stratégie au moment de l’exécution pour tous


les points de terminaison gérés par un agent  Cilium, utilisez la commande suivante et
choisissez le mode Par défaut, Toujours ou Jamais.
$ cilium config PolicyEnforcement={default,always,never}

La stratégie est écrite par rapport à un modèle de liste autorisée dans lequel le trafic sera
bloqué s’il n’existe pas de règle d’autorisation explicite.

Les règles des couches L3 et L4 peuvent être spécifiées à l’aide des méthodes suivantes :

Étiquettes
Utilisez un sélecteur pour regrouper les Pods.

Services
Définissez une stratégie à l’aide d’un service Kubernetes.

Entités
Étendues prédéfinies spéciales gérées par Cilium.

IP/CIDR
Une IP ou une plage IP (CIDR).

Noms DNS
Un nom DNS.

204 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


Par exemple, si vous souhaitez autoriser le trafic entre deux Pods étiquetés, vous pouvez
créer la stratégie suivante :
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: "l3-rule"
spec:
endpointSelector:
matchLabels:
role: backend
ingress:
- fromEndpoints:
- matchLabels:
role: frontend

Vous pouvez appliquer cette stratégie en exécutant :


$ cilium policy import l3-rule-example-1.yaml

En outre, si vous souhaitez créer un exemple L4 dans lequel vous souhaitez limiter le DNS
de sortie aux serveurs DNS publics de Google, vous pouvez écrire la règle illustrée dans
l’exemple suivant :
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "allow-to-google-dns"
spec:
endpointSelector:
{}
egress:
- toCIDR:
- 8.8.8.8/32
- 8.8.8.4/32
toPorts:
- ports:
- port: '53'
protocol: UDP
- ports:
- port: '53'
Protocol: TCP

Vous pouvez appliquer la règle en exécutant :


$ cilium policy import l4-rule-example-1.yaml

En dernier lieu, Cilium permet également la visibilité L7 pour le trafic HTTP(s), Kafka et
DNS. Dans l’exemple suivant, nous autorisons le trafic vers le point de terminaison /admin
sur le port 80 pour tous les Pods :
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "Allow HTTP GET /admin
endpointSelector:
{}
ingress:

Cilium 205
- fromEndpoints:
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET"
path: "/admin"

Vous trouverez des détails plus spécifiques sur les règles de stratégie L7 dans la documentation
Cilium et la stratégie de couche 7.

Cilium fournit un outil interactif qui permet de modéliser les


stratégies de réseau et de les visualiser. Vous pouvez le tester sur la
page Éditeur Cilium.

Observabilité
Comme nous l’avons mentionné brièvement plus tôt, Hubble est l’endroit où les opérateurs
Cilium peuvent observer ce qui se passe dans leur infrastructure. Une fois que vous aurez
installé Hubble et le test de connectivité (à partir des étapes d’installation), vous serez en
mesure d’observer les flux réseau au sein de l’espace de noms. Dans cette section, nous irons
plus loin et nous utiliserons une nouvelle fonctionnalité Cilium qui fournit une visibilité de
protocole pour les couches 3 et 4, afin de vous aider à obtenir des détails plus spécifiques
sur le trafic au niveau de chaque port. Vous devrez implémenter des stratégies de couche 7
pour obtenir des fonctionnalités d’observabilité de cette dernière.
Pour activer la visibilité sur la couche 3 et la couche 4, vous devrez activer les annotations
sur vos Pods. Voici un exemple :
$ kubectl annotate pods --all io.cilium.proxy-visibility="<Egress/53/UDP/DNS>, \
<Ingress/80/TCP/HTTP>"

Cela permettra de mesurer le trafic  DNS sortant (sortie), ainsi que le trafic entrant
(entrée) HTTP vers le Pod.
Les informations de visibilité sont représentées par une liste de tuples séparés par des
virgules dans l’annotation :
<{Traffic Direction}/{L4 Port}/{L4 Protocol}/{L7 Protocol}>

Lorsqu’il est activé, Hubble affiche des informations spécifiques au protocole. La figure 8-7
montre un exemple de visibilité DNS.

206 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


Figure 8-7. Exemple d’observabilité dans le trafic DNS

Si vous souhaitez apprendre à inspecter les connexions chiffrées TLS (Transport Layer
Security), consultez la page Inspection des connexions chiffrées TLS.

Flannel
Flannel est un système d’infrastructure réseau (OSI) de couche  3 pour Kubernetes
qui était initialement intégré au projet CoreOS de Red Hat. Il s’agit de l’un des CNI les
plus simples pour Kubernetes. Il s’appuie sur un agent binaire, nommé flanneld, pour
être exécuté sur chaque hôte afin de configurer un réseau Pod de superposition, puis de
stocker la configuration dans le magasin de données Kubernetes. Flannel ne fournit que la
connectivité réseau. Il ne prend en charge aucun type d’application de la stratégie de réseau.

Déploiement de Flannel
Flannel peut être installé rapidement lorsque vous configurez votre cluster en exécutant :
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/ \
Documentation/kube-flannel.yml

Flannel 207
L’application de kube-flannel.yml définira ce qui suit :

• 
ClusterRole et ClusterRoleBinding pour le contrôle d’accès basé sur les rôles (RBAC).
• Le compte de service Kubernetes qui sera utilisé par Flannel.
• Un ConfigMap contenant à la fois une configuration CNI et une configuration de
Flannel. Le réseau de la configuration de Flannel doit correspondre au système de
routage interdomaine sans classe (CIDR) du réseau du Pod. Le choix du back-end est
également effectué ici et la valeur par défaut est VXLAN.
• Un DaemonSet pour chaque architecture afin de déployer le Pod Flannel sur chaque nœud.
Le Pod comporte deux conteneurs : le démon Flannel lui-même et un initContainer
pour le déploiement de la configuration CNI à un emplacement que le kubelet peut lire.

Lorsque vous exécuterez des Pods, des adresses IP leur seront allouées à partir du CIDR
de réseau du Pod. Quel que soit le nœud sur lequel ces Pods se retrouveront, ils seront en
mesure de communiquer entre eux.

Ce manifeste va configurer le réseau du Pod afin qu’il soit


10.244.0.0/16 et qu’il utilise le serveur principal VXLAN par défaut.
Un masque 10.244.X.0/24 sera donné à chaque nœud du Pod.

Vous pouvez reconfigurer votre back-end de façon à ce qu’il utilise un autre espace
d’adresse, un masque plus petit ou des paramètres de back-end différents en modifiant les
données écrites dans net-conf.json dans le fichier fkube-flannel.yaml. Vous trouverez plus
d’informations sur la configuration du back-end dans les pages Documentation sur le back-
end Flannel et Configuration de Flannel.
Désormais, les nœuds de travail Kubernetes auront des adresses IP à partir de votre VNet
Azure, mais n’importe quel Pod déployé aura des adresses  IP issues de la plage d’IP du
réseau de Pod configurée. Si vous souhaitez disposer de plusieurs réseaux Pod (pour la
séparation du trafic réseau), vous devrez exécuter un processus flanneld distinct.

Exploration de Flannel
Flannel est un CNI Kubernetes plus simple qui nécessite une configuration minimale. Il
dispose de back-ends utilisés comme des plug-ins qui créent des réseaux de superposition
entre les machines d’un réseau (voir la figure 8-8). Les back-ends de transport pris en charge
sont les suivants :

• VXLAN
• Host-gw
• Protocole UDP (User Datagram Protocol)
• Alivpc

208 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


• Alloc
• Cloud privé virtuel (VPC) Amazon
• Moteur de calcul Google (GCE)
• IPIP
• IPsec

Figure 8-8. Exemple d’architecture Flannel

Flannel 209
L’équipe de projet de flanelle recommande VXLAN ou host-gw comme back-ends pour la
communication. Si vous avez besoin de chiffrer le trafic, IPsec est également une option.
Vous trouverez plus d’informations sur les back-ends pris en charge dans la documentation
de Flannel.
Flannel ne vous permet pas d’exécuter plusieurs réseaux à partir d’un seul démon ; vous
devrez exécuter des démons distincts avec différents fichiers de configuration.

Stratégie Azure
Bien qu’il soit sans aucun doute utile d’exécuter activement un logiciel qui protège votre
infrastructure basée sur le cloud, la stratégie  Azure vous permet de définir une stratégie
globale sur de nombreux aspects de votre infrastructure cloud, en particulier en ce qui con-
cerne la sécurité des ressources.
La stratégie Azure exécute trois fonctions clés :

• Crée des stratégies qui empêchent ou appliquent une configuration donnée


• Permet de rendre compte de la conformité à votre propre stratégie, ainsi que des
critères de référence du secteur
• Garantit la conformité de vos ressources grâce à une correction en bloc pour les
ressources existantes et à une correction automatique pour les nouvelles ressources

Les cas d’utilisation potentiels de la stratégie Azure incluent la mise en œuvre de la


gouvernance pour la cohérence des ressources, la conformité réglementaire, la sécurité, les
coûts et la gestion.
Les définitions de stratégie Azure sont définies au format JSON et appliquées de manière
hiérarchique ; cela signifie qu’une stratégie peut être appliquée à un groupe de gestion (ainsi
qu’à tous les abonnements/ressources qu’il contient) et une stratégie plus indulgente peut
être définie sur un groupe de gestion distinct (ainsi que tous les abonnements/ressources
qu’il contient).

Démarrage rapide de la stratégie Azure


Azure contient des stratégies prédéfinies par défaut. Vous pouvez les afficher en exécutant
cette commande dans votre terminal :
$ az policy definition list

Ou vous pouvez les afficher dans le portail (figure 8-9).

210 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


Figure 8-9. Présentation des définitions de stratégie Azure disponibles

La stratégie Azure inclut également des initiatives, qui sont des collections de stratégies
alignées sur une norme du secteur (par exemple, PCI v3.2.1:2018).
À ce stade, les stratégies ont été créées, mais aucune portée ne leur a été attribuée. Vous
pouvez appliquer une stratégie sur un abonnement (ainsi que toutes les ressources au sein
de celui-ci), ou vous pouvez l’appliquer à un groupe de gestion qui couvrira tous les groupes
de gestion enfants et les abonnements.
Dans tous les cas, vous devrez faire enregistrer le fournisseur de ressources Microsoft.
PolicyInsights pour votre ou vos abonnements. Vous pouvez l’activer en exécutant :
$ az provider register --namespace 'Microsoft.PolicyInsights'

La stratégie Azure dispose de divers modes d’application de la stratégie, également nommés


effets. Il existe actuellement sept types d’effets de stratégie Azure :

• Ajouter
• Auditer
• AuditIfNotExists
• Refuser

Stratégie Azure 211


• DeployIfNotExists
• Désactivé
• Modifier

Vous pouvez en savoir plus sur leur utilisation dans l’article « Comprendre les effets de la
stratégie Azure ».

Création de votre propre stratégie Azure


Il est facile de créer votre propre stratégie Azure. Dans l’exemple suivant, nous créons une
stratégie qui autorise uniquement le déploiement de ressources dans les régions Azure au
sein des États-Unis. Si un utilisateur tente de créer une ressource dans une région autre que
celle des États-Unis, il sera refusé :
{
"properties": {
"displayName": "Allowed locations",
"description": "This policy enables you to restrict the locations your organization
can specify when deploying resources.",
"mode": "Indexed",
"metadata": {
"version": "1.0.0",
"category": "Locations"
},
"parameters": {
"allowedLocations": {
"type": "array",
"metadata": {
"description": "The list of locations that can be specified
when deploying resources",
"strongType": "location",
"displayName": "Allowed locations"
},
"defaultValue": [ "westus2", "eastus", "eastus2", "southcentralus",
"centralus", "northcentralus", "westus", "westcentralus", "westus3" ]
}
},
"policyRule": {
"if": {
"not": {
"field": "location",
"in": "[parameters('allowedLocations')]"
}
},
"then": {
"effect": "deny"
}
}
}
}

212 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


Nous allons créer la stratégie en exécutant :
$ az policy definition create --name 'allowed-regions' --display-name 'Deny non-US regions' \
--description 'This policy ensures that resources are only created in US regions.' \
--rules 'region-rule.json' --params ‘region-params.json’---mode All

Dans la réponse, nous verrons le chemin de la nouvelle stratégie dans le champ id, par
exemple :
"id": "/subscriptions/<subscription-ID>/providers/Microsoft.Authorization/ \
policyDefinitions/allowed-regions"

Nous allons affecter notre politique à notre abonnement en exécutant :


$ az policy assignment create --name 'allowed-regions' --scope ‘ \
/subscriptions/<subscription-id>' --policy ‘/subscriptions/<subscription-ID>/ \
providers/Microsoft.Authorization/policyDefinitions/allowed-regions"

La stratégie est désormais appliquée. Si nous devions essayer de créer une ressource dans
une région autre que celle des États-Unis, nous recevrions une erreur similaire à celle
illustrée à la figure 8-10.

Figure 8-10. Message d’erreur sur le déploiement d’un nouveau groupe de ressources en


raison de la stratégie Azure

Limites de la stratégie Azure


La stratégie Azure a des limites en termes de nombre de stratégies,
d’exceptions, de paramètres et de conditionnels qui peuvent être
appliqués. Ces limites peuvent varier en fonction de la portée de
la stratégie. Consultez la documentation pour plus d’informations.

Stratégie Azure pour Kubernetes


La stratégie Azure prend également en charge Kubernetes, ce qui vous permet d’obtenir
des informations stratégiques sur votre déploiement Kubernetes et de créer des stratégies
relatives à Kubernetes. Vous trouverez plus d’informations sur la procédure à suivre en lisant
« Comprendre la stratégie Azure pour les clusters Kubernetes » et « Sécuriser votre cluster
avec Azure Policy », qui vous explique comment appliquer la stratégie Azure à Kubernetes.

Stratégie Azure 213


Open Policy Agent
Open Policy agent (OPA) est un moteur de stratégie open source à usage général qui vous
permet d’effectuer des actions de stratégie d’autorisation sur votre logiciel (figure 8-11). OPA
vous permet de spécifier la stratégie en tant que code et fournit des API simples pouvant
être intégrées aux logiciels.

Figure 8-11. Fonctionnement du service OPA

Les stratégies OPA sont codifiées dans un langage déclaratif appelé Rego (voir la
documentation sur le langage de la stratégie). Les stratégies sont ensuite déployées sur le
service OPA sur l’hôte. Contrairement à un certain nombre de systèmes que nous avons
abordés dans ce chapitre, OPA n’effectue aucune exécution, il effectue plutôt des décisions
stratégiques. OPA évalue les stratégies et les données pour produire des résultats de requête
renvoyés au client. Les stratégies sont écrites dans un langage déclaratif de haut niveau et
peuvent être chargées de manière dynamique dans OPA, à distance via des API ou via le
système de fichiers local.
OPA fournit une API REST que les services/systèmes tiers tels que Kubernetes, SSH, Sudo
et Envoy peuvent utiliser pour répondre à des questions telles que les suivantes :

• Quels utilisateurs peuvent accéder aux ressources


• Quel trafic de sortie des sous-réseaux est autorisé
• Sur quels clusters une charge de travail doit être déployée
• Quels binaires des registres peuvent être téléchargés
• Avec quelles fonctionnalités d’OS un conteneur peut être exécuté
• À quel moment de la journée les accès au système ont lieu

Vous trouverez plus d’informations sur la page de documentation d’API REST OPA. Les


API clés services par l’OPA sont les suivantes :

214 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


GET /v1/policies
Obtient toutes les stratégies

GET /v1/policies/<name>
Renvoie une stratégie spécifique

PUT /v1/policies/<name>
Crée ou met à jour une stratégie

DELETE /v1/policies/<name>
Supprime une stratégie

GET/POST /v1/data/<name>
Vous permet de retourner ou de créer un document
OPA vous permet de centraliser plusieurs stratégies de sécurité sur plusieurs éléments
d’infrastructure dans un seul système. Vous trouverez une liste complète des intégrations
OPA sur la page Écosystème OPA. Sans OPA, vous devrez mettre en œuvre la gestion des
stratégies pour votre logiciel à partir de zéro. Les composants requis tels que le langage
de stratégie (syntaxe et sémantique) et le moteur d’évaluation doivent être soigneusement
conçus, mis en œuvre, testés, documentés, puis conservés pour garantir un comportement
correct et une expérience utilisateur positive pour vos clients.

Déploiement d’OPA sur Kubernetes


Le déploiement d’OPA sur Kubernetes est simple :

1. Créez le fichier de définition de déploiement (deployment-opa.yaml) :


apiVersion: apps/v1
kind: Deployment
metadata:
name: opa
labels:
app: opa
spec:
replicas: 1
selector:
matchLabels:
app: opa
template:
metadata:
labels:
app: opa
name: opa
spec:
containers:
- name: opa
image: openpolicyagent/opa:edge
ports:
- name: http
containerPort: 8181

Open Policy Agent 215


args:
- "run"
- "--ignore=.*" # exclude hidden dirs created by Kubernetes
- "--server"
- "/policies"
volumeMounts:
- readOnly: true
mountPath: /policies
name: policy
volumes:
- name: policy
configMap:
name: policy

2. Créez le fichier de définition de service (servica-opa.yaml) :


kind: Service
apiVersion: v1
metadata:
name: opa
labels:
app: opa
spec:
type: NodePort
selector:
app: opa
ports:
- name: http
protocol: TCP
port: 8181
targetPort: 8181

3. Appliquez les deux fichiers :


$ kubectl create -f deployment-opa.yaml
$ kubectl create -f service-opa.yaml

Déploiement de stratégie avec OPA


Comme indiqué précédemment, OPA est un moteur de stratégie généralisée qui peut être
ajouté en tant que plug-in à un certain nombre d’écosystèmes de logiciels. Dans cet exemple,
nous allons configurer l’accès SSH aux machines à l’aide d’OPA.
Pour cet exemple, nous aurons trois groupes :
sre
Il s’agit des administrateurs de toutes les applications.
foo-frontend
Ce sont les contributeurs de l’application foo-frontend.
bar-backend
Ce sont les contributeurs de l’application bar-backend.
Nous allons mettre en œuvre la stratégie suivante :

216 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


• Le groupe sre peut se connecter en SSH à n’importe quel hôte et exécuter des
commandes sudo.
• Les autres utilisateurs de groupes associés au développement de l’application peuvent
se connecter à l’hôte, mais ils ne peuvent pas exécuter les commandes sudo.

Les exemples suivants vont créer une stratégie qui permet l’autorisation SSH pour le groupe
sre et le groupe associé à l’application :
package sshd.authz

import input.pull_responses
import input.sysinfo

import data.hosts

# By default, users are not authorized.


default allow = false

# Allow access to any user that has the "admin" role.


allow {
data.roles["sre"][_] == input.sysinfo.pam_username
}

# Allow access to any user who contributed to the code running on the host.
#
# This rule gets the "host_id" value from the file "/etc/host_identity.json."
# It is available in the input under "pull_responses" because we
# asked for it in our pull policy above.
#
# It then compares all the contributors for that host against the username
# that is asking for authorization.
allow {
hosts[pull_responses.files["/etc/host_identity.json"].host_id].contributors[_] == \
sysinfo.pam_username
}

# If the user is not authorized, then include an error message in the response.
errors["Request denied by administrative policy"] {
not allow
}

Nous allons créer une deuxième stratégie qui autorise uniquement l’accès sudo pour le
groupe sre :
package sudo.authz

# By default, users are not authorized.


default allow = false

# Allow access to any user that has the "admin" role.


allow {
data.roles["sre"][_] == input.sysinfo.pam_username
}

# If the user is not authorized, then include an error message in the response.
errors["Request denied by administrative policy"] {
not allow
}

Open Policy Agent 217


Maintenant, nous pouvons enregistrer ces deux stratégies dans OPA :
curl -X PUT --data-binary @sshd_authz.rego localhost:8181/v1/policies/sshd/authz
curl -X PUT --data-binary @sudo_authz.rego localhost:8181/v1/policies/sudo/authz

Une fois ces commandes exécutées avec succès, notre stratégie sera appliquée et seuls les
utilisateurs du groupe sre auront un accès SSH et sudo à toutes les machines. Pendant ce
temps, le groupe de services approprié ne disposera d’un accès SSH que pour la machine sur
laquelle le service sera déployé.

Résumé
Bien que l’utilisation d’une infrastructure native du cloud présente des avantages
considérables pour l’opérateur, elle constitue un risque important pour votre entreprise si
l’infrastructure n’est pas bien sécurisée. Dans ce chapitre, nous avons examiné un moyen de
sécuriser et d’auditer votre infrastructure. Tout d’abord, nous avons examiné la norme CNI
et la façon dont elle est le fondement d’autres plateformes telles que Flannel et Cilium. Nous
avons examiné comment ces systèmes peuvent être utilisés pour fournir une connectivité
et une sécurité réseau à votre infrastructure cloud. Ensuite, nous avons examiné les
mécanismes de stratégie non-réseau pour la gestion de votre infrastructure cloud via la
stratégie Azure, ainsi que la stratégie pour les applications via l’agent de stratégie ouverte.
La création de votre infrastructure cloud grâce à une configuration réseau sécurisée et à une
stratégie bien définie vous aidera à vous assurer que votre infrastructure évolue de manière
sécurisée. Maintenant que vous comprenez les bases de la mise en réseau des conteneurs et
du cloud, nous allons orienter la discussion vers le stockage et le service de données dans
le cloud.

218 Chapitre 8 : Mise en réseau et gestion des stratégies : de véritables gardiens


CHAPITRE 9
Bases de données et stockage distribués :
la banque centrale

L’un des plus grands obstacles à surmonter lors de la création d’une infrastructure native
du cloud est la mise en place d’un stockage fiable et évolutif pour une utilisation en ligne
et hors ligne. Bien que les premiers services de stockage exécutés dans le cloud aient été
désapprouvés, un certain nombre de projets de stockage natif du cloud ont réussi, le plus
important d’entre eux ayant été Vitess. Les services cloud, y compris Azure, ont également
considérablement investi dans les disques gérés pour les machines virtuelles et les virtual
machine scale sets, ainsi que les comptes de stockage avancé (par exemple, Gen2) et les
services de bases de données gérées (comme MySQL, SQL  Server et Redis). Tout cela a
fait du stockage un concept de premier ordre dans le cloud d’Azure. Dans ce chapitre, nous
allons examiner pourquoi vous devez exécuter des solutions de stockage à grande échelle
dans Azure et comment les orchestrer de façon à faciliter leur gestion.

La nécessité d’avoir des bases de données distribuées


dans l’architecture native du cloud
Bien qu’Azure fournisse un certain nombre de solutions de données gérées (SQL et NoSQL),
il se peut que vous deviez exécuter des configurations plus importantes et plus spécialisées
tout en travaillant dans le cloud. L’écosystème de projets de base de données Cloud Native
Computing Foundation (CNCF) auto-gérées vous permettra d’effectuer une mise à l’échelle
supérieure à celle des services de la plateforme Azure en tant que service (PaaS) et vous
donnera davantage de fonctionnalités et d’options de performances.
Comme nous le verrons plus loin dans ce chapitre, le paysage CNCF énonce un ensemble de
solutions de stockage principalement conçues pour les opérations dans le cloud. L’avantage
de ces systèmes réside dans le fait que les frais généraux de gestion des serveurs sont
minimisés, tandis que vous avez toujours le contrôle complet sur le système.

219
Dans ce chapitre, nous allons examiner certains des magasins de données matures qui font
partie du paysage CNCF. Vitess, Rook, TiKV et etcd sont tous des magasins de données cloud
matures qui sont capables de s’adapter à une taille énorme et sont exécutés en tant que systèmes
de données de première classe sur Azure. Ces systèmes représentent les éléments constitutifs
fondamentaux d’un écosystème de données exploité dans une infrastructure cloud.

Exécution de charges de travail statiques sur AKS


Au moment de la rédaction de ce document, Azure Kubernetes
Service (AKS) prend en charge plusieurs zones de disponibilité (à
savoir, plusieurs datacenters au sein d’une région). Toutefois, il ne
prend pas en charge plusieurs domaines d’erreur. Cela signifie que si
votre cluster AKS se trouve dans une seule zone de disponibilité, il est
théoriquement possible d’avoir des temps d’arrêt si plusieurs nœuds
de votre cluster ne sont pas disponibles. Nous vous conseillons de
fractionner votre cluster  AKS sur plusieurs zones de disponibilité
jusqu’à ce qu’AKS prenne en charge les domaines d’erreur.

Options de stockage et de base de données dans Azure


Avant de commencer à créer vos magasins de données cloud, vous devez évaluer
soigneusement votre cas d’utilisation, en particulier le stockage, les performances et le coût
de ce que vous essayez de créer. Vous constaterez peut-être que les services PaaS d’Azure
sont parfois mieux adaptés à un cas d’utilisation particulier, plutôt que d’utiliser un produit
CNCF. Par exemple, l’offre de stockage de Hadoop Distributed File System (HDFS) d’Azure,
les comptes de stockage Gen2, est une solution extrêmement économique pour stocker de
grands volumes de données.
Au moment de la rédaction de ce document, Azure fournit les services de stockage et de
base de données suivants :
• Azure Cosmos DB
• Azure Cache pour Redis
• Azure Database pour :
— MySQL
— PostgreSQL
— MariaDB
• Comptes de stockage (fournissant un BLOB, un système de fichiers réseau [NFS] et un
stockage hiérarchique)
• Azure Storage Explorer

220 Chapitre 9 : Bases de données et stockage distribués : la banque centrale


Nous vous conseillons d’évaluer les performances de ces offres Azure, notamment en tenant
compte des performances, de la disponibilité et du coût d’un service PaaS, afin de vous
assurer que vous utilisez le système approprié pour votre cas d’utilisation. Azure fournit un
outil de calcul des tarifs extrêmement utile pour modéliser les coûts.
Si vous estimez qu’un service PaaS Azure ne répond pas à vos besoins, continuez à lire ce
chapitre pour en savoir plus sur d’autres solutions de stockage de données potentielles.

Introduction à Vitess : MySQL distribué et partitionné


Vitess est un système de cluster de base de données pour la mise à l’échelle horizontale de
MySQL et MariaDB. Il a été créé par les ingénieurs de YouTube en 2010 pour protéger et
mettre à l’échelle leurs bases de données. Il fournit un certain nombre de fonctionnalités
qui facilitent les performances, la sécurité et la surveillance, mais surtout, il offre des outils
puissants pour gérer les topologies de base de données et fournir une excellente prise en
charge de la partition et du repartitionnement verticaux et horizontaux. Dans de nombreux
aspects, il vous permet d’exploiter des bases de données SQL de manière NoSQL, tout en
bénéficiant de tous les avantages liés à l’exécution d’une base de données relationnelle.

Pourquoi exécuter Vitess ?


Vous vous posez peut-être la question suivante : « Pourquoi devrais-je exécuter Vitess au
lieu d’utiliser Azure Cosmos DB ? » Bien que Cosmos DB soit une option attrayante pour
les réplicas et les écritures distribuées à l’échelle mondiale, le modèle de coût actuel rend
son exécution à grande échelle (milliers de requêtes par seconde) extrêmement coûteuse,
Vitess devenant ainsi une offre attrayante pour ceux qui ont besoin d’une base de données
relationnelle à haut débit avec une configuration plus personnalisable.
Vitess offre les avantages suivants :
Évolutivité
Vous pouvez augmenter ou diminuer des schémas individuels.
Simplicité de gestion
Un plan de contrôle centralisé gère toutes les opérations liées à la base de données.
Protection
Vous pouvez réécrire des requêtes, créer des listes de refus de requête et ajouter des
listes de contrôle d’accès (ACL) au niveau de la table.
Gestion des partitions
Vous pouvez facilement créer et gérer des partitions de base de données.
Performances
Le fonctionnement interne est essentiellement conçu pour les performances, y compris
le pool de connexions client et le deduping de requête.

Introduction à Vitess : MySQL distribué et partitionné 221


Vitess vous aide à gérer les frais généraux opérationnels et de mise à l’échelle des bases de
données relationnelles et de l’intégrité de votre cluster. En outre, Vitess offre un éventail
plus large d’éléments configurables que les offres de base de données cloud traditionnelles
(y compris Cosmos DB), ce qui vous permettra de répondre à vos besoins de mise à l’échelle
et de réplication.

Architecture Vitess
Vitess fournit le partitionnement en tant que service en créant un système de style
middleware, tout en continuant à utiliser MySQL pour stocker des données. L’application
cliente se connecte à un démon connu sous le nom de VTGate (voir la figure 9-1). VTGate
est un routeur prenant en charge les clusters qui achemine les requêtes vers les instances
VTTablet appropriées, qui gèrent chaque instance de MySQL. Le service de topologie stocke
la topologie de l’infrastructure, y compris l’emplacement où les données sont stockées et la
capacité de chaque instance VTTablet à servir les données.

Figure 9-1. Architecture Vitess

Vitess fournit également deux interfaces administratives : vtctl, une interface de ligne de
commande ; et vtctld, une interface web.
VTGate est un routeur de requête (ou proxy) pour acheminer des requêtes du client vers les
bases de données. Le client n’a pas besoin de connaître quoi que ce soit, sauf l’emplacement
où se trouve l’instance de VTGate, ce qui signifie que l’architecture client-serveur est simple.

222 Chapitre 9 : Bases de données et stockage distribués : la banque centrale


Déploiement de Vitess sur Kubernetes
Vitess bénéficie d’une prise en charge exceptionnelle pour Kubernetes et son utilisation est
très simple. Dans cette procédure pas à pas, nous allons créer un cluster et un schéma, puis
développer le cluster :
1. Commencez par cloner le référentiel vitess, puis transférez-le dans le dossier vitess/
examples/operator :
$ git clone git@github.com:vitessio/vitess.git
$ cd vitess/examples/operator

2. Installez maintenant l’opérateur Kubernetes en exécutant les opérations suivantes :


$ kubectl apply -f operator.yaml

3. Ensuite, créez un cluster Vitess initial en exécutant :


$ kubectl apply -f 101_initial_cluster.yaml

4. Vous serez en mesure de vérifier que l’installation du cluster initial a été une réussite en
procédant comme suit :
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
example-etcd-faf13de3-1 1/1 Running 0 78s
example-etcd-faf13de3-2 1/1 Running 0 78s
example-etcd-faf13de3-3 1/1 Running 0 78s
example-vttablet-zone1-2469782763-bfadd780 3/3 Running 1 78s
example-vttablet-zone1-2548885007-46a852d0 3/3 Running 1 78s
example-zone1-vtctld-1d4dcad0-59d8498459-kwz6b 1/1 Running 2 78s
example-zone1-vtgate-bc6cde92-6bd99c6888-vwcj5 1/1 Running 2 78s
vitess-operator-8454d86687-4wfnc 1/1 Running 0 2m29s

Vous disposez maintenant d’un petit cluster Vitess à une seule zone exécuté avec deux réplicas.
Vous trouverez d’autres exemples d’opérations de base de données sur la page d’exemple des
opérations Kubernetes Vitess.
L’un des cas d’utilisation les plus courants que vous rencontrerez est la nécessité d’ajouter
ou de supprimer des réplicas de la base de données. Pour ce faire, vous pouvez facilement
exécuter les opérations suivantes :
$ kubectl edit planetscale.com example

# To add or remove replicas, change the replicas field of the appropriate resource
# to the desired value. E.g.

keyspaces:
- name: commerce
turndownPolicy: Immediate
partitionings:
- equal:
parts: 1
shardTemplate:
databaseInitScriptSecret:
name: example-cluster-config
key: init_db.sql
replication:
enforceSemiSync: false
tabletPools:

Introduction à Vitess : MySQL distribué et partitionné 223


- cell: zone1
type: replica
replicas: 2 # Change this value

En ce qui concerne la création de stockage de données distribuées, vous


devez notamment déterminer la taille de votre domaine d’échec. Vitess
vous conseille de stocker un maximum de 250  Go de données par
serveur. Bien que cela soit justifié par les performances de MySQL,
d’autres tâches opérationnelles régulières sont plus complexes avec
une taille de partition/un domaine d’échec plus volumineux. Si vous
souhaitez plus d’informations sur les raisons pour lesquelles Vitess
recommande 250 GB par serveur, consultez le blog sur Vitess.

Présentation de Rook : orchestrateur de stockage pour


Kubernetes
Jusqu’à présent, nous avons couvert le stockage des bases de données relationnelles et le service
hautes performances, mais il existe d’autres types de stockage qui doivent être servis et utilisés
dans le cloud. Ils sont connus sous le nom de systèmes de fichiers blob. Les magasins d’objets
blob sont parfaits pour stocker des images, des vidéos et d’autres fichiers qui ne conviennent
pas à une base de données relationnelle. Le stockage d’objets blob entraîne quelques problèmes
supplémentaires liés au stockage et à la réplication de grandes quantités de données.
Rook est un fichier natif du cloud, un bloc et un orchestrateur de stockage open source
d’objets qui fournit un stockage blob défini par le logiciel. Rook a obtenu le statut de CNCF
en  2018 et il a été spécifiquement conçu pour l’exécution du stockage sur Kubernetes.
Semblable à Vitess, Rook gère une grande partie des opérations quotidiennes d’un cluster,
y compris la mise à l’échelle et la correction automatique. De plus, il automatise d’autres
tâches telles que la récupération d’urgence, la surveillance et les mises à niveau.

L’architecture de Rook
Rook est un orchestrateur Kubernetes et non une véritable solution de stockage. Rook
prend en charge les systèmes de stockage suivants :

• Ceph, une solution de stockage distribué hautement évolutive pour le stockage en bloc,
le stockage d’objets et les systèmes de fichiers partagés avec des années de déploiements
en production
• 
Cassandra, une base de données NoSQL hautement disponible présentant des
performances ultra-rapides, une cohérence réglable et une évolutivité massive
• NFS, qui permet aux hôtes distants de monter des systèmes de fichiers sur un réseau et
d’interagir avec ces systèmes de fichiers comme s’ils étaient montés localement

Rook peut orchestrer un certain nombre de fournisseurs de stockage sur un cluster


Kubernetes. Chacun dispose de son propre opérateur pour déployer et gérer les ressources.

224 Chapitre 9 : Bases de données et stockage distribués : la banque centrale


Déploiement de Rook sur Kubernetes
Dans cet exemple, nous allons déployer un cluster Cassandra à l’aide de l’opérateur Rook
(Cassandra) Kubernetes. Si vous déployez un cluster Ceph via Rook, vous pouvez également
utiliser un tableau Helm pour déployer :

1. Clonez l’opérateur Rook :


$ git clone --single-branch --branch master https://github.com/rook/rook.git

2. Installez l’opérateur :
$ cd rook/cluster/examples/kubernetes/cassandra
$ kubectl apply -f operator.yaml

3. Vous pouvez vérifier que l’opérateur est installé en exécutant :


$ kubectl -n rook-cassandra-system get pod

4. Maintenant, accédez au dossier cassandra et créez le cluster :


$ cd rook/cluster/examples/kubernetes/cassandra
$ kubectl create -f cluster.yaml

5. Pour vérifier que tous les nœuds souhaités sont en cours d’exécution, émettez la
commande suivante :
$ kubectl -n rook-cassandra get pod -l app=rook-cassandra

Il est exceptionnellement facile d’augmenter ou de diminuer votre cluster Cassandra à


l’aide de la commande Kubernetes kubectl edit. Vous pouvez simplement augmenter
ou diminuer la valeur de Spec.Members afin de dimensionner le cluster en conséquence :
$ kubectl edit clusters.cassandra.rook.io rook-cassandra
# To scale up a rack, change the Spec.Members field of the rack to the desired value.
# To scale down a rack, change the Spec.Members field of the rack to the desired value
# After editing and saving the yaml, check your cluster's Status and Events for information
# on what's happening:

apiVersion: cassandra.rook.io/v1alpha1
kind: Cluster
metadata:
name: rook-cassandra
namespace: rook-cassandra
spec:
version: 3.11.6
repository: my-private-repo.io/cassandra
mode: cassandra
annotations:
datacenter:
name: us-east2
racks:
- name: us-east2
members: 3 # Change this number up or down

$ kubectl -n rook-cassandra describe clusters.cassandra.rook.io rook-cassandra

Présentation de Rook : orchestrateur de stockage pour Kubernetes 225


Vous pouvez obtenir plus de détails sur les éléments configurables
de Cassandra dans la documentation sur Cassandra CRD.

De même, les clusters Ceph et NFS peuvent être déployés facilement à l’aide de
l’opérateur Rook.

Présentation de TiKV
TiKV (Titanium key-value), créé par PingCAP, Inc., est un magasin clé-valeur open source,
distribué et transactionnel. Contrairement à de nombreux systèmes NoSQL et de clé-valeur,
TiKV fournit des API simples (brutes), ainsi que des API transactionnelles qui offrent une
conformité à l’atomicité, à la cohérence, à l’isolation et à la durabilité (ACID).

Pourquoi utiliser TiKV ?


TiKV fournit les fonctionnalités suivantes :

• Géoréplication
• Évolutivité horizontale
• Transactions distribuées cohérentes
• Prise en charge du coprocesseur
• Partitionnement automatique

TiKV est une option intéressante en raison de ses fonctions de partitionnement automatique
et de géoréplication, ainsi que de sa capacité à évoluer pour stocker plus de 100  To de
données. Il fournit également de solides garanties de consensus grâce à son utilisation du
protocole Raft pour la réplication.

Architecture TiKV
Comme nous l’avons mentionné précédemment, TiKV dispose de deux API qui peuvent
être utilisées pour différents cas d’utilisation : brute et transactionnelle. Le tableau 9-1 décrit
les différences entre ces deux API.

Tableau 9-1. API TiKV : brute versus transactionnelle


Brute Transactionnelle
Description Une API de valeur de clé de niveau inférieur pour interagir Une API de clé-valeur de niveau supérieur qui
directement avec les paires clé/valeur individuelles fournit une sémantique ACID
Atomicité Clé unique Plusieurs clés
Utilisation dans Votre application ne nécessite pas de transactions distribuées Votre application nécessite des transactions
les cas suivants... ou de contrôle de simultanéité multiversion (MVCC) distribuées et/ou des MVCC

226 Chapitre 9 : Bases de données et stockage distribués : la banque centrale


TiKV utilise RocksDB comme conteneur de stockage dans chaque nœud et utilise des
groupes Raft pour fournir des transactions distribuées, comme illustré à la figure 9-2. Le
pilote de placement agit en tant que gestionnaire de cluster et s’assure que les contraintes de
réplication ont été respectées pour toutes les partitions et que la charge des données a été
équilibrée sur le pool. En dernier lieu, les clients se connectent aux nœuds TiKV à l’aide du
protocole Google Remote Procedure Call (gRPC), qui est optimisé pour les performances.

Figure 9-2. Architecture TiKV

Dans un nœud TiKV (illustré à la figure 9-3), RocksDB fournit les mécanismes de stockage
sous-jacents et Raft fournit un consensus pour les transactions. L’API TiKV fournit une
interface pour interagir avec les clients et le coprocesseur gère les requêtes qui ressemblent
à du SQL et assemble le résultat du stockage sous-jacent.

Figure 9-3. Architecture d’instance TiKV

Présentation de TiKV 227


Déploiement de TiKV sur Kubernetes
TiKV peut être déployé via plusieurs méthodes d’automatisation, notamment Ansible,
Docker et Kubernetes. Dans l’exemple suivant, nous allons déployer un cluster de base avec
Kubernetes et Helm :

1. Installez la définition de ressource personnalisée TiKV :


$ kubectl apply -f https://raw.githubusercontent.com/tikv/tikv-operator/master/ \
manifests/crd.v1beta1.yaml

2. Nous devrons explorer l’opérateur Helm. Tout d’abord, ajoutez le référentiel PingCap :
$ helm repo add pingcap https://charts.pingcap.org/

3. Créez maintenant un espace de noms pour tikv-operator :


$ kubectl create ns tikv-operator

4. Installez l’opérateur :
$ helm install --namespace tikv-operator tikv-operator pingcap/tikv-operator \
--version v0.1.0

5. Déployez le cluster :
$ kubectl apply -f https://raw.githubusercontent.com/tikv/tikv-operator/master/ \
examples/basic/tikv-cluster.yaml

6. Vous pouvez vérifier l’état du déploiement en exécutant :


$ kubectl wait --for=condition=Ready --timeout 10m tikvcluster/basic

Cela va créer un cluster de stockage à hôte unique avec 1 Go de stockage et une instance de
pilote de placement (DP).
Vous pouvez modifier les paramètres de réplicas et de stockage dans la définition de
cluster afin de répondre à vos besoins. Nous vous conseillons d’exécuter votre stockage avec
au moins trois réplicas à des fins de redondance. Par exemple, si vous exécutez kubectl
edit ikv.org TikvCluster et que vous modifiez les réplicas en les définissant sur 4 et
 

le stockage en le définissant sur 500 Gi :


apiVersion: tikv.org/v1alpha1
kind: TikvCluster
metadata:
name: basic
spec:
version: v4.0.0
pd:
baseImage: pingcap/pd
replicas: 4
# if storageClassName is not set, the default Storage Class of the Kubernetes cluster
# will be used
# storageClassName: local-storage
requests:
storage: "1Gi"
config: {}
tikv:
baseImage: pingcap/tikv

228 Chapitre 9 : Bases de données et stockage distribués : la banque centrale


replicas: 4
# if storageClassName is not set, the default Storage Class of the Kubernetes cluster
# will be used
# storageClassName: local-storage
requests:
storage: "500Gi"
config: {}

vous obtiendrez quatre réplicas d’une partition de stockage de 500 Go.

Plus d’informations sur etcd


Nous avons utilisé etcd (furtivement) tout au long de ce livre. Ici, nous allons en parler plus
en détail.
etcd est un magasin de clé-valeur distribué fortement cohérent (consensus Raft) qui est
conçu pour être utilisé dans les systèmes distribués. Il est conçu pour gérer gracieusement
choix des leaders et les défaillances telles que les interruptions de réseau et les temps
d’arrêt inattendus des nœuds. etcd dispose d’une API simple (figure 9-4), c’est pourquoi
vous retrouverez un certain nombre de systèmes que nous avons abordés dans ce livre (par
exemple, Kubernetes, TiKV, Calico et Istio) qui l’utilisent en arrière-plan.
Souvent, etcd est utilisé pour stocker des valeurs de configuration, des indicateurs de
fonction et des informations de découverte de services au format clé-valeur. etcd permet
aux clients de regarder ces éléments pour les modifier et il se reconfigure lorsque ces
éléments changent. etcd convient également aux choix du leadership et aux utilisations de
verrouillage. etcd est utilisé comme back-end de découverte de services dans Kubernetes,
ainsi que pour l’orchestration dans Rook.

Figure 9-4. Architecture opérationnelle d’etcd

Si vous utilisez etcd pour votre infrastructure de base, vous comprendrez rapidement
l’importance de l’exécution fiable de cette infrastructure. Étant donné qu’un cluster etcd
est un élément essentiel de votre infrastructure, nous vous recommandons les éléments
suivants pour garantir la stabilité d’un cluster etcd dans Azure.

Plus d’informations sur etcd 229


Plateforme matérielle
Les performances cohérentes des magasins clé-valeur s’appuient fortement sur les
performances du matériel sous-jacent. Un cluster de clé-valeur cohérent mal exécuté peut
avoir un impact considérable sur les performances de votre système distribué. Il en va de
même pour etcd. Si vous effectuez des centaines de transactions par seconde, vous devez
utiliser un disque SSD (Managed Solid State Drive) avec votre machine virtuelle (nous
vous conseillons un SSD premium ou ultra) pour éviter la dégradation des performances.
Vous aurez besoin d’au moins quatre cœurs pour l’exécution d’etcd et le débit évoluera
avec l’augmentation des cœurs du processeur. Vous trouverez plus d’informations sur la
planification des capacités et les configurations matérielles pour le cloud sur la page générale
du matériel et dans le Guide AWS (etcd ne publie pas actuellement de guide spécifique à
Azure).
Nous vous conseillons d’exécuter etcd sur les références Azure suivantes :

• Standard_D8ds_v4 (processeur à huit cœurs)


• Standard_D16ds_v4 (processeur à 16 cœurs)

Ces références vous permettent explicitement d’associer des SSD premium ou ultra, ce qui
permettra d’augmenter les performances du cluster.

Comme pour tout cas d’utilisation en production dont nous avons


parlé dans ce livre, nous vous conseillons vivement d’activer la mise
en réseau accélérée sur toutes les instances de calcul Azure.

Mise à l’échelle et correction automatiques


Il est également important de parler de la façon dont la mise à l’échelle automatique et l’auto-
correction fonctionnent avec etcd. La mise à l’échelle automatique n’est pas recommandée
pour etcd car elle pourrait avoir des conséquences inattendues si les ajouts ou soustractions
de capacité nuisent aux performances du cluster. Cela étant dit, l’activation de la correction
automatique (auto-guérison) de mauvaises instances etcd est considérée comme acceptable.
Vous pouvez désactiver la mise à l’échelle automatique pour etcd en exécutant la commande
suivante sur votre cluster Kubernetes :
$ kubectl delete hpa etcd

Cela désactivera le HPA (Horizontal Pod Autoscaler), qui est utilisé dans Kubernetes pour
dimensionner automatiquement les clusters. Vous trouverez plus d’informations sur le HPA
dans la documentation sur Kubernetes.

230 Chapitre 9 : Bases de données et stockage distribués : la banque centrale


Mise à l’échelle automatique Azure
Azure fournit la mise à l’échelle automatique en tant que fonctionnalité
dans un certain nombre de ses produits, y compris les virtual machine
scale sets et les fonctions Azure. Les fonctionnalités de mise à l’échelle
automatique vous permettent d’augmenter et de réduire efficacement
vos ressources, le cas échéant. Avec les virtual machine scale sets, vous
pouvez attendre jusqu’à 10 minutes pour que les nouvelles machines
virtuelles soient disponibles lors de l’évolutivité.

Disponibilité et sécurité
etcd est un service de plan de contrôle, c’est pourquoi il est important qu’il soit hautement
disponible et que les communications soient sécurisées. Nous vous conseillons d’exécuter
au moins trois nœuds etcd dans un cluster pour garantir la disponibilité du cluster. Le débit
du cluster dépendra des performances du stockage. Nous vous recommandons vivement de
ne pas utiliser de mécanismes de découverte statique pour vos nœuds de cluster et d’utiliser
plutôt des enregistrements SRV DNS, comme conseillé dans le Guide de clustering etcd.

TLS etcd
etcd prend en charge le chiffrement TLS (Transport Layer Security) client-serveur et d’égal à
égal (serveur-serveur). Nous vous conseillons vivement de l’activer, car etcd est un système
de plan de contrôle. Nous n’allons pas entrer dans les détails de la mise en œuvre ici (car
ils dépendent grandement de votre infrastructure). Vous pouvez trouver des options de
configuration dans la documentation sur la sécurité d’etcd.

Contrôle basé sur les rôles


Depuis etcd  v2.1, le contrôle d’accès basé sur les rôles (RBAC) est une fonctionnalité de
l’API  etcd. À l’aide du RBAC, vous pouvez créer des modèles d’accès qui ne permettent
qu’à certains utilisateurs (ou applications) d’accéder à un sous-ensemble des données etcd.
Traditionnellement, cela se fait en fournissant l’authentification  HTTP de base lors de
la création de requêtes  HTTP vers  etcd. Depuis etcd  v3.2, si vous utilisez l’option de
configuration --client-cert-auth=true, le nom commun (CN) du certificat TLS client
sera utilisé en tant qu’utilisateur (au lieu d’une combinaison nom d’utilisateur/mot de passe).
Vous trouverez des exemples illustrant comment appliquer le RBAC à votre espace de
données etcd dans la documentation.

Résumé
Malgré les idées fausses courantes, il est possible d’exécuter des systèmes de données à
grande échelle en mode natif dans Azure. Dans ce chapitre, nous avons passé en revue
les raisons pour lesquelles il est avantageux d’utiliser des systèmes de données natifs du
cloud dans le cloud d’Azure. Nous avons abordé quatre systèmes : Vitess (relationnel), Rook

Résumé 231
(blob), TiKV (clé-valeur) et etcd (configuration de clé-valeur/découverte de services).
Ces systèmes sont le socle d’une architecture native du cloud qui stocke et sert les données en
ligne. De plus, ils offrent un large important par rapport à l’utilisation de composants PaaS.
Vous devriez maintenant savoir quel logiciel gérer, ainsi que quels services PaaS utiliser et
comment les déployer dans votre infrastructure.
Le cloud, et en particulier Kubernetes, est devenu un endroit beaucoup plus convivial pour
exécuter votre infrastructure avec état. Bien que les comptes de stockage Azure Gen2 soient
une excellente ressource pour le stockage blob, les logiciels natifs du cloud peuvent vraiment
vous aider à créer une infrastructure à grande échelle et de longue durée.
Maintenant que vous comprenez comment il est possible de stocker et de servir des données
dans un environnement cloud, examinons comment déplacer des données entre les systèmes
à l’aide de la messagerie en temps réel.

232 Chapitre 9 : Bases de données et stockage distribués : la banque centrale


CHAPITRE 10
Comprendre le message

Dans ce chapitre, nous allons discuter des avantages de la messagerie dans les architectures
natives du cloud et fournir un bref historique des modèles de messagerie afin que vous
puissiez comprendre leur origine et l’évolution de la communication interprocessus vers les
systèmes distribués natifs du cloud.
À la fin de ce chapitre, vous serez en mesure de déployer les implémentations de messagerie
les plus courantes utilisées dans les applications et les microservices natifs du cloud dès
aujourd’hui. Nous comparerons les fonctionnalités de solutions de messagerie matures
telles que RabbitMQ et Kafka qui sont en production depuis des années, et nous mettrons
en œuvre des modèles de messagerie courants dans NATS. Étant donné que ce livre se
concentre sur Azure, vous serez en mesure de gérer les offres de messagerie Azure à l’aide
de Terraform. Nous fournirons également de petits exemples de code en Python montrant à
quel point il est facile de produire et de consommer des messages et de valider le déploiement
approprié de l’infrastructure de messagerie pour Azure et NATS.

Les besoins en matière de messagerie


La nécessité pour les composants de l’application et du système de transmettre des messages
en temps quasi réel et d’échanger des données n’est pas propre au cloud, ni nouvelle dans les
microservices basés sur les conteneurs ou même les applications client/serveur en réseau.
Toute personne qui a regroupé des commandes afin de répondre à une invite Shell a utilisé
la communication entre les processus, qui est une forme de messagerie de base.
Même les systèmes d’exploitation les plus primitifs offrent la possibilité de transmettre
des messages et de partager des données. Cet espace de problème (et diverses solutions)
remonte aux années 1970 et a continué à évoluer dans les années 2000, en particulier dans
le secteur des services financiers, où il était nécessaire de communiquer des informations
boursières aux clients, d’échanger des données entre les fournisseurs de services et d’autres
institutions, et de traiter les ordres. Contrairement aux interactions client/serveur que nous
rencontrons le plus souvent dans les applications Internet ou d’entreprise,la messagerie

233
s’appuie sur un intermédiaire (appelé courtier) se trouvant entre l’expéditeur et le récepteur
qui ne suit généralement pas le paradigme de requête-réponse afin que la communication
devienne asynchrone. Nous verrons pourquoi c’est important sous peu.
À mesure que le cloud se développait dans les années  2010, l’utilisation des services de
messagerie gérés, tels que les serveurs Amazon  SQS et RabbitMQ fonctionnant sur des
machines virtuelles, a joué un rôle dans l’amélioration de la fiabilité et de l’évolutivité des
sites web. Vers le milieu des années 2010, Kafka est devenu une solution populaire pour les
cas d’utilisation de messagerie, y compris l’agrégation de journaux, l’analyse du flux de clics
et les pipelines de données. NATS est une autre implémentation de messagerie légère et avec
des performances élevées qui est devenue populaire pour les cas d’utilisation de l’analyse de
périphérie et l’Internet des objets (IoT), en raison de sa capacité à s’adapter à des appareils
de plus petite taille et à sa facilité d’exécution.
Les modèles de files d’attente et de messagerie publication/abonnement ont fait partie des
premiers services gérés offerts par les fournisseurs de cloud et Azure dispose de plusieurs
services de messagerie, y compris Azure Event Grid, Azure Event Hubs et Azure Service Bus,
qui fournissent des protocoles et des implémentations basés sur des normes compatibles avec
Java, .NET, Python et d’autres kits SDK. Ces services permettent la migration des charges de
travail existantes et/ou le développement de nouvelles applications natives du cloud.
Avant de plonger plus profondément dans l’histoire de la messagerie, voici les raisons
pour lesquelles et les moments où vous devrez implémenter la messagerie dans votre
infrastructure ou en vue de son utilisation par des applications :
Améliorez les performances et le débit
L’utilisation de modèles de messagerie (qu’il s’agisse de files d’attente simples ou
de modèles de publication/abonnement plus complexes) permet une évolutivité
horizontale et une mise en parallèle des charges de travail. La mise à l’échelle des
producteurs, des consommateurs et des courtiers en fonction de la charge est possible
grâce aux approches de mise à l’échelle automatique des machines virtuelles, telles que
les ensembles de machines virtuelles Azure, via Kubernetes ou le service cloud.
Renforce la résilience
La capacité d’avoir plusieurs expéditeurs (producteurs) et récepteurs (consommateurs)
qui échangent des messages offre une meilleure résilience. En outre, le fait que les
messages puissent s’accumuler dans une file d’attente (les expéditeurs continuent à
envoyer des messages) lors les clients n’effectuent pas le traitement garantit que les
messages ne sont pas perdus et que vous pouvez effectuer une maintenance sur vos
agents de travail. Selon le modèle de mise en file d’attente, le système peut survivre
aux défaillances de connectivité entre les composants, ainsi qu’aux échecs d’application
dans les producteurs ou les consommateurs.

234 Chapitre 10 : Faire passer le message


Garantit une opération asynchrone
Bien que les erreurs de délai d’expiration de la passerelle 502 tant redoutées sur Apache
et Nginx se produisent moins fréquemment aujourd’hui, quand nous les voyons,
nous nous rappelons comment les premiers sites web rencontraient des problèmes
d’évolutivité lorsqu’un grand nombre de demandes dépassaient les capacités de réponse
des serveurs web. Le même principe s’applique lorsqu’un client de base de données
se connecte à une base de données back-end et que la base de données manque de
threads ou s’épuise. Lorsque les agents de travail s’adaptent en fonction de la longueur
de la file d’attente, il en résulte des taux élevés de traitement des messages répondant
à la demande.  Les files d’attente de messages permettent également de définir des
limites de service plus petites et sont courantes dans les architectures d’applications
de microservices. Enfin, étant donné que les applications ne communiquent pas
directement (comme dans le cas des API  RPC ou même REST), elles peuvent se
concentrer sur les données échangées, en supposant qu’elles mettent en œuvre un
protocole de messagerie standard.

Exemple de cas d’utilisation de messagerie :


ingestion des journaux et analyse de données
Pour illustrer certaines des améliorations concrètes liées à la mise en place des files d’attente
de messages dans une architecture, nous allons décrire une solution de logiciel en tant que
service (SaaS) pour l’analyse des journaux que nous pouvons créer et exécuter. Le traitement
des journaux et des événements sont des cas d’utilisation courants pour la mise en œuvre
des services de messagerie et de streaming.
Le produit collecte les journaux système et de sécurité des appareils sur site, puis il les analyse
et les ingère dans un moteur de recherche en texte intégral qui permet aux analystes de
sécurité de détecter et de trouver des attaques sur leur réseau. Nous avons considérablement
simplifié la description de cette plateforme, mais les exemples suivants illustrent les avantages
de l’introduction de files d’attente de messages, comme nous l’avons déjà vu.
Passons en revue trois générations de l’architecture de ce produit et explorons comment
l’utilisation de la messagerie a évolué.

Génération 1 : sans files d’attente


La première génération de la solution était une collecte d’événements en temps réel à partir
d’un appareil sur place attribué à un équilibreur de charge qui distribuait des journaux à
plusieurs nœuds d’ingestion et de recherche (collecteurs de journaux), comme illustré à
la figure 10.1. Il s’agissait d’une architecture étroitement couplée dans le cadre de laquelle
un grand nombre d’événements collectés (en fonction des modèles d’utilisation des
clients) pouvaient entraîner des ralentissements lors de l’ingestion, ce qui pouvait ensuite
compliquer les recherches dans l’interface utilisateur.

Exemple de cas d’utilisation de messagerie : ingestion des journaux et analyse de données 235
Figure 10.1. Génération 1, sans files d’attente

Génération 2 : avec les files d’attente cloud et le stockage d’objets


La deuxième itération de cette solution a utilisé le stockage d’objets du fournisseur cloud et
les files d’attente de messages pour ingérer des paquets de messages, au lieu de les envoyer
directement à une API d’ingestion. Sur chaque nœud d’index et de recherche, il y avait un agent
de travail léger qui retirait les messages de la file d’attente. L’agent de travail contenait le chemin
d’accès vers le paquet de journaux dans le stockage d’objets, comme illustré à la figure 10.2.

Figure 10.2. Génération 2, avec les files d’attente cloud et le stockage d’objets

236 Chapitre 10 : Faire passer le message


Ces deux solutions avaient une architecture d’application monolithique, un problème réglé
dans la troisième génération de la plateforme. Cette génération a également déplacé la logique
d’analyse vers le cloud et réduit les coûts opérationnels pour les équipes client sur site.

Génération 3 : avec file d’attente de publication et d’abonnement basé


sur la mémoire
La troisième génération de la plateforme a mis en œuvre une architecture de microservices et
des files d’attente basées sur la mémoire hautes performances qui permettaient aux services
individuels de traiter les messages pour fournir des analyses avancées à l’aide d’une base
de données de documents au lieu du moteur de recherche précédent basé sur un SGBDR.
Cela séparait les domaines de défaillance et de performance, et permettait la mise à l’échelle
horizontale du système, comme l’illustre la figure 10.3.

Figure 10.3. Génération 3, avec une architecture de microservices et des files d’attente mises
à l’échelle horizontalement

L’adoption de systèmes de messagerie peut être un élément clé pour contribuer à l’évolution
d’un produit. Dans l’exemple précédent, le système de messagerie a été utilisé pour le
traitement des événements de mise à l’échelle. Cependant, les systèmes de messagerie
peuvent être utilisés à d’autres fins, telles que le transport d’analyses, de journaux ou de
toute autre donnée formatée qui n’a pas besoin d’être traitée en temps réel.

Exemple de cas d’utilisation de messagerie : ingestion des journaux et analyse de données 237
Les bases des plateformes de messagerie
Le besoin en composants logiciels (qu’il s’agisse d’applications client ou serveur, ou de
programmes individuels exécutés sur un seul ordinateur) est antérieur au cloud et même
à Internet. Tous les systèmes d’exploitation, sauf les systèmes intégrés les plus primitifs qui
sont à usage général, offrent aux programmes la capacité d’envoyer des données à d’autres
programmes et d’en recevoir de ces derniers.

Comparaison entre la messagerie et le streaming


Le paysage de la Cloud Native Computing Foundation (CNCF) (illustré à la figure 10.4)
nomme la catégorie de messagerie Streaming et messagerie, mais dans ce chapitre, nous nous
concentrerons principalement sur la messagerie. Kafka, NATS et RabbitMQ fournissent des
capacités de streaming, mais celles-ci n’étaient pas activées par défaut, ni utilisées au début
par ces plateformes de messagerie, contrairement à ce que proposaient les plateformes
d’analyse de données en streaming telles que Spark et Flink. Nous discuterons des concepts
de messagerie en détail dans la section suivante, mais il est important de noter ici qu’une
considération clé pour les systèmes de diffusion est que les opérations se produisent sur
plusieurs messages, souvent avec une fenêtre d’événement temporelle ou ordonnée dans les
opérations de mémoire pour les agrégations ou le filtrage.

Notions de base en messagerie


Comprendre les similitudes et les différences entre les solutions de messagerie
contemporaines peut vous aider à déterminer quel outil convient le mieux au travail ou au
problème en question. Comme nous l’avons vu tout au long de ce livre, il y a toujours plus
d’une façon de créer quelque chose, et ce n’est pas toujours une décision facile de choisir le
bon chemin jusqu’à ce que les capacités annoncées de l’outil soient testées et validées avec
votre infrastructure et votre cas d’utilisation précis. Avant d’explorer comment RabbitMQ,
Apache Kafka et NATS s’intègrent dans l’infrastructure et les applications natives du cloud,
nous devons comprendre les concepts de base et les capacités communes des systèmes de
messagerie, de files d’attente et de streaming.

238 Chapitre 10 : Faire passer le message


Figure 10.4. Le paysage du streaming et de la messagerie CNCF

Producteurs et consommateurs
Comme lorsque vous communiquez avec un autre être humain, un message a toujours un
expéditeur et un destinataire, mais il peut être envoyé directement ou non, et vous pouvez
obtenir une réponse ou non. Lorsque vous envoyez un e-mail, il y a plusieurs intermédiaires
(voir la figure 10.5). Ces parties incluent votre client de messagerie électronique, les serveurs
de votre fournisseur de messagerie électronique (agents de transfert de messages [MTA],
qui jouent un rôle de courtier), le serveur du destinataire (un autre MTA) et un client
de messagerie électronique de réception. Dans cet exemple, il y a un certain nombre de
consommateurs et de producteurs ; le client de messagerie électronique met en file d’attente
les messages à envoyer/produire à/pour les serveurs de messagerie du fournisseur de
services Internet, et l’agent de transfert de messages met ensuite en file d’attente les messages
envoyés, puis il les envoie/produit à/pour l’agent de transfert de messages du destinataire.
Le client de messagerie électronique du destinataire recevra et consommera ensuite les
messages de l’agent de transfert de messages de son fournisseur de services Internet.

Notions de base en messagerie 239


Figure 10.5. Le protocole de messagerie SMTP, un exemple de système de mise en file d’attente

Comme dans le cas de la mise en réseau où il existe des modèles de monodiffusion,


multidiffusion et de diffusion, dans la messagerie, il peut arriver qu’un seul consommateur
traite les données d’un seul producteur ou de plusieurs consommateurs (comme un e-mail).
Les messages peuvent être envoyés ou extraits par les consommateurs en fonction de la
plateforme et de la configuration.

Courtiers et clustering
Dans notre exemple d’e-mail précédent, les composants intermédiaires, les agents de
transfert de messages, agissent en tant que courtiers entre l’expéditeur et le destinataire.
Dans de nombreux cas, le logiciel d’agents de transfert de messages peut être mis en cluster.
Un ou plusieurs courtiers dans un cluster permettent aux expéditeurs et aux destinataires
d’être découplés les uns des autres. RabbitMQ, Kafka et NATS, les mises en œuvre de
messagerie abordées dans ce chapitre, nécessitent au moins un courtier, et suivant le principe
de la plupart des protocoles de système distribués, ces plateformes requièrent souvent un
minimum de trois courtiers et doivent généralement comporter un nombre impair d’entre
eux (pour les algorithmes de cohérence). Les considérations relatives au clustering incluent
la réplication d’état, l’élection de responsable, le comportement d’appartenance au nœud et
le partitionnement de cluster en cas de défaillance du réseau (ainsi que le problème tant
redouté de déconnexion cérébrale).
En ce qui concerne les clusters (qui peuvent rapidement devenir un goulot d’étranglement),
vous devez également tenir compte de la façon dont ils peuvent être mis à l’échelle sans
temps d’arrêt. La surcharge opérationnelle de la maintenance des clusters de messagerie à
haut débit et à faible latence est la raison pour laquelle les équipes désignent le fournisseur
de services cloud pour gérer l’infrastructure afin que les développeurs puissent se
concentrer sur le développement et l’optimisation des applications des producteurs et des
consommateurs.

240 Chapitre 10 : Faire passer le message


Durabilité et persistance
Toute personne qui a déjà envoyé un e-mail à une adresse inexistante a reçu des messages
d’erreur d’un serveur de messagerie indiquant qu’après un certain temps, le message a
expiré. Votre message est alors stocké sur un système externe après avoir quitté votre client
de messagerie.
Selon le cas d’utilisation de la messagerie et le type de données stockées et traitées, les données
peuvent être plus ou moins critiques ou avoir plus ou moins de périssabilité, et il peut être
plus ou moins essentiel que chaque message (ou transaction) soit stocké avant le traitement.
Comme dans le cas des applications client et serveur TCP (Transmission Control Protocol)
et UDP (User Datagram Protocol), il est important de prendre en compte la quantité de
logique de gestion des erreurs et celle de nouvelle tentative qui est mise en place au niveau
de la couche application par rapport à la couche de transport sous-jacente lors de la création
d’un système de production. Les systèmes de messagerie distribuent des renseignements
aux producteurs, aux consommateurs et aux courtiers de différentes manières.
De plus, les mécanismes utilisés pour stocker les messages varient d’un système à l’autre.
Certains systèmes ont un stockage purement basé sur la mémoire, ce qui les rend moins
durables, en particulier dans les environnements cloud, tandis que d’autres ont des solutions
sur disque qui, bien qu’elles rencontrent des difficultés dans les environnements cloud,
offrent une expérience plus durable et plus fiable.

Distribution de message
Outre la source et de la destination, il existe trois modèles importants que vous devez
connaître au sujet des plateformes axées sur les messages, comme nous allons le voir dans
ce chapitre :
Au moins une fois
Implique qu’il pourrait y avoir des doublons ; en particulier, si un consommateur est
tombé en panne et n’a pas avisé le courtier que le message avait été reçu, ou qu’un
message pouvait être distribué à plusieurs consommateurs
Une fois au maximum
Suppose qu’un message peut être envoyé, mais ne pas être réellement reçu ou reconnu
par un consommateur, ce qui implique une nouvelle distribution dont le client est
responsable
Une seule fois
Une situation très limitée dans laquelle les transactions sont nécessaires et les messages
doivent être distribués une seule fois
L’architecture de messagerie peut prendre en charge un ou plusieurs de ces modèles. Ceux-
ci peuvent peut-être être configurables par le courtier lui-même ou dans les bibliothèques
client des consommateurs et des producteurs.

Notions de base en messagerie 241


Sécurité
La sensibilité des données contenues dans l’objet de votre message peut varier, il est donc
nécessaire d’avoir des contrôles flexibles sur qui peut produire et consommer ces rubriques.
Un système de messagerie doit vérifier l’identité du producteur et du consommateur, puis
appliquer des autorisations de lecture et d’écriture à l’objet. De plus, les données qu’un
système conserve doivent être chiffrées au repos.
Maintenant que vous comprenez certaines des notions de base de la messagerie, examinons
quelques modèles de messagerie courants que vous pourriez rencontrer.

Modèles de messagerie courants


Nous allons examiner trois architectures de messagerie courantes que vous verrez dans
l’infrastructure moderne.

File d’attente simple


Les producteurs (P) publient dans une file d’attente (B) et, lorsque les consommateurs
(C)  lisent cette dernière, les messages sont supprimés et aucun autre consommateur ne
pourra les traiter.

Publication et abonnement
Comme illustré à la figure  10.6, un producteur (P) envoie des messages et tous les
consommateurs qui écoutent (abonnés) (C) les reçoivent. Il peut y avoir ou non un accusé
de réception, mais si les consommateurs ne sont pas en ligne, ils manqueront le message.

Figure 10.6. Un système de message de publication et d’abonnement simple

File d’attente durable


Comme illustré à la figure 10.7, un producteur (P) envoie des messages au courtier (B) et
(C) consomme indépendamment le traitement des messages à l’aide d’une liste ordonnée
basée sur un pointeur, mais il peut revenir à un point connu dans la liste, comme le premier
message dans une fenêtre donnée.

242 Chapitre 10 : Faire passer le message


Figure 10.7. Une file d’attente de messages ordonnée durable

Jusqu’à présent, nous avons couvert beaucoup de théories sur les bases de la messagerie et
de l’architecture. Nous allons maintenant explorer les différentes plateformes de messagerie
natives du cloud.

Une vue d’ensemble des plateformes de messagerie


populaires natives du cloud
Dans cette section, nous passerons en revue un certain nombre de plateformes de messag-
erie populaires natives du cloud qui sont devenues monnaie courante dans l’infrastructure
moderne.

RabbitMQ
Bien que RabbitMQ ait plus de 15 ans et soit évidemment antérieur à l’adoption publique
généralisée du cloud et des microservices, il s’agit d’un système de messagerie éprouvé qui a
optimisé des charges de travail critiques et qui continue d’être pertinent dans le monde des
conteneurs et des clusters Kubernetes. L’une des raisons en est que RabbitMQ a été développé
dans Erlang/OTP, un langage de programmation fonctionnel conçu pour l’évolutivité, la
concurrence et la fiabilité. Il est donc idéal pour le développement de systèmes distribués
qui traitent des millions à des centaines de millions de messages par jour avec des temps
d’arrêt très faibles.

Apache Kafka
En 2010, les ingénieurs de LinkedIn ont constaté que RabbitMQ ne pouvait pas s’adapter
à la nature complexe et à la taille de leur écosystème. Kafka a été créé pour permettre à la
messagerie de devenir le cœur de la pile technologique de LinkedIn et a réussi à évoluer
jusqu’à atteindre des billions de messages par jour sur le site.
Plus loin dans ce chapitre, nous parlerons d’Azure Event Hubs, un service compatible avec
Apache Kafka et permettant aux applications de simplement mettre à jour la chaîne de con-
nexion du consommateur et du producteur, mais qui peut ne pas rendre toutes les fonction-
nalités de Kafka disponibles. Managed Kafka fait également partie de HDInsight, une large
suite de services d’analyse dans le cloud sur Azure. HDInsight dépasse la portée de ce livre.

Une vue d’ensemble des plateformes de messagerie populaires natives du cloud 243
CNCF CloudEvents
CloudEvents a été lancé pour la première fois en 2017 au sein du groupe de travail sans serveur
CNCF. Selon les propres mots de CNCF : « CloudEvents est une spécification pour décrire
les données d’événement dans des formats courants afin d’assurer l’interopérabilité entre les
services, les plateformes et les systèmes. » Bien que ce projet ait un grand potentiel (et soit
pris en charge par Azure Event Grids), nous ne le couvrirons pas plus en détail dans ce livre.

Exploration de la messagerie Cloud avec NATS


NATS est une autre mise en œuvre de messagerie à code source libre qui a été acceptée par CNCF.
NATS a été lancé pour la première fois en 2011 et a été initialement mis en œuvre dans Ruby en
tant que couche de messagerie et de découverte de services pour Cloud Foundry. Il a ensuite été
réécrit à Golang et est activement en cours de développement aujourd’hui. Bien qu’il ne soit pas
aussi populaire que RabbitMQ ou Kafka, NATS offre plusieurs options facilement déployables
en raison de son serveur binaire unique disponible sur plusieurs systèmes d’exploitation, de
l’architecture de processeur et des référentiels Docker officiels. Les bibliothèques client sont
disponibles pour tous les langages de programmation populaires. La simplicité, l’opérabilité, la
performance et la sécurité sont citées comme les avantages de NATS par rapport à d’autres mises
en œuvre de messagerie, au même titre que la possibilité d’utiliser facilement un ensemble de
fonctionnalités minimales avec une préférence pour la distribution « une fois maximum ».
Étant donné que le protocole de publication et d’abonnement de base ne met pas en place
la persistance des messages, les consommateurs doivent être actifs pour recevoir le message,
ce qui rend NATS optimal pour l’échange de données hautement périssables et de données
fréquemment mises à jour, comme celles des capteurs. Par défaut, les messages seront acheminés
vers tous les abonnés qui écoutent cette rubrique, comme pour le réseau de multidiffusion.
Compte tenu de sa faible empreinte de mémoire et de ses performances extrêmement élevées,
NATS est également idéal pour les cas d’utilisation de l’analyse de périphérie et de l’IoT.
Pour une mise en œuvre de messagerie aussi légère qui se concentre sur la simplicité, NATS
dispose de capacités d’authentification et d’autorisation relativement sophistiquées et prend
en charge la multilocation par défaut via les comptes. NATS Streams et NATS JetStream
prennent en charge la mémoire et la persistance sur disque. La publication, l’abonnement
et le streaming de NATS ont pris en charge la haute disponibilité et l’évolutivité horizontale
dès le début, et JetStream les met en œuvre depuis début 2021.

Architecture du protocole NATS


Comme RabbitMQ, NATS offre plusieurs modèles de messagerie et ses capacités ont continué
d’évoluer de la mise en œuvre initiale de publication et d’abonnement, au streaming NATS
(comme illustré à la figure 10.8), à son nouveau sous-système de stockage, JetStream. Lancé
en 2020, JetStream est désormais disponible dans NATS 2.2.x. NATS inclut également la
prise en charge native du protocole MQTT.

244 Chapitre 10 : Faire passer le message


Figure 10.8. Architecture NATS de base

Étant donné que l’architecture de base est la publication et l’abonnement, les producteurs
de NATS sont des éditeurs et ses consommateurs sont des abonnés. NATS JetStream utilise
l’abstraction interne des consommateurs.
L’échange le plus simple possible entre un éditeur et un abonné utilisant l’interface de ligne
de commande de NATS est la meilleure façon de voir à quel point le protocole est simple.
Examinons un exemple. Dans le code suivant, un producteur envoie le message : « arf »,
avec comme objet : dog.food:
$ echo "arf" | nats pub dog.food
16:10:43 Reading payload from STDIN
16:10:43 Published 4 bytes to "dog.food"

Dans le code suivant, un consommateur s’abonne au service pour tous les messages qui
commencent par « dog. » :
$ nats sub dog.*
16:09:49 Subscribing on dog.*
[#1] Received on "dog.food"

Clustering de plusieurs serveurs NATS


NATS permet à plusieurs serveurs d’être mis en cluster pour fournir la haute disponibilité
et l’équilibrage de charge.
Le cluster le plus simple peut être démarré en procédant comme suit :

• Configuration d’une URL de cluster avec l’argument -cluster lors du démarrage


du serveur
• Spécification des itinéraires dans les nœuds de serveur supplémentaires

En supposant que nous ayons deux serveurs Linux différents sur 192.168.2.238
et 192.168.2.247, nous pouvons configurer une cluster avec les commandes suivantes :
nats-server -DV -cluster nats://192.168.2.238:4248
nats-server -DV -cluster nats://192.168.2.247:4248 -routes nats://192.168.2.238:4248

Exploration de la messagerie Cloud avec NATS 245


Utilisation du serveur NATS Docker
Outre RPM et debs, Synadia distribue et gère des images Docker qui sont probablement le
moyen le plus simple d’exécuter un serveur NATS, comme le montre l’exemple 10.1.

Exemple 10.1. Exécution de NATS à l’aide de Docker

$ docker run -p 4222:4222 -p 8222:8222 -v ~/tmp/jetstream:/tmp/jetstream nats -js -m 8222


[1] 2021/06/26 21:23:21.572029 [INF] Starting nats-server
[1] 2021/06/26 21:23:21.572258 [INF] Version: 2.3.0
[1] 2021/06/26 21:23:21.572289 [INF] Git: [56a144a]
[1] 2021/06/26 21:23:21.572311 [INF] Name: NDTRIDIRUYTNR5TKQCEKWFSVEKVPRPA2LICRPESRDOAFEDC
UPUSDKGX5
[1] 2021/06/26 21:23:21.572378 [INF] Node: N3Qm3ud7
[1] 2021/06/26 21:23:21.572399 [INF] ID: NDTRIDIRUYTNR5TKQCEKWFSVEKVPRPA2LICRPESRDOAFEDC
UPUSDKGX5
[1] 2021/06/26 21:23:21.574923 [INF] Starting JetStream
[1] 2021/06/26 21:23:21.576592 [INF]
[1] 2021/06/26 21:23:21.576622 [INF]
[1] 2021/06/26 21:23:21.576631 [INF]
[1] 2021/06/26 21:23:21.576639 [INF]
[1] 2021/06/26 21:23:21.576647 [INF]
[1] 2021/06/26 21:23:21.576656 [INF] https://docs.nats.io/jetstream
[1] 2021/06/26 21:23:21.576664 [INF]
[1] 2021/06/26 21:23:21.576672 [INF] ---------------- JETSTREAM ----------------
[1] 2021/06/26 21:23:21.576697 [INF] Max Memory: 2.06 GB
[1] 2021/06/26 21:23:21.576711 [INF] Max Storage: 4.64 GB
[1] 2021/06/26 21:23:21.576779 [INF] Store Directory: "/tmp/nats/jetstream"
[1] 2021/06/26 21:23:21.576800 [INF] -------------------------------------------
[1] 2021/06/26 21:23:21.579799 [INF] Starting http monitor on 0.0.0.0:8222
[1] 2021/06/26 21:23:21.580581 [INF] Listening for client connections on 0.0.0.0:4222
[1] 2021/06/26 21:23:21.581147 [INF] Server is ready

Nous pouvons passer les arguments complets de la ligne de commande. Dans ce cas, nous
précisons un volume à monter dans notre répertoire ~/tmp à utiliser pour la persistance
JetStream.

Surveillance des serveurs NATS


NATS expose un point de terminaison de surveillance qui peut être affiché à l’aide du nav-
igateur, de la ligne de commande ou du navigateur web, comme illustré à la figure 10.9.

246 Chapitre 10 : Faire passer le message


Figure 10.9. Point de terminaison de surveillance NATS

Voici un exemple de sortie du point de terminaison de surveillance NATS :


$ curl http://100.119.182.45:8222/varz
{
"server_id": "ND6FZROVHED32BDLBTMCPXEN4LIKB2ROR3HO3GZ6DLDB64Z54SLG5GOD",
"server_name": "ND6FZROVHED32BDLBTMCPXEN4LIKB2ROR3HO3GZ6DLDB64Z54SLG5GOD",
"version": "2.2.2",
"proto": 1,
"git_commit": "a5f3aab",
"go": "go1.16.3",
"host": "0.0.0.0",
"port": 4222,
"connect_urls": [
"192.168.2.247:4222",
"192.168.122.1:4222",
"172.17.0.1:4222",
"100.119.182.45:4222",
"[fd7a:115c:a1e0:ab12:4843:cd96:6277:b62d]:4222",
"192.168.122.1:4222",
"172.17.0.1:4222",
"100.119.135.53:4222",
"[fd7a:115c:a1e0:ab12:4843:cd96:6277:8735]:4222",
"192.168.2.238:4222"
],
"max_connections": 65536,
"ping_interval": 120000000000,
"ping_max": 2,
"http_host": "0.0.0.0",
"http_port": 8222,
"http_base_path": "",
"https_port": 0,
"auth_timeout": 2,

Exploration de la messagerie Cloud avec NATS 247


"max_control_line": 4096,
"max_payload": 1048576,
"max_pending": 67108864,
"cluster": {
"name": "xCQWcOMbOGCpM7ESUUctIc",
"addr": "192.168.2.247",
"cluster_port": 4248,
"auth_timeout": 2,
"urls": [
"192.168.2.238:4248"
],
"tls_timeout": 2
},
"gateway": {},
"leaf": {},
"jetstream": {},
"tls_timeout": 2,
"write_deadline": 10000000000,
"start": "2021-05-03T15:13:05.512147331Z",
"now": "2021-05-03T15:41:53.509877951Z",
"uptime": "28m47s",
"mem": 12353536,
"cores": 6,
"gomaxprocs": 6,
"cpu": 0,
"connections": 1,
"total_connections": 1,
"routes": 1,
"remotes": 1,
"leafnodes": 0,
"in_msgs": 117,
"out_msgs": 115,
"in_bytes": 67297,
"out_bytes": 66346,
"slow_consumers": 0,
"subscriptions": 144,
"http_req_stats": {
"/": 3,
"/accountz": 1,
"/connz": 1,
"/gatewayz": 0,
"/leafz": 1,
"/routez": 0,
"/subsz": 1,
"/varz": 2
},
"config_load_time": "2021-05-03T15:13:05.512147331Z",
"system_account": "$SYS"
}

Vous pouvez utiliser un outil tel que Telegraf pour extraire ces points de terminaison HTTP
et les envoyer à un back-end de métrique en amont ou à l’exportateur Prometheus.

248 Chapitre 10 : Faire passer le message


Persistance de NATS avec JetStream
Bien que NATS ait introduit une mise en œuvre de persistance appelée NATS Streaming
en 2019, elle sera obsolète en juin 2023. La mise en œuvre de deuxième génération, appelée
JetStream, est en cours de développement depuis  2020 et sera l’option préférée pour les
cas d’utilisation à l’avenir. Bien que JetStream dispose d’un référentiel GitHub séparé, il
est maintenant intégré à NATS  Server  2.2.x et pris en charge par l’interface de ligne de
commande de NATS, dont nous discuterons sous peu. La publication et l’abonnement de
NATS n’offraient que la distribution « une fois maximum ». JetStream permet la distribution
«  une seule fois  » et ressemble plus étroitement au comportement des files d’attente
RabbitMQ et des objets Kafka (voir la figure 10.10).

Figure 10.10. Architecture de traitement JetStream

Sécurité NATS
Un chapitre entier pourrait facilement être écrit sur la configuration de l’éventail des
capacités d’authentification, d’autorisation et de chiffrage de NATS 2.3.x. Dans cette section,
nous couvrirons les étapes de base nécessaires pour élever le niveau en ce qui concerne les
valeurs par défaut non sécurisées que nous avons utilisées dans la plupart de nos exemples
jusqu’à présent, mais qui sont inappropriées pour les déploiements de production. Une
solide infrastructure de gestion des identités et des secrets est le fondement des systèmes
cloud sécurisés, mais cela dépasse la portée de ce livre. NATS fournit un sous-système
d’autorisations robuste qui permet de définir le contrôle granulaire sur une base thématique
et utilise des magasins d’identités distribués.

Exploration de la messagerie Cloud avec NATS 249


Authentification basée sur Nkey
Bien que NATS prenne toujours en charge l’authentification par jeton, ainsi que par nom
d’utilisateur et mot de passe, l’authentification par Nkey est recommandée à l’avenir. Il
utilise le chiffrement asymétrique et ne nécessite pas le stockage d’une clé partagée sur le
serveur au format texte brut ou haché. La gestion des clés est extrêmement simple.
Pour générer les paires de clés, utilisez l’utilitaire nk qui se trouve dans le référentiel nkeys :
$ nk -gen user -pubout
SUAGWCAMMXKR43EDIXVC5FEA5G3767ALGQR75N27NGPK37KZUURS7F32FE
UBFHQJERKEBE323BXQEVT6257CIRJ4CIC5LGT6R2LJ524GQTUQHXCUK3

La clé publique (qui commence par un « U ») est configurée sur le serveur :
$ cat server.conf
net: 0.0.0.0
port: 4242

authorization {
users: [
{ nkey: UBFHQJERKEBE323BXQEVT6257CIRJ4CIC5LGT6R2LJ524GQTUQHXCUK3 }
]
}

La clé privée (ou « seed », ce qui explique le préfixe « S ») est stockée sur le disque et trans-
mise en tant qu’argument au client NATS ou en tant que variable dans le code. Comme
pour toute donnée, il est préférable que ces clés soient stockées dans un magasin de secrets
externe tel qu’Azure Key Vault ou en tant que secret Kubernetes :
$ cat client.nkey
SUAGWCAMMXKR43EDIXVC5FEA5G3767ALGQR75N27NGPK37KZUURS7F32FE

$ nats -s nats://127.0.0.1:4242 --nkey client.nkey pub "foo"


12:57:17 Published 4 bytes to "foo"

Authentification TLS
Bien que nous disposions d’une authentification sécurisée (et éventuellement d’une autori-
sation, selon la façon dont nous avons configuré les autorisations d’objet), l’utilisation de
Nkey seule n’est pas adéquate pour la sécurité du réseau, car NATS est, par défaut, un pro-
tocole de texte brut ASCII.
Comme la plupart des protocoles natifs du cloud, NATS prend en charge le chiffrement TLS
mutuel. En général, le plus grand défi avec TLS est la gestion des certificats et des clés, ce qui
dépasse la portée de ce livre. À des fins de démonstration, mkcert fournit un chemin plus
facile qu’OpenSSL pour générer les paires de clés d’autorité de certification (CA), de client
et de serveur nécessaires pour le chiffrement de bout en bout.

250 Chapitre 10 : Faire passer le message


Tout d’abord, nous installons l’autorité de certification et créons des certificats de client et
de serveur dans le répertoire ~/nats :
$ mkdir ~/nats && cd ~/nats

# Create the CA
$ mkcert -install

# Create the server


$ mkcert -cert-file server-cert.pem -key-file server-key.pem localhost 127.0.0.1 ::1

# Generate a certificate for client authentication.


$ mkcert -client -cert-file client-cert.pem -key-file client-key.pem localhost \
::1 127.0.0.1 email@localhost
$ cp -av `mkcert -CAROOT`/* ~/nats

Ensuite, nous démarrons le serveur (dans le répertoire ~/nats) et nous ajoutons les
paramètres de certificat TLS :
$ nats-server -DV --tls --tlscert=server-cert.pem --tlskey=server-key.pem -ms 8222

En dernier lieu, nous nous connectons au client


$ nats --tlscert=client-cert.pem --tlskey=client-key.pem account info

Sur le serveur, nous devrions voir une connexion TLS réussie :


[10108] 2021/06/28 14:46:40.126469 [DBG] 127.0.0.1:50192 - cid:11 - Client connection created
[10108] 2021/06/28 14:46:40.126573 [DBG] 127.0.0.1:50192 - cid:11 - Starting TLS client
connection handshake
[10108] 2021/06/28 14:46:40.157446 [DBG] 127.0.0.1:50192 - cid:11 - TLS handshake complete
[10108] 2021/06/28 14:46:40.157468 [DBG] 127.0.0.1:50192 - cid:11 - TLS version 1.3, cipher
suite TLS_AES_128_GCM_SHA256
[10108] 2021/06/28 14:46:40.157584 [TRC] 127.0.0.1:50192 - cid:11 - <<- [CONNECT
{"verbose":false,"pedantic":false,"tls_required":true,"name":"NATS CLI Version 0.0.23",
"lang":"go","version":"1.11.0","protocol":1,"echo":true,"headers":true,"no_responders":true}]

Déploiement de NATS sur Kubernetes


NATS peut être installé rapidement à l’aide d’un tableau Helm. Le tableau Helm installera
également un opérateur Prometheus et un gestionnaire de certificats qui vous permettront
d’utiliser pleinement les métriques et la prise en charge TLS qui viennent avec NATS :

1. Commencez par ajouter le référentiel Helm :


$ helm repo add nats https://nats-io.github.io/k8s/helm/charts/
$ helm repo update

2. Puis installez le serveur NATS :


$ helm install my-nats nats/nats

3. Installez le serveur de diffusion NATS (appelé stan) :


$ helm install my-stan nats/stan --set stan.nats.url=nats://my-nats:4222

Exploration de la messagerie Cloud avec NATS 251


4. A
 près ces étapes, il y aura un inspecteur Grafana que vous pourrez voir en redirigeant
le port vers le pod :
kubectl port-forward deployments/nats-surveyor-grafana 3000:3000

5. Vous
 pourrez afficher Grafana dans votre navigateur à l’adresse http://127.0.0.1:3000/d/
nats/nats-surveyor?refresh=5s&orgId=1.

Votre déploiement d’aide offre un certain nombre de configurables Helm. Vous pouvez en
savoir plus à ce sujet sur la page de configuration du déploiement de NATS Helm.
Vous pouvez coder sur NATS d’une manière très simple en Python, en utilisant le paquet
nats-py. Dans l’exemple suivant, vous codez un producteur et un consommateur de base
en Python. NATS fournit un serveur public dans lequel vous pouvez effectuer des tests. Si
vous souhaitez effectuer des tests dans votre propre environnement, remplacez nc = await
nats.connect (« nats://demo.nats.io:4222 ») par l’adresse de votre instance NATS :

import time

import asyncio
import nats
from nats.aio.errors import ErrConnectionClosed, ErrTimeout, ErrNoServers

async def run():


# The Nats project provides a public demo server `nats://demo.nats.io:4222`.
# You can add your own Nats server on the next line
nc = await nats.connect("nats://demo.nats.io:4222")

async def message_handler(msg):


subject = msg.subject
reply = msg.reply
data = msg.data.decode()
print("Received a message on '{subject} {reply}': {data}".format(
subject=subject, reply=reply, data=data))

# Simple publisher and async subscriber via coroutine.


sub = await nc.subscribe("foo", cb=message_handler)

await nc.publish("foo", b'Message1')


await nc.publish("foo", b'Message2')

time.sleep(5)
# Remove interest in subscription
await sub.unsubscribe()

# Terminate connection to NATS.


await nc.drain()

if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()

252 Chapitre 10 : Faire passer le message


Services de messagerie Azure
Lorsque les offres de cloud public commercial sont devenues disponibles pour la première
fois au milieu des années 2000, les services de stockage et de messagerie (comme AWS S3
et AWS SQS) ont été parmi les premiers à être disponibles pour les clients. Microsoft Azure
fournit plusieurs services de messagerie gérés pour prendre en charge les mises en œuvre
propres au cloud, ainsi que des protocoles de messagerie basés sur des normes comme
AMQP et JMS, ainsi que des modèles comme la file d’attente des agents de travail et la
publication/l’abonnement que nous avons déjà explorés dans ce chapitre. Toutes les options
Azure fournissent une distribution « au moins une fois ».
Lorsque vous choisissez entre Azure Service Bus, Azure Event Hubs et Azure Event Grid,
vous devez prendre en compte les critères suivants :

• Compatibilité avec les plateformes et les protocoles filaires de messagerie sur site
(ActiveMQ, RabbitMQ), ainsi que les outils
• Niveau d’intégration requis avec d’autres services gérés Azure
• Besoin en messagerie et transactions ordonnées
• Capacité à gérer le streaming d’événements
• Utilisation de pipelines de big data
• Taux d’ingestion maximal (événements par seconde)
• Modèles d’événements asynchrones disponibles
• Tarification

L’un des principaux avantages de l’utilisation des services de messagerie Azure est la
possibilité de tirer parti de la gestion des capacités et des secrets avec les solutions de gestion
des identités et des accès d’Azure. Examinons maintenant en détail les offres Azure.

Azure Service Bus


Azure Service Bus est un service de files d’attente géré qui fournit des fonctionnalités de
niveau entreprise comparables à celles d’ActiveMQ et de RabbitMQ, mais sans la surcharge
de gestion de plusieurs courtiers sur des machines virtuelles ou des déploiements en
plus de Kubernetes. Azure Service Bus peut couvrir plusieurs zones de disponibilité avec
Azure Service Bus Premium et est conçu pour prendre en charge la migration à partir de
mises en œuvre JMS 2.0 sur site.
Service Bus prend en charge la file d’attente de messages simples et les modèles de publication
et d’abonnement dont nous avons discuté au début du chapitre. Le modèle le plus courant est
le « modèle d’attraction », dans lequel les consommateurs interrogent le point de terminaison
du service pour savoir s’il y a de nouveaux messages. Comme avec RabbitMQ et Kafka,
plusieurs consommateurs peuvent traiter les messages aussi rapidement que possible lorsque
les messages sont disponibles, ce qui permet de répartir le travail entre plusieurs nœuds

Services de messagerie Azure 253


d’agents de travail. Azure Service Bus met en œuvre AMQP 1.0 et dispose de kits SDK et
d’exemples de code bien documentés pour .NET, Java, Python, Go, etc.

Concepts de Service Bus
Les abstractions Azure Service Bus (voir les figures 10.11 et 10.12 pour les architectures de
référence) fournissent des fonctionnalités similaires à celles disponibles dans NATS.

Figure 10-11. Architecture de référence de Service Bus

Figure 10-12. Architecture de messagerie simple

Espaces de noms. Les espaces de noms permettent la segmentation des rubriques, des files
d’attente et des services en tant que conteneur pour les composants de messagerie, de la
même manière qu’un réseau virtuel sert une limite pour les composants réseau tels que
les machines virtuelles et les équilibreurs de charge. Kafka ne fournit pas une abstraction
comparable, mais les hôtes virtuels RabbitMQ et les comptes NATS le font.

Files d’attente, rubriques et abonnements. Les files d’attente Azure Service Bus fournissent un


ensemble ordonné de messages FIFO qui sont extraits de manière similaire aux consommateurs
Kafka et RabbitMQ ou aux consommateurs basés sur l’attraction NATS, tandis que les rubriques
sont principalement destinées aux modèles de publication et d’abonnement ainsi qu’aux

254 Chapitre 10 : Faire passer le message


communications de type un à plusieurs pour échanger des messages avec un grand nombre
de consommateurs. Les abonnements permettent aux consommateurs de recevoir des
messages en fonction des règles de filtrage. Comme avec NATS JetStream, les messages sont
récupérés à partir de l’abonnement (similaire au consommateur de JetStream) et non de la
rubrique elle-même (comparable au flux NATS JetStream).

Gestion d’Azure Service Bus avec Terraform


Bien que Microsoft fournisse un kit SDK Python pour la gestion de Service  Bus, nous
nous en tiendrons à Terraform pour la création d’espaces de noms, de files d’attente et
de rubriques Service  Bus. L’exemple de code suivant déploie de nouveaux servicebus,
espace de noms, file d’attente et rubrique :
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "example" {


name = "terraform-servicebus"
location = "Central US"
}

resource "azurerm_servicebus_namespace" "example" {


name = "mdfranz-servicebus-namespace"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
sku = "Standard"

tags = {
source = "terraform"
}
}

resource "azurerm_servicebus_queue" "example" {


name = "mdfranz-servicebus-queue"
resource_group_name = azurerm_resource_group.example.name
namespace_name = azurerm_servicebus_namespace.example.name
enable_partitioning = true
}

resource "azurerm_servicebus_topic" "example" {


name = "mdfranz-servicebus-topic"
resource_group_name = azurerm_resource_group.example.name
namespace_name = azurerm_servicebus_namespace.example.name
enable_partitioning = true
}

En utilisant azure-cli, nous pouvons voir les ressources qui ont été créées :
$ az servicebus queue list --namespace-name mdfranz-servicebus-namespace -g \
terraform-servicebus
[
{
"accessedAt": "0001-01-01T00:00:00+00:00",
"autoDeleteOnIdle": "10675199 days, 2:48:05.477581",
"countDetails": {
"activeMessageCount": 0,

Services de messagerie Azure 255


"deadLetterMessageCount": 0,
"scheduledMessageCount": 0,
"transferDeadLetterMessageCount": 0,
"transferMessageCount": 0
},
"createdAt": "2021-04-14T02:24:28.260000+00:00",
"deadLetteringOnMessageExpiration": false,
"defaultMessageTimeToLive": "10675199 days, 2:48:05.477581",
"duplicateDetectionHistoryTimeWindow": "0:10:00",
"enableBatchedOperations": true,
"enableExpress": false,
"enablePartitioning": true,
"forwardDeadLetteredMessagesTo": null,
"forwardTo": null,
"id": "/subscriptions/1bf91ee3-5b21-4996-bd72-ae38e8f26ce9/resourceGroups/ \
terraform-servicebus/providers/Microsoft.ServiceBus/namespaces/ \
mdfranz-servicebus-namespace/queues/mdfranz-servicebus-queue",
"location": "Central US",
"lockDuration": "0:01:00",
"maxDeliveryCount": 10,
"maxSizeInMegabytes": 81920,
"messageCount": 0,
"name": "mdfranz-servicebus-queue",
"requiresDuplicateDetection": false,
"requiresSession": false,
"resourceGroup": "terraform-servicebus",
"sizeInBytes": 0,
"status": "Active",
"type": "Microsoft.ServiceBus/Namespaces/Queues",
"updatedAt": "2021-04-14T02:24:28.887000+00:00"
}
]

$ az servicebus topic list --namespace-name mdfranz-servicebus-namespace -g \


terraform-servicebus
[
{
"accessedAt": "0001-01-01T00:00:00+00:00",
"autoDeleteOnIdle": "10675199 days, 2:48:05.477581",
"countDetails": {
"activeMessageCount": 0,
"deadLetterMessageCount": 0,
"scheduledMessageCount": 0,
"transferDeadLetterMessageCount": 0,
"transferMessageCount": 0
},
"createdAt": "2021-04-14T12:32:46.243000+00:00",
"defaultMessageTimeToLive": "10675199 days, 2:48:05.477581",
"duplicateDetectionHistoryTimeWindow": "0:10:00",
"enableBatchedOperations": false,
"enableExpress": false,
"enablePartitioning": true,
"id": "/subscriptions/SUBSCRIPTION/resourceGroups/terraform-servicebus/providers/ \
Microsoft.ServiceBus/namespaces/mdfranz-servicebus-namespace/topics/ \
mdfranz-servicebus-topic",
 

"location": "Central US",


"maxSizeInMegabytes": 81920,
"name": "mdfranz-servicebus-topic",
"requiresDuplicateDetection": false,

256 Chapitre 10 : Faire passer le message


"resourceGroup": "terraform-servicebus",
"sizeInBytes": 0,
"status": "Active",
"subscriptionCount": 0,
"supportOrdering": false,
"type": "Microsoft.ServiceBus/Namespaces/Topics",
"updatedAt": "2021-04-14T12:32:46.487000+00:00"
}
]

Nous pouvons également voir les ressources dans la console Azure, comme le montre
la figure 10.13.

Figure 10.13. Affichage du déploiement de Service Bus sur le portail Azure

Nous pouvons également voir les métriques globales dans les espaces de noms et cliquer sur
une file d’attente précise qui fournit plus de détails sur le stockage et la taille des messages
(voir la figure 10.14).

Figure 10.14. Affichage de la file d’attente de Service Bus sur le portail Azure

Services de messagerie Azure 257


Envoi et réception de messages à et depuis une file d’attente de Service Bus en Python
En supposant que nous ayons réussi à créer l’infrastructure lors des étapes précédentes,
nous pouvons récupérer une chaîne de connexion que nous pouvons utiliser en exécutant
une commande d’interface de ligne de commande Azure :
$ az servicebus namespace authorization-rule keys list --resource-group $RES_GROUP \
--namespace-name
$NAMESPACE_NAME --name RootManageSharedAccessKey --query primaryConnectionString wBY=

Azure fournit des bibliothèques à code source libre pour envoyer et recevoir des messages
au service Azure dans tous les langages de programmation populaires. L’exemple suivant
crée un producteur et un consommateur simple en Python :

from azure.servicebus import ServiceBusClient, ServiceBusMessage

import os
connstr = os.environ['SERVICE_BUS_CONN_STR']
queue_name = os.environ['SERVICE_BUS_QUEUE_NAME']

messages = ["Testing 1","Testing 2", "Testing 3"]

with ServiceBusClient.from_connection_string(connstr) as client:


s = client.get_queue_sender(queue_name)
r = client.get_queue_receiver(queue_name)

print(s,r)

for m in messages:
s.send_messages(ServiceBusMessage(m))

while True:
print(r.receive_messages(max_message_count=1,max_wait_time=10))

Outre RabbitMQ, les lecteurs qui connaissent déjà AWS  SNS et SQS trouveront que
Service Bus est simple et capable de répondre à des cas d’utilisation similaires.

Azure Event Hubs
Azure Event Hubs est conçu pour fournir certaines des fonctionnalités d’Apache Kafka afin
de répondre aux cas d’utilisation pour le partitionnement et les groupes de consommateurs,
mais dans un service cloud géré (voir la figure 10.15). Bien que vous perdiez la visibilité
sur l’infrastructure principale que vous auriez lors de l’exécution de Kafka sur des
machines virtuelles ou Kubernetes, vous obtenez une intégration plus étroite avec des
services de données supplémentaires tels que le stockage de grands objets binaires Azure,
Azure Data Lakes et Azure Stream Analytics, pour n’en nommer que quelques-uns. Azure
prend également en charge les protocoles AMQP  1.0 et Kafka  1.0 afin de préserver la
compatibilité avec les bibliothèques clientes.

258 Chapitre 10 : Faire passer le message


Figure 10.15. Architecture d’Azure Event Hubs

Azure  Event  Hubs a quelques concepts transférés d’Azure  Service  Bus. Nous allons
rapidement les passer en revue ici :

Producteurs
Ce sont les clients qui envoient des messages à Azure Event Hubs.

Partitions
Les messages sont répartis entre les partitions pour permettre le stockage des réplicas
du message.

Groupe de clients
Ce sont des clients qui travaillent à l’unisson pour consommer des messages produits
de manière coordonnée.

Gestion d’Azure Event Hubs avec Terraform


Le code Terraform nécessaire pour approvisionner les ressources requises nécessaires à l’envoi
de messages à Azure Event Hubs ressemble beaucoup à ce que nous utilisions précédemment
pour configurer une file d’attente et un objet dans Azure Service Bus. Nous devons déclarer le
groupe de ressources et l’espace de noms dont notre instance Event Hubs fera partie :
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "example" {


name = "mdfranz-eventbus-group"
location = "East US"
}

resource "azurerm_eventhub_namespace" "example" {


name = "mdfranz-eventhub-namespace"
location = "East US"

Services de messagerie Azure 259


resource_group_name = azurerm_resource_group.example.name
sku = "Standard"
capacity = 1
}

resource "azurerm_eventhub" "example" {


name = "mdfranz-eventhub"
namespace_name = azurerm_eventhub_namespace.example.name
resource_group_name = azurerm_resource_group.example.name
partition_count = 1
message_retention = 1
}

resource "azurerm_eventhub_consumer_group" "example" {


name = "mdfranz-consumergroup"
resource_group_name = azurerm_resource_group.example.name
namespace_name = azurerm_eventhub_namespace.example.name
eventhub_name = azurerm_eventhub.example.name
}

La création d’un groupe de consommateurs est facultative, car il existe un groupe de


consommateurs par défaut. Notez également que le fait d’avoir plusieurs groupes de
consommateurs nécessite une SKU standard par opposition à une SKU de base.

Comme c’était le cas avec Azure Event Bus, il existe plusieurs façons de s’authentifier auprès
des points de terminaison Event  Hubs. Le moyen le plus simple consiste à utiliser une
chaîne de connexion, comme illustré à l’exemple suivant :
$ az eventhubs namespace authorization-rule keys list --resource-group mdfranz-eventbus- \
group --namespace-name mdfranz-eventhub-namespace --name RootManageSharedAccessKey
{
"aliasPrimaryConnectionString": null,
"aliasSecondaryConnectionString": null,
"keyName": "RootManageSharedAccessKey",
"primaryConnectionString": "Endpoint=sb://mdfranz-eventhub-namespace.servicebus.windows. \
net/;SharedAccessKeyName=RootManageSharedAccessKey; \
SharedAccessKey=jfMUbfQZX8UJglcCKLAeIp5CZVLcwcSGpuKdJX/CMzk=",
"primaryKey": "jfMUbfQZX8UJglcCKLAeIp5CZVLcwcSGpuKdJX/CMzk=",
"secondaryConnectionString": "Endpoint=sb://mdfranz-eventhub-namespace.servicebus. \
windows.net/;SharedAccessKeyName=RootManageSharedAccessKey; \
SharedAccessKey=gFy7SuuGjw12GfoHXd6UQeZdy5Y0xu/gStZtQUhaxsY=",
"secondaryKey": "gFy7SuuGjw12GfoHXd6UQeZdy5Y0xu/gStZtQUhaxsY="
}

Le paramètre EntityPath=mdfranz-eventhub, c’est-à-dire :


Endpoint=sb://mdfranz-eventhub-namespace.servicebus.windows.net/;
SharedAccessKeyName=RootManageSharedAccessKey;EntityPath=mdfranz-
eventhubSharedAccessKey=jfMUbfQZX8UJglcCKLAeIp5CZVLcwcSGpuKdJX/CMzk=
il doit être ajouté à la chaîne de connexion, car si vous créez un
client à l’aide de NewHubfromConnectionString, la méthode Send ne
permet pas de préciser un nom de grille d’événement en tant que
paramètre, vous devez donc ajouter un EntityPath à la place.

260 Chapitre 10 : Faire passer le message


Azure Event Grid
Azure Event Grid a été publié pour une disponibilité générale en 2018 et est le meilleur des
courtiers de messagerie proposés par Azure, car il permet à plusieurs sources d’événements
d’être acheminées vers diverses destinations au sein de l’écosystème Azure. Les messages
peuvent être envoyés vers et depuis les services de messagerie existants dont nous venons de
parler (Azure Service Bus et Azure Event Hubs), ainsi que vers des services et des points de
terminaison supplémentaires (comme illustré à la figure 10.16), par exemple Azure Functions,
Webhooks, Logic Apps, et à d’autres fins intégrées à l’application (par exemple, le suivi de la
façon dont les utilisateurs interagissent avec l’application). Azure Event Hubs est une offre
« sans serveur » de premier plan dans laquelle la facturation se fait par événement.

Figure 10.16. Écosystème Event Grid


Sur la figure 10.16, notez la nature de type push d’Event Grid par opposition à la topologie
push-pull générale à laquelle nous sommes habitués dans d’autres systèmes.

Azure  Event  Grid utilise une terminologie qui diffère légèrement des autres plateformes
dont nous avons discuté :

Domaines
Les domaines sont analogues aux espaces de noms, ce qui sépare les cas d’utilisation
dans le locataire.

Rubriques
Pour Event  Grid, il existe deux types de rubriques  : les rubriques système, qui
proviennent des systèmes Azure (Event Hubs, stockage de grands objets binaires, etc.),
comme illustré à gauche à la figure 10.16, et les rubriques personnalisées, qui peuvent
être votre propre schéma personnalisé encapsulé dans le schéma Event Grid.

Services de messagerie Azure 261


Déploiement et utilisation de grilles d’événements
Les grilles d’événements Azure ont un déploiement Terraform minimaliste, comme le
montre l’exemple suivant :
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "East US"
}

resource "azurerm_eventgrid_topic" "example" {


name = "my-eventgrid-topic"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name

tags = {
environment = "Production"
}
}

Veillez à modifier le nom de la rubrique, car elle est globalement unique dans l’ensemble
de la région Azure dans laquelle vous effectuez le déploiement. Une fois ce déploiement
Terraform appliqué, une nouvelle rubrique Event Grid sera créée avec le nom https://<event-
grid-topicname>.<topic-location>.eventgrid.azure.net/api/events; ou, dans notre cas, https://
myeventgrid-topic.east-us.eventgrid.azure.net/api/events. Vous pourrez trouver la clé d’accès
pour la rubrique en exécutant az eventgrid topic key list --resourcegroup
example-resources --name my-eventgrid-topic.

Voici un producteur simple qui utilise le package Python azure-eventgrid :


from azure.identity import DefaultAzureCredential
from azure.eventgrid import EventGridPublisherClient, EventGridEvent

event = EventGridEvent(
data={"team": "azure-sdk"},
subject="Door1",
event_type="Azure.Sdk.Demo",
data_version="2.0"
)

credential = DefaultAzureCredential("<access-key>")
endpoint = `https://my-eventgrid-topic.east-us.eventgrid.azure.net/api/events"
client = EventGridPublisherClient(endpoint, credential)
client.send(event)

Un certain nombre d’exemples intéressants sur la façon de coder avec Event  Grid sont
disponibles dans la bibliothèque cliente Azure Event Grid pour Python.

262 Chapitre 10 : Faire passer le message


Résumé
Vous disposez de nombreuses options en ce qui concerne le choix des technologies de
messagerie à mettre en place avec des applications nouvelles ou existantes. Comme
d’habitude, la réponse à «  quoi  » et «  comment  » faire fonctionner une technologie est
souvent « cela dépend! » Il existe un chevauchement important des fonctionnalités entre
les offres de messagerie open source et Azure dont nous avons discuté dans ce chapitre.
Assurez-vous que vous comprenez vos exigences en matière de durabilité, d’évolutivité
et de sécurité avant de choisir un service de messagerie, afin de garantir la réussite du
déploiement de votre infrastructure de messagerie. Nous allons maintenant passer à notre
dernière infrastructure, l’informatique sans serveur.

Résumé 263
CHAPITRE 11
Sans serveur

Jusqu’à présent, nous nous sommes largement concentrés sur les concepts sur site, ainsi
que sur la discussion et la mise en œuvre de leurs équivalents basés sur le cloud. Dans ce
chapitre, nous approfondirons un nouveau type d’architecture informatique connu sous le
nom de « sans serveur » et qui est profondément lié au cloud computing. Plus précisément,
nous examinerons les plateformes sans serveur courantes, notamment les applications de
fonctions Azure, Knative, KEDA et OpenFaaS.

Introduction à l'informatique sans serveur


Comme pour les conteneurs, il y a beaucoup de battage médiatique autour de l’informatique
sans serveur et des possibilités qu’elle offre. Ce chapitre examinera les caractéristiques et les
avantages de l’informatique sans serveur.

Qu’est-ce que l’informatique sans serveur ?


Contrairement aux conteneurs, qui contiennent encore en grande partie un certain nombre de
concepts basés sur l’hôte, l’informatique sans serveur fait abstraction de tout cela, ce qui signifie
que vous devez simplement vous préoccuper de votre capacité d’exécuter une fonction logicielle.
L’informatique sans serveur est aujourd’hui nettement plus populaire en raison de la
possibilité de créer des applications rapidement et de les exécuter sans se soucier de la
gestion de l’infrastructure. En outre, l’informatique sans serveur considère la mise à l’échelle
automatique comme une fonctionnalité haut de gamme, ce qui signifie que vous n’avez pas
à vous soucier de quoi que ce soit d’autre que d’écrire du code et de le déployer à l’aide de
mécanismes de déploiement de cadre standardisés.
L’avantage de l’informatique sans serveur est que la maintenance et la configuration du
serveur ne posent généralement aucun problème. De plus, si vous souhaitez exécuter
rarement une fonction logicielle, les fonctions sans serveur peuvent être une option
beaucoup plus économique, car vous payez à la demande et non par réservation des
ressources informatiques (c’est-à-dire, le calcul).

265
Le paysage de l’informatique sans serveur, comme mentionné précédemment, est un espace
raisonnablement nouveau qui a innové rapidement. Outre les grands fournisseurs de
services cloud qui proposent une plateforme de fonction, d’autres sociétés Internet comme
Cloudflare, Netlify et Twilio fournissent également des fonctionnalités sans serveur.

Qu’est-ce qu’une fonction sans serveur ?


Une fonction sans serveur est un petit programme de courte durée qui s’exécute lorsqu’il
est déclenché. Les fonctions sans serveur peuvent être déclenchées au moyen d’une
requête HTTP, à l’aide d’un timer, ou dans Azure Event Grid en déclenchant une fonction
basée sur un événement Cosmos DB ou Service Bus.
Une fonction aura généralement un seul objectif autonome qui est exécuté rapidement et a
des dépendances limitées. En outre, ce n’est généralement pas un point de terminaison avec
beaucoup de requêtes par seconde, car la performance est une préoccupation secondaire
des fonctions sans serveur.
Vous avez peut-être entendu parler d’utilitaires comme https://icanhazip.com, un service
qui renvoie simplement l’adresse IP publique à partir de laquelle vous vous connectez. Ceci
peut être écrit et déployé facilement comme fonction sans serveur :
async function handleRequest(request) {
const clientIP = request.headers.get("CF-Connecting-IP")
return new Response(clientIP)
}

addEventListener("fetch", event => {


event.respondWith(handleRequest(event.request))
})

L’exemple précédent crée un écouteur d’événement pour événement de type « extraction » (qui
est un HTTP GET), et lorsque cet événement est déclenché, la fonction handleRequest
est appelée. La fonction analyse un en-tête de requête HTTP, CF-Connecting-IP, c’est-
à-dire l’IP qui fait la demande à la fonction sans serveur. La fonction répond alors avec
l’adresse IP à l’origine de la demande.

Le paysage sans serveur


Comme mentionné précédemment, bien qu’il s’agisse d’un nouvel espace, le paysage sans
serveur a connu un développement rapide de son système. Le paysage sans serveur CNCF
(illustré à la figure 11.1) se compose d’un certain nombre de sections :
Outils
Cadres logiciels qui aident à créer des logiciels sans serveur
Sécurité
Logiciel spécialement conçu pour sécuriser l’infrastructure sans serveur
Infrastructures
Cadres logiciels pour la création de fonctions sans serveur

266 Chapitre 11 : Sans serveur


Plateforme hébergée
Fournisseurs publics qui offrent des fonctions sans serveur
Plateforme installable
Logiciel que vous pouvez installer pour exécuter des fonctions sans serveur

Figure 11.1. Paysage sans serveur de la CNCF

Nous ne le couvrirons pas en détail dans ce livre mais, au-delà des cadres et des fournisseurs
de services cloud principaux qui proposent des offres sans serveur, les fournisseurs de
réseau de périphérie (ou CDN) offrent également une fonctionnalité sans serveur ou de
« calcul en périphérie ». Cette fonctionnalité est devenue extrêmement populaire dans des
entreprises comme Cloudflare.

Avantages de l’approche sans serveur


Lorsqu’elle est utilisée correctement, l’approche sans serveur peut présenter des avantages
majeurs pour l’utilisateur :
Réduire les coûts
Vous êtes facturé pour le nombre de fois ou la durée d’exécution de votre fonction, ce
qui signifie que vous n’avez pas à payer pour les ressources cloud inactives.
Élimination des frais généraux opérationnels
Compte tenu de la simplicité du modèle de fonction et des dépendances limitées, la
nécessité de provisionner ou de gérer activement la fonction est considérablement
réduite.

Introduction à l'informatique sans serveur 267


Mise à l’échelle automatique
La fonction s’adapte automatiquement et aucun effort supplémentaire n’est nécessaire
pour augmenter ou diminuer le nombre d’agents de travail de la fonction.
Cycles de développement plus rapides
Lier tous les points précédents permet de générer un cycle d’ingénierie plus rapide, ce
qui peut conduire à une productivité accrue des développeurs et des opérateurs.

Inconvénients potentiels de l’approche sans serveur


L’approche sans serveur peut présenter quelques inconvénients qui doivent également être
pris en compte :
Débogabilité
Lorsque tout le calcul est de courte durée, le débogage de votre fonction est
exceptionnellement complexe.
Grand nombre de fonctions
Bien que la mise à l’échelle automatique soit intrinsèquement intégrée à toutes les
fonctions sans serveur, il y a un certain nombre d’effets indésirables à l’utilisation
continuelle de la mise à l’échelle automatique. Premièrement, cela peut nuire aux
performances et, deuxièmement, la gestion du regroupement de connexions en aval
devient plus compliquée (en particulier avec les bases de données).
Dépendance envers un fournisseur
Un inconvénient de toutes les plateformes sans serveur est que votre code de fonction
est directement lié à la plateforme logicielle sur laquelle vous exécutez vos fonctions.
Cela limite considérablement votre portabilité au fil du temps.
Maintenant que nous avons expliqué ce qu’est l’informatique sans serveur et certains
avantages de l’utilisation de cette technologie, nous allons examiner diverses plateformes
sur lesquelles exécuter des fonctions sans serveur.

Azure Function Apps


Les applications de fonction Azure sont les offres d’applications sans serveur de la
plateforme d’Azure. Les applications de fonction offrent la possibilité d’exécuter du code
ou des conteneurs dans des environnements Linux ou Windows avec la prise en charge de
plusieurs langages de programmation, notamment :

• Plateforme .NET (C#, F#)


• JavaScript et TypeScript
• Python
• Java
• PowerShell Core

268 Chapitre 11 : Sans serveur


Une application de fonction Azure aura un plan associé qui dicte la façon dont votre
application évoluera, les fonctionnalités disponibles et le prix que vous payez. À l’heure
actuelle, il existe trois options :
Consommation
Il s’agit d’un plan de base dans lequel la fonction évolue automatiquement et vous ne
payez que lorsque vos fonctions sont en cours d’exécution.
Fonctions Premium
Elles s’adaptent automatiquement en fonction de la demande en utilisant des agents de
travail préchauffés. Elles sont exécutées avec des instances plus puissantes et permettent
certaines fonctionnalités réseau avancées.
Plan App Service
Ce plan est adapté aux scénarios de longue durée dans lesquels vous avez également
besoin d’une mise à l’échelle et d’une facturation prédictives.
Vous trouverez plus d’informations sur chaque plan sur la page de documentation des
options d’hébergement d’Azure Functions.

Architecture d’une application de fonction


L’architecture d’application d’Azure (voir la figure 11.2) s’intègre à la suite plus large d’offres
Azure, y compris Cosmos  DB, les comptes de stockage et Azure  Pipelines. L’intégration
native avec Cosmos DB et les comptes de stockage en fait une offre attrayante pour une
intégration simple avec d’autres offres Azure. Nous ne le verrons pas dans ce livre, mais les
fonctions Azure sont également liées à l’offre d’intégration et de déploiement continu (CI/
CD) dans Azure Pipelines et peuvent être surveillées par Azure Monitor.

Figure 11.2. Architecture de référence des fonctions sans serveur d’Azure

Lors de la création d’une application de fonction, vous devez également lier un


compte de stockage. Cela peut être utilisé comme stockage temporaire. Chaque

Azure Function Apps 269


application sera provisionnée avec un point de terminaison  HTTP public
lors de la création afin que la fonction soit accessible. Il est possible de sécuriser l’application
de fonction à l’aide d’un point de terminaison privé.

Création d’une application de fonction


Dans cette section, nous allons créer une application de fonction à l’aide du portail Azure :

1. Nous allons commencer par ouvrir le portail Azure et cliquer sur Créer sous Application
de fonction.
2. Nous allons maintenant remplir les paramètres de base de l’application de fonction :
a. Nous allons créer un nouveau groupe de ressources appelé serverless-test.
b. Le nom DNS de notre application sera ourtestfunctionapp.azureweb sites.
net.
c. Nous exécuterons du code (une fonction) au lieu d’un conteneur.
d. Le langage sera Python v3.9.
e. L’application de fonction sera déployée dans la région Est des États-Unis.
3. Dans l’onglet Hébergement :
a. Nous allons créer un nouveau compte de stockage appelé functionstorageaccount.
Comme les noms de compte de stockage sont uniques au niveau mondial, vous
devrez choisir votre propre nom.
b. Dans la mesure où nous exécutons Python, nous devons exécuter Linux.
c. 19 ptEnfin, nous utiliserons le plan de consommation dans le cadre de cet exemple.
4. Dans l’onglet Surveillance :
a. Nous désactiverons Application Insights.
b. Nous cliquerons ensuite sur Examiner + Créer.

Maintenant que nous avons créé notre application de fonction, nous devons créer une
fonction. Visual Studio Code a une extension, Azure Functions, qui simplifie la création, le
test et la publication d’une fonction :

1. Si vous n’avez pas installé Visual Studio Code, vous pouvez le télécharger à partir de la
page de téléchargement de Visual Studio Code.
2. Une fois que vous aurez installé Visual Studio Code, vous devrez installer l’extension
Azure Functions.
3. Cliquez sur l’icône Azure dans la barre d’activité sur le côté gauche de l’écran, ce qui
affichera la fenêtre Azure Functions. Cliquez sur Créer un nouveau projet.
4. Une boîte de dialogue apparaîtra dans laquelle vous devrez choisir un répertoire local
dans lequel enregistrer votre code.

270 Chapitre 11 : Sans serveur


5. Vous serez ensuite invité à entrer des informations sur le projet dans des fenêtres
contextuelles Visual Studio Code distinctes :
a. Sélectionnez une langue pour votre projet de fonction : choisissez Python.
b. Sélectionnez un alias Python pour créer un environnement virtuel  : choisissez
l’emplacement de votre interprète Python.
c. Si l’emplacement n’est pas affiché, tapez le chemin d’accès complet à votre binaire
Python.
d. Sélectionnez un modèle pour la première fonction de votre projet  : choisissez
Déclencheur HTTP.
e. Fournissez un nom de fonction : saisissez HttpExample.
f. Choisissez un niveau d’autorisation : choisissez Anonyme, qui permet à n’importe
qui d’appeler votre point de terminaison de fonction. Pour en savoir plus sur les
niveaux d’autorisation, consultez la documentation des clés d’autorisation.
g. Sélectionnez la façon dont vous souhaitez ouvrir votre projet : choisissez Ajouter à
l’espace de travail.
h. À l’aide de ces informations, Visual Studio Code génère un projet Azure Functions
avec un déclencheur HTTP. Vous pouvez afficher les fichiers de projet locaux dans
l’Explorateur. Pour en savoir plus sur les fichiers créés, consultez la documentation
des fichiers de projet générés.
6. À ce stade, vous aurez créé les grandes lignes d’un projet. Ajoutez le code suivant au
fichier HttpExample, qui renverra l’adresse IP du client en faisant une demande à la
fonction (similaire à icanhazip.com) :
import azure.functions as func

def main(req: func.HttpRequest ) -> func.HttpResponse:


response = False
if (req.headers.get('X-Forwarded-For')):
response = req.headers.get('X-Forwarded-For').split(':')[0]
else:
response = "127.0.0.1"
return func.HttpResponse(response)

7. 
Testez votre fonction en appuyant sur F5 sur votre clavier, puis en accédant
à http:// localhost:7071/api/HttpExample.
8. Pour déployer sur Azure, vous devrez cliquer sur le bouton Se connecter à Azure dans
la fenêtre Azure Functions.
9. Cliquez sur le bouton Télécharger, qui vous permettra de télécharger votre fonction sur
l’application que vous avez créée précédemment.

Maintenant que nous avons examiné les offres natives d’Azure, discutons des applications
de fonction déployées avec Kubernetes.

Azure Function Apps 271


Knative
Knative est une plateforme sans serveur basé sur Kubernetes. Le secteur l’a adoptée rapidement
en raison de l’effervescence autour de Kubernetes au moment de sa sortie. L’un des avantages
de Knative est que les fonctions sont empaquetées et exécutées en tant que conteneurs Docker,
ce qui réduit au minimum l’effort d’intégration dans un environnement cloud existant.

Architecture de Knative
Knative a deux modes de fonctionnement :

• Knative Serving utilise Kubernetes pour déployer des fonctions ou des conteneurs sans
serveur et a une prise en charge complète de la mise à l’échelle automatique (y compris
jusqu’à zéro Pods). Il a également l’avantage de prendre en charge les solutions de mise
en réseau du cloud comme Gloo et Istio.
• Knative  Eventing permet aux développeurs de développer des applications qui
reposent sur une architecture orientée événements. Dans le cadre d’un déploiement
d’événements, vous pouvez préciser les producteurs d’événements (sources) auxquelles
il est nécessaire de répondre.

Voyons comment installer Knative sur Kubernetes.

Installation et exécution de Knative Serving sur Kubernetes


Dans cette section, nous allons exécuter un système de service Knative de base :

1. Commencez par installer les ressources personnalisées :


$ kubectl apply -f https://github.com/knative/net-kourier/releases/download/v0.26.0/ \
kourier.yaml

2. Installez ensuite les composants de service :


$ kubectl apply -f https://github.com/knative/serving/releases/download/v0.26.0/ \
serving-core.yaml

Si vous souhaitez utiliser l’une des intégrations réseau (Kourier, Ambassador, Contour,
Istio), vous pouvez suivre les instructions de la couche réseau.
3. Vous devez maintenant créer votre application. Nous utiliserons la bibliothèque Flask
de Python pour créer une application simple :
Import logging
import os

from flask import Flask

app = Flask(__name__)
Log =

@app.route('/', methods=['POST'])
def hello_world():
return f'Hello world. Data {request.data}\n'

272 Chapitre 11 : Sans serveur


if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

4. Vous avez maintenant besoin d’une image Docker contenant votre application sans
serveur :
FROM python:3.7-slim

RUN pip install Flask gunicorn

WORKDIR /app
COPY . .

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 app:app

5. Créez et poussez votre nouvelle image sans serveur à l’aide de :


$ docker push https://myharborinstallation.com/helloworld:v1

6. Maintenant, définissez votre service sans serveur dans un fichier appelé hello-world.yaml :
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld
namespace: default
spec:
template:
metadata:
name: helloworld
spec:
containers:
- image: docker.io/test/helloworld:v1

7. Déployez la fonction à l’aide de :


$ kubectl apply -f hello-world.yaml

8. À ce stade, votre fonction sans serveur sera déployée. Vous pourrez trouver l’URL de la
fonction en exécutant :
$ kubectl get ksvc hello-world --output=custom-columns=NAME:.metadata.name, \
URL:.status.url

9. Testez votre fonction en appelant l’URL :


$ curl -X POST <URL found from above> -d "My Data"
Hello World: My Data

Maintenant qu’une fonction de service de base est en cours d’exécution, regardons


Knative Eventing.

Knative 273
Installation et exécution de Knative Eventing sur Kubernetes
Dans cette section, nous allons installer les composants de Knative Eventing et écrire un
système d’événements simple :

1. Commencez par installer les ressources personnalisées :


$ kubectl apply -f https://github.com/knative/eventing/releases/download/v0.26.0/ \
eventing-crds.yaml

2. Installez maintenant les composants de service :


$ kubectl apply -f https://github.com/knative/eventing/releases/download/v0.26.0/ \
eventing-core.yaml

À ce stade, les composants d’événements seront installés. Nous allons réutiliser notre
image Docker de la section précédente. Cependant, nous allons configurer Knative
afin de pouvoir envoyer un message  HTTP à un bus de messages dans Knative au
lieu de publier sur un serveur d’applications HTTP. Lorsque le message atteint le bus
de messages, il déclenche notre serveur d’applications Python. Dans un exemple plus
compliqué, vous pouvez définir une rubrique Kafka comme source d’événements.
3. Créez un fichier knative-eventing.yaml :
# Namespace for sample application with eventing enabled
apiVersion: v1
kind: Namespace
metadata:
name: knative-samples
labels:
eventing.knative.dev/injection: enabled
---
# A default broker
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
name: default
namespace: knative-samples
annotations:
# Note: you can set the eventing.knative.dev/broker.class annotation to change
# the class of the broker.
# The default broker class is MTChannelBasedBroker, but Knative also supports
# use of the other class.
eventing.knative.dev/broker.class: MTChannelBasedBroker
spec: {}
---
# hello-world app deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
namespace: knative-samples
spec:
replicas: 1
selector:
matchLabels: &labels
app: hello-world

274 Chapitre 11 : Sans serveur


template:
metadata:
labels: *labels
spec:
containers:
- name: hello-world
image: https://myharborinstallation.com/helloworld:v1
imagePullPolicy: IfNotPresent
---
# Service that exposes helloworld-python app.
# This will be the subscriber for the Trigger
apiVersion: v1
kind: Service
metadata:
name: hello-world
namespace: knative-samples
spec:
selector:
app: hello-world
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
# Knative Eventing Trigger to trigger the helloworld-python service
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
name: hello-world
namespace: knative-samples
spec:
broker: default
filter:
attributes:
type: dev.knative.samples.hello-world
source: dev.knative.samples/helloworldsource
subscriber:
ref:
apiVersion: v1
kind: Service
name: hello-world

4. Appliquez la configuration knative-eventing.yaml sur Kubernetes :


$ kubectl apply -f knative-eventing.yaml

Cela créera un nouvel espace de noms Kubernetes appelé knative-samples, qui


permet la gestion des événements, et un courtier de messages qui transmettra des
messages à notre serveur d’applications Python (sur les événements).
5. Nous allons maintenant envoyer un événement à notre courtier de messages Knative. Voici
sa réponse :
$ curl -v "broker-ingress.knative-eventing.svc.cluster.local/knative-samples/default" \
-X POST \
-d 'My data'

Event received. Context: Context Attributes,


specversion: 0.3
type: dev.knative.samples.hello-world
source: dev.knative.samples/helloworldsource

Knative 275
id: 536808d3-88be-4077-9d7a-6a3f162705f79
time: 2021-11-09T06:21:26.09471798Z
datacontenttype: application/json
Extensions,
Knativearrivaltime: 2021-11-09T06:21:26Z
knativehistory: default-kn2-trigger-kn-channel.knative-samples.svc.cluster.local
traceparent: 00-971d4644229653483d38c46e92a959c7-92c66312e4bb39be-00

Hello World Message "My data"


Responded with event Validation: valid
Context Attributes,
specversion: 0.2
type: dev.knative.samples.hifromknative
source: knative/eventing/samples/hello-world
id: 37458d77-01f5-411e-a243-a459bbf79682
Data,
My data

KEDA
KEDA signifie Kubernetes Event-Driven Autoscaling (Mise à l’échelle pilotée par les
événements Kubernetes). KEDA vous permet de mettre à l’échelle n’importe quel conteneur
dans Kubernetes en fonction du nombre d’événements entrants. KEDA est un module
complémentaire de Kubernetes et il utilise l’Horizontal Pod Autoscaler (HPA) pour mettre
à l’échelle en fonction de différentes sources d’événements, y compris Apache  Kafka,
Azure Event Hubs et le streaming NATS.

Architecture KEDA
Une architecture KEDA type est représentée à la figure 11.3, où KEDA interagit directement
avec le serveur API Kubernetes, HPA et la source du déclencheur configurée.
Le composant KEDA remplit les rôles suivants :
Contrôleur et dimensionneur
Le contrôleur et le dimensionneur augmenteront ou diminueront le nombre de Pods
en fonction du signal de l’adaptateur de métriques.
Adaptateur de métriques
L’adaptateur de métriques agit comme un serveur de métriques Kubernetes qui
fournit des informations de longueur de file d’attente ou de décalage de flux à l’agent.
Le dimensionneur utilise ces informations pour faire augmenter ou diminuer les
conteneurs.
Vous pouvez voir tous les dimensionneurs KEDA dans la documentation sur les
dimensionneurs.

276 Chapitre 11 : Sans serveur


Figure 11.3. Architecture de référence KEDA

Installation de KEDA sur Kubernetes


KEDA s’installe plus facilement à l’aide de l’opérateur Helm sur Kubernetes. Nous allons
voir cette procédure maintenant :

1. Ajoutez le référentiel KEDA Helm :


$ helm repo add kedacore https://kedacore.github.io/charts

2. Installez le tableau Helm :


$ kubectl create namespace keda
$ helm install keda kedacore/keda --namespace keda

Après cela, votre cluster Kubernetes sera prêt pour le déploiement de fonctions.
Dans l’exemple suivant, nous allons créer un système d’événements de démonstration de
faisabilité avec une file d’attente Azure  Service  Bus. Nous allons produire des messages
vers la file d’attente Azure Service Bus à l’aide d’un script Python, puis utiliser KEDA pour
mettre à l’échelle un travail de consommation Python :

1. Saisissez votre chaîne de connexion Service Bus à l’aide de la méthode manuelle décrite


dans la documentation Azure ou en exécutant :
$ az servicebus namespace authorization-rule keys list \
-n RootManageSharedAccessKey -g <group> --namespace-name <namespace> \
--query primaryConnectionString -o tsv

KEDA 277
2. Vous devrez obtenir les valeurs codées en Base64 de la chaîne de connexion et le nom
de la file d’attente en exécutant :
$ echo -n "<connection string>" | base64
$ echo -n "<queue name>" | base64

3. Maintenant, créez un fichier appelé secret.yaml. Remplacez les valeurs servicebus-


queue et servicebus-connectionstring par les chaînes Base64 générées à l’étape
précédente :
apiVersion: v1
kind: Secret
metadata:
name: sample-job-secret
namespace: default
data:
servicebus-queue: <base64_encoded_servicebus_queue_name>
servicebus-connectionstring: <base64_encoded_servicebus_connection_string>

4. Créez un deuxième fichier, appelé keda.yaml, qui créera votre ScaledJob. Il exécutera
une image Docker (Docker.pkg.github.com/prabdeb/sample-python-keda-
service-bus-scaler/consumer:latest) lorsqu’il sera déclenché :
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: auth-service-bus-sample-job
spec:
secretTargetRef:
- parameter: connection
name: sample-job-secret
key: servicebus-connectionstring
---
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: servicebus-queue-so-sample-job
namespace: default
spec:
jobTargetRef:
parallelism: 1 # max number of desired pods
completions: 1 # desired number of successfully finished pods
activeDeadlineSeconds: 600 # Specifies the duration in seconds relative to the
startTime that the job may be active before the system tries to terminate it;
value must be positive integer
backoffLimit: 6 # Specifies the number of retries before marking this job failed.
Defaults to 6
Template:e
metadata:
labels:
app: sample-keda-job
spec:
containers:
- name: sample-keda-job
image: docker.pkg.github.com/prabdeb/sample-python-keda-service-bus-scaler/
consumer:latest
env:
- name: SERVICE_BUS_CONNECTION_STR

278 Chapitre 11 : Sans serveur


valueFrom:
secretKeyRef:
name: sample-job-secret
key: servicebus-connectionstring
- name: SERVICE_BUS_QUEUE_NAME
valueFrom:
secretKeyRef:
name: sample-job-secret
key: servicebus-queue
restartPolicy: Never
pollingInterval: 30 # Optional. Default: 30 seconds
successfulJobsHistoryLimit: 100 # Optional. Default: 100.
# How many completed jobs should be kept.
failedJobsHistoryLimit: 100 # Optional. Default: 100.
# How many failed jobs should be kept.
#envSourceContainerName: {container-name} # Optional. Default:
# .spec.JobTargetRef.template.spec
# .containers[0]
maxReplicaCount: 100 # Optional. Default: 100
scalingStrategy:
strategy: "custom" # Optional. Default: default.
# Which Scaling Strategy to use.
customScalingQueueLengthDeduction: 1 # Optional. A parameter
# to optimize custom ScalingStrategy.
customScalingRunningJobPercentage: "0.5" # Optional. A parameter
# to optimize custom ScalingStrategy.
triggers:
- type: azure-servicebus
metadata:
# Required: queueName OR topicName and subscriptionName
queueName: <servicebus_queue_name>
# Required: Define what Azure Service Bus
# to authenticate to with Managed Identity
namespace: <servicebus_namespace>
messageCount: "10" # default 5
authenticationRef:
name: auth-service-bus-sample-job # authenticationRef would need either
podIdentity or define a connection parameter

5. Maintenant, vous pouvez appliquer les deux fichiers à Kubernetes en utilisant :


$ kubectl apply -f keda-config/jobs/secret.yaml
$ kubectl apply -f keda-config/jobs/keda.yaml

6. Vérifiez que votre ScaledJob est prêt en exécutant :


$ kubectl get ScaledJob
$ kubectl get TriggerAuthentication

7. Vous pouvez tester votre déploiement KEDA en exécutant un script de test. Vous devez
d’abord configurer l’environnement Python :
$ python3 -m venv .
$ source bin/activate
$ pip3 install logger
$ pip3 install azure-servicebus

8. Vous devez maintenant créer votre programme qui envoie des messages à la file d’attente
Azure Service Bus. Vous devrez modifier connection_string et queue_name :

KEDA 279
import os
import sys
import time
import yaml
from logger import logger
from azure.servicebus import ServiceBusClient, ServiceBusMessage

def send_a_list_of_messages(sender):
messages = [ServiceBusMessage(f"Message in list {i}") for i in range(1000)]
sender.send_messages(messages)
logger.info("Sent a list of 1000 messages")

def main():
with open("azure-service-bus.yaml", 'r') as stream:
config = yaml.safe_load(stream)

logger.info("Start sending messages")


connection_string = <FIXME>
queue_name = <FIXME>

queue = ServiceBusClient.from_connection_string(conn_str=connection_string, \
queue_name=queue_name)

with queue:
sender = queue.get_queue_sender(queue_name=queue_name)
with sender:
send_a_list_of_messages(sender)

logger.info("Done sending messages")


logger.info("-----------------------")

if __name__ == "__main__":
main()

9. Exécutez python3 send_message_queue.py. Cela créera 1 000 messages et les enverra à


la file d’attente Azure Service Bus.

Vous pouvez voir les messages ingérés, puis traités dans la file d’attente Azure Service Bus
(figure 11-4).

Figure 11-4. File d’attente Azure Service Bus

280 Chapitre 11 : Sans serveur


Dans la section suivante, nous allons examiner OpenFaaS, qui fournit des fonctions simples
et sans aucun élément superflu sur les systèmes autonomes et les clusters Kubernetes.

OpenFaaS
OpenFaaS est un autre service de fonction axé sur les événements pour Kubernetes qui
vous permet d’écrire des fonctions dans n’importe quel langage, puis de les emballer dans
des conteneurs Docker ou compatibles OCI.

Architecture d’OpenFaaS
L’architecture de référence OpenFaaS (comme illustré à la figure 11-5) est raisonnablement
similaire à Keda. Toutefois, OpenFaaS s’appuie sur des métriques de Prometheus pour agir
comme un signal de mise à l’échelle automatique.

Figure 11-5. Architecture de référence OpenFaaS

Installation d’OpenFaaS
OpenFaaS dispose de trois méthodes d’installation recommandées :

• Utiliser arkade
• Utiliser helm
• Installer avec Flux ou ArgoCD

Pour être cohérents avec le reste du livre, nous allons déployer OpenFaaS à l’aide de Helm :

1. 
Créez les espaces de noms (un premier pour les services de base OpenFaaS
et un deuxième pour les fonctions) :
$ kubectl apply -f https://raw.githubusercontent.com/openfaas/faas-netes/master/ \
namespaces.yml

2. Ajoutez le graphique Helm OpenFaaS :


$ helm repo add openfaas https://openfaas.github.io/faas-netes/

OpenFaaS 281
3. Enfin, déployez OpenFaaS :
$ helm repo update \
&& helm upgrade openfaas --install openfaas/openfaas \
--namespace openfaas \
--set functionNamespace=openfaas-fn \
--set generateBasicAuth=true

4. Vous serez en mesure de récupérer votre mot de passe d’administrateur en


exécutant la commande suivante (cela sera utilisé pour accéder au tableau de bord
OpenFaaS) :
$ PASSWORD=$(kubectl -n openfaas get secret basic-auth -o \
jsonpath="{.data.basic-auth-password}" | base64 --decode) && \
echo "OpenFaaS admin password: $PASSWORD"

Maintenant que nous avons installé OpenFaaS, nous allons écrire une fonction OpenFaaS.

Écriture de votre première fonction OpenFaaS


Dans cette section, nous allons utiliser l’interface de ligne de commande faas-cli pour créer
et déployer rapidement une fonction sans serveur :

1. Téléchargez faas-cli en exécutant brew install faas-cli sur OSX ou en exécutant


curl -sSL https://cli.openfaas.com | sudo sh sur Linux.
2. Créez un dossier pour placer votre nouvelle fonction :
$ mkdir -p ~/functions && cd ~/functions

3. Utilisez faas-cli pour créer un nouveau projet de fonction Python :


$ faas-cli new --lang python helloworld

Cela va créer trois nouveaux fichiers :


$ ls
helloworld/handler.py
helloworld/requirements.txt
helloworld.yml

Le fichier handler.py sera là où vous écrivez votre fonction. Le fichier requirements.txt


est le même qu’un fichier Python requirements. txt standard. Le fichier helloworld. yml
définit un certain standard pour votre fonction.
4. Dans le fichier handler.py, nous allons définir notre fonction. Cette fonction va
imprimer « Hello World! You said: » plus les données affichées dans le point de
terminaison de la fonction :
def handle(req):
print("Hello World! You said: " + req)

5. En dernier lieu, dans votre fichier helloworld.yml, vous définirez les métadonnées
relatives à votre fonction. http://127.0.0.1:31112 est l’adresse par défaut du serveur de
passerelle/API OpenFaaS :
provider:
name: openfaas
gateway: http://127.0.0.1:31112

282 Chapitre 11 : Sans serveur


functions:
hello-python:
lang: python
handler: ./hello-world
image: hello-world

6. Créez l’image sans serveur et poussez-la vers le registre Harbor que vous avez créé
au chapitre 2 :
$ faas-cli build -f ./hello-world.yml
$ faas-cli push -f ./hello-world.yml

7. Déployez-le sur le cluster Kubernetes :


$ faas-cli deploy -f ./hello-world.yml
Deploying: hello-python.
No existing service to remove
Deployed.
200 OK
URL: http://127.0.0.1:31112/function/hello-python

8. L’étape précédente génère une URL que vous pouvez utiliser pour les tests. Utilisez curl
pour la tester :
$ curl -X POST 127.0.0.1:31112/function/hello-world -d "My name is Michael"
Hello world! My name is Michael

Par défaut, OpenFaaS utilise les requêtes par seconde (rps) comme mécanisme pour
effectuer la mise à l’échelle automatique. Vous pouvez trouver plus d’informations sur la
configuration de la mise à l’échelle automatique d’OpenFaaS sur la page de documentation
de la mise à l’échelle automatique.
Vous disposerez alors d’une fonction OpenFaaS en cours d’exécution sur Kubernetes. Vous
pouvez également exécuter OpenFaaS en dehors de Kubernetes à l’aide de faasd. Vous
trouverez plus d’informations sur la page de déploiement d’OpenFaaS.
Enfin, OpenFaaS est créé au-dessus de la HPA  Kubernetes. Vous trouverez plus
d’informations sur  les tests de charge et la mise à l’échelle de votre fonction OpenFaaS sur
la page du tutoriel OpenFaaS Kubernetes.

Résumé
Bien que l’approche sans serveur ne convienne absolument pas à tout le monde, lorsqu’elle
est utilisée dans les bons cas de figure, elle peut offrir des avantages importants. L’espace
sans serveur est toujours en évolution, mais il y a très peu d’obstacles qui entravent son
utilisation, ce qui en fait une technologie passionnante. Dans ce chapitre, nous avons exploré
quatre façons différentes d’exécuter des fonctions sans serveur sur Azure. Tout d’abord,
nous avons examiné les applications de la fonction Azure, puis nous avons vu trois grands
projets CNCF : Knative, KEDA et OpenFaaS. À ce stade, nous avons décrit les principaux
piliers de l’infrastructure native du cloud. Au chapitre 12, nous reverrons rapidement ce que
vous avez appris au cours de cette découverte.

Résumé 283
CHAPITRE 12
Conclusion

Nous sommes arrivés à la fin du livre et nous allons donc passer en revue notre parcours.
Tout au long de ce livre, nous avons cherché à fournir une ressource unique que vous
pouvez utiliser pour créer et exécuter une infrastructure native du cloud dans Azure. Bien
qu’Azure ait été notre priorité, vous pouvez également appliquer ces connaissances pour
créer et exploiter votre propre environnement natif du cloud sur différentes plateformes de
fournisseurs cloud. Passons en revue ce que vous avez appris jusqu’à présent.
Nous avons commencé cet ouvrage en revenant sur la façon dont le monde de l’infrastructure
et des services a évolué au fil du temps grâce à l’introduction du cloud. Pour tirer pleinement
parti de ce que le cloud peut offrir, nous devons mettre au point des solutions natives du
cloud et pas seulement les adapter rétroactivement au cloud. Nous avons vu que le cloud
natif est la solution pour exploiter pleinement la puissance du cloud.
Au chapitre  2, nous avons présenté les tremplins qui permettent de concevoir un
environnement natif du cloud moderne à l’aide d’Azure. Nous avons créé un compte Azure
et nous avons fait nos premiers pas avec Ansible, Terraform et Packer. En les employant
comme outils clés, nous pouvons activer les services prêts pour la production de manière
automatisée. Nous avons utilisé Microsoft Azure comme fournisseur de cloud et, tout au
long du livre, nous avons utilisé à la fois des services natifs du cloud et Azure pour illustrer
la façon dont les infrastructures modernes sont conçues et prises en charge.
Au chapitre  3, nous vous avons présenté le conteneur et le runtime de conteneur, qui
constituent les principes fondamentaux d’un monde natif du cloud moderne. Nous avons
abordé les couches d’abstraction qui composent l’écosystème de conteneurs.
Nous avons ensuite vu le puissant orchestrateur Kubernetes, au chapitre 4 et au chapitre 5,
dans lesquels nous avons mis en évidence la façon dont cet outil important peut être utilisé
dans l’infrastructure cloud moderne afin de faciliter les opérations de la plateforme pour les
applications natives du cloud. Kubernetes a conduit à la création et à l’adaptation de la Cloud
Native Computing Foundation, en devenant le premier projet diplômé. Cela a entraîné

285
beaucoup de simplification du monde de la conteneurisation et une meilleure adaptation
de l’écosystème natif du cloud. Nous avons examiné les différents composants et concepts
qui rendent ce système opérationnel à grande échelle et nous avons appris à créer le cluster
Kubernetes à la fois manuellement et en utilisant Azure AKS en tant que service géré.
À ce stade, nous avons vu les bases d’une infrastructure native du cloud composée du strict
minimum. L’étape suivante a consisté à ajouter une manière d’obtenir des informations sur les
applications et les infrastructures. Donc, au chapitre 6, nous avons introduit le concept des
trois piliers de l’observation, afin de vous permettre de découvrir cette énorme infrastructure
et cette pile d’applications. En créant des systèmes observables, vous disposez d’un contrôle
renforcé sur les systèmes distribués complexes qui interagissent au-delà des limites. Dans ce
chapitre, nous avons également examiné l’observabilité, qui est de plus en plus nécessaire dans
l’environnement natif du cloud. Avec les trois piliers de l’observation et une introduction aux
méthodes préférées de journalisation, de surveillance et de suivi pour les systèmes distribués
modernes sur Azure, nous avons conclu ce chapitre en présentant brièvement la façon dont
Azure propose ses solutions intégrées pour effectuer des tâches similaires avec Azure Monitor.
Lorsque nous parlons de limites et de frontières, nous sommes confrontés au problème de
la communication de service à service dans cet environnement en mutation dynamique.
Pour résoudre ce problème, nous avons présenté la découverte et le maillage des services au
chapitre 7. Ces fonctionnalités permettent de rechercher des services et de communiquer
avec eux efficacement. Nous avons également présenté en détail la nécessité de mettre en
place des maillages de services et des proxies afin de contrôler efficacement l’environnement
global où résident vos microservices. Nous avons abordé CoreDNS comme DNS par défaut,
en remplaçant kube-dns, dans l’environnement Kubernetes. Nous avons vu les maillages
de services à l’aide d’Istio, qui utilise Envoy comme plan de données pour obtenir des
informations utiles sur les services et fournit l’ensemble complet de fonctionnalités du
maillage de services Istio. Vers la fin du chapitre, nous avons présenté Kiali, qui fournit une
console de gestion complète sur un maillage de services basé sur Istio.
Au chapitre 8, nous sommes passés au problème de la gestion des stratégies et de la mise
en réseau dans les environnements natifs du cloud. Nous vous avons présenté deux outils
principaux : Calico et Flannel, qui servent d’interface de réseau de conteneurs. Nous avons
également expliqué comment vous pouvez appliquer des stratégies à l’échelle de la pile à
l’aide de la gestion des stratégies ouverte. Nous avons examiné la norme CNI et la façon
dont elle est le fondement d’autres plateformes telles que Flannel et Cilium. Nous avons
examiné comment ces systèmes peuvent être utilisés pour fournir une connectivité et une
sécurité réseau à votre infrastructure cloud. Nous avons également examiné les mécanismes
de stratégie non-réseau pour la gestion de votre infrastructure cloud via la stratégie Azure,
ainsi que la stratégie pour les applications via l’agent de stratégie ouverte.
Au chapitre  9, nous avons examiné comment le stockage est perçu et conçu dans les
environnements natifs du cloud à l’aide de Vitess en tant que base de données distribuée.
Nous avons également abordé Rook, un orchestrateur de stockage étendu qui peut vous
aider à réduire la charge liée à la gestion d’un système de stockage distribué mettent
notamment en place la mise à l’échelle et la réparation automatiques. Dans ce chapitre,
nous avons examiné pourquoi il est avantageux d’utiliser des systèmes de données

286 Chapitre 12 : Conclusion


natifs du cloud dans le cloud d’Azure. Nous avons abordé quatre systèmes  : Vitess
(relationnel), Rook (BLOB), TiKV (clé-valeur) et etcd (configuration de clé-valeur/
découverte de services). Ces systèmes sont le socle d’une architecture native du cloud qui
stocke et sert les données en ligne. De plus, ils offrent un large important par rapport à
l’utilisation de composants PaaS.
Au chapitre 10, nous avons parlé de la façon dont les services de messagerie et de streaming
sont créés et déployés dans des environnements natifs du cloud à l’aide de technologies
traditionnelles telles que NATS, gRPC, Apache Kafka et RabbitMQ.
Enfin, au chapitre 11, nous avons exploré quatre façons différentes d’exécuter des fonctions
sans serveur sur Azure. Tout d’abord, nous avons examiné les applications de la fonction
Azure, puis nous avons vu trois grands projets CNCF : Knative, KEDA et OpenFaaS.
Nous espérons que vous comprenez désormais comment créer et utiliser une infrastructure
native du cloud. Vous disposez désormais des connaissances nécessaires pour créer et
exploiter votre propre infrastructure native du cloud sur Azure.

Et ensuite ?
Le paysage CNCF est relativement vaste et il contient un grand nombre de projets qui sont
constamment conçus et soutenus par de nombreuses grandes entreprises (figure 12-1). Ces
projets sont créés à partir de zéro, en tenant compte de l’évolutivité et de la fiabilité.

Figure 12-1. Le paysage CNCF

Nous vous encourageons vivement à explorer d’autres technologies dans le paysage CNCF


pour créer votre environnement natif du cloud en fonction de votre cas d’utilisation, et nous
vous invitons également à découvrir le paysage sans serveur CNCF (figure 12-2).

Et ensuite ? 287
Figure 12-2. Paysage sans serveur de la CNCF

Le paysage sans serveur CNCF met en évidence l’informatique sans serveur et le modèle
de programmation associé, ainsi que les formats de messages. Bien que l’informatique
sans serveur soit largement disponible depuis un certain temps, le développement de cette
approche implique un large éventail de possibilités. Vous pouvez explorer l’informatique
sans serveur si votre charge de travail est asynchrone, sporadique, sans état et hautement
dynamique en termes d’évolution des exigences de l’entreprise. Vous pouvez également
commencer à utiliser différents services Azure et voir comment ils se comportent par
rapport aux technologies CNCF.
La plupart de ces projets sont open source et le résultat des efforts de nombreux ingénieurs
qui contribuent activement à la communauté. L’open source apporte également une grande
quantité de contrôle de la qualité à la conception globale de ces projets. Cela signifie que
les technologies natives du cloud sont bien gérées dans le monde entier et sont facilement
adaptables. Il est également important de noter que la plupart de ces technologies natives
du cloud sont le résultat de problèmes difficiles qui dont dus être résolus efficacement. Par
conséquent, il est tout aussi important de prendre des risques et de poser des questions
difficiles afin d’obtenir une compréhension approfondie de vos systèmes qui finiront par
vous conduire à trouver les bonnes solutions pour résoudre des problèmes complexes.
Le paysage natif du cloud ne cesse de croître et nous vous encourageons à l’explorer encore
davantage afin de l’utiliser pleinement. Nous vous souhaitons bonne chance.

288 Chapitre 12 : Conclusion


Index

A Apache Kafka (voir Kafka)


AAD (Azure Active Directory), 11 Apache Mesos (voir Mesos)
abonnements, 12 API
dans Azure Service Bus, 255 activation de la maintenance des
pour Azure Container Registry, 56 environnements Cloud, 7
stratégie Azure appliquée sur, 211 Azure, service principal pour interagir avec, 202
accès à distance, génération d’un fichier kubeconfig LXC, orchestration de, 50
pour, 110 servi par OPA, 214
ACI (voir Azure Container Instances) TiKV, brut et transactionnel, 226
Adresses IP API brute (TiKV), 226
CIDR réseau pod, 208 API REST
création d’IP publiques pour l’équilibreur de agents Mesos axés sur, 42
charge dans le cluster Kubernetes dans fournie par le démon LXD, 50
Azure, 102-104 fournie par OPA, 214
récupération avec la fonction sans serveur, 266 Kubernetes, 136
services dans Kubernetes, 90 API transactionnel (TiKV), 226
IP de cluster, port de nœud et équilibreur de applications Cloud natives, 2
charge dans un nœud, 91 découplage de l’infrastructure, 10
agents de transfert de messages (voir MTA) applications découplées de l’infrastructure, 10
agents Mesos, 42 applications multiniveaux d’images de
AKS (voir Azure Kubernetes Service) conteneurs, 49
AlertManager (Prometheus), 126 architecture d’application monolithique, 237
altérations (fonction de planification de architecture de mise en œuvre de type sidecar pour
Kubernetes), 95 un suivi distribué, 159
Amazon SQS, 234 atelier rubber-docker, 46
AMQP, 253 authentification
AMQP 1.0, Azure Service Bus, 254 Nkey, 250
annotations (Kubernetes), 74 TLS, utilisation dans NATS, 250
Ansible, 31, 285 authentification de Nkey, 250
génération du fichier kubeconfig pour l’accès à auto-régénération, 68
distance et la validation de cluster, 110 boucle de rapprochement en tant que moteur
utilisation pour déployer et configurer des dans Kubernetes, 70
nœuds de contrôleur Kubernetes, 106-109 Azure
utilisation pour déployer et configurer des avec CNI, 191
nœuds de travail Kubernetes, 109 création de compte, 12
utilisation pour trouver des hôtes pour etcd sur, 230
Prometheus, 134

289
options de stockage et de base de données, 220 Azure Storage Explorer, 220
services de messagerie, 234 azure_sd_config, 135
Azure Cloud Shell (voir Cloud Shell) AzureDisk, 84
Azure Container Instances (ACI), 60-64 AzureFile, 83
déploiement, 61-64
mises en garde dans l'exécution, 60
Azure Event Grid, 234, 261-263
B
backend, 15
bibliothèque client pour Python, 262
backends de transport (Flannel), 208
critères lors du choix, 253
balises
déclenchement de fonctions sans serveur, 266
configuration dans l’outil de suivi Jaeger, 156
déploiement et utilisation de grilles
création pour des images Docker, 48
d’événements, 262
banque de données (Calico), 194
écosystème Event Grid, 261
bases de données distribuées, 219-224, 286
terminologie différente des autres plateformes, 261
besoin d’architecture native dans le Cloud, 219
Azure Event Hubs, 234, 258-261
My SQL distribués et partitionné, à l’aide de
architecture, 258
Vitess, 221-224
concepts, 259
options de base de données Azure, 220
critères lors du choix, 253
Bases de données NoSQL
gestion avec Terraform, 259
Cassandra, 224
Kafka et, 243
exploitation des bases de données SQL en tant
Azure Files, 82
que, 221
Azure Function Apps, 268-271
bases de données SQL, exploitation de manière
architecture, 269
NoSQL, 221
création d’une application de fonction, 270
Bash, 18
langages de programmation pris en charge, 268
bibliothèques client
plans, 269
pour le suivi distribué, 153
Azure Functions, création d’une fonction, 270
Prometheus, 126
Azure Key Vault, 250
bin packing, 36, 68
Azure Kubernetes Service (AKS), 286
BIRD (Calico), 194
configuration réseau, y compris Calico, 197
blocs de serveurs dans CoreDNS, 167
contrôleur d’entrées d’Application Gateway, 92
boucle de rapprochement (gestionnaire de
création et gestion d’un cluster Kubernetes, 111
contrôleur Kube), 70
déploiement de Calico via l’installation AKS, 196
exécution de charges de travail statiques sur, 220
installation Cilium sur, 202 C
installation d'Istio sur, 175 Calico, 193-200
prise en charge des plug-ins Azure VNet, 191 architecture de base, 194
Azure Monitor, 159, 286 avantages de l’utilisation, 193
Azure Pipeline, 33 déploiement, 195-197
Azure Service Bus, 234, 253-258 installation de calicoctl, 196
concepts, 254-255 installation manuelle de Calico sur le cluster
espaces de noms, 254 Kubernetes, 196
files d’attente, rubriques et abonnements, 254 mise en œuvre de la stratégie de sécurité, 198-
création d’un système d’événements avec une 200
file d'attente Service Bus utilisation, 197
critères lors du choix, 253 activation du plan de données eBPF, 197
envoi/réception de messages dans la file calicoctl, 194
d’attente activation de eBPF avec, 198
file d'attente, 277-281 Cassandra, 224
gestion avec Terraform, 255-258 déploiement du cluster Cassandra à l’aide de
Python, 258 Rook, 225-226

290 | Index
certificats stockage, 101
création de certificat auto-signé, 180 création des images de machine pour les
génération d’un certificat TLS pour les nœuds machines de travail et de contrôleur, 100
de contrôleur Kubernetes avec cfssl, 107 création d'un groupe de ressources, 100
sécurisation d’entrée Kubernetes avec un certificat création d'un réseau virtuel Azure, 102
et une clé TLS encodés en base 64, 94 déploiement et configuration des nœuds de
TLS, 250 contrôleur avec Ansible, 106-109
certificats SSL, 52 déploiement et configuration des nœuds de
cgroup, 40 travail avec Ansible, 109
cgroups (groupes de contrôle), 38 génération du fichier kubeconfig pour
contrôle des fonctionnalités Linux, 40 l’accès à distance et la validation de
chiffrement des données de cluster Kubernetes au cluster, 110
repos, 107 création dans Azure à l'aide d'AKS, 111
CIDR (Classless Inter-Domain Routing) déploiement d’applications et de services à l’aide
réseau Flanelle et pod CIDR, 208 de Helm, 113-119
sécurité de CIL pour, 200 composants de Helm, 114
Cilium, 190, 200-207 création de tableaux pour vos applications, 118
déploiement sur le cluster Kubernetes, 201-204 gestion des versions Helm, 117
installation d’AKS Cilium, 202, 202 installation et gestion de Helm, 114-117
installation de Cilium auto-gérée, 201 CNCF (voir Cloud Native Computing Foundation)
installation de Hubble, 203 CNI (Container Network Interface), 190-193, 286
intégration avec votre Cloud, 204-207 avantages de l’utilisation, 191
observabilité, 206 exemple de configuration, 190
pare-feu hôte, 204-206 fonctionnement avec Azure, 191
Citadel (Istiod), 174 implémentation Cilium de son propre, 200
classes de stockage, 82, 83 plug-in CNI Calico, 194
clients (messagerie), 239 primitives gérées par, 190
cloud projets, 192
défis de, 2 command apply (Terraform), 14, 16, 24, 26
parcours vers, 1 commande apply (kubectl), 80, 87
Cloud Native Computing Foundation (CNCF), 4, 285 application d’un manifeste de tâche, 97
CloudEvents, 244 commande cluster-info (kubectl), 77
orchestrateurs de conteneur, 41 commande curl, 145
paysage de streaming et de messagerie, 238 commande delete de pod (kubectl), 84
paysage sans serveur, 266, 287 commande describe (kubectl), 77
paysage, 287 describe deployment, 88
projets de base de données, 219 description de l’état d'un Job, 97
Cloud Shell, 17, 18 vérification du proxy sidecar en cours
création d’un principal de service à utiliser avec d’exécution dans un pod, 178
Terraform, 18 commande destroy (Terraform), 16, 27
CloudEvents (CNCF), 244 commande dig, 168, 170
ClusterRole, 208 commande docker build, 48
ClusterRoleBinding, 208 utilisation des options, 49
clusters (Kubernetes) commande edit (kubectl), 87, 228
création à partir de zéro dans Azure, 99-111 commande get (kubectl), 77
configuration de la mise en réseau et du get hpa, 89
routage des pods, 109 get svc, 91
création d’adresses IP publiques pour commande get nodes (kubectl), 76
l’équilibreur de charge, 102-104 -o wide flag, 76
création d’instances de travail et de commande init (Terraform), 16
contrôleur, 104-106 commande logs (kubectl), 78, 146
création d’un backend de compte de commande mise à l'échelle automatique (kubectl), 89

Index | 291
commande plan (Terraform), 24 KEDA, 276
commande run (kubectl), 79 contrôleurs d’admission (Kubernetes), 177
communication inter-processus, 233 copie en écriture (CoW), 40
complexité opérationnelle, 3 CoreDNS, 286
compte de stockage et blob, création à partir de installation and configuration, 167-169
Terraform, 24 plug-in propre à Azure, 171
comptes de stockage, 220 présentation de, 165
compteurs (Prometheus), 131 processus de requête, 166
concepteurs, 29 Corefile, 167
conditionnement, avantages des conteneurs pour, 37 courtiers, 234
confd (Calico), 194 et clustering, 240
config (Helm), 114 CRI-O, 43
ConfigMap, 208
configuration ipPools, 196
containerd, 42
D
DaemonSets, 75
Containers Kata, 49
Flannel CNI, 208
conteneur sidecar, 72
Fluentd, 147
conteneurisation d'applications, 35-66, 285
déploiement sur le cluster Kubernetes, 148
autres plateformes de conteneurs, 49-50
utilisation en production, 94
avantages de l’utilisation de conteneurs, 35-37
versus ReplicaSets, 94
conditionnement et déploiement, 37
débit, amélioration de la messagerie, 234
isolation, 36
débogabilité, manque de, sans serveur, 268
sécurité, 36
découverte de service, 68, 89, 134, 164-172, 286
composants de l’exécution d’un conteneur, 40-43
à propos, 164
orchestrateurs de conteneur, 41
configurations de découverte de services, 135
plateformes logicielles de conteneur, 41
etcd, 229
Docker, 46-49
dans Kubernetes, avec CoreDNS, 169-171
exécution de Docker sur Azure, 60-66
DNS Azure, 171
primitives de conteneur de base, 37-40
installation et configuration CoreDNS, 167-169
spécification OCI, 43-46
présentation de CoreDNS, 165
conteneurs, 43
déploiements
Azure Monitor, 159
automatique, avec Kubernetes, 68
hébergement de microservices, 67
avantages des conteneurs pour, 37
contexte (dans Kubernetes), 78
objet Deployment dans Kubernetes, 73, 84
contrats de niveau de service (SLA), 7
utilisation dans des environnements de
contrôle d’accès en fonction du rôle (RBAC)
production, 86
rôles dans Flannel, 208
déploiements sans interruption de service, 86
utilisation dans etcd, 231
dérive de configuration, 10
contrôles d’intégrité dans Kubernetes, 80
désinstallation d'une version Helm, 118
sonde de démarrage, 81
développement
sondes de disponibilité, 80
axé sur l’observation, 124
sondes de préparation, 81
cycles de développement plus rapides sans
contrôles de validation pour le cluster Kubernetes
serveur, 268
dans Azure, 110
développement futur, 287
contrôleur d'entrée, 74
DevOps, 8
application Gateway pour Azure Kubernetes
Azure DevOps et infrastructure en tant que
Service , configuration, 92
code, 33
contrôleurs
dimensionneurs (KEDA), 276
création d’instances de contrôleur pour le
directive EXPOSE (Dockerfile), 47
cluster Kubernetes dans Azure, 104-106
directive source (Fluentd), 140
déploiement et configuration des nœuds de
paramètre @label, 144
contrôleur Kubernetes avec Ansible, 106-109
disponibilité

292 | Index
de services PaaS, 221 échantillonnage contextuel, 153
etcd, 231 échantillonnage, 153
Disque Azure, 82 effets (stratégie Azure), 211
disques de stockage dans Azure, 82 Elasticsearch
disques gérés, 219 déploiement à l’aide de Helm sur Kubernetes, 148
disques SSD (Solid State Drive), 230 écriture des enregistrements sur un point de
distribution (message), 241 terminaison de cluster avec un plug-in
DNS Fluentd, 141
adresses IP et noms dans un cluster, 170 Enregistrements de type A pour services dans un
DNS Azure, 171 cluster, 170
gestion de DNS de clusters (voir CoreDNS) enregistrements SRV DNS, 231
limitation du DNS de sortie aux serveurs DNS entrées, 74
publics Google avec Cilium, 205 avec plusieurs chemins pour un domaine, 93
mappage du service Kubernetes au nom DNS, 91 objet Ingress Kubernetes, utilisation en
observation du trafic DNS avec Cilium et production, 92
Hubble, 206 Envoy (plan de données), 174, 286
DNS Azure, 171 injection automatique de proxy sidecar, 177
Docker Swarm, 41 équilibrage de charge
Docker, 42, 46-49 automatisation par des orchestrateurs de
création de votre première image Docker, 46-48 conteneurs, 68
exécution de tous les composants Jaeger dans contrôleur d’entrées dans Kubernetes, 74
une image de conteneur unique, 155 dans Kubernetes, 68
exécution sur Azure, 60-66 HTTP, effectué par l’objet Ingress Kubernetes, 92
exécution du moteur de conteneur Docker, équilibrage de charge de la couche 7 dans
65-66 Kubernetes, 92
utilisation d'Azure Container Instances, équilibrage de charge HTTP dans Kubernetes, 92
60-64 équilibreurs de charge
image contenant une application sans serveur, 273 création d’IP publiques pour l’équilibreur de
meilleures pratiques d’utilisation, 48 charge Kubernetes dans Azure, 102-104
moteur d’exécution, 43 IP de cluster, port de nœud et équilibreur de
stockage d'images Docker dans un registre de charge dans un nœud Kubernetes, 91
conteneurs, 59 espaces de noms, 39
Dockerfiles, 46 composants Istio sur AKS, 176
création, 47 dans Azure Service Bus, 254, 255
défini, 48 dans Kubernetes, 74
domaine de cluster (Kubernetes), 170 obtention d’informations sur, avec kubectl, 77
domaine d'échec dans Vitess, 224 état
domaines, 170 dans Terraform, 15
(voir aussi CoreDNS ; DNS) définition pour un conteneur, spécification
dans Azure Event Grid, 261 OCI, 45
entrée avec plusieurs chemins pour, 93 détermination pour le cluster Kubernetes, 71
durabilité des messages, 241 état du réseau virtuel téléchargé par Terraform
modèle de file d’attente durable, 242 sur le compte de stockage, 27
exécution de charges de travail statiques
sur AKS, 220
E etcd, 71, 229-231
eBPF (Berkeley Packet Filter étendu)
architecture opérationnelle, 229
activation en tant que plan de données Calico, 197
disponibilité et sécurité, 231
prise en charge par Calico, 193
mise à l’échelle et correction automatiques, 230
utilisation avec la version du noyau ultérieure à
plateforme matérielle, 230
4.16, 198
étiquettes (Kubernetes), 74
utilisation par le projet Cilium, 200

Index | 293
étiquetage des pods, 87 backends de transport pris en charge, 208
istio-injection, 177 Flask, 46, 66
obtention d’informations sur, avec kubectl, 78 utilisation pour créer une application Knative
étiquettes (Prometheus), 126 Serving, 272
étiquetage, 136 Fluentd, 138-146
filtrage des métriques avec, 130 agissant en tant que couche de journalisation, 138
exemple de bloc de ressources (Terraform), 15 déploiement à l’aide de DaemonSet, 95
exportateurs (Prometheus), 126 @include directive, 144
ExternalName, 91 installation td-agent, 139
instruction filter, 142
instruction label, 143
F instruction match, 141
faas-cli, utilisation pour créer et déployer une
instruction source, 140
fonction sans serveur
instruction système, 143
faasd, 283
fonction, 282
failureThreshold, 81
fonctionnalité de calcul en périphérie, 267
Felix (Calico), 194
fonctionnalités du processus, contrôle Linux de, 40
fichier .dockerignore, 49
fonctionnalités, contrôle Linux pour processus ou
fichier Chart.yaml, 119
fournisseur de ressources Microsoft.
fichier custom-resources.yaml, 196
PolicyInsights, 211
fichier d’inventaire (Ansible), 32
fournisseurs (Terraform), 14
fichier kubeconfig, 78
exemple de bloc fournisseur, 15
génération pour l’accès à distance et la
function irate, 130
validation de cluster, 110
téléchargement pour le cluster AKS, 112
fichiers journaux, 122 G
affichage pour CoreDNS, 168 Galley (Istiod), 174
configuration dans l’outil de suivi Jaeger, 156 georéplication dans TiKV, 226
ingestion des journaux et analyse de gestion des pods, 78-85
données, exemple de cas d’utilisation de contrôles d'intégrité, 80
messagerie, 235-237 limites des ressources, 81
file d’attente de publication et d’abonnement basé volumes, 82
sur la mémoire, 237 gestion des stratégies, 189, 286
file d’attente durable dans la messagerie, 242 Open Policy Agent (OPA), 214-218
exemple de cas d’utilisation de messagerie, solution de sécurité et de stratégie de réseau de
ingestion des journaux et analyse de Calico, 193-200
données, 235-237 Stratégie Azure, 210-213
génération 1, sans files d’attente, 235 gestionnaire de contrôleur Kube, 70
génération 2 : avec les files d’attente Cloud et gestionnaire de packages pour Kubernetes
le stockage d’objets, 236 (voir Helm)
modèle de file d’attente simple dans la GlobalNetworkPolicy (Calico), 199
messagerie, 242, 253 groupes de clients, 259, 260
files d'attente, 234 groupes de gestion, 12
dans Azure Service Bus, 254, 255 définition d'une stratégie Azure sur, 189, 210
envoi/réception de messages dans la file stratégie Azure appliquée, 211
d’attente dans Python, 258 groupes de ressources, 12
fin des fichiers journaux, événements de lecture création avec Terraform, 22
Fluentd à partir de, 141 création, 100
Flannel, 207-210 dans Azure DNS, 171
déploiement, 207 Registre de conteneur Azure, 56
exemple d'architecture, 210
utilisation, 208

294 | Index
informatique serverless, 265-283, 287
H à propos, 265
Harbor, 51-54
avantages de, 267
configuration pour l’installation, 52
Azure Function Apps, 268-271
création d'une image Packer pour déployer
architecture de référence des fonctions sans
Harbor, 53
serveur d’Azure, 269
installation, 51
création d’une application de fonction, 270
vérification de l'installateur, 52
fonctions sans serveur, 266
stockage des images Docker dans, 59
inconvénients potentiels, 268
HDInsight, 243
KEDA, 276-281
Helm, 113-119
architecture, 276
histogrammes (Prometheus), 133
installation sur Kubernetes, 277-281
Horizontal Pod Autoscaler (HPA), 88, 230
OpenFaaS, 281-283
hôtes
architecture, 281
installation et configuration de Prometheus
écriture d'une fonction OpenFaaS, 282-283
sur, 127
installation sur Kubernetes, 281
recherche des hôtes pour être récupérés par
paysage sans serveur CNCF, 266, 287
Prometheus, 134-136
plateforme Knative, 272-276
HTTPS
architecture Knative, 272
exposition des points de terminaison aux
installation et exécution de Knative Eventing
services Kubernetes sur les pods, 74
sur Kubernetes 274-276
utilisation avec Harbor, 52, 54
installation et exécution de Knative Serving
Hubble
sur Kubernetes, 272
installation, 203
infrastructure anti-fragile, 9
observabilité avec Cilium, 206
infrastructure en tant que code (IaC), 7-33, 285
avantages, 8, 9
I Azure DevOps et, 33
idempotence prise en main d’Azure et de l’environnement de
infrastructure en tant que code, 9 développement, 11-13
modèles ARM, 28 création du compte Azure, 12
image de conteneur, 37 installation d'Azure CLI, 13
(voir aussi images [conteneur]) notions de base d’Azure et préparation de
spécification d’image OCI, 44 l'environnement, 11
images (conteneur) infrastructure immuable, 10, 29
création de votre première image Docker, 46-48 infrastructure native Cloud, 2
spécification d’image OCI, 44 adoption avec Azure, 4
stockage d'images Docker dans un registre, 59 développement futur dans, 287
images de machine développer et exécuter avec Azure, 285
création avec Packer, 29-31 obtention d’informations sur les applications et
création pour les machines de travail et de l’infrastructure, 286
contrôleur dans Kubernetes, 100 philosophie, 10
implémentation de la messagerie NAT, 234 initialDelaySeconds, 80
exploration de la messagerie avec, 244-253 initiatives (stratégie Azure), 211
architecture du protocole NATS, 244-248 inspecteur Grafana, 252
avantages de NATS, 244 instance de pilote de placement (DP), 228
déploiement de NATS sur Kubernetes, 251 instances (dans Prometheus), 127
persistance de NATS avec JetStream, 249 instruction filter (Fluentd), 142
sécurité de NATS, 249 plug-ins supplémentaires, 142
fonctionnalités de streaming, 238 instruction label (Fluentd), 143
IMS, 253 instruction match (Fluentd), 141
@include directive (Fluentd), 144 instruction système (Fluentd), 143

Index | 295
interface de ligne de commande (CLI) interface utilisateur, accès, 181
affichage du groupe de ressources Terraform à journaux, métriques et suivis fournis, 186
partir de, 23 passerelle, service virtuel et règle de
azure-cli, 255 destination, 180
calicoctl dans Calico, 194 tableau de bord indiquant le trafic entrant, 182
installation d'Azure CLI, 13 tableau de bord montrant l’injection
Internet des objets (IoT), utilisation de la automatique de proxy sidecar dans l’espace
messagerie, 234 de noms par défaut, 182
isolation assurée par les conteneurs, 36 Kibans, 149
Istio, 159, 286 kit de ressources de l'infrastructure à clé publique/
architecture, 175 Transport Layer Security (PKI/TLS) de Cloud
gestion des maillages de services à l’aide de (PKI/TLS), 107
Kiali, 179-186 kit de ressources PKI/TLS, 107
présentation de, 174 Knative, 272-276
Istiod (plan de contrôle), 174 architecture, 272
installation et exécution de Knative Eventing
sur Kubernetes 274-276
J installation et exécution de Knative Serving sur
jauges (Prometheus), 132
Kubernetes, 272
JetStream (NATS), 244, 244
kube-apiserver, 70
persistance avec, 249
kubectl, 76-98
journalisation
Informations générales et commandes liées aux
courtiers et, 240
clusters, 76
de plusieurs serveurs NATS, 245
installation de Flannel, 207
journalisation au niveau du cluster, 146
installation de Hubble via, 203
journalisation dans le monde Cloud natif, 138-149
installation du binaire sur OS X et Linux, 108
à l’aide de Fluentd, 138-146
téléchargement et installation de Calico, 196
exécution de Fluentd sur Kubernetes, 146-149
vérification des ressources créées pour le tableau
journalisation, 286
Tomcat dans Helm, 116
analyse des journaux et requêtes de journaux
kube-dns vs. CoreDNS, 165
avec Azure Monitor, 160
kubelets, 42, 71
exécution du conteneur de journalisation dans
extraction des nœuds kubelet à l’aide de
Kubernetes, 95
Prometheus, 137
JSON
kube-proxy, 71
définitions de stratégie Azure, 210
désactivation du déploiement de, 198
lecture des hôtes par Prometheus à partir du
Kubernetes en production, 85-98
fichier inventory.json, 134
DaemonSets, 94
utilisation avec Terraform, 14
Horizontal Pod Autoscaler – HPA (échelle
horizontale de pod), 88
K objet Ingress, 92
Kafka, 234 objets de déploiement, 86
fonctionnalités de streaming, 238 ReplicaSets, 85
présentation, 243 tâches, 96
KEDA – Kubernetes Event-Driven Autoscaling Kubernetes Event-Driven Autoscaling – Mise à
(Mise à l’échelle pilotée par les événements l’échelle pilotée par les événements Kubernetes
Kubernetes), 276-281 (voir KEDA)
architecture, 276 Kubernetes, 4, 41, 67-98, 285
installation de KEDA sur Kubernetes, 277-281 composants, 69-72
Kiali (console de gestion pour Istio), 179-186, 286 nœuds de travail, 71
découverte du tableau de bord, 183 plan de contrôle, 70
exposition à Internet, 180 contrôleurs d’admission, 177
installation de Kiali sur le cluster Kubernetes, 179 création d'un cluster dans Azure, 99-120

296 | Index
création d'un cluster à partir de zéro, 99-111 limitation de débit, 153
déploiement d’applications et de services à limites de ressources (Kubernetes), 81
l’aide de Helm, 113-119 Linux
utilisation d'Azure Kubernetes Service, 111 étiquettes SELinux, 37
découverte de service avec CoreDNS, 169-171 image de machine, en s’appuyant sur Azure avec
déploiement de Calico via l’installation AKS, 196 Packer, 29
déploiement de NATS sur, 251 primitives de conteneur, 37-40
déploiement de TiKV sur, 228-229 prise en charge de Calico pour, 193
déploiement de Vitess sur, 223-224 réduction de l’accès du conteneur aux API du
déploiement d'Open Policy Agent sur, 215 noyau, 36
etcd utilisé comme backend de découverte de livraison des messages, au mois une fois 241
services dans, 229 fourni par les services de messagerie Azure, 253
Flannel CNI, 207-210 livraison des messages, une fois maximum, 241
Fluentd sur, 146-149 livraison des messages, une seule fois, 241, 249
fonctions prêtes à l'emploi offertes, 68 locataires, 11
installation de KEDA sur, 277-281 logiciel de conteneur, 41
installation de Kiali sur le cluster, 179 logiciel en tant que service (SaaS), 2, 2
installation d'Istio sur AKS, 175 Loki, outil d’agrégation de journaux, 149
installation d'OpenFaaS sur, 281 LXC (conteneurs Linux), 49
installation et exécution de Knative Eventing LXD (logiciel de gestion de conteneurs), 50
sur, 274-276
installation et exécution de Knative Serving
sur, 272
M
machines virtuelles
installation manuelle de Calico sur le
Azure Monitor pour, 159
cluster, 196
Azure, prise en charge des plug-ins VNet
Interface Runtime du conteneur, mise en œuvre
Azure,  191
CRI-O de, 43
magasin clé-valeur transactionnel (voir TiKV)
numéronyme K8s, 68
magasins de clé-valeur
objets serveur API, 72-76
etcd, magasin de clé-valeur distribué, 229
observation, exploitation et gestion des clusters
TiKV, 226
avec kubectl, 76-98
maillage des services, 172-187, 286
Kubernetes en production, 85-98
architecture générale, 172
orchestrateur de stockage Rook, 224-226
défini, 164
plateforme Knative, 272
injection automatique de proxy sidecar, 177
Prometheus sur, rôles de découverte de
installation d'Istio sur AKS, 175
services, 136-138
Istio, gestion à l’aide de Kiali, 179-186
remplacement de kube-dns par CoreDNS, 165
plan de contrôle, 173
ressources pour apprentissage supplémentaire, 75
plan de données, 173
service de fonction axé sur les événements
Présentation d’Istio, 174
OpenFaaS pour, 281
ManagedImageId, 101
stratégie Azure pour, 213
manifestes (pod), 79
configuration des sondes de préparation et de
L disponibilité, 81
Label-Selector, 74 limites des ressources dans, 81
Langage CRuby (Fluentd), 138 MariaDB, base de données Azure pour, 220
langage de programmation Erlang/OTP, 243 mémoire
langage de programmation Ruby, implémentation limite maximale dans le manifeste de pods, 81
C de, 138 technique de gestion, copie en écriture, 40
langage HashiCorp (HCL), 14 Mesos, 41
langage propre au domaine, HCL, 14 messagerie, 233-263, 287
Langage Rego, 214 bases des plateformes de messagerie, 238
liaison, 70 besoin de, 233-235

Index | 297
raisons de l'implémentation de la communications entre routage et sécurité,
messagerie, 234 dans l'ingestion des journaux et l'analyse de
exemple de cas d’utilisation, ingestion des données, exemple de cas d’utilisation de
journaux et analyse de données, 235-237 messagerie, 237
génération 1, sans files d’attente, 235 entre, 172
génération 2 : avec les files d’attente Cloud et maillage de service gérant la communication
le stockage d’objets, 236 entre,
génération 3 : avec file d’attente de maillage de service Istio simplifiant la
publication et d’abonnement basé sur la communication entre, 174
mémoire, 237 passage d’une architecture monolithique à, 10
messagerie Cloud avec NAT, 244-253 sécurité, 163
modèles courants, 242-243 mise à l’échelle automatique
file d’attente durable, 242 approches de machine virtuelle, 234
file d’attente simple, 242 Azure, 231
publication et abonnement, 242 en serverless, 265, 268
notions de base, 238-242 etcd et, 230
courtiers et clustering, 240 KEDA, 276-281
distribution de message, 241 OpenFaaS, 281, 283
durabilité et persistance, 241 mise à l’échelle horizontale avec les pods, 73
producteurs et consommateurs, 239 mise à l’échelle, 234
sécurité, 242 (voir également mise à l’échelle automatique)
présentation des plateformes de messagerie files d’attente de messages mise à l’échelle
Cloud natives populaires horizontalement, 237
Apache Kafka, 243 mise à niveau d’une version Helm, 117
CNCF CloudEvents, 244 mise en réseau, 189-218, 286
plateformes, 243-244 CNI (Container Network Interface), 190-193
RabbitMQ, 243 avantages de l’utilisation, 191
services de messagerie Azure, 253 fonctionnement avec Azure, 191
Azure Event Grid, 261-263 projets CNI, 192
Azure Event Hubs, 258-261 configuration de pods dans le cluster
Azure Service Bus, 253-258 Kubernetes sur Azure, 109
choisir parmi, 253 création d’un réseau virtuel Azure à partir de
métriques, 122 Terraform, 25-102
à partir de points de terminaison de intégrations de réseau pour Knative
surveillance NATS, 248 Serving, 272
adaptateur de métriques dans KEDA, 276 outil de mise en réseau Cilium, 200-207
Azure Monitor, 160 déploiement de Cilium, 201-204
métriques Cilium sur le trafic DNS et HTTP, 206 solution de sécurité et de stratégie de réseau de
Prometheus avec le maillage de service Istio, 182 Calico, 193-200
surveillance avec Prometheus dans le Cloud déploiement de Calico, 195-197
composants et architecture de mise en œuvre de la stratégie de sécurité
Prometheus, 125-127 Calico, 198-200
installation et configuration de utilisation de Calico, 197-198
Prometheus, 127 système d’infrastructure réseau (OSI) de
instrumentation des applications, 130-134 couche 3 pour Kubernetes, 207-210
monde natif, 125-138 modèles
node_exporter, 129 dans ReplicaSets Kubernetes, 85
Prometheus sur Kubernetes, 136-138 des fournisseurs de Cloud pour la mise en
recherche d'hôtes, 134-136 œuvre de l'IaC, 8
microservices, 67 modèles Terraform et ARM, 28
avantages de, 163 Packer, 29

298 | Index
modèles Azure Resource Manager (ARM), 8 avec Cilium, 200, 206
Terraform et, 28 développement piloté par, 124
modèles de monodiffusion, multidiffusion et de journalisation dans le monde Cloud
diffusion, 240 natif, 138-149
modèles de publication/abonnement, 234, 242 Fluentd sur Kubernetes, 146-149
dans NATS, 245 journalisation avec Fluentd, 138-146
file d’attente de publication et d’abonnement présentation de, 121-124
basé sur la mémoire, 237 piliers de, journaux, métriques et suivi, 122
prise en charge par Azure Service Bus, 253 suivi distribué dans le monde Cloud natif, 150-159
modes de mise en œuvre de la stratégie réseau architecture du système de suivi général et
(Cilium), 204 ensemble de suivi, 153
module d’extension forward (Fluentd), 140, 141 concepts de suivi clé, 151-153
déploiement de la fonction Knative Serving sur suivi des normes, des outils et des
Kubernetes, 273 instruments de code, 154-159
écriture de votre première fonction sur-ensemble de la surveillance, 123
OpenFaaS, 282-283 surveillance des métriques avec Prometheus
fonctions (sans serveur), 266 dans un monde Cloud natif
grand nombre de, 268 composants et architecture de
module d’extension http (Fluentd), 140 Prometheus, 125-127
modules (Terraform), 15 installation et configuration de Prometheus, 127
modules d’extension d’entrée (Fluentd), 140 instrumentation des applications, 130-134
récupération des journaux d’événements monde Cloud natif, 125-138
provenant de sources externes, 140 node_exporter, 129
MTAs (agents de transfert de messages), 239 Prometheus sur Kubernetes, 136-138
clustering, 240 recherche d'hôtes, 134-136
MutatingAdmissionController, 177 utilisation de Kiali pour les maillages de
MySQL services Istio, 179
Azure Database pour, 220
distribués et partitionné, Vitess, 221-224
navigateur d’expression (Prometheus), 128
O
Open Policy Agent (OPA), 214-218
API clés servis par, 214
N déploiement de stratégie avec, 216-218
NetworkPolicy (Calico), 199 déploiement sur Kubernetes, 215
NFS – Network File System (système de fichiers intégrations, page Écosystème OPA, 215
réseau), 224 OpenCensus, 154
nœud de contrôle, 32 OpenFaaS, 281-283
nœuds architecture, 281
création de nœuds d'instance de travail et de création de votre première fonction, 282-283
contrôleur pour le cluster Kubernetes dans installation sur Kubernetes, 281
Azure, 104-106 OpenSSL, 250
node_exporter (Prometheus), 129 OpenTelemetry, 154
rôle de nœud, 136 instrumentation du code avec, 157
nœuds de travail (Kubernetes), 71 OpenTracing, 154
création pour un cluster dans Azure, 104-106 instrumentation du code d'application avec, 155
déploiement et configuration avec Ansible, 109 opérateur (Kiali), 180
nœuds gérés, 32 opération asynchrone (messagerie), 235
nom du registre (Azure Container Registry), 56 orchestrateur de stockage Rook, 224-226
nom métrique (Prometheus), 126 architecture, 224
observabilité, 121-161, 286 déploiement sur Kubernetes, 225-226
à l’aide d’Azure Monitor, 159 orchestrateurs (conteneur), 41, 67
au-delà de trois piliers, 123 (voir aussi Kubernetes)

Index | 299
orchestrateurs de conteneur, 40 pipelines CI/CD
(voir aussi Kubernetes ; orchestrateurs) applications de fonction d’Azure et, 269
orchestration de conteneur, 68 Pipelines Azure, 33
outil cfssl, 107 plan (Terraform), 14, 16
outil de suivi Jaeger, 154 plan de contrôle (Kubernetes), 70
instrumentation du code à l’aide des normes de plan de contrôle (maillage de services), 173
suivi et Jaeger, 154-159 Istiod, 174
outil de traçage Zipkin, 154 plan de données (maillage de services), 173
outils IaC de premier plan, 13 Envoy, utilisation par Istio, 174
Ansible, 31 planificateur Kube, 70
Packer, 29-31 planificateurs, 67
Terraform, 14-28 fonctions de planification dans Kubernetes,
altérations et tolérances, 95
planificateur Kube, 70
P plateforme en tant que service (PaaS), 2
Packer, 29-31, 285
Azure, 11
création d’images de contrôleur et d’instances
Docker, 46
de travail, 100
services de stockage et de base de données
création d’une image Linux sur Azure, 29
Azure, 220
création d'une image pour déployer Harbor, 53
plateformes de conteneur
installation de Docker CE via l’image Packer, 65
Containers Kata, 49
installation, 29
Docker, 46-49
paires de clés, génération pour l’authentification
LXC et LXD, 49
Nkey, 250
plateformes communes, 42
paramètre de stockage, modification pour le cluster
playbooks (Ansible), 31
TiKV, 228
plug-in de cache (CoreDNS), 168
partitionnement
plug-in d'erreurs (CoreDNS), 168
fonctions de partitionnement automatique de
plug-in file (Fluentd), 141
TiKV, 226
plug-in filter_geoip (Fluentd), 143
prise en charge de Vitess pour, 221
plug-in filter_record_transformer (Fluentd), 142
partitionnement logique, 74
plug-in filter_stdout (Fluentd), 143
partitions (message), 259
plug-in in_exec (Fluentd), 141
passerelle (Kiali), 180
plug-in in_syslog (Fluentd), 140
performance
plug-in in_tail (Fluentd), 141
amélioration de la messagerie, 234
plug-in in_unix (Fluentd), 141
de services PaaS, 221
plug-in log (CoreDNS), 168
fonctions sans serveur et, 266
plug-in out_copy (Fluentd), 142
périodes, 151
plug-in out_elasticsearch (Fluentd), 141
en suivi pour le service de réservation de films, 157
plug-in stdout (Fluentd), 142
période de demande entrante et sortante, 154
plugins de sortie (Fluentd), 141
periodSeconds, 80
plug-ins IPAM, CNI, 192
persistance (messages), 241
plug-ins Main (CNI), 192
JetStream NATS, 246, 249
plug-ins Meta (CNI), 192
protocole de publication et d’abonnement et, 244
pods Nginx, 79
PersistenceVolumeClaims (PVCs), 82, 84
Pods, 72
Pilot (Istiod), 174
configuration de la mise en réseau et du routage
pilotage, 286
de pod pour le cluster Kubernetes dans
de serveurs NAT, 246
Azure, 109
infrastructure et applications modernes natives
disponibilité pendant les mises à jour, 88
du Cloud, 125
gestion avec kubectl, 78-85
(voir aussi Prometheus)
création de pods à l’aide de la syntaxe
observabilité en tant que sur-ensemble de, 123

300 | Index
déclarative dans les manifestes de pods, 79 rôle d’entrée, 138
création de pods avec la commande run, 79 rôle de nœud, 136
injection automatique de proxy sidecar, 178 rôle de pod, 137
proxy du sidecar Istio sur, 176 rôle de points de terminaison, 137
Point de terminaison TCP pour Fluentd, 140 rôle de service, 137
Points de terminaison HTTP, définition et rôles, 136-138
exposition des métriques à l’aide de, 130 recherche d'hôtes, 134-136
points de terminaison privés, 58 utilisation d’Ansible, 134
Portail Azure utilisation d'azure_sd_config, 135
affichage du déploiement de Service Bus, 257 utilisation d'un fichier JSON ou YAML, 134
affichage du réseau virtuel en cours de création, 26 PromQL, 128
création de compte Azure via, 12 propagation de contexte distribué, 152
création de locataires AAD avec, 11 propagation du contexte, 152
création d'un cluster AKS, 111 protocole de messagerie SMTP, exemple de système
création d'une instance de conteneur Azure, 61 de mise en file d’attente, 240
icône Cloud Shell dans votre compte, 18 protocole gRPC (Google Remote Procedure Call), 227
interaction d'Azure CLI avec, 13 protocole Raft, 226, 227
recherche d’Azure Container Registry, 55 utilisation par etcd, 229
utilisation pour créer une application de protocoles et modèles de messagerie, 253
fonction, 270 provisionneurs, 29
vérification de la création d’images de machine proxys de sidecar (maillage de service), 172, 172
Packer dans, 30 injection automatique de proxy Envoy, 177
vérification de la création du groupe de pushgateway (Prometheus), 126
ressources Terraform, 22 Python, 32
ports bibliothèque Flask, 272
serveur de zone DNS, 167 configuration de l’environnement Python pour
services dans Kubernetes, 90 tester
affichage du mappage de port, 91 déploiement KEDA, 279
PostgreSQL, base de données Azure pour, 220 file d’attente Azure Service Bus, envoi/réception
PowerShell, 18 de messages, 258
principal de service, création pour Terraform, 18 paquet nats-py, 252
Prise en charge protocole MQTT, NAT Jetstream, 244 producteur utilisant le paquet Python azure-
producteurs et consommateurs (messagerie), 239 eventgrid, 262
modèle de file d’attente simple, 242
producteurs, 259
programmes de Berkeley Packet Filter (BPF), 40
Q
quotas de ressources, 74
(voir aussi eBPF)
projets open source (CNCF), 288
Prometheus, 125-138 R
composants et architecture, 125-127 RabbitMQ, 234
tâches et instances, 127 fonctionnalités de streaming, 238
installation and configuration, 127 présentation, 243
installation avec Istio, 182 redirection de port, 148
instrumentation des applications, 130-134 déploiement local de redirection Kibana, 149
compteurs, 131 Redis, 124, 157
histogrammes, 133 Azure Cache pour, 220
jauges, 132 réétiquetage (dans Prometheus), 136
résumé, 133 références
node_exporter, 129 exécution d'etcd sur les références Azure, 230
exécution sur Kubernetes, service de Registre de conteneur Azure, 57
découverte référentiel de tableau et interactions avec les

Index | 301
clients, 113 réseaux virtuels, 64
composants, 114 CNI fonctionnant avec Azure, 191
création de tableaux pour vos applications, 118 création d’un réseau virtuel Azure à partir de
déploiement de Elasticsearch sur Kubernetes, 148 Terraform, 25-27
déploiement de Kibana sur Kubernetes, 149 création d’un réseau virtuel Azure pour
gestion des versions, 117 héberger le cluster Kubernetes, 102
installation de FaaS sur Kubernetes, 281 DNS dans, 171
installation de KEDA sur Kubernetes, 277 résilience, renforcement avec la messagerie, 234
installation de Kiali sur Kubernetes, 179 ressource personnalisée (Kiali), 180
installation de NATS sur Kubernetes, 251 restauration d'une version Helm, 118
installation de TiKV sur Kubernetes, 228 résumé (Prometheus), 133
installation d'Istio sur AKS, 175 RocksDB, 227
installation et gestion, 114-117 rôle d’entrée, 138
installation d’un tableau Helm sur rôle de pod, 137
Kubernetes, 115 rôle de points de terminaison, 137
modification des valeurs par défaut du rôle de service, 137
tableau, 116 routage
recherche de référentiels Helm, 115 Calico, 194
Référentiel GitHub pour ce livre, 20 CIDR, 208
référentiel PingCap, 228 CNI, 190
référentiels (Helm), 114 configuration de pods dans le cluster
ajout d’un référentiel de tableau, 114 Kubernetes sur Azure, 109
recherche, 115 dans Istio, 174, 174
régions, 12 dans les entrées Kubernetes, 93
choix de la région pour Azure Container dans les espaces de noms Linux, 39
Registry, 56 dans les maillages de service, 172, 173
Registre de conteneur Azure, 55-59 Fluentd, 140, 143
registre de services, 164 Kiali dans Istio, 180, 185
registres de conteneurs, 50-59 rubriques
produits natifs du Cloud actuels, 50 dans Azure Event Grid, 261
stockage des images Docker dans, 59 dans Azure Service Bus, 254, 255
stockage d'images sécurisé avec Harbor, 51-54 runtimes (conteneur), 42
règle de destination (Kiali), 180 runtimes de conteneur, 42
règles d’autorisation de couche 3 et de couche 4
(Cilium), 204
activation de la visibilité, 206
S
Seccomp (SECure COMPuting), 40
Règles de visibilité de la couche 7 dans Cilium, 205
Seccomp-BPF, 40
réparation automatique, etcd, 230
secrets
réplicas, nombre à créer dans Kubernetes
paires de clés partagées en tant que secret
ReplicaSets, 85
Kubernetes, 250
ReplicaSets, 73
spécification du support TLS pour l’entrée
DaemonSets vs., 94
Kubernetes avec un objet Secret, 94
gestion des objets Deployment, 87
sécurité
Kubernetes en production, 85
etcd TLS et RBAC, 231
réplication
fourni par les conteneurs, 36
fonctionnalités TiKV pour, 226
messagerie, 242
modification du paramètre de réplicas pour le
NATS, 249
cluster TiKV, 228
Authentification basée sur Nkey, 250
requêtes HTTP GET, 266
authentification TLS, 250
réseau virtuel Azure (VNet), plug-ins connectant
outil de mise en réseau Cilium, 200
des conteneurs à, 191
stratégie de sécurité Calico, mise en

302 | Index
œuvre, 198-200 mise à jour du chemin d'accès à l’emplacement
sélecteurs (étiquette), 74 de la clé SSH pour les machines de travail et
dans ReplicaSets, 85 de contrôleur, 104
séries chronologiques, données stockées StatefulSets, 75
Prometheus en tant que, 126 stockage blob, 15, 23
Serveur API création de stockage blob Azure, 24
kube-apiserver, 70 stockage d'objets, 236
utilisation d’objets Kubernetes, 72-76 stockage Hadoop Distributed File System
annotations, 74 (HDFS), 220
contrôleur d'entrée, 74 stockage, 219-232, 286
DaemonSets, 75 base de données distribuée, 219
déploiements, 73 (voir aussi bases de données distribuées)
espaces de noms, 74 clés, 250
étiquettes et sélecteurs, 74 création d’un backend de compte de stockage
Pods, 72 pour le cluster Kubernetes dans Azure, 101
ReplicaSets, 73 etcd, 229-231
services, 73 magasin clé/valeur etcd, 71
StatefulSets, 75 messages, 241
Tâches, 75 objets de stockage dans Kubernetes, 84
serveur Tomcat, déploiement avec Helm, 116 options d'Azure pour, 220
serveur web Nginx orchestrateur de stockage Rook pour
déploiement de la nouvelle version, 87 Kubernetes, 224-226
utilisation avec Azure Container Instances, 64 TiKV (Titanium Key-Value), 226-229
serveurs stratégie Azure, 189, 210-213
NATS création de votre propre stratégie, 212-213
clustering, 245 démarrage rapide, 210-212
pilotage, 246 initiatives, 211
utilisation du serveur NATS Docker, 246 modes ou effets d'application des
serveur Prometheus, 126 politiques, 211
service virtuel (Kiali), 180 stratégies intégrées, 210
services fonctions clés, 210
compte de service Kubernetes pour Flannel, 208 limites, 213
objet Service dans Kubernetes, 73 pour Kubernetes, 213
utilisation dans des environnements de streaming, 287
production, 89 messagerie vs., 238
sans serveur, définition dans un fichier, 273 serveur de diffusion (stan) NAT, installation sur
shells Kubernetes, 251
Bash et PowerShell, 18 Streams (NATS), 244
Cloud Shell, 17 suivi, distribué, dans le monde Cloud
sonde de démarrage, 81 natif, 150-159, 286
sondes de disponibilité, 80 architecture du système de suivi général et
sondes de préparation, 81 ensemble de suivi, 153
sondes, 80 concepts de suivi clé, 151-153
(voir aussi contrôle d'intégrité dans Kubernetes) échantillonnage, 153
spécification de runtime (OCI), 45 périodes, 151
spécification OCI (Open Container Initiative), 43-46 propagation du contexte, 152
spécification d’image, 44 consommation de données de suivi avec
spécification de runtime, 45 Azure Monitor, 160
SSH suivi des normes, des outils et des instruments
configuration d'accès aux machines à l’aide de code, 154-159
d’OPA, 216-218 syntaxe déclarative
génération d’une paire de clés SSH, 105 Azure CLI pour scripter de façon déclarative

Index | 303
l'infrastructure Cloud Azure, 25
dans Kubernetes, 68, 73, 74 modèles ARM, 28
infrastructure, 13 gestion d’Azure Event Hubs avec, 259
langage déclaratif, Rego, 214 gestion d’Azure Service Bus avec, 255-258
modèles ARM, 28 installation, 17
utilisation des manifestes de pods pour la terminologie de base, 14
création de pods dans Kubernetes, 79 utilisation de base et configuration de
utilisation par Terraform, 14 l’infrastructure avec, 20
syscalls, filtrage, 40 TiKV (Titanium Key-Value), 226-229
système d’événements, Knative Eventing, 274-276 architecture, 226
système de stockage Ceph, 224 déploiement sur Kubernetes, 228-229
systèmes d’exploitation, conteneurs en cours TLS (Transport Layer Security)
d’exécution sur, 35, 43 authentification, utilisation dans NATS, 250
inspection des connexions chiffrées TLS avec
Cilium, 207
T kit de ressources PKI/TLS pour les certificats
tableaux (Helm), 114
TLS, 107
création pour vos applications, 118
spécification en créant l’objet Secret
éléments dans le répertoire de tableau, 118
Kubernetes, 94
modification de fichiers dans le répertoire
utilisation du chiffrement TLS dans etcd, 231
de tableau, 119
tolérances (fonction de planification de
installation sur Kubernetes, 115
Kubernetes), 95
modification des valeurs par défaut du
traces, 123
tableau, 116
assemblage, 154
restauration d’un tableau déployé, 118
défini, 152
tâches (Kubernetes), 75
trafic ICMP, autorisation avec Calico, 199
objet Job, création et gestion des pods, 96
type NodePort, 90
tâches (Prometheus), 127
types de métriques quatre cœurs, 126
td-agent (Fluentd)
Typha (Calico), 194
changement de configuration, 145
installation, 139
Terraform, 14-28, 285 U
charge de travail, 16 utilitaire mkcert, 250
commandes courantes, 17
configuration de l’accès au compte Azure, 18
configuration de la mise en réseau et du routage
V
variable ARM_ACCESS_KEY, 101
de pod pour le cluster Kubernetes dans
variables d’environnement, configuration pour
Azure, 109
Terraform sur machine locale, 19
création d’IP publiques pour l’équilibreur de
versions (Helm), 114
charge Kubernetes dans Azure, 102-104
gestion, 117
création d’un backend de compte de stockage
désinstallation d’une version, 118
pour le cluster Kubernetes dans Azure, 101
mise à niveau d’une version, 117
création d’un cluster AKS, 111
restauration d'une version, 118
création d’un groupe de ressources pour le
vérification d’une version, 117
cluster Kubernetes dans Azure, 100
virtualisation d’infrastructure, 191
création d'instance de travail et de contrôleur pour
virtualisation, 1
le cluster Kubernetes dans Azure, 104-106
CNI permettant une véritable virtualisation
création d'un réseau virtuel Azure, 102
d’infrastructure, 191
déploiement d'Azure Container Registry via, 58
Visual Studio code, extension Azure Functions, 270
déploiement d'Azure Event Grids, 262
Vitess, 221-224
exploration de l’infrastructure Azure avec 23
architecture, 222
création de stockage blob Azure, 24
avantages de l’utilisation, 221
création d'un réseau virtuel et d'instances

304 | Index
déploiement sur Kubernetes, 223-224
volume persistant (PV), 82, 84
Y
YAML, 32
statique, 83
Prometheus lisant les hôtes à partir d’un fichier
volumes (Kubernetes), 82
statique, 134
classe de stockage, dynamique, 83
PersistentVolumeClaims (PVC), 82
volume persistant, statique, 83 Z
vtcld, 222 zones (DNS), 165, 167
vtctl, 222 DNS Azure, 171
VTGate, 222
VTTablet, 222
vue cartographique de l’application (Azure
Monitor), 160
vue Diagnostics de transaction (Azure Monitor), 160
VXLAN
utilisation avec Calico, 197
utilisation avec Flannel, 208

W
Windows
mise en réseau, CNI Azure et, 191
prise en charge de Calico pour le plan de
données HNS Windows, 193

X
XDP, 198

Index | 305
À propos des auteurs
Nishant Singh est un ingénieur senior spécialisé dans la fiabilité du site chez LinkedIn. Son
travail consiste à améliorer la fiabilité du site, tout en privilégiant la réduction du temps
moyen de détection (MTTD) et du temps moyen de réponse (MTTR) face aux incidents.
Avant de rejoindre LinkedIn, il était ingénieur DevOps chez Paytm et Gemalto, et consacrait
son temps à la création de solutions personnalisées pour les clients, ainsi qu’à la gestion et
la maintenance des services sur le Cloud public. Nishant s’intéresse vivement à l’ingénierie
de la fiabilité des sites et à la création de systèmes distribués.
Michael Kehoe est un ingénieur senior chargé de la sécurité du personnel chez Confluent.
Auparavant, il était chargé de la réponse aux incidents, de la récupération d’urgence, de
l’ingénierie de la visibilité et des principes de fiabilité en tant qu’ingénieur senior de fiabilité
de site chez LinkedIn. Dans le cadre de ses fonctions chez LinkedIn, il a mené les efforts
de l’entreprise pour automatiser la migration vers Microsoft Azure. Michael est spécialisé
dans la gestion d’une grande infrastructure système, comme le démontre son travail chez
LinkedIn (applications, automatisation et infrastructure) et à l’Université du Queensland
(réseaux). Il a également consacré du temps à la création de petits satellites à la NASA et à
l’écriture de logiciels d’environnements thermiques à Rio Tinto.

Colophon
L’animal qui se trouve sur la page de couverture de Cloud Native Infrastructure with
Azure est un geai de Steller (Cyanocitta stelleri), un geai à crête trouvé dans l’ouest de
l’Amérique du Nord et centrale. Les geais de Steller résident généralement dans les
forêts de conifères, mais peuvent également être rencontrés dans les forêts de feuillus.
Nommé d’après Georg Wilhelm Steller, le naturaliste allemand qui a découvert cette espèce
pour la première fois en 1741, ce geai de couleur bleue est distinct du geai bleu de l’est de
l’Amérique du Nord, avec la crête noire exceptionnelle qui le caractérise.
Les geais de Steller sont omnivores ; leur régime alimentaire se compose principalement de
noix, de graines de pin, de glands, de baies et d’autres fruits additionnés d’insectes, de petits
rongeurs et d’œufs.
Bien que le statut de conservation du geai de Steller soit répertorié comme étant peu
préoccupant, la plupart des animaux figurant sur les couvertures O’Reilly sont menacés et
ils tous sont importants pour le monde entier.
L’illustration de la couverture est de Karen Montgomery, basée sur une gravure d’illustration
antique de l’histoire naturelle royale de Lydekker. Les polices de couverture sont Gilroy
Semibold et Guardian Sans. La police de texte est Adobe Minion Pro ; la police d’en-tête est
Adobe Myriad Condensed ; et la police de code est Ubuntu Mono de Dalton Maag.
Apprenez des experts.
Devenez-en un vous-même.
Livres | Cours en ligne en direct
Réponses instantanées | Événements virtuels
Vidéos | Apprentissage interactif

Commencez sur oreilly.com.


©2022 O’Reilly Media, Inc. O’Reilly est une marque déposée d’O’Reilly Media, Inc. | 175

Vous aimerez peut-être aussi