Skip to content
Retour au blog

Automatiser la création d'images Docker avec GitHub Actions

Découvrez comment mettre en place un pipeline CI/CD complet avec GitHub Actions pour builder et publier automatiquement vos images Docker sur le GitHub Container Registry.
Désiré KOUASSI

Désiré KOUASSI

@kdesire09

Automatiser la création d'images Docker avec GitHub Actions

Automatiser la création d'images Docker avec GitHub Actions

Dans le monde du développement moderne, l'automatisation est clé. L'une des tâches les plus courantes est la création et la publication d'images Docker. Au lieu de le faire manuellement à chaque nouvelle version, nous pouvons utiliser GitHub Actions pour l'automatiser.

Dans cet article, je vais vous expliquer pas à pas comment fonctionne le workflow de déploiement (build-and-push.yml) que j'utilise pour ce projet.

1. Déclencheurs (Triggers) : Quand le workflow s'exécute-t-il ?

Tout workflow commence par définir les conditions de son exécution.

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      tag:
        description: 'Version tag (ex: v1.0.0)'
        required: true
        type: string

Ici, le workflow se déclenche de deux manières :

  • Automatiquement : À chaque fois qu'un tag Git commençant par v (par exemple v1.0.0) est poussé sur le dépôt.
  • Manuellement : Grâce à workflow_dispatch, on peut lancer le workflow depuis l'interface GitHub en spécifiant un tag manuellement.

2. Variables d'environnement

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE22: true

Nous définissons quelques variables pour éviter de répéter les mêmes informations :

  • REGISTRY : Nous utilisons le GitHub Container Registry (ghcr.io).
  • IMAGE_NAME : Le nom de l'image correspondra automatiquement au nom de notre dépôt GitHub.

3. Configuration du Job et Permissions

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

Le job s'exécute sur un environnement Ubuntu fourni par GitHub. Il est crucial de configurer les permissions correctement. Le workflow a besoin d'un accès en lecture au code source (contents: read) et d'un accès en écriture pour publier l'image Docker (packages: write).

4. Les étapes du Workflow (Steps)

Voici le cœur du processus, divisé en plusieurs actions.

Étape 1 : Récupération du code

- name: Checkout repository
  uses: actions/checkout@v6

On commence par télécharger le code source du dépôt pour que GitHub Actions puisse travailler dessus.

Étape 2 : Préparation de Docker Buildx

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v4

Buildx est un plugin Docker indispensable qui offre des fonctionnalités avancées, notamment la mise en cache, ce qui accélère considérablement les builds ultérieurs.

Étape 3 : Authentification

- name: Log in to GitHub Container Registry
  uses: docker/login-action@v4
  with:
    registry: ${{ env.REGISTRY }}
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

Avant de publier, le workflow doit se connecter au registre. L'avantage d'utiliser GitHub Actions est que le GITHUB_TOKEN est généré automatiquement ; pas besoin de créer un secret manuellement.

Étape 4 : Gestion des tags et métadonnées

- name: Extract metadata for Docker
  id: meta
  uses: docker/metadata-action@v6
  with:
    images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
    tags: |
      type=ref,event=tag
      type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' && inputs.tag != '' }}
      type=raw,value=latest,enable=true

Cette action génère intelligemment les tags pour notre image :

  • Le tag Git déclencheur (v1.0.0).
  • Le tag fourni manuellement (en cas d'exécution via workflow_dispatch).
  • Un tag latest appliqué automatiquement pour pointer vers la version la plus récente.

Étape 5 : Build et Push

- name: Build and push Docker image
  uses: docker/build-push-action@v7
  with:
    context: .
    push: true
    tags: ${{ steps.meta.outputs.tags }}
    labels: ${{ steps.meta.outputs.labels }}
    cache-from: type=gha
    cache-to: type=gha,mode=max

C'est ici que la magie opère. Cette action construit l'image en utilisant le Dockerfile situé à la racine (context: .), lui applique les tags générés précédemment, puis la pousse sur le registre.

Voici un exemple de Dockerfile multi-stage adapté à une application Nuxt utilisant pnpm :

ARG NODE_VERSION=24.18.0-alpine

FROM node:${NODE_VERSION} AS build

WORKDIR /app
COPY pnpm-lock.yaml package.json pnpm-workspace.yaml ./
# COPY patches ./patches

# Install pnpm directly and install dependencies
RUN npm install -g pnpm@10.34.5 && \
    pnpm install --frozen-lockfile

COPY . .
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN pnpm build

FROM node:${NODE_VERSION} AS final
WORKDIR /app
COPY --from=build /app/.output .output
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]

Note de performance : Les paramètres cache-from et cache-to utilisant type=gha (GitHub Actions cache) permettent de sauvegarder les couches Docker entre chaque exécution, réduisant drastiquement le temps de build.

5. Lier ce workflow à Release-It

Pour pousser ce niveau d'automatisation encore plus loin, j'utilise l'outil release-it pour générer les tags automatiquement au lieu de le faire avec des commandes Git manuelles.

Dans mon fichier package.json, j'ai un script dédié :

{
  "scripts": {
    "release": "dotenv release-it"
  }
}

Et une configuration .release-it.json :

{
  "git": {
    "requireCleanWorkingDir": false,
    "tagName": "v${version}",
    "commitMessage": "chore: release v${version}"
  },
  "github": {
    "release": true
  },
  "npm": {
    "publish": false
  }
}

En tapant simplement pnpm run release dans mon terminal, l'outil me guide pour :

  1. Choisir le type de version (patch, minor, major).
  2. Mettre à jour la version dans package.json.
  3. Créer le commit et le tag Git (v1.1.13 par exemple).
  4. Créer une Release officielle sur GitHub.

Dès que release-it pousse le nouveau tag commençant par v, notre workflow GitHub Actions se déclenche automatiquement !

6. Utiliser l'image Docker générée (Dépôts publics et privés)

Une fois le workflow terminé avec succès, votre image Docker est disponible sur le GitHub Container Registry (GHCR).

Si votre dépôt est public

Tout le monde peut télécharger et exécuter votre image sans aucune authentification. Il suffit de lancer :

docker pull ghcr.io/votre-nom-utilisateur/votre-depot:latest
docker run -p 3000:3000 ghcr.io/votre-nom-utilisateur/votre-depot:latest

Si votre dépôt est privé

Pour les dépôts privés, l'image n'est pas accessible publiquement par défaut. Vous devez d'abord vous authentifier auprès de GHCR avant de pouvoir faire un docker pull.

  1. Créer un Personal Access Token (PAT) sur GitHub (dans Settings > Developer settings > Personal access tokens). Ce token doit avoir la permission read:packages.
  2. Se connecter à GHCR depuis votre serveur ou machine locale :
    echo $CR_PAT | docker login ghcr.io -u votre-nom-utilisateur --password-stdin
    
    (Ici, $CR_PAT contient le token que vous avez généré).
  3. Télécharger et lancer l'image comme d'habitude :
    docker pull ghcr.io/votre-nom-utilisateur/votre-depot:latest
    

Astuce : Vous pouvez aussi rendre l'image publique indépendamment de la confidentialité du code source si vous le souhaitez, en modifiant les paramètres de visibilité du package dans l'onglet "Packages" de votre compte GitHub.

Note sur le tag latest : Bien que les exemples ci-dessus utilisent :latest par commodité, vous n'êtes pas obligés de l'utiliser. En environnement de production, il est vivement recommandé de cibler une version spécifique générée par votre workflow (par exemple :v1.1.13) pour figer la version et éviter les mauvaises surprises lors des déploiements.

Conclusion

Avec ce fichier de configuration, publier une nouvelle version de votre application devient aussi simple que de créer un tag Git. L'intégration de la mise en cache et la gestion automatique des tags font de ce workflow une solution robuste et prête pour la production.