Skip to main content

Comment et quand utiliser les labels Docker / annotations de conteneur OCI

Écrit par
blog feature docker labels

3 novembre 2021

0 minutes de lecture

La plupart des images de conteneurs sont créées à partir de Dockerfiles qui combinent des instructions telles que FROM, RUN, COPY, ENTRYPOINT, etc., pour construire les couches d’une image conforme à OCI. Pourtant, une instruction étonnamment peu utilisée est LABEL. Dans cet article, nous allons examiner les labels (« annotations » dans la spécification d’image OCI), leur utilité, quelques usages standardisés et des pratiques pour renforcer la sécurité de vos conteneurs.

Dans la suite de cet article, nous parlerons de labels plutôt que d’annotations, car c’est le terme le plus couramment utilisé. C’est parti !

Que sont les labels d’image Docker ?

Les labels d’image Docker vous permettent d’ajouter des métadonnées clé-valeur directement à votre image. Ces données ne sont pas accessibles à un conteneur exécuté à partir de l’image, mais elles sont utiles pour consigner, par exemple, l’emplacement du code source de l’image, l’équipe qui en assure le support ou le build CI qui l’a créée.

Les métadonnées d’image Docker / OCI expliquées

Si vous avez déjà créé un type de package logiciel, vous savez que, dans la plupart des cas, il contient le logiciel, la configuration et parfois des données fonctionnelles, ainsi que des métadonnées sur le package lui-même.

Par exemple, les fichiers Java .jar sont essentiellement des archives .zip, mais ils contiennent tous un répertoire de premier niveau META-INF avec plusieurs fichiers et répertoires qui, selon la spécification Java 2 Platform, « ... sont reconnus et interprétés par Java 2 Platform pour configurer les applications, les extensions, les chargeurs de classes et les services ». Si nous ouvrons un fichier .jar créé avec l’outil de build populaire Maven, nous y trouverons généralement, entre autres, un répertoire maven contenant notamment le pom.xml Maven effectif et le fichier pom.properties utilisés pour créer le .jar. (À titre d’information, « POM » signifie Project Object Model de Maven.)

RPM, APT, NPM et la plupart des autres outils de gestion de packages intègrent des métadonnées similaires. Les outils s’en servent pour installer ou exécuter les logiciels contenus dans les packages, tandis que les dépôts ou les systèmes de surveillance à l’exécution les utilisent à des fins utilitaires.

Capture d’écran du terminal affichant l’historique d’une image Docker : commandes, point d’entrée, utilisateur, variable d’environnement, arguments, labels et tailles des images.

Les images de conteneurs contiennent elles aussi des métadonnées dans leurs couches. Lorsque vous affichez l’« historique » d’une image, vous verrez souvent des couches de taille nulle. Elles ne contiennent aucune modification du système de fichiers, mais plutôt des métadonnées destinées à être utilisées à l’exécution et généralement ajoutées par les commandes du Dockerfile :

  • USER : utilisateur sous lequel exécuter le processus

  • ENV : variable (et sa valeur) à définir dans l’environnement des processus

  • ARG : argument de build transmis au conteneur de build et utilisé comme variable d’environnement dans le contexte du build

  • CMD : commande et/ou paramètres à utiliser pour démarrer le processus (ENTRYPOINT est similaire)

  • LABEL : paires clé/valeur non utilisées par le moteur d’exécution

La plupart de ces éléments sont bien connus et utilisés dans chaque Dockerfile, mais les métadonnées de label sont souvent négligées, car elles ne sont pas nécessaires à l’exécution de vos conteneurs.

Pourquoi utiliser des labels dans les images de conteneurs ?

Les labels peuvent servir à de nombreuses fins : documenter les versions, ajouter les coordonnées des responsables du projet ou encore fournir des informations sur l’utilisation à l’exécution. L’un des cas d’utilisation les plus courants consiste à documenter la création de l’image. Ces informations peuvent enrichir la chaîne d’approvisionnement logicielle de l’artefact image.

Types de métadonnées de label Docker / annotation d’image OCI

Labels standardisés

Les métadonnées sur la création et l’origine des images sont si courantes que l’équipe OCI publie un ensemble standardisé de clés, toutes préfixées par « org.opencontainers.image. », notamment :

  • source : URL permettant d’obtenir le code source utilisé pour créer l’image

  • revision : identifiant de révision du contrôle de version pour le logiciel empaqueté

  • base.digest : condensat (hash) de l’image sur laquelle repose cette image

  • base.name : référence de l’image sur laquelle repose cette image

  • version : version du logiciel empaqueté

Labels personnalisés

Comme il s’agit simplement de paires clé-valeur, votre projet ou votre organisation peut y inscrire à peu près tout ce qu’il souhaite. Voici quelques idées (avec, par exemple, le préfixe « com.mycorp.myteam. ») :

  • ci-build : URL de l’exécution du projet CI qui a créé l’image

  • releasenotes : notes de version du logiciel empaqueté

  • healthz : point de terminaison HTTP pour les vérifications d’état

  • docker.run : exemple de commande Docker pour exécuter un conteneur à partir de cette image

  • k8s.deployment : YAML Kubernetes encodé en Base64 pour un déploiement utilisant cette image

Ce dernier exemple est intéressant : vous pouvez effectivement stocker dans la valeur d’un label à peu près n’importe quel contenu encodable en Base64. Vous pourriez donc récupérer cette image, puis exécuter une commande comme celle-ci pour obtenir un exemple de fichier YAML de déploiement, que vous pourriez ensuite utiliser dans un cluster Kubernetes :

$ docker image inspect myimage:tag | jq -r ".[].Config.Labels.\"com.mycorp.myteam.k8s.deployment\"" | base64 -d
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: snyk
  name: snyk
spec:
  replicas: 1
  selector:
    matchLabels:
      app: snyk
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: snyk
    spec:
      containers:
      - image: ericsmalling/snyklabeldemo:m
        name: snyklabeldemo
        resources: {}
status: {}

Vous pouvez même envoyer directement le résultat à kubectl et déployer aussitôt, sans avoir besoin de charts Helm ni de fichiers YAML distincts dans votre dépôt Git !

docker image inspect myimage:tag | jq -r ".[].Config.Labels.\"com.mycorp.myteam.k8s.deployment\"" | base64 -d | kubectl apply -f - 

deployment.apps/snyk created

Exploiter les labels Docker / annotations OCI

Comme vous pouvez l’imaginer, les labels peuvent recevoir toutes les valeurs auxquelles votre système CI a accès. Il suffit d’inspecter les labels pour faire le lien entre les images et/ou les conteneurs en cours d’exécution et leurs sources, leur documentation, etc. Prenons l’exemple d’une organisation qui publie des images avec le label standard OCI org.opencontainers.image.source, qui contient l’URL du dépôt SCM d’où provient l’image. Pour savoir quelles images issues de quels dépôts s’exécutent sur un hôte Docker donné, vous pourriez lancer une commande comme celle-ci :

$ docker inspect $(docker ps -q) --format='{{ .Id }} {{ index .Config.Labels "org.opencontainers.image.source" }}'

17f4ee967870c49d3ffeb1c49973071c99c63377b2f9bbf987f7c3e4a21d331c https://repo.mycorp.com/team-volton/redlion
c958ffc87c2bd5af500d24eff1ccb3ee21992a5cbcb429fafcc651aa182b66ba <no value>

Le résultat affiche les ID de deux conteneurs en cours d’exécution. L’un d’eux possède le label, dont la valeur est donc affichée.

Les choses se compliquent un peu dans un cluster Kubernetes, où vous n’aurez probablement pas accès aux sockets du moteur de conteneurs sur les nœuds du cluster. Malheureusement, kubectl ne fournit aucune API permettant d’accéder à ces labels. Il faut donc faire preuve d’un peu d’ingéniosité. Le script Bash suivant recherche les images exécutées dans l’espace de noms du contexte actuel, puis interroge le registre d’images pour récupérer les métadonnées de l’image et renvoyer les informations des labels :

$ cat labelgrep.sh
#!/bin/bash
FINDLABEL=$1
FINDVAL=$2

IMAGES=$(kubectl get pods -o json | jq -r ".items[].spec.containers[].image" | uniq)

for i in $IMAGES; do
	VAL=$(regctl image inspect ${i} --format '{{ index .Config.Labels "'${FINDLABEL}'" }}')
	if [[ "$VAL" != "" && ( "$FINDVAL" == "" || "$VAL" == "$FINDVAL") ]]; then
	  echo "[${i}] ${FINDLABEL}=${VAL}"
  fi
done

J’utilise ici l’excellent outil regctl du projet open source regclient. Il permet d’obtenir des informations sur les images directement depuis un registre, sans avoir à récupérer l’image dans mon environnement local pour l’inspecter. Je peux également exécuter ce script où je veux, sans avoir besoin d’un moteur d’exécution de conteneurs.

Exécutons maintenant ce script sur un cluster et recherchons les dépôts associés aux images qui y sont exécutées :

$ ./labelgrep.sh org.opencontainers.image.source
[images.mycorp.com/voltron/redlion] org.opencontainers.image.source=https://repo.mycorp.com/team-volton/redlion
[images.mycorp.com/voltron/bluelion] org.opencontainers.image.source=https://repo.mycorp.com/team-volton/bluelion

Comme vous pouvez le constater, deux images s’exécutent actuellement dans des pods de mon cluster qui portent le label org.opencontainers.image.source.

Ces exemples sont relativement simples, mais vous pouvez certainement reprendre ces concepts pour créer vos propres scripts ou appels d’API et répondre aux besoins de votre organisation.

Intégration de Snyk

Pour la sécurité, l’une des fonctionnalités les plus intéressantes est la prise en charge, par l’analyse d’images Snyk, de la mise en correspondance automatique de l’image analysée avec son Dockerfile, grâce aux labels d’image.

L’intégration de Snyk avec les dépôts de code source permet déjà de détecter et d’analyser statiquement les Dockerfiles présents dans votre code, ainsi que les images importées depuis vos registres de conteneurs. Jusqu’à récemment, toutefois, vous deviez gérer vous-même leur mise en correspondance. Il fallait soit ajouter manuellement la référence du Dockerfile à l’image, soit mettre en place votre propre automatisation via des appels d’API.

Désormais, il vous suffit d’ajouter le label standard OCI org.opencontainers.image.source avec l’URL du dépôt qui contient le Dockerfile. Snyk établira automatiquement la correspondance lors de l’importation de l’image.

Page de détails d’une image Docker montrant l’image associée « docker-imageleric », mise en évidence par des annotations rouges.

Exemple de projet d’analyse d’image automatiquement lié

Pour conclure

En résumé, le label ou l’annotation d’image, souvent oublié, est un outil puissant qui vous permet d’intégrer des métadonnées directement dans vos images. L’utilisation de clés standardisées peut vous aider non seulement à documenter l’origine d’une image, mais aussi à fournir des informations aux outils de déploiement et de sécurité afin de mieux comprendre votre environnement déployé.

Vous pouvez dès aujourd’hui profiter de la fonctionnalité de liaison automatique des images de Snyk : il vous suffit d’ajouter le label standard OCI org.opencontainers.image.source aux Dockerfiles actuellement analysés dans votre compte. Vous n’avez pas de compte Snyk ? Inscrivez-vous gratuitement pour en profiter dès maintenant !

Je serais curieux de savoir comment vous utilisez les labels. Ces idées sont-elles nouvelles pour vous, ou vos projets les utilisent-ils déjà ? De quelles autres façons intéressantes ajoutez-vous des annotations à vos images ? Quelles nouvelles intégrations aimeriez-vous voir, avec Snyk ou d’autres outils ? Faites-moi part de vos idées en me mentionnant (@ericsmalling) sur Twitter, je serais ravi de connaître votre avis !

La sécurité des conteneurs, pensée pour les développeurs

Snyk détecte et corrige automatiquement les vulnérabilités dans les images de conteneurs et les workloads Kubernetes.