Article 3 - Docker + GitHub Actions

Automatiser le build et le push d’une app Node.js
Dans cette troisième étape, je m’attaque à la conteneurisation de mon application Next.js (portfolio personnel), et à la mise en place d’une pipeline CI/CD pour automatiser le build de l’image Docker, son push sur Docker Hub, et le redéploiement automatique sur Kubernetes.
CI/CD : création de l’image Docker
L’objectif est de :
- Produire une image Docker légère et optimisée
- La publier automatiquement sur Docker Hub
- Redémarrer le déploiement sur le cluster sans downtime
Tout est défini dans une pipeline GitHub Actions située dans le dépôt de mon application, sous .github/workflows/deploy.yml
.
Construction de l’image Docker
Voici le Dockerfile
utilisé :
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
RUN npm run build
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=build /app/package*.json ./
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3000
CMD ["npm", "run", "start"]
🧠 Quelques remarques :
- Multi-stage build : pour séparer la phase de build et l’environnement d’exécution.
- Base alpine : pour une image plus légère.
package*.json
copié avant le code : permet de cacher la couche de dépendances tant quepackage.json
ne change pas → gain de temps.- Le
EXPOSE 3000
est informel (utile pour la doc ou des outils comme Docker Desktop), mais ce n’est pas lui qui publie le port.
Pipeline dans le dépôt applicatif
La pipeline deploy.yml
contient deux jobs :
- Le build & push de l’image
- Le redéploiement sur le cluster Kubernetes
name: Deploy Website
on:
push:
branches:
- main
workflow_dispatch:
env:
NAMESPACE: default
DEPLOYMENT_NAME: portfolio
jobs:
build_push_image:
name: Build & Push Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: woulf/portfolio:latest
rollout_k8s:
name: Restart Kubernetes Deployment
runs-on: ubuntu-latest
needs: build_push_image
steps:
- name: Set up Kubernetes context
uses: azure/k8s-set-context@v3
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Restart Deployment
run: |
kubectl rollout restart deployment/${{ env.DEPLOYMENT_NAME }} -n ${{ env.NAMESPACE }}
kubectl rollout status deployment/${{ env.DEPLOYMENT_NAME }} -n ${{ env.NAMESPACE }}
Gestion des secrets
Aucun identifiant n’est codé en dur. Tout est stocké de manière sécurisée via les GitHub Secrets :
DOCKER_USERNAME
/DOCKER_PASSWORD
→ pour Docker HubKUBECONFIG
→ pour se connecter à mon cluster MicroK8s à distance
Résultat
✅ Chaque push sur main
génère une image Docker à jour, la publie automatiquement, et redéploie l’application sans downtime.
➡️ Dans le prochain article, je vous explique comment j’ai versionné l’infrastructure Kubernetes dans un second dépôt, et mis en place une pipeline GitOps pour garder le cluster synchronisé.