import yaml
YAML 101
Introduction à l’utilisation de YAML
, un langage lisible et expressif devenu incontournable dans l’éco-système DevOps.
Dérouler les slides ci-dessous ou cliquer ici pour afficher les slides en plein écran.
Qu’est-ce que le format YAML
YAML
est un acronyme récursif signifiant YAML Ain’t Markup Language. YAML
est un langage de sérialisation de données au format texte : il permet de structurer de la donnée dans des fichiers textuels, à l’instar d’autres formats populaires (CSV
, JSON
, XML
, etc.).
Sa spécificité par rapport aux autres formats populaires est qu’il est conçu pour être à la fois expressif et facile à lire pour un humain. Expressif dans la mesure où il permet de représenter de l’information hiérarchique, ce que ne permet pas le format CSV
par exemple qui représente essentiellement des données tabulaires (“fichier plat”). Lisible car, contrairement aux languages dont la structure est basée sur des balises (markup) comme XML
ou bien délimitée par des symboles comme l’accolade en JSON
, YAML
se démarque par une syntaxe basée sur l’indentation — comme Python
.
Voici un exemple typique d’un fichier YAML
, qui compare les principaux langages utilisés pour représenter des données hiérarchiques dans un format textuel1.
---
type: tutorial
domain:
- devops
language:
- yaml:
name: YAML Ain't Markup Language
born: 2001
legibility: awesome
- json:
name: JavaScript Object Notation
born: 2001
legibility: good
- xml:
name: Extensible Markup Language
born: 1996
legibility: bad
---
YAML
et l’approche GitOps
Grâce à sa lisibilité, le langage YAML
est rapidement devenu un standard dans les écosystèmes DevOps
et MLOps
dans la mesure où il facilite grandement l’interaction entre les humains et les systèmes automatisés. Il permet de définir de manière lisible des règles, basées sur des paramètres, qui peuvent facilement être interprétées à la fois par des humains (du fait de sa lisibilité) et par des machines (du fait de sa structure hiérarchisée).
En particulier, le YAML
est devenu un élément central des environnements de déploiement modernes basés sur une approche dite déclarative (Note 1). Au lieu de spécifier étape par étape comment déployer une ressource (approche impérative), YAML
permet de décrire simplement l’état final souhaité de l’environnement. C’est alors l’outil utilisé (par exemple, Kubernetes
dans l’exemple ci-dessous) qui gère les détails d’implémentation de manière la plus adaptée. On parle souvent d’approche Infrastructure as Code : l’infrastructure peut être complètement décrite dans des fichiers de configuration YAML
— qu’on appelle manifestes — qui permettent de décrire l’état souhaité de celle-ci. Cette manière de spécifier les environnements favorise la reproductibilité, chaque état de l’infrastructure étant clairement décrit et pouvant être reproduit simplement.
A titre d’analogie, l’approche déclarative est également au cœur du langage SQL
.
En SQL
, on décrit le traitement souhaité dans un langage proche du langage naturel (SELECT ce groupe de variables, FILTER ces individus, etc.) et c’est le moteur d’exécution sous-jacent qui choisit comment effectuer les calculs demandés de manière optimale.
C’est notamment une des raisons de la popularité du langage SQL
dans l’éco-système big data : il est possible d’appliquer une même requête à des données de volumétries très différentes dans la mesure où le moteur d’exécution sous-jacent va convertir la requête de manière appropriée. Par exemple, une requête SQL
sera convertie par Spark
en opérations MapReduce
distribuées permettant de traiter des volumes considérables de données (voir chapitre big data pour plus de détails).
Bien entendu, ces fichiers décrivant les environnements souhaités ont vocation à être versionnés sur un dépôt Git
. Cette extension directe de l’approche Infrastructure as Code se nomme le GitOps : l’architecture est décrite sous forme de manifestes (YAML
) et ces manifestes sont versionnés sur un dépôt Git
qui devient ainsi la source de vérité unique de spécification de l’environnement. Cette approche favorise à la fois la traçabilité — tout changement est documenté dans l’historique du dépôt Git
— et l’automatisation — l’infrastructure peut être déployée automatiquement à partir du dépôt Git
grâce à des outils de déploiement continus orientés GitOps comme ArgoCD
(cf. chapitre déploiement).
Voici un exemple illustrant ce concept dans le contexte de Kubernetes
. On déclare dans un manifeste YAML
une ressource de type Pod
, qui a des métadonnées (un nom) et des spécifications. On y déclare le conteneur que le Pod
doit déployer : celui d’une API FastAPI
(voir l’application fil rouge pour plus de détails). On passe à ce conteneur une variable d’environnement (cf. chapitre Linux 101) qui spécifie le modèle à déployer via l’API au runtime. Ainsi, on décrit dans ce manifeste l’état souhaité, et Kubernetes
se charge du déploiement effectif du conteneur.
example.yaml
kind: Pod
metadata:
name: my-api-pod
spec:
containers:
- name: api
image: my_dh_account/my_fast_api:0.0.1
env:
- name: MODEL
value: deepseek-ai/DeepSeek-R1
YAML
vs. JSON
Les langages YAML
et JSON
sont très proches dans la représentation hiérarchique de l’information qu’ils permettent. Fonctionnellement, le langage YAML
est un superset du langage JSON
. Cela signifie que tout ce qui est représentable en JSON
peut l’être en YAML
mais l’inverse n’est pas nécessairement vrai — même s’il est très rare en pratique de ne pas pouvoir faire la conversion dans les deux sens. Plusieurs outils en ligne, comme YAML
-to-json et json-to-YAML
, permettent de convertir facilement ces formats entre eux.
Comparons à titre d’illustration les représentations YAML
et JSON
du manifeste Kubernetes
précédent.
kind: Pod
metadata:
name: my-api-pod
spec:
containers:
- name: api
image: my_dh_account/my_fast_api:0.0.1
env:
- name: MODEL
value: deepseek-ai/DeepSeek-R1
{
"kind": "Pod",
"metadata": {
"name": "my-api-pod"
},
"spec": {
"containers": [
{
"name": "api",
"image": "my_dh_account/my_fast_api:0.0.1",
"env": [
{
"name": "MODEL",
"value": "deepseek-ai/DeepSeek-R1"
}
]
}
]
}
}
La comparaison entre les deux représentations illustre clairement la différence majeure entre les deux langages : le YAML
est facilement lisible par l’humain là où le JSON
est plus verbeux et ressemble donc plus à un “langage machine”. En effet, YAML
utilise l’indentation pour structurer les données là où JSON
utilise des accolades pour structurer ses objets, ce qui limite sa lisibilité mais le rend également moins susceptible aux erreurs d’indentation.
Il n’est donc pas étonnant que YAML
soit devenu le langage de référence dans le monde du GitOps
, les fichiers de configuration déclaratifs étant généralement générés et lus par des humains. A l’inverse, JSON
reste le langage standard de communication avec une API, dont les réponses sont généralement lues par d’autres applications ou bien par le biais d’un langage de programmation, ce à quoi se prête très bien une structure plus verbeuse mais aussi plus robuste.
Caractéristiques d’un fichier YAML
Les fichiers YAML
portent généralement les extensions .yaml
ou .yml
. En pratique — et cela est vrai pour tous les formats de fichier en pratique — ce n’est pas l’extension qui fait qu’un fichier texte est un fichier YAML
, mais bien la validité de son contenu au regard de la norme YAML
. On doit donc respecter un certains nombres de règles et utiliser les types prévus pour produire un fichier YAML
.
Première règle : le contenu d’un fichier YAML
est organisé en paires clé-valeur imbriquées, à la manière d’un dictionnaire en Python
. La structure hiérarchique, c’est à dire l’imbrication des dictionnaires de données, est marquée par l’indentation. Par convention, on indente avec deux espaces (pas de tabulation!) à chaque niveau de hiérarchie.
Seconde règle : on doit utiliser les types de données supportés par YAML
. En pratique, ils sont suffisamment nombreux pour couvrir la plupart des usages :
- Chaînes de caractères : de la donnée textuelle. On peut choisir de l’entourer ou non de guillemets. Par convention, tout ce qui n’est pas des autres types de cette liste est au format textuel, il n’est donc pas indispensable de mettre des guillemets (à l’inverse du
JSON
). - Numériques : entiers ou flottants (exemple :
42
,3.14
). Attention à ne pas mettre de guillemets dans ce cas, sinon les nombres seront reconnus comme des chaînes de caractères. - Booléens : représentés par
true
oufalse
(tout en minuscule!). Là encore, pas de guillemets. - Listes : éléments précédés d’un tiret (
-
). Les éléments d’une liste peuvent être, et sont souvent, à plusieurs niveaux, le YAML permettant une structure imbriquée.
Agrémentons encore un peu le manifeste de déploiement Kubernetes
pour faire apparaître l’ensemble de ces types.
kind: Pod
metadata:
name: my-api-pod
spec:
containers:
- name: api
image: my_dh_account/my_fast_api:0.0.1
env:
- name: MODEL
value: deepseek-ai/DeepSeek-R1
- name: DEBUG
value: true
ports:
- containerPort: 8000
On voit ici que dans notre paramètre env
, on a pu mettre plusieurs valeurs. Même si on ne connait pas les détails de l’implémentation sous-jacente, on peut se douter qu’on va injecter deux variables d’environnement à notre API dont les valeurs sont définies dans ce fichier.
Les clés-valeurs acceptées sont des conventions, elles dépendent de chaque solution logicielle. Mais, globalement, les paramètres d’instruction acceptés par telle ou telle machine se ressemblent. Dans le cadre de l’application fil rouge, nous proposons l’utilisation de Github Actions
, qui repose sur la spécification Mustache
, d’Argo
et de Kubernetes
. Ces solutions techniques répondent à des besoins différents mais présentent le point commun de recevoir toutes trois leurs instructions depuis des YAML
.
Le principal point de vigilance lorsqu’on écrit du YAML
concerne l’indentation. Celle-ci est essentielle à la structuration des données. L’indentation recommandée est de deux espaces ; jamais de tabulations, qui peuvent générer des erreurs d’interprétation car il s’agit de caractères spéciaux.
Pour prévenir ces erreurs, de nombreux outils de validation existent :
- IDE : les IDE standards comme
VSCode
,PyCharm
, etc. supportent nativement leYAML
et indiquent donc immédiatement les erreurs d’indentation ou de syntaxe. Il est utile d’avoir dans son IDE une coloration des indentations pour faire moins d’erreur: dansVSCode
, vous pouvez installer l’extension indent-rainbow pour cela2. - Linters : des outils spécialisés pour valider le contenu d’un fichier
YAML
. YAMLlint en est un choix courant.
Si vous utilisez des actions Github
, l’interface de Github
depuis votre navigateur effectue un diagnostic de la validité formelle du YAML. Si vous committez un fichier non valide formellement (par exemple à cause d’une erreur d’identation), Github
ne lancera pas l’action. Il peut donc être utile de vérifier, si une action ne se lance pas, si cela vient d’un problème de formattage du fichier en l’ouvrant directement depuis Github
.
Utilisation programmatique
Comme on l’a vu, le langage YAML
est pensé avant tout pour être écrit et lus par des humains. Cela dit, on peut vouloir parfois générer par exemple de nombreux fichiers YAML
à partir d’un même template, ou bien récupérer de manière automatisée des informations dans un fichier de configuration YAML
. De la même manière qu’un fichier JSON
par exemple, il est tout à fait possible d’interagir avec un fichier YAML
de manière programmatique.
En Python
, le package de référence pour manipuler du YAML
est PyYAML. Illustrons quelques commandes de base.
Supposons que le fichier de déploiement Kubernetes
qu’on a utilisé tout a long de ce tutoriel soit écrit dans un fichier api-deployment.yaml
. Commençons par l’importer et récupérer quelques informations.
La syntaxe pour importer le contenu d’un fichier YAML
est semblable à celle pour n’importe quel fichier. On utilise la fonction safe_load
du package PyYAML
.
with open("api-deployment.yaml", "r") as file_in:
= yaml.safe_load(file_in)
manifest
print(manifest)
{'kind': 'Pod', 'metadata': {'name': 'my-api-pod'}, 'spec': {'containers': [{'env': [{'name': 'MODEL', 'value': 'deepseek-ai/DeepSeek-R1'}, {'name': 'DEBUG', 'value': True}], 'image': 'my_dh_account/my_fast_api:0.0.1', 'name': 'api', 'ports': [{'containerPort': 8000}]}]}}
Le contenu est chargé en Python
sous la forme d’un dictionnaire, qui est la manière naturelle en Python
de représenter de l’information hiérarchique. A partir de là, on peut requêter le contenu du fichier YAML
à la manière de n’importe quel dictionnaire Python
. Récupérons par exemple le nom du modèle déployé.
print(
manifest"spec")
.get("containers")[0]
.get("env")[0]
.get("value")
.get(
)# ou en version plus succincte:
# print(manifest["spec"]["containers"][0]["env"][0]["value"])
deepseek-ai/DeepSeek-R1
Le chemin dans le dictionnaire reflète les différents objets qui structurent un fichier YAML
. Les clés (“spec”, “containers”) correspondent aux dictionnaires en YAML
, et les index numériques (“0” ici) correspondent à la sélection d’un élément par position dans une liste. Ici, on récupère la spécification du premier conteneur — et le seul en l’occurrence, mais le Pod
pourrait héberger plusieurs conteneurs — et, pour ce conteneur, on récupère la première variable d’environnement (MODEL
) dont on extrait la valeur (le nom du modèle).
A présent, intéressons-nous au cas où l’on voudrait modifier une information du fichier YAML
et exporter un manifeste actualisé. On va changer la valeur de la variable d’environnement DEBUG
de true
à false
. En pratique, on fait la modification sur le dictionnaire en Python
, puis on exporte le dictionnaire au format YAML
.
# Vérification du contenu actuel
print(manifest["spec"]["containers"][0]["env"][1])
# Modification du contenu
"spec"]["containers"][0]["env"][1]["value"] = False
manifest[print(manifest["spec"]["containers"][0]["env"][1])
{'name': 'DEBUG', 'value': True}
{'name': 'DEBUG', 'value': False}
L’opération a fonctionné sur l’objet en mémoire dans Python
. On exporte finalement le dictionnaire au format YAML
.
with open('api-deployment-modifie.yaml', 'w') as file_out:
yaml.dump(manifest, file_out)
# Vérification
print(open("api-deployment-modifie.yaml", "r").read())
kind: Pod
metadata:
name: my-api-pod
spec:
containers:
- env:
- name: MODEL
value: deepseek-ai/DeepSeek-R1
- name: DEBUG
value: false
image: my_dh_account/my_fast_api:0.0.1
name: api
ports:
- containerPort: 8000
Le manifeste exporté a bien été modifié comme attendu.
Conclusion
Du fait de sa prédominance dans l’éco-système DevOps
/ MLOps
, la maîtrise du langage YAML
est aujourd’hui indispensable pour tout data scientist impliqué dans des projets visant la mise en production. Sa syntaxe lisible mais puissante et sa compatibilité avec le paradigme déclaratif en font un langage de choix pour la déclaration de manifestes en mode GitOps. Cette approche est progressivement devenue un standard pour le déploiement industrialisé d’applications et sera centrale dans les chapitres suivants sur le déploiement et le MLOps.
Footnotes
L’exemple présenté est inspiré de ce tutoriel.↩︎
Dans le cadre de l’application fil rouge, si vous créez votre
VSCode
à partir de la configuration proposée, celle-ci est installée par défaut pour vous.↩︎