Cloud

GitHub Actions + Azure Container Instances : un lab CI/CD de 40 minutes

GitHub Actions + Azure Container Instances : un lab CI/CD de 40 minutes

Un lab clé en main de 40 minutes pour démystifier GitHub Actions et Azure Container Instances.

Introduction

J’ai préparé ce lab pour un Knowledge Sharing interne chez Devoteam. L’objectif : montrer concrètement comment fonctionne le déploiement automatisé avec des containers, de A à Z, en partant de zéro avec un site imaginaire simpliste. Pas de théorie interminable, juste du concret.

On crée un site, on le containerise, on pousse sur GitHub, et ça se déploie tout seul sur Azure. Le tout en 40 minutes. Je partage ici le déroulé complet pour ceux qui voudraient le reproduire ou l’adapter. ⬇️

Si vous avez envie de voir l’ensemble du code de test immédiatement, je vous invite à consulter le repository GitHub qui est disponible à l’adresse suivante.

Pourquoi ce lab ?

Le constat

Beaucoup de développeurs et administrateurs système n’ont jamais touché aux containers. Ils en entendent parler, voient passer des mots comme Docker, Kubernetes, CI/CD, mais n’ont jamais franchi le pas. La raison ? Une impression de complexité et un manque de cas d’usage concret pour démarrer.

L’objectif de la démo

Au travers un exemple ultra basique, nous allons dérouler un exemple concret et visuel que vous pourrez reproduire de votre côté.

Architecture de test que nous allons construire

À la fin du lab, l’idée c’est de pouvoir faire les choses suivantes :

  • Créé un site web simple (moi je pars sur quelque-chose de visuel et basique mais votre site pourrait tout à fait être composé de centaines de pages)
  • Containerisé l’application avec Docker
  • Mis en place un pipeline CI/CD avec GitHub Actions
  • Déployé automatiquement sur Azure Container Instances

Les 2 briques Azures que nous allons utiliser :

  • ACR (Azure Container Registry) : ton Docker Hub privé dans Azure, il stocke les images de nos containers.
  • ACI (Azure Container Instances) : le serveur qui exécute ton container

Les prérequis

Avant de commencer, assurez-vous d’avoir :

  • Docker Desktop installé et fonctionnel
  • Un compte GitHub
  • Un abonnement Azure (un abonnement d’essai suffit)
  • Azure CLI

Étape 1 : Créer un site web simple

On part sur une structure classique que tout le monde connaît : HTML, CSS et JavaScript séparés. Pas de framework, pas de complexité. L’objectif est que les participants se concentrent sur le déploiement, pas sur le code. Tout en ayant quelque-chose de visuel à observer.

index.html

<!DOCTYPE html>
<html>
<head>
  <title>Demo GitHub Actions - Devoteam</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <img src="https://upload.wikimedia.org/wikipedia/commons/7/79/Dev_logo_rgb.png" alt="Devoteam" class="logo">
    <h1>Hello Devoteam!</h1>
    <p>Déploiement automatique via GitHub Actions</p>
    <div class="version">Version: 1.0.0</div>
    <div class="tech-stack">Node.js + Docker + Azure Container Instances</div>
  </div>
</body>
</html>

style.css

* { 
  box-sizing: border-box; 
}

body { 
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; 
  display: flex; 
  justify-content: center; 
  align-items: center; 
  min-height: 100vh; 
  margin: 0;
  background: linear-gradient(135deg, #f8485e 0%, #3c3c3a 100%);
  color: white;
}

.container { 
  text-align: center;
  padding: 2rem;
}

.logo {
  width: 200px;
  margin-bottom: 2rem;
}

h1 { 
  font-size: 2.5rem; 
  margin-bottom: 0.5rem;
  font-weight: 600;
}

p { 
  font-size: 1.2rem; 
  opacity: 0.9;
  color: #fca2ae;
}

.version { 
  margin-top: 2rem; 
  padding: 1rem 2rem; 
  background: rgba(255,255,255,0.15); 
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.2);
}

.tech-stack {
  margin-top: 1.5rem;
  font-size: 0.9rem;
  color: #d7ebe7;
}

server.js

Le fichier JavaScript qui sert nos fichiers statiques :

const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
  // Déterminer le fichier à servir
  let filePath = req.url === '/' ? 'index.html' : req.url.substring(1);
  
  // Déterminer le type de contenu
  const ext = path.extname(filePath);
  const contentTypes = {
    '.html': 'text/html',
    '.css': 'text/css',
    '.js': 'application/javascript'
  };
  const contentType = contentTypes[ext] || 'text/plain';

  // Lire et servir le fichier
  fs.readFile(filePath, (err, content) => {
    if (err) {
      res.writeHead(404);
      res.end('File not found');
    } else {
      res.writeHead(200, { 'Content-Type': contentType + '; charset=utf-8' });
      res.end(content);
    }
  });
});

const port = process.env.PORT || 3000;
server.listen(port, () => {
  console.log('Server running on port ' + port);
});

package.json

{
  "name": "devo-tribe-azure-ks-dec-2025",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  }
}

Evidemment, n’hésitez pas à renommer les fichiers selon vos envies ou à changer le code HTML. J’ai séparé l’ensemble du code en plusieurs fichiers distincts afin de retrouver la logique standard du développement web à savoir un fichier CSS, HTML et JavaScript.

A ce moment-là vous pouvez déjà vérifier dans vos navigateur le look de notre page de test – mais nous allons plus loin afin de pouvoir mettre note site en container. ⬇️

Étape 2 : Containeriser avec Docker

Nous allons ajouter un nouveau fichier qui va nous permettre de créer un container. Pour que ça marche, vous devez avoir installé et démarré Docker Desktop sur votre ordinateur.

Le Dockerfile expliqué

C’est ce fichier qui va nous permettre de créer un container de notre site.

FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --production
COPY server.js ./
COPY index.html ./
COPY style.css ./
EXPOSE 3000
CMD ["npm", "start"]

Ligne par ligne, voici ce que cela dit :

  1. FROM node:20-alpine : Part d’une image Linux légère avec Node.js préinstallé
  2. WORKDIR /app : Crée et se place dans le dossier /app
  3. COPY package.json : Copie la config des dépendances
  4. RUN npm install : Installe les dépendances
  5. COPY ... : Copie nos fichiers
  6. EXPOSE 3000 : Documente le port utilisé (sur lequel notre site sera visible)
  7. CMD : Commande lancée au démarrage

Build et test local

Exécutez les commandes suivantes :

docker build -t devo-tribe-azure-ks-dec-2025 .
docker run -d -p 3000:3000 --name devo-tribe-azure-ks-dec-2025  devo-tribe-azure-ks-dec-2025

Vous devriez avoir le rendu suivant dans votre terminal :

Création d’une image de Container

Et dans Docker Desktop, vous verrez que vous avez 1 image et 1 container en cours d’exécution basé sur cette image.

Création d’un container (en cours d’exécution)

Par défaut, notre site sera accessible sur le port 3000. Donc ouvrez http://localhost:3000 pour votre vote site et vérifiez que ça fonctionne bien.

Aperçu de notre site de test, localement sur notre post de travail

Point important : je n’ai même pas Node.js installé sur ma machine. Tout est dans le container. C’est justement ça tout l’intérêt de Docker dans notre cas d’usage. 👍

Voici ce que j’ai dans mon VS Code :

Vision dans mon Visual Studio Code

A ce niveau, vous pouvez donc modifier votre site internet, ajouter des pages, transformer le code, créer une nouvelle version et valider localement sur votre ordinateur que le rendu est conforme à vos attentes. Quand vous êtes prêt, n’oubliez pas commit et push votre code au sein de votre repos GitHub (nous en aurons besoin pour utiliser GitHub Actions).

Étape 3 : Préparer Azure

Créer les ressources

Connectez-vous en Azure CLI à votre Tenant et souscription. Commençons par créer une Resource Group de test et notre objet Azure Container Registry.

$RG_NAME="RG-Demo-CICD"
$ACR_NAME="acrdevoteamdemo"
az acr create --resource-group $RG_NAME --name $ACR_NAME --sku Basic

Note : le nom de l’ACR doit être unique et en minuscules.

Créer le Service Principal

Le Service Principal permet à GitHub de s’authentifier à Azure (nous en aurons besoin afin de permettre à GitHub Actions de gérer notre code et mettre à jour notre code tout seul) :

$RG_NAME="RG-Demo-CICD"
$SUBSCRIPTION_ID="votre-subscription-id"
az ad sp create-for-rbac --name "sp-github-actions-demo" --role contributor --scopes /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME

Cette commande retourne un JSON avec 4 valeurs importantes :

  • appId : AZURE_CLIENT_ID
  • password : AZURE_CLIENT_SECRET
  • tenant : AZURE_TENANT_ID
  • Et votre AZURE_SUBSCRIPTION_ID

Gardez les de côté car nous allons créer des GitHub Secrets pour chacune de ces infos. Si vous le souhaitez, vous pouvez également aller vérifier dans Entra ID et vous verrez que vous disposez d’une nouvelle App Registration correspondant au nom de notre Service Principal : sp-github-actions-demo.

Configurer les secrets GitHub

Création des secrets dans GitHub

Dans votre repo GitHub : Settings → Secrets and variables → Actions → New repository secret

Créez ces 4 secrets :

  • AZURE_CLIENT_ID
  • AZURE_CLIENT_SECRET
  • AZURE_TENANT_ID
  • AZURE_SUBSCRIPTION_ID
C’est un passage important c’est ce qui va nous éviter de mettre nos identifiants / mots de passe et autres secrets dans notre code en clair. Cela serait d’autant plus problématique que nous avons créé un repository GitHub public. 😉

Étape 4 : Le workflow GitHub Actions

Toujours dans votre dossier de travail, nous allons générer un dossier ainsi qu’un fichier avec l’extension YML à un emplacement bien particulier. Ne vous trompez pas car c’est ce qui permet à GitHub de savoir qu’il va devoir réaliser des tâches seul (ce que l’on appelle un workflow).

Dans notre scénario de test, je vous rappelle que l’objectif c’est que dès que nous réalisons un commit + push de notre code et bien cela soit mis à jour tout seul dans Azure ! ♾️

Créez le fichier .github/workflows/deploy.yml :

name: Build and Deploy to Azure

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Azure login
        uses: azure/login@v2
        with:
          creds: |
            {
              "clientId": "${{ secrets.AZURE_CLIENT_ID }}",
              "clientSecret": "${{ secrets.AZURE_CLIENT_SECRET }}",
              "subscriptionId": "${{ secrets.AZURE_SUBSCRIPTION_ID }}",
              "tenantId": "${{ secrets.AZURE_TENANT_ID }}"
            }
      
      - name: Login to Azure Container Registry
        run: |
          az acr login --name acrdevoteamdemo
      
      - name: Build and push image to ACR
        run: |
          az acr build \
            --registry acrdevoteamdemo \
            --image devo-tribe-azure-ks-dec-2025:${{ github.sha }} \
            --image devo-tribe-azure-ks-dec-2025:latest \
            --file Dockerfile \
            .
      
      - name: Deploy to Azure Container Instances
        run: |
          az container create \
            --resource-group RG-Demo-CICD \
            --name devo-tribe-azure-ks-dec-2025 \
            --image acrdevoteamdemo.azurecr.io/devo-tribe-azure-ks-dec-2025:latest \
            --registry-login-server acrdevoteamdemo.azurecr.io \
            --registry-username ${{ secrets.AZURE_CLIENT_ID }} \
            --registry-password ${{ secrets.AZURE_CLIENT_SECRET }} \
            --dns-name-label devo-tribe-azure-ks-dec-2025 \
            --ports 80 \
            --os-type Linux \
            --cpu 1 \
            --memory 1 \
            --environment-variables PORT=80

Ce que fait ce workflow ↙️

➡️ Le bloc on définit les déclencheurs : le workflow s’exécute à chaque push sur la branche main, ou manuellement via l’interface GitHub (c’est le workflow_dispatch).

➡️ Les variables d’environnement (env) centralisent la configuration : nom du registry, de l’image, du resource group, etc. Ça évite de répéter ces valeurs partout et facilite l’adaptation à d’autres projets.

➡️ Le job build-and-deploy tourne sur une machine Ubuntu hébergée par GitHub. Les steps s’enchaînent :

  1. Checkout : récupère le code du repo
  2. Azure login : s’authentifie à Azure avec le Service Principal (les credentials sont stockés dans les secrets GitHub)
  3. Login ACR : se connecte au Container Registry pour pouvoir y pousser notre image
  4. Build and push : construit l’image Docker directement dans Azure (via az acr build) et la stocke dans l’ACR
  5. Deploy to ACI : crée ou met à jour le container dans Azure Container Instances avec l’image qu’on vient de builder

Le container est configuré avec 1 CPU, 1 Go de RAM, et expose le port 80. La variable d’environnement PORT=80 est passée au container pour que notre serveur Node.js écoute sur le bon port.

Tout est prêt – il ne nous reste plus qu’à tester ! 🤯

Étape 5 : La magie du déploiement automatique

Premier déploiement

Une fois que vous êtes prêt, il ne vous reste plus qu’à commit & push votre code sur GitHub. Si votre workflow est bon, cela aura pour effet d’exécuter notre workflow GitHub Actions qui doit créer notre Azure Container Instance et publier notre code. Notre site devrait alors être accessible via une URL générée par Azure. 😉

Que vous fassiez votre push & commit via VS Code ou en CLI, vous pourrait observer l’effet dans GitHub Actions :

Exécution de notre workflow GitHub Actions

Notre workflow s’est correctement exécuté. Aucune erreur. ✅

Nous pouvons maintenant aller dans notre Resource Group de test et voir si notre ACI a été créé :

Création d’un nouvel ACI – Azure Container Instance – pour héberger notre site de test

Et retrouver l’URL d’accès : devo-tribe-azure-ks-dec-2025.francecentral.azurecontainer.io dans mon cas.

Notre ACI a bien été créé, on retrouve son URL d’accès publique

Attention lors du test, notre site écoute sur le port 80 et les nouveaux navigateurs d’aujourd’hui ont tendance à forcer le https sur le port 443/TCP. Donc forcez bien http sans le S dans l’URL. 😉

Bravo, notre site est fonctionnel ! ✅

Mise à jour et publication automatique

Maintenant ce qui est intéressant : imaginez que vous avez des mises à jour à effectuer sur votre site. Dans mon exemple, je vais juste changer dans index.html le Version 1.0.0 en 2.1.0 par exemple (ou même un couleur dans le CSS).

Il vous suffit de faire vos modifications et quand vous avez terminé, sauvegardez et faîtes un commit + push de votre code sur GitHub. Cela aura pour effet automatique de déclencher notre workflow et mettre à jour notre site. 😎

git add .
git commit -m "Update to version 2.0.0"
git push

Rafraîchissez la page Azure après 2 minutes : la version a changé automatiquement. Pas de FTP, pas de copier-coller, pas d’intervention manuelle. Votre site est à jour. 👍

Mise à jour de notre site automatique

Nous pouvons voir que notre site a bien été mis à jour !

Avec le cache du navigateur et d’Azure (et si vous n’êtes pas patient comme moi), vous pouvez parfois avoir besoin de redémarrer le container pour que les mises à jour soient visibles immédiatement : az container restart –name devo-tribe-azure-ks-dec-2025 –resource-group RG-Demo-CICD

Conclusion

Ce que nous avons compris :

  • Les containers isolent l’application : « ça marche sur ma machine » devient « ça marche partout »
  • Le CI/CD automatise le déploiement : push = déploiement, pas besoin d’intervention
  • C’est reproductible : le workflow est versionné dans Git, tout le monde peut le relancer
  • C’est accessible : pas besoin d’être expert DevOps pour commencer

Pour aller plus loin

Ce lab utilise Azure Container Instances (ACI) qui est parfait pour une démo ou du dev. Pour la production, explorez :

  • Azure Container Apps : scaling automatique, plus de résilience
  • Azure Kubernetes Service (AKS) : pour les architectures microservices complexes
  • Azure App Service : si vous n’avez pas besoin de containers

Cet article est basé sur une présentation / démo interne réalisée au sein de Devoteam M Cloud en décembre 2025. Vous pouvez retrouver le repository Github avec le code en suivant ce lien. Ainsi qu’une copie de la présentation PowerPoint ci-dessous.

Share
Published by
thibault

Recent Posts

MCT 2025-2026 : renouvellement de mon statut Microsoft Certified Trainer

MCT 2025-2026 : renouvellement de mon statut Microsoft Certified Trainer Mon statut Microsoft Certified Trainer…

4 jours ago

S3NS obtient la aualification SecNumCloud : un pas de géant pour la Sécurité du Cloud

S3NS Obtient la Qualification SecNumCloud : Un pas de géant pour la Sécurité du Cloud…

5 jours ago

Déployer automatiquement son site avec GitHub Actions et SFTP

Déployer automatiquement son site avec GitHub Actions et SFTP Il y a quelques temps j'ai…

2 semaines ago

Proton Sheets : une nouvelle alternative à Excel Online et Google Sheets

Proton Sheets : Une Nouvelle Alternative à Excel Online et Google Sheets Introduction Proton Sheets,…

3 semaines ago

Azure Bastion : connexion RDP avec Entra ID depuis le portail Azure (Preview)

Azure Bastion : authentification Entra ID en preview pour les connexions RDP via le portail…

3 semaines ago

Plex met fin à la gratuité : Ce que cela signifie pour les utilisateurs

Plex met fin à la gratuité : Ce que cela signifie pour les utilisateurs Introduction…

3 semaines ago