## Introduction

Les étiquettes d'images Docker sont des éléments mobiles dans tout pipeline de livraison de conteneurs. Une étiquette comme `myapp:latest` peut pointer vers différents résumés au fil du temps, et une étiquette `v1.2.3` peut être écrasée par accident. Sans surveillance, les équipes ne découvrent la dérive des étiquettes que lorsqu'un déploiement échoue. Ce guide présente ce qu'il faut surveiller dans l'étiquetage des images Docker, les métriques et signaux de journaux pertinents, comment construire des règles d'alerte et que faire en cas d'incident. Vous repartirez avec des commandes concrètes, des extraits de configuration et une liste de contrôle opérationnelle.

Surveiller l'étiquetage des images ne consiste pas seulement à observer un registre. Cela englobe les métadonnées de construction, les événements de registre, les extractions en environnement d'exécution et l'application des politiques. L'objectif est de détecter les erreurs tôt : un développeur étiquette un commit comme `latest`, un travail CI pousse vers le mauvais dépôt, ou une étape de promotion oublie de signer l'image. En traitant les étiquettes comme des objets d'observabilité de premier ordre, vous réduisez les retouches et améliorez la confiance dans les versions.

La valeur pour les praticiens est immédiate. Vous pouvez appliquer les modèles présentés ici avec des outils courants : la CLI Docker, les API de registre, Prometheus, Alertmanager et des scripts shell. Les exemples utilisent un registre local et des exportateurs simples, ce qui vous permet de tester l'approche sur un ordinateur portable avant de passer à l'échelle en production. Le premier projet pilote utile doit être étroit, mesurable et facile à inspecter localement avant le déploiement.

## Inventaire des versions et de l'environnement

Avant d'activer la surveillance, documentez votre topologie d'étiquetage des images. Répondez à ces questions :

- Quels registres utilisez-vous ? (Docker Hub, registre auto-hébergé, registre cloud)
- Quelles étiquettes sont considérées comme canoniques ? (`latest`, semver, SHA Git, identifiant de build)
- Qui ou quoi crée les étiquettes ? (développeurs, systèmes CI, scripts de version)
- Où les images sont-elles extraites ? (développement local, préproduction, production)

Pour ce guide, supposez un environnement minimal :

- Docker Engine 24.0 ou ultérieur sur un seul hôte Linux pour les tests locaux.
- Un conteneur de registre local fonctionnant sur `localhost:5000`.
- Une image d'application exemple nommée `myapp`.
- Des composants de surveillance : une instance Prometheus qui collecte les exportateurs, et Alertmanager pour les notifications.
- Accès shell avec les commandes `curl`, `jq` et `docker`.

Si vous avez un registre cloud, ajustez les points de terminaison en conséquence. Les concepts restent les mêmes.

Tableau d'exemple d'environnement construit :

| Composant | Version | Objectif |
|---|---|---|
| Docker Engine | 24.0.7 | Construire et exécuter des conteneurs |
| Registre | 2.8.3 | Stocker et servir des images |
| Prometheus | 2.48.0 | Collecter les métriques |
| Alertmanager | 0.26.0 | Router les alertes |
| Image myapp | 1.4.2 | Application sous test |

Assurez-vous que le registre est joignable et que vous pouvez pousser des images. Exécutez `docker run -d -p 5000:5000 --name registry registry:2` pour démarrer un registre local. Testez avec `docker pull alpine && docker tag alpine localhost:5000/alpine && docker push localhost:5000/alpine`.

## Chemin de configuration sûr

Commencez par une configuration de surveillance limitée qui ne nécessite pas de modifier les pipelines de build existants. Vous pourrez ensuite l'intégrer au CI/CD. Le chemin sûr comporte trois étapes :

- Collecter des instantanés de l'état du registre. Interrogez périodiquement l'API du registre pour obtenir les listes d'étiquettes et les résumés de manifeste. Stockez les résultats sous forme de séries temporelles.
- Ajouter des étiquettes de métadonnées au moment de la construction. Utilisez les étiquettes Docker pour enregistrer le commit source, l'horodatage de construction et l'étiquette attendue. Ces métadonnées voyagent avec l'image et sont interrogeables à l'exécution.
- Déployer un exportateur observateur d'étiquettes. Un petit script ou service qui interroge le registre et expose des métriques Prometheus. Cela vous donne un tableau de bord et des alertes sans toucher aux systèmes de production.

### Étape 1 : Script d'instantané de l'état du registre

Créez un script `tag_snapshot.sh` qui interroge le catalogue et les étiquettes du registre. Pour un registre local, l'API est simple :

```bash
#!/bin/bash
# tag_snapshot.sh - Collecte les étiquettes et résumés des images du registre local
REGISTRY="localhost:5000"
OUTPUT_DIR="./snapshots"
mkdir -p "$OUTPUT_DIR"

# Obtenir la liste des dépôts
repos=$(curl -s "http://$REGISTRY/v2/_catalog" | jq -r '.repositories[]')

for repo in $repos; do
  # Obtenir les étiquettes pour chaque dépôt
  tags=$(curl -s "http://$REGISTRY/v2/$repo/tags/list" | jq -r '.tags[]?')
  for tag in $tags; do
    # Obtenir le résumé du manifeste pour l'étiquette
    digest=$(curl -sI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
      "http://$REGISTRY/v2/$repo/manifests/$tag" | grep -i docker-content-digest | tr -d '\r' | awk '{print $2}')
    echo "$repo:$tag $digest" >> "$OUTPUT_DIR/tags.txt"
  done
done
```

Exécutez ce script manuellement d'abord. Résultat attendu dans `snapshots/tags.txt` :

```text
alpine:3.18 sha256:abc123...
alpine:latest sha256:abc123...
myapp:1.4.2 sha256:def456...
myapp:latest sha256:def456...
```

Vous avez maintenant une base de référence. Tout changement de résumé pour la même étiquette est un signal.

### Étape 2 : Ajouter des étiquettes de métadonnées au moment de la construction

Modifiez votre Dockerfile ou votre commande de build pour inclure des étiquettes. Exemple pour une application Node.js :

```dockerfile
FROM node:20-alpine
LABEL org.opencontainers.image.source="https://github.com/yourorg/myapp" \
      org.opencontainers.image.revision="${GIT_COMMIT}" \
      org.opencontainers.image.created="${BUILD_TIMESTAMP}" \
      org.opencontainers.image.version="${APP_VERSION}"
COPY . /app
WORKDIR /app
CMD ["node", "server.js"]
```

Construisez avec des arguments de build :

```bash
docker build \
  --build-arg GIT_COMMIT=$(git rev-parse HEAD) \
  --build-arg BUILD_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
  --build-arg APP_VERSION=1.4.2 \
  -t localhost:5000/myapp:1.4.2 .
```

Inspectez les étiquettes :

```bash
docker inspect localhost:5000/myapp:1.4.2 --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}'
```

Résultat attendu : le SHA du commit Git. Ces étiquettes deviennent interrogeables dans la surveillance à l'exécution et l'analyse des incidents.

### Étape 3 : Déployer un exportateur observateur d'étiquettes

Utilisez un exportateur Prometheus simple qui exécute la logique d'instantané selon un calendrier et expose des métriques. Pour la démonstration, utilisez cron et un collecteur textfile. Si vous exécutez Prometheus, vous pouvez utiliser le collecteur textfile de Node Exporter.

Créez un script qui écrit les métriques dans `/var/lib/node_exporter/textfile_collector/tags.prom` :

```bash
#!/bin/bash
# export_tags.sh - Écrit les métriques d'étiquettes pour le collecteur textfile de node_exporter
REGISTRY="localhost:5000"
OUTPUT="/var/lib/node_exporter/textfile_collector/tags.prom"

# Métrique : nombre d'étiquettes par dépôt
echo "# HELP docker_registry_tags_total Total tags per repository" > "$OUTPUT"
echo "# TYPE docker_registry_tags_total gauge" >> "$OUTPUT"

curl -s "http://$REGISTRY/v2/_catalog" | jq -r '.repositories[]' | while read repo; do
  tag_count=$(curl -s "http://$REGISTRY/v2/$repo/tags/list" | jq '.tags | length')
  echo "docker_registry_tags_total{repository=\"$repo\"} $tag_count" >> "$OUTPUT"
done

# Métrique : horodatage de création de l'étiquette (époque Unix)
echo "# HELP docker_image_tag_created_timestamp Time when tag was created (approx)" >> "$OUTPUT"
echo "# TYPE docker_image_tag_created_timestamp gauge" >> "$OUTPUT"

# Pour chaque dépôt et étiquette, obtenir le manifeste et extraire la date de création
curl -s "http://$REGISTRY/v2/_catalog" | jq -r '.repositories[]' | while read repo; do
  curl -s "http://$REGISTRY/v2/$repo/tags/list" | jq -r '.tags[]?' | while read tag; do
    # Obtenir le manifeste, puis le résumé de configuration, puis le blob de configuration avec la date de création
    manifest=$(curl -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
      "http://$REGISTRY/v2/$repo/manifests/$tag")
    config_digest=$(echo "$manifest" | jq -r '.config.digest')
    config_blob=$(curl -s "http://$REGISTRY/v2/$repo/blobs/$config_digest")
    created=$(echo "$config_blob" | jq -r '.created')
    # Convertir en époque (nécessite la commande date prenant en charge -d)
    epoch=$(date -d "$created" +%s 2>/dev/null || echo 0)
    echo "docker_image_tag_created_timestamp{repository=\"$repo\",tag=\"$tag\"} $epoch" >> "$OUTPUT"
  done
done
```

Planifiez avec cron toutes les 5 minutes. Configurez node_exporter pour inclure le collecteur textfile.

Configuration de collecte Prometheus pour node_exporter :

```yaml
scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']
```

Vous pouvez maintenant interroger `docker_registry_tags_total` et `docker_image_tag_created_timestamp` dans Prometheus.

## Vérification et diagnostics

Après avoir configuré l'exportateur, vérifiez que les flux de données et les métriques apparaissent correctement.

### Vérifier la sortie de l'exportateur

Exécutez `export_tags.sh` manuellement et inspectez le fichier généré :

```bash
cat /var/lib/node_exporter/textfile_collector/tags.prom
```

Vous devriez voir des lignes comme :

```text
# HELP docker_registry_tags_total Total tags per repository
# TYPE docker_registry_tags_total gauge
docker_registry_tags_total{repository="alpine"} 2
docker_registry_tags_total{repository="myapp"} 2
# HELP docker_image_tag_created_timestamp Time when tag was created (approx)
# TYPE docker_image_tag_created_timestamp gauge
docker_image_tag_created_timestamp{repository="alpine",tag="3.18"} 1680000000
docker_image_tag_created_timestamp{repository="alpine",tag="latest"} 1680000000
docker_image_tag_created_timestamp{repository="myapp",tag="1.4.2"} 1690000000
docker_image_tag_created_timestamp{repository="myapp",tag="latest"} 1690000000
```

### Interroger Prometheus

Ouvrez l'interface Prometheus et exécutez des requêtes :

- `docker_registry_tags_total` affiche le nombre d'étiquettes.
- `docker_image_tag_created_timestamp{repository="myapp"}` affiche les époques de création.

Pour les alertes, vous avez besoin de règles. Créez `tag_alerts.yml` :

```yaml
groups:
  - name: docker_tag_alerts
    rules:
      - alert: TagCountIncreased
        expr: increase(docker_registry_tags_total[1h]) > 10
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Le dépôt {{ $labels.repository }} a ajouté de nombreuses étiquettes"
          description: "Le nombre d'étiquettes a augmenté de plus de 10 au cours de la dernière heure."
      - alert: StaleTag
        expr: time() - docker_image_tag_created_timestamp > 30 * 86400
        for: 1h
        labels:
          severity: info
        annotations:
          summary: "L'étiquette {{ $labels.tag }} dans {{ $labels.repository }} date de plus de 30 jours"
```

Chargez ces règles dans Prometheus et vérifiez la page Alertes.

### Simuler un changement d'étiquette

Poussez une nouvelle image vers la même étiquette :

```bash
docker build -t localhost:5000/myapp:1.4.2 . --build-arg APP_VERSION=1.4.2
docker push localhost:5000/myapp:1.4.2
```

Attendez la prochaine exécution de l'exportateur. `docker_image_tag_created_timestamp` pour `myapp:1.4.2` devrait être mis à jour. Vous pouvez ensuite configurer une alerte en cas d'écrasement d'étiquette en suivant les changements d'horodatage.

## Modes de défaillance et récupération

La surveillance elle-même peut échouer. Préparez-vous à ces scénarios.

### Erreurs du script d'exportateur

Si `curl` échoue en raison de l'indisponibilité du registre, l'exportateur peut écrire des métriques incomplètes. Ajoutez une gestion des erreurs :

```bash
if ! curl -s --fail "http://$REGISTRY/v2/_catalog" > /tmp/catalog.json; then
  echo "# docker_registry_up 0" > "$OUTPUT"
  exit 1
fi
```

Exposez une métrique `docker_registry_up` pour que les alertes puissent se déclencher lorsque le registre est hors service.

### Échecs de collecte Prometheus

Si node_exporter est arrêté, Prometheus marque la cible comme étant arrêtée. Configurez une alerte :

```yaml
- alert: NodeExporterDown
  expr: up{job="node"} == 0
  for: 5m
```

### Fatigue liée aux alertes

Trop d'alertes poussent les équipes à les ignorer. Ajustez les seuils en fonction de la base de référence. Pour les augmentations du nombre d'étiquettes, utilisez un seuil dynamique ou observez d'abord les taux normaux. Envisagez de regrouper les alertes par dépôt et d'utiliser des règles d'inhibition dans Alertmanager.

### Retour en arrière

Si un changement de surveillance pose problème, le retour en arrière est simple :

- Supprimez ou commentez le fichier de règles Prometheus et rechargez.
- Arrêtez la tâche cron et supprimez le script d'exportateur.
- Revenez aux étiquettes Dockerfile précédentes si des problèmes de build surviennent.

Les images sous-jacentes ne sont pas affectées. Vous pouvez toujours réactiver la surveillance après avoir corrigé le problème.

## Pièges courants et erreurs

La mise en œuvre de la surveillance des étiquettes d'images Docker peut mal tourner de manière subtile. Voici les pièges les plus courants, pourquoi ils surviennent et comment les éviter ou s'en remettre.

### 1. Écrasement des étiquettes mutables

**Pourquoi cela arrive :** Des étiquettes comme `latest` ou une étiquette semver sont déplacées vers un nouveau résumé sans politique. Les développeurs peuvent le faire manuellement, ou les travaux CI réutilisent la même étiquette pour chaque build.

**Comment éviter/récupérer :** Traitez les étiquettes comme immuables une fois poussées. Utilisez des étiquettes uniques par build (par exemple, le SHA Git) et effectuez les promotions via des étiquettes distinctes. Si un écrasement se produit, utilisez votre historique d'instantanés pour retrouver le résumé précédent et le re-étiqueter.

### 2. Ignorer les limites de l'API du registre

**Pourquoi cela arrive :** Interroger un registre trop agressivement peut entraîner des limites de débit, en particulier sur Docker Hub. L'exportateur s'exécute toutes les 5 minutes mais peut effectuer plusieurs appels API par dépôt, ce qui peut s'accumuler.

**Comment éviter/récupérer :** Mettez en cache les réponses lorsque c'est possible et utilisez des notifications de registre au lieu de l'interrogation si disponible. Augmentez l'intervalle d'interrogation et implémentez un backoff en cas d'erreurs.

### 3. Étiquettes de métadonnées incomplètes

**Pourquoi cela arrive :** Les étiquettes sont ajoutées au Dockerfile mais les arguments de build ne sont pas transmis, ce qui entraîne des valeurs vides. Cela rend impossible le suivi du commit source ou de l'heure de construction.

**Comment éviter/récupérer :** Imposez la présence des étiquettes dans le CI avec une étape de lint. Utilisez `docker inspect` pour vérifier les étiquettes après le build. Ajoutez une vérification dans votre pipeline qui échoue si les étiquettes requises sont manquantes.

### 4. Seuils d'alerte basés sur des hypothèses

**Pourquoi cela arrive :** Définir des règles d'alerte comme `increase(docker_registry_tags_total[1h]) > 10` sans comprendre le comportement normal. Un dépôt très actif peut légitimement ajouter plus de 10 étiquettes par heure.

**Comment éviter/récupérer :** Exécutez l'exportateur pendant une semaine et collectez des données de référence. Utilisez des métriques d'histogramme ou des seuils dynamiques basés sur la moyenne et l'écart type. Ajustez les alertes en conséquence.

### 5. Point de défaillance unique dans l'exportateur

**Pourquoi cela arrive :** Le script d'exportateur s'exécute sur un seul hôte via cron. Si cet hôte meurt, la surveillance s'arrête silencieusement.

**Comment éviter/récupérer :** Exécutez l'exportateur en tant que conteneur ou service systemd avec des contrôles de santé. Utilisez une plateforme de surveillance qui alerte lorsque l'exportateur lui-même est arrêté (par exemple, la métrique `up` de Prometheus).

## Liste de contrôle opérationnelle

Utilisez cette liste de contrôle pour les opérations quotidiennes, hebdomadaires et en cas d'incident. Attribuez un responsable unique pour chaque tâche afin de garantir le suivi.

| Tâche | Fréquence | Commande ou vérification | Responsable |
|---|---|---|---|
| Vérifier la sortie de l'exportateur | Quotidienne | `cat /var/lib/node_exporter/textfile_collector/tags.prom` | Ingénieur DevOps (ex. Alex Chen) |
| Vérifier les cibles Prometheus | Quotidienne | Interface Prometheus -> Statut -> Cibles : toutes actives | Ingénieur de garde SRE |
| Examiner les alertes actives | Quotidienne | Interface Prometheus -> Alertes, acquitter et résoudre | Ingénieur de garde SRE |
| Inspecter l'âge des étiquettes | Hebdomadaire | Requête `docker_image_tag_created_timestamp < (time() - 30*86400)` | Gestionnaire de version (ex. Maria Garcia) |
| Nettoyer les anciennes étiquettes | Hebdomadaire | Utiliser l'API du registre ou un script pour supprimer les étiquettes non marquées ou très anciennes | Ingénieur DevOps |
| Tester le routage des alertes | Mensuelle | Envoyer une alerte de test à Alertmanager et vérifier le canal de notification | Ingénieur de garde SRE |
| Sauvegarder les données du registre | Hebdomadaire | Arrêter le conteneur du registre, archiver `/var/lib/registry`, redémarrer | Ingénieur DevOps |
| Réviser la politique d'étiquetage | Trimestrielle | S'assurer que les conventions de nommage et la politique de rétention conviennent toujours | Responsable plateforme (ex. James Okafor) |

En cas d'incident (par exemple, une étiquette incorrecte poussée en production), suivez ces étapes :

- Identifiez l'étiquette et le dépôt concernés à partir de l'alerte.
- Vérifiez l'historique des résumés à partir des instantanés pour retrouver le bon résumé précédent.
- Re-étiquetez le bon résumé pour restaurer l'étiquette :

```bash
docker pull myapp@<good_digest>
docker tag <good_digest> localhost:5000/myapp:1.4.2
docker push localhost:5000/myapp:1.4.2
```

- Informez l'équipe et mettez à jour le runbook.
- Après résolution, examinez pourquoi la mauvaise étiquette a été poussée et ajoutez des mesures de prévention (par exemple, des vérifications CI).

## Conclusion

La surveillance de l'étiquetage des images Docker vous donne une visibilité sur une partie critique du cycle de vie des conteneurs. En collectant l'état du registre, en ajoutant des métadonnées de construction et en exposant des métriques Prometheus, vous pouvez alerter sur les étiquettes obsolètes, les changements inattendus et la disponibilité du registre. Les étapes pratiques de ce guide fournissent un projet pilote sûr et limité que vous pouvez exécuter localement et étendre à la production.

Prochaines étapes vérifiées :

- Configurez un registre local et exécutez le script d'instantané pour obtenir une base de référence.
- Ajoutez des étiquettes à vos Dockerfiles et poussez une image de test.
- Déployez l'exportateur textfile et les règles Prometheus.
- Simulez un écrasement d'étiquette et confirmez que les alertes se déclenchent.
- Utilisez la liste de contrôle opérationnelle pour maintenir le système.

N'oubliez pas que l'objectif n'est pas de collecter toutes les métriques possibles, mais de se concentrer sur les signaux qui préviennent les incidents liés aux étiquettes. Commencez petit, mesurez et itérez.