Unité 3.1 : Registre de conteneurs et pipelines d'images
Introduction
Vous êtes sur le point de conteneuriser le service et le frontend web de TaskBoard API et vous avez besoin d'un registre pour pousser les images avant que Managed Kubernetes puisse les récupérer. Le Registre de conteneurs IONOS vous offre un ou plusieurs registres dédiés et authentifiés qui parlent le registre Docker HTTP API V2 et acceptent tout artefact conforme à OCI. Tout ce que vous faites avec lui, la mise en service, la création de jetons, la connexion, la poussée et la collecte de garbage, est piloté par Terraform, le REST API, et la CLI Docker.
Cette unité commence au niveau du code. Vous allez mettre en service un registre en tant que code d'infrastructure, créer un jeton étendu, authentifier Docker, et construire le pipeline de construction-étiquetage-poussée sur lequel le REST du Module 3 dépend. Deux contraintes spécifiques à IONOS façonnent chaque exemple ici : l'authentification est basée uniquement sur des jetons sans contrôle d'accès basé sur des rôles et sans ACL par référentiel, et le nom d'hôte du registre n'existe pas jusqu'à ce que le registre atteigne l'état Running. Les deux vous causeront des problèmes en CI si vous les ignorez, ils sont donc intégrés aux modèles de code ci-dessous.
1. Provisionnement du registre avec Terraform
Le registre est une ressource de premier plan Terraform. Vous déclarez un nom et un emplacement, appliquez, et attendez que le provisionnement asynchrone soit terminé avant que le nom d'hôte soit alloué. Les name et location sont immuables après leur création, donc le choix de ces éléments est une décision à sens unique par registre.
Le nom du registre doit être d'au plus 63 caractères. Si vous essayez de créer un registre avec un nom qui est déjà pris dans toute la plateforme, le API renvoie HTTP 409 Conflict avec le message already exists: name is not available. Traitez le nom comme un handle global, et non comme un handle par compte, et préfixez-le avec votre projet pour éviter les collisions.
1.1 La ressource ionoscloud_container_registry
L'emplacement doit être un emplacement de Container Registry pris en charge. Container Registry est disponible en de/fra (DE/FRA), donc votre valeur location est de/fra.
terraform {
required_providers {
ionoscloud = {
source = "ionos-cloud/ionoscloud"
version = ">= 6.0.0"
}
}
}
provider "ionoscloud" {
# token read from IONOS_TOKEN env var
}
resource "ionoscloud_container_registry" "taskboard" {
name = "taskboard-prod"
location = "de/fra"
garbage_collection_schedule {
days = ["Saturday", "Sunday"]
time = "19:30:00+00:00"
}
}
output "registry_hostname" {
value = ionoscloud_container_registry.taskboard.hostname
}
La sortie hostname suit le modèle {registry-name}.cr.{location-with-dash}.ionos.com. Elle est vide jusqu'à ce que le registre atteigne Running, donc toute étape en aval qui la lit doit s'exécuter après que apply soit entièrement terminé. Le registre n'a que deux états de cycle de vie, New et Running, et seul Running est utilisable.
1.2 Provisionnement via le REST API
Si vous provisionnez en dehors de Terraform, le registre est créé avec un POST vers le point de terminaison des registres. Les mêmes propriétés garbageCollectionSchedule, location, et name s'appliquent.
curl --location \
--request POST 'https://api.ionos.com/containerregistries/registries' \
--header "Authorization: Bearer ${IONOS_TOKEN}" \
--header 'Content-Type: application/json' \
--data-raw '{
"properties": {
"garbageCollectionSchedule": {
"days": ["Saturday", "Sunday"],
"time": "19:30:00+00:00"
},
"location": "de/fra",
"name": "taskboard-prod"
}
}'
La réponse 201 ne comprend pas de nom d'hôte car l'hôte n'est pas encore alloué. Interrogez GET /containerregistries/registries/{registryId} jusqu'à ce que l'état soit Running avant de lire le nom d'hôte. Les appels de liste sont paginés avec des jetons : passez limit en tant que paramètre de requête et suivez nextPageToken pour parcourir de grandes collections.
2. Jetons et authentification
L'authentification est basée sur les jetons docker login uniquement. Il n'y a pas de RBAC, pas de dépôts par équipe, et pas d'accès anonyme ou de dépôt public. Un jeton est l'unité d'accès, et vous le définissez par registre ou par chemin de dépôt avec un ensemble d'actions autorisées.
2.1 Création d'un jeton avec Terraform
Un jeton a un nom, un état, une date d'expiration facultative, et une ou plusieurs étendues. Chaque étendue a un type de Registry ou Repository, un chemin, et une liste d'actions tirée de pull, push, et delete. Le nom du jeton doit comporter 3 à 63 caractères, commencer par a-z, se terminer par un caractère alphanumérique, et ne contenir que des caractères alphanumériques et des tirets, et il est immuable après création.
resource "ionoscloud_container_registry_token" "ci_push" {
container_registry_id = ionoscloud_container_registry.taskboard.id
name = "ci-push"
status = "enabled"
# minimum expiry is 1 hour in the future; on expiry the token is DELETED
expiry_date = "2027-01-01T00:00:00Z"
scopes {
type = "Repository"
name = "taskboard/*"
actions = ["push", "pull"]
}
}
output "ci_push_password" {
value = ionoscloud_container_registry_token.ci_push.password
sensitive = true
}
Deux comportements sont importants pour l'automatisation. Le mot de passe du jeton est affiché une seule fois, donc capturez-le à partir de l'état Terraform ou de la réponse API immédiatement et stockez-le dans votre gestionnaire de secrets. Lorsque la date d'expiration est atteinte, le jeton est supprimé, et non désactivé, donc un travail de rotation doit créer le remplacement avant que l'ancien n'expire ou que votre pipeline perde l'accès en cours de déploiement.
2.2 Création d'un jeton via le API et la connexion
Le point de terminaison du jeton est POST /containerregistries/registries/{registryId}/tokens. Les jetons sont de deux types, un jeton d'accès permanent au registre et un jeton temporaire limité par expiryDate. La date d'expiration minimale est d'une heure dans le futur.
curl --location \
--request POST "https://api.ionos.com/containerregistries/registries/${REGISTRY_ID}/tokens" \
--header "Authorization: Bearer ${IONOS_TOKEN}" \
--header 'Content-Type: application/json' \
--data-raw '{
"properties": {
"name": "ci-push",
"status": "enabled",
"scopes": [
{ "type": "Repository", "name": "taskboard/*", "actions": ["push", "pull"] }
]
}
}'
La réponse contient les informations d'identification une seule fois. Utilisez le nom du jeton comme nom d'utilisateur et le mot de passe du jeton comme mot de passe pour docker login contre le nom d'hôte du registre.
echo "${CR_TOKEN_PASSWORD}" | docker login taskboard-prod.cr.de-fra.ionos.com \
--username ci-push \
--password-stdin
Un jeton peut être dans l'un des trois états suivants : enabled, disabled, ou deleted. Désactivez un jeton pour révoquer l'accès immédiatement sans le supprimer ; supprimez-le lorsqu'il n'est plus nécessaire.
2.3 Choix de la méthode pour créer et utiliser les informations d'identification
Le tableau suivant compare les chemins d'accès pour les informations d'identification du registre afin que vous puissiez choisir celui qui convient le mieux à chaque flux de travail.
| Approche | Meilleur pour | Outil | Durée de vie des informations d'identification |
|---|---|---|---|
| Ressource de jeton Terraform | Identités de service CI provisionnées en tant que code | HCL | Jusqu'à expiry_date (supprimé à l'expiration) |
| Création de jeton API | Travaux de rotation scriptés | curl/HTTP | Jusqu'à expiry_date ou suppression manuelle |
docker login |
Push/pull de développement local | CLI Docker | Session, basée sur le jeton |
Puisque les étendues sont par registre ou par chemin de dépôt et qu'il n'y a pas de RBAC, modèlez un jeton par pipeline CI et un par développeur plutôt que de partager un seul jeton large. Définissez les jetons CI sur push plus pull sur les chemins de dépôt qu'ils possèdent, et définissez les jetons de déploiement en lecture seule sur pull uniquement.
3. Construction et publication d'images
Avec un registre en cours d'exécution et un jeton en main, le flux de travail d'image est standard Docker contre le nom d'hôte du registre. La discipline qui le rend de qualité de production est des constructions multi-étapes pour de petites images et des balises Git-SHA immuables pour des déploiements traçables.
3.1 Fichier Docker multi-étapes pour la TaskBoard API
Une construction multi-étapes compile ou installe des dépendances dans une étape de construction épaisse et copie uniquement les artefacts de runtime dans une image finale svelte. Des images plus petites se téléchargent plus rapidement sur Kubernetes et comportent moins de packages pour que le scanner de vulnérabilités les signale.
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install --no-cache-dir -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /install /usr/local
COPY . .
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "taskboard.api:app"]
3.2 Étiquetage avec le SHA Git et publication
Étiquetez chaque construction avec le SHA Git immuable afin qu'une image déployée corresponde à un commit exact. Une étiquette latest est suffisante comme pointeur de commodité, mais ne déployez jamais à partir de celle-ci ; déployez à partir du SHA.
REGISTRY=taskboard-prod.cr.de-fra.ionos.com
GIT_SHA=$(git rev-parse --short HEAD)
docker build -t "${REGISTRY}/taskboard/api:${GIT_SHA}" \
-t "${REGISTRY}/taskboard/api:latest" .
docker push "${REGISTRY}/taskboard/api:${GIT_SHA}"
docker push "${REGISTRY}/taskboard/api:latest"
Le modèle de pipeline est la construction, l'étiquetage avec le SHA Git, la publication vers le registre, puis la référence de l'étiquette SHA dans le manifeste Kubernetes (couvert dans l'unité 3.2). Notez qu'un jeton accordant push nécessite également pull : la publication d'une image nécessite la lecture des couches existantes, donc une portée de publication uniquement est insuffisante et un jeton push doit également comporter pull.
4. Collecte de déchets et analyse de vulnérabilités
Deux fonctionnalités du registre s'exécutent sur le serveur et valent la peine d'être configurées tôt. La collecte de déchets récupère le stockage des couches non référencées, et l'analyse de vulnérabilités met en évidence les CVE dans vos artefacts poussés.
4.1 Collecte de déchets
La collecte de déchets libère le stockage occupé par les données de couche qui ne sont plus référencées par aucun tag. Elle est désactivée par défaut car la bonne planification dépend de votre rythme de poussée et de suppression, vous devez donc la configurer explicitement. Vous l'avez déjà configurée dans la ressource Terraform à la section 1.1 via le bloc garbage_collection_schedule avec days et un time.
garbage_collection_schedule {
days = ["Saturday", "Sunday"]
time = "19:30:00+00:00"
}
Pendant la collecte de garbage, le registre entre dans un état en lecture seule : les pulls continuent à fonctionner, mais les pushes sont bloqués jusqu'à la fin de l'exécution, donc planifiez-la pour une fenêtre de faible trafic pour éviter d'interrompre les pushes et les déploiements de CI/CD. Notez que le registre Docker V2 API ne peut pas supprimer un référentiel entier ; pour supprimer un référentiel, vous appelez directement le Registre de conteneurs IONOS API avec DELETE /containerregistries/registries/{id}/repositories/{url-encoded-name}, et le nom du référentiel dans l'URL doit être encodé en URL, par exemple taskboard/api devient taskboard%2Fapi, ou l'appel retourne 404.
4.2 Analyse de vulnérabilités
L'analyse de vulnérabilités analyse les artefacts poussés pour les CVE connus. Il s'agit d'une fonctionnalité complémentaire, et une fois activée sur un registre, elle ne peut pas être désactivée, donc activez-la délibérément. Les résultats de l'analyse sont signalés par artefact et par référentiel, y compris lesquels ont des correctifs connus, vous pouvez donc contrôler la promotion d'une image sur ses résultats d'analyse dans la CI.
Traitez l'analyse comme une porte de qualité : après l'étape de push, interrogez les résultats de l'analyse pour l'artefact étiqueté SHA et faites échouer le pipeline si les résultats dépassent votre seuil avant que l'étape de déploiement ne se poursuive.
5. Intégration CI
Dans CI, vous n'exécutez pas un docker login interactif. Vous stockez les informations d'identification du jeton en tant que secrets de pipeline et les fournissez à docker login --password-stdin. Le nom d'hôte du registre, le nom du jeton et le mot de passe du jeton sont les trois valeurs dont votre pipeline a besoin.
5.1 GitHub Actions
Stockez CR_HOSTNAME, CR_USERNAME, et CR_PASSWORD en tant que secrets de référentiel ou d'environnement, puis connectez-vous et poussez dans le workflow.
name: build-and-push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to IONOS Container Registry
run: |
echo "${{ secrets.CR_PASSWORD }}" | docker login "${{ secrets.CR_HOSTNAME }}" \
--username "${{ secrets.CR_USERNAME }}" --password-stdin
- name: Build and push
run: |
SHA=$(git rev-parse --short HEAD)
IMG="${{ secrets.CR_HOSTNAME }}/taskboard/api:${SHA}"
docker build -t "$IMG" .
docker push "$IMG"
5.2 GitLab CI
L'équivalent du pipeline GitLab utilise des variables CI/CD pour les trois mêmes valeurs et le service Docker-dans-Docker pour la construction.
build-and-push:
image: docker:24
services:
- docker:24-dind
variables:
DOCKER_HOST: tcp://docker:2376
script:
- echo "$CR_PASSWORD" | docker login "$CR_HOSTNAME" --username "$CR_USERNAME" --password-stdin
- SHA=$(echo "$CI_COMMIT_SHA" | cut -c1-7)
- docker build -t "$CR_HOSTNAME/taskboard/api:$SHA" .
- docker push "$CR_HOSTNAME/taskboard/api:$SHA"
only:
- main
Puisque les jetons sont le seul modèle d'informations d'identification, faites pivoter le jeton CI selon un calendrier et mettez à jour le secret en parallèle. Chaque client est limité à 60 requêtes par seconde et 100 connexions simultanées, ce qui est généreux pour un seul pipeline, mais il est utile de le savoir si vous élargissez des constructions de matrice parallèles qui poussent toutes en même temps.
API Référence Quick Card
Points de terminaison clés API pour le registre de conteneurs :
| Méthode | Point de terminaison | Description |
|---|---|---|
GET |
/containerregistries/registries |
Liste des registres (paginés via limit + nextPageToken) |
POST |
/containerregistries/registries |
Créer un registre |
GET |
/containerregistries/registries/{registryId} |
Obtenir les détails du registre (et l'état/le nom d'hôte) |
POST |
/containerregistries/registries/{registryId}/tokens |
Créer un jeton d'accès |
DELETE |
/containerregistries/registries/{id}/repositories/{url-encoded-name} |
Supprimer un référentiel entier (V2 API ne peut pas) |
URL de base : https://api.ionos.com
Authentification : Authorization: Bearer <token>
Laboratoire de code
Objectif : Mettre en place un registre de conteneurs, créer un jeton étendu, construire l'image TaskBoard API, et la pousser avec un tag Git-SHA.
Prérequis :
- Compte IONOS Cloud avec le jeton API exporté en tant que
IONOS_TOKEN - Terraform et Docker installés localement
- Un
Dockerfilepour le TaskBoard API (voir Section 3.1)
Étape 1 : Mettre en place le registre et le jeton
export IONOS_TOKEN="your-api-token"
terraform init
terraform apply -auto-approve
Sortie attendue :
ionoscloud_container_registry.taskboard: Creation complete
Outputs:
registry_hostname = "taskboard-prod.cr.de-fra.ionos.com"
Étape 2 : Lire le nom d'hôte du registre et le mot de passe du jeton
REGISTRY=$(terraform output -raw registry_hostname)
CR_PASSWORD=$(terraform output -raw ci_push_password)
echo "$REGISTRY"
Sortie attendue :
taskboard-prod.cr.de-fra.ionos.com
Étape 3 : Se connecter au registre
echo "$CR_PASSWORD" | docker login "$REGISTRY" --username ci-push --password-stdin
Sortie attendue :
Login Succeeded
Étape 4 : Construire l'image
docker build -t "${REGISTRY}/taskboard/api:$(git rev-parse --short HEAD)" .
Sortie attendue :
=> exporting to image
=> => naming to taskboard-prod.cr.de-fra.ionos.com/taskboard/api:a1b2c3d
Étape 5 : Pousser l'image
docker push "${REGISTRY}/taskboard/api:$(git rev-parse --short HEAD)"
Sortie attendue :
a1b2c3d: digest: sha256:... size: 1786
Étape 6 : Vérifier via le API
REGISTRY_ID=$(terraform output -raw registry_id)
curl -s --request GET \
"https://api.ionos.com/containerregistries/registries/${REGISTRY_ID}/repositories" \
--header "Authorization: Bearer ${IONOS_TOKEN}" | jq '.items[].id'
Sortie attendue :
"taskboard/api"
Liste de validation :
- [ ] Le registre a atteint
Runninget a exposé un nom d'hôte - [ ]
docker logina retournéLogin Succeeded - [ ] L'image a été poussée avec un tag Git-SHA et est visible via les référentiels API
Nettoyage :
terraform destroy -auto-approve
Erreurs courantes
Erreurs de développement à éviter avec Container Registry :
-
Lire le nom d'hôte avant que le registre ne soit en cours d'exécution
- Problème : La pipeline
docker loginéchoue avec une erreur de résolution DNS immédiatement aprèsterraform apply. - Pourquoi cela se produit-il : Le nom d'hôte est vide jusqu'à ce que le registre quitte l'état
Newet atteigneRunning. La lecture trop tôt donne une valeur vide ou non résolvable. - Correction : Contrôler l'étape de connexion à l'état
Running. Interroger le registre jusqu'à ce qu'il soit prêt avant de se connecter :
until [ "$(curl -s -H "Authorization: Bearer $IONOS_TOKEN" \ https://api.ionos.com/containerregistries/registries/$REGISTRY_ID \ | jq -r '.metadata.state')" = "Running" ]; do sleep 5; done - Problème : La pipeline
-
Envoyer un jeton sans autorisation de téléchargement
- Problème :
docker pushéchoue avec une erreur d'autorisation même si le jeton a l'actionpush. - Pourquoi cela se produit-il : L'envoi lit les couches existantes pour les dédoublonner, donc l'envoi nécessite un téléchargement. Une portée
pushuniquement est rejetée. - Correction : Toujours inclure
pullaux côtés depushdans la portée :
actions = ["push", "pull"] - Problème :
-
Laisser un jeton expirer au milieu d'un déploiement
- Problème : Un déploiement CI qui a fonctionné hier échoue maintenant l'authentification, et le jeton a simplement disparu.
- Pourquoi cela se produit-il : Lorsque la date d'expiration d'un jeton est atteinte, il est supprimé, et non désactivé, il n'y a donc pas de période de grâce et pas de chemin de réactivation.
- Correction : Faire tourner à l'avance de l'expiration. Créer le jeton de remplacement, mettre à jour le secret CI, puis laisser l'ancien expirer. Ne jamais définir une expiration plus courte que votre cadence de version, et n'oubliez pas que l'expiration minimale est d'une heure dans le futur.
Résumé
Vous pouvez maintenant provisionner un registre de conteneurs IONOS en tant que code infrastructurel, créer des jetons d'accès à portée définie, authentifier le Docker CLI et les pipelines CI contre celui-ci, et pousser des images de production à plusieurs étapes étiquetées par le SHA Git. Vous savez également comment configurer la collecte de garbage pour récupérer le stockage et activer l'analyse de vulnérabilités en tant que porte de promotion. Ce registre est la source de vérité pour les images que l'Unité 3.2 déployera sur le Managed Kubernetes.
Le modèle d'accès basé uniquement sur les jetons du registre, sans RBAC et sans listes de contrôle d'accès par répository, détermine la façon dont vous organisez les informations d'identification : un jeton à portée définie par pipeline et par développeur, tourné avant l'expiration car les jetons expirés sont supprimés purement et simplement. Gardez à l'esprit la contrainte d'hôte après l'exécution pour chaque workflow automatisé, et le pipeline de construction-étiquetage-poussée que vous avez construit ici devient la première étape du flux complet CI/CD dans l'Unité 3.3.
Points clés :
- Provisionner les registres avec
ionoscloud_container_registry;nameetlocationsont immuables et le nom est unique au niveau mondial (une copie renvoie HTTP 409) - Le nom d'hôte suit
{name}.cr.{location-with-dash}.ionos.comet est vide jusqu'à ce que le registre soitRunning - L'authentification est basée uniquement sur les jetons
docker login, avec des portées deRegistryouRepositoryet des actionspull,push,delete;pushnécessitepull - Les mots de passe des jetons sont affichés une seule fois et les jetons expirés sont supprimés, et non désactivés, donc capturez-les et faites-les tourner de manière proactive
- La collecte de garbage est désactivée par défaut et configurée sur un calendrier ; l'analyse de vulnérabilités est une fonctionnalité supplémentaire qui ne peut pas être désactivée une fois activée
Terminologie importante :
- Jeton d'accès au registre : L'information d'identification pour
docker login, à portée définie par registre ou par chemin de répository ; existe en types permanents et temporaires (délimités par expiration) - Portée : Une concession de permission de jeton, un type (
RegistryouRepository), un chemin, et un ensemble d'actions à partir depull,push,delete - Collecte de garbage : La récupération côté serveur des couches d'images non référencées, désactivée par défaut et exécutée sur un calendrier configuré
- Analyse de vulnérabilités : Une fonctionnalité supplémentaire qui analyse les artefacts poussés pour les CVE par artefact et par répository ; irréversible une fois activée
- Étiquette Git-SHA : Une étiquette d'image immuable définie sur le SHA de validation, de sorte qu'une image déployée correspond à un engagement exact
Prochaines étapes
Continuer l'apprentissage : Unité 3.2 : Kubernetes Déploiement et opérations
Sujets connexes :