— Cloud et DevOps
Multi-cloud, conteneurs, livraison
Le cloud est une couche de commodité que j'assemble — pas un fournisseur auquel je suis enchaîné.
Je déploie sur GCP, Azure et AWS, avec des runtimes edge et des backends gérés, en choisissant le service le plus fort pour chaque rôle et en orchestrant entre fournisseurs. Conteneurs, CI/CD et infrastructure as code sont au centre ; Linux tourne en dessous au niveau administrateur.
Je ne choisis pas un cloud pour y plier chaque problème. Je choisis le service le plus fort pour chaque rôle et j'orchestre entre fournisseurs.
Le multi-cloud, pour moi, n'est pas un mot à la mode — c'est un refus délibéré de laisser un fournisseur posséder l'architecture. GCP, Azure et AWS font chacun certaines choses mieux que les autres, et les runtimes edge et les backends gérés remplissent des rôles qu'aucun des trois grands ne couvre proprement. Le travail consiste à bien choisir par rôle et à garder l'ensemble portable.
Ce qui rend cela possible, c'est la couche en dessous : conteneurs et infrastructure as code au centre, pour qu'une charge définie une fois puisse cibler le fournisseur sur lequel un projet vit déjà. Le cloud devient une commodité que je peux remplacer, plutôt qu'une dépendance autour de laquelle je dois planifier.
Et sous l'orchestrateur, il reste un hôte. J'exploite Linux au niveau administrateur — réglage du noyau, durcissement sysctl, systemd, runtimes de conteneurs et le réseau sous les services — parce que les parties que la plupart héritent par défaut sont les parties que je préfère exploiter délibérément.
clouds principaux sur lesquels je déploie — GCP, Azure et AWS — choisis par rôle, pas par habitude
fournisseurs dont je laisse dépendre l'architecture ; le cloud est une couche de commodité que je peux remplacer
runtimes edge que j'exécute à la frontière du réseau — Cloudflare Workers et Vercel Edge
backends de base de données gérés que je garde prêts — Supabase, Firebase, PlanetScale, Neon
Une application, trois clouds, sans verrouillage.
Une charge que je garde portable : conteneurs et infrastructure as code au centre, déployables vers le fournisseur sur lequel un projet tourne déjà. Chaque fournisseur est câblé pour le rôle qu'il fait le mieux, et l'edge se place devant tous.
Sélection de services — l'outil le plus fort par rôle
- Entraînement et serving de modèles
- Vertex AI (GCP)
- Requêtes analytiques à grande échelle
- BigQuery (GCP)
- Bus d'événements / diffusion
- Pub/Sub (GCP) · SQS/SNS (AWS)
- Conteneurs avec descente à zéro
- Cloud Run (GCP) · Container Apps (Azure)
- Kubernetes complet
- GKE · AKS · EKS
- Fonctions pilotées par événements
- Cloud Functions · Azure Functions · Lambda
- Stockage d'objets
- Cloud Storage (GCP) · S3 (AWS)
- Base de données relationnelle
- RDS (AWS) · Neon · PlanetScale
- Calcul edge
- Cloudflare Workers · Vercel Edge
- CDN
- CloudFront (AWS)
Pour chaque rôle, je choisis le fournisseur qui le fait le mieux, puis j'orchestre entre eux — le cloud devrait être une commodité que je peux remplacer, jamais une dépendance qui possède le produit.
À quoi sert vraiment chaque cloud.
Les quatre onglets ci-dessous ne sont pas un classement. Chacun est un ensemble de rôles qu'un fournisseur donné fait bien, et la discipline consiste à apparier une charge à celui qui convient plutôt qu'à tout forcer sur un seul compte. GCP pour les données et les modèles, Azure au sein de l'écosystème Microsoft, AWS comme choix par défaut large, et l'edge et les backends gérés pour la voie rapide.
Google Cloud — là où vivent généralement les données et les modèles
GCP est l'endroit où je place les charges qui touchent aux données et aux modèles. Vertex AI pour l'entraînement et le serving, BigQuery pour les requêtes analytiques sur de grandes tables, et Pub/Sub comme bus de messages quand des services doivent diffuser des événements sans se connaître.
Pour le calcul, je recours à Cloud Run quand un conteneur doit descendre à zéro entre les requêtes, Cloud Functions pour de petits gestionnaires pilotés par événements, et GKE quand une charge a besoin de toute la surface de Kubernetes. Firestore et Cloud Storage couvrent l'état documentaire et les objets.
- Vertex AI pour l'entraînement et le serving de modèles
- Cloud Run · Cloud Functions · GKE pour le calcul sur tout le spectre de mise à l'échelle
- Pub/Sub · BigQuery · Firestore · Cloud Storage pour la messagerie, l'analytique, l'état et les objets
Azure — quand un projet vit déjà dans l'écosystème Microsoft
Quand un client tourne déjà sur l'identité et les outils Microsoft, lutter contre cela est un effort gaspillé. Azure Kubernetes Service offre le même contrat Kubernetes que j'utilise ailleurs, donc une charge définie comme des conteneurs et des manifestes s'y déplace avec peu de changements.
Azure Functions couvre le calcul piloté par événements, Container Apps gère le cas du conteneur avec descente à zéro, et Cognitive Services est une voie pragmatique vers la vision, la parole et le langage quand construire le modèle en interne n'est pas l'objet du projet.
- AKS pour Kubernetes géré au sein de l'écosystème Microsoft
- Functions et Container Apps pour le calcul par événements et la descente à zéro
- Cognitive Services pour la vision, la parole et le langage comme capacité gérée
AWS — le choix par défaut le plus large, avec le catalogue de services le plus profond
AWS est le choix par défaut le plus large : quand une équipe y est déjà, ou quand un service géré spécifique est la réponse la plus propre, j'y déploie directement. Lambda pour les fonctions pilotées par événements, ECS ou EKS pour les conteneurs selon la part de Kubernetes que l'équipe veut gérer.
S3 pour le stockage d'objets durable, RDS pour les bases de données relationnelles gérées, et SQS et SNS pour les files et le pub/sub. CloudFront se place devant comme CDN. Les mêmes conteneurs et la même infrastructure as code qui ciblent GCP ciblent aussi celui-ci.
- Lambda pour les fonctions · ECS / EKS pour les conteneurs
- S3 · RDS pour le stockage d'objets et relationnel
- SQS · SNS pour les files et le pub/sub · CloudFront à la périphérie
Runtimes edge et backends gérés — la voie rapide
À la frontière du réseau, j'exécute Cloudflare Workers et Vercel Edge : du code qui s'exécute au plus près de l'utilisateur, avec des démarrages à froid mesurés en millisecondes, pour le routage, les contrôles d'authentification et des transformations légères avant qu'une requête n'atteigne une région.
Pour les produits qui doivent avancer vite, un backend géré justifie sa place. Supabase et Firebase fournissent authentification, stockage et base de données sans monter de serveurs ; PlanetScale et Neon offrent du SQL géré et ramifiable. Je les choisis quand la vitesse vers un produit fonctionnel compte plus que la possession de l'infrastructure.
- Cloudflare Workers et Vercel Edge à la frontière
- Supabase et Firebase comme backends gérés complets
- PlanetScale et Neon pour du SQL géré et ramifiable
L'unité de déploiement est la même où qu'elle atterrisse.
Docker · Kubernetes · sécurité des conteneurs
Une image immuable, ordonnancée par Kubernetes, identique chez tous les fournisseurs.
Tout est livré sous forme de conteneur. Une charge est empaquetée comme une image Docker immuable, construite une fois, et cette image exacte est ce qui tourne dans chaque environnement — il n'y a pas de reconstruction qui pourrait silencieusement différer entre staging et production.
Kubernetes l'ordonnance de la même façon que le cluster soit GKE, AKS ou EKS, donc la charge est portable par construction. La sécurité est intégrée à l'image plutôt qu'ajoutée après : images de base minimales, utilisateurs non-root, systèmes de fichiers en lecture seule, capacités Linux retirées, et une analyse avant tout push.
- Une image immuable, construite une fois, exécutée partout
- Ordonnancée à l'identique sur GKE, AKS ou EKS
- Images de base minimales, non-root, lecture seule, capacités retirées
- Analysée avant d'atteindre un registre
Charge Kubernetes — forme d'exploitation
- Orchestrateur
- Kubernetes — GKE, AKS ou EKS
- Unité de déploiement
- Image de conteneur immuable, build unique
- Mise à l'échelle
- Autoscaling horizontal des pods sur métriques
- Config et secrets
- ConfigMaps et Secrets, montés à l'exécution
- Ingress
- Équilibreur de charge géré · CDN devant
- Déploiement
- Rolling, canary ou blue-green
- Source d'image
- Registre avec étiquettes immuables et signées
L'image est construite une fois et promue inchangée.
Un pipeline que je traite comme une infrastructure non négociable. À partir d'un commit, l'image est construite et testée une fois, analysée, poussée avec une étiquette immuable, et promue à travers chaque porte — pour que ce qui tourne en production soit octet par octet ce qui a passé les tests.
Du commit à la production — GitHub Actions / GitLab CI
- 01 Commit Un push vers le dépôt est le seul déclencheur ; rien n'est construit à la main.
- 02 Build + test GitHub Actions ou GitLab CI construit l'image du conteneur une fois et lance la suite de tests dessus.
- 03 Analyse L'image est analysée pour les vulnérabilités connues et l'arbre des dépendances est vérifié avant de pouvoir avancer.
- 04 Push L'image signée est poussée vers un registre avec une étiquette immuable — jamais écrasée.
- 05 Appliquer l'IaC L'infrastructure as code planifie le changement, montre le diff, puis l'applique pour que l'environnement corresponde au dépôt.
- 06 Promotion La même image est déployée derrière un commutateur canary ou blue-green, la version précédente à une commande de distance.
Le pipeline, sous forme de fichier.
Un workflow GitHub Actions réduit — construire l'image une fois, la tester, l'analyser, puis la pousser avec une étiquette immuable. La même image est ensuite promue vers chaque environnement ; rien n'est reconstruit en aval.
name: build-and-deploy
on:
push:
branches: [ main ]
jobs:
ship:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
env:
IMAGE: ghcr.io/${{ github.repository }}:${{ github.sha }}
steps:
- uses: actions/checkout@v4
- name: Build image (once)
run: docker build -t "$IMAGE" .
- name: Test
run: docker run --rm "$IMAGE" go test ./...
- name: Scan for vulnerabilities
run: trivy image --exit-code 1 --severity HIGH,CRITICAL "$IMAGE"
- name: Push immutable tag
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
docker push "$IMAGE" L'image, définie telle qu'elle est livrée.
Un Dockerfile multi-étapes — compiler dans une image de build complète, puis ne copier que le binaire dans un runtime minimal qui tourne en utilisateur non-root. Surface réduite, rien dans l'image dont le programme n'a pas besoin.
# --- build stage: full toolchain, thrown away after compile ---
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /out/app ./cmd/app
# --- runtime stage: minimal, non-root, only the binary ---
FROM gcr.io/distroless/static:nonroot
COPY --from=build /out/app /app
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/app"] Déclarée, planifiée, révisée, appliquée.
L'environnement vit dans le dépôt
Personne ne fait exister la production en cliquant.
Chaque élément d'infrastructure — réseaux, clusters, files, bases de données — est déclaré comme du code et vit dans le contrôle de version à côté de l'application. Un changement est proposé comme un diff, planifié comme une exécution à blanc, révisé comme tout autre code, puis appliqué.
L'idée est que le dépôt est la seule source de vérité. Une replanification périodique détecte toute dérive manuelle, de sorte que l'environnement en direct est toujours réconcilié avec ce que le code dit qu'il devrait être. Un environnement devient reproductible plutôt que le produit de clics mémorisés dans une console.
- Réseaux, clusters, files et bases de données comme du code
- Le plan montre le diff avant tout changement
- Révisé comme du code, pas cliqué dans une console
- Les vérifications de dérive gardent le dépôt comme autorité
Infrastructure as code — de déclarer à réconcilier
- 01 Écrire L'infrastructure est déclarée comme du code — réseaux, clusters, files, bases de données — dans le contrôle de version aux côtés de l'application.
- 02 Planifier Une exécution à blanc calcule le diff entre l'état déclaré et l'état en direct, de sorte que chaque changement est visible avant de se produire.
- 03 Réviser Le plan est révisé comme tout autre changement de code ; personne ne clique dans une console pour muter la production.
- 04 Appliquer Le plan est appliqué ; l'environnement en direct correspond désormais exactement au dépôt.
- 05 Vérifier la dérive Des replanifications périodiques détectent les changements manuels, de sorte que l'état déclaré reste la seule source de vérité.
Linux au niveau administrateur.
Au-dessus de l'orchestrateur il y a Kubernetes ; en dessous il reste un hôte Linux, et c'est une couche que j'exploite délibérément plutôt que d'en hériter.
Les conteneurs ne suppriment pas le système d'exploitation — ils reposent dessus. J'exploite Linux au niveau administrateur : réglage du noyau pour la charge, durcissement sysctl pour resserrer la surface du noyau à l'exécution, systemd pour définir des services avec politiques de redémarrage et limites de ressources, les runtimes de conteneurs eux-mêmes, et le réseau qui transporte chaque paquet de la périphérie jusqu'à un pod.
C'est le même instinct qui traverse le reste de mon travail. Les parties que la plupart acceptent par défaut — les paramètres du noyau, les règles de pare-feu, l'image de base à partir de laquelle un conteneur est construit — sont les parties que je préfère comprendre et fixer à dessein, parce que c'est de là que viennent silencieusement la fiabilité et la sécurité.
Réglage du noyau
Ajuster les paramètres du noyau selon la charge — limites de descripteurs de fichiers, tampons réseau, comportement de l'ordonnanceur — plutôt que d'accepter les valeurs par défaut de la distribution.
Durcissement sysctl
Resserrer la surface du noyau à l'exécution via sysctl : réglages de la pile réseau, protections de l'espace d'adressage, et désactivation de ce qu'un serveur n'a aucune raison d'exposer.
systemd
Services définis comme unités systemd avec politiques de redémarrage, limites de ressources et ordre de dépendances, pour que l'hôte se comporte de façon prévisible entre les redémarrages.
Runtimes de conteneurs
Travailler au niveau du runtime — Docker et la couche OCI dessous — y compris les namespaces, les cgroups et l'intérieur de l'image, pas seulement les commandes de haut niveau.
Réseau
Le réseau sous les services : routage, règles de pare-feu, DNS, terminaison TLS et le chemin qu'un paquet emprunte de la périphérie jusqu'à un pod.
Sécurité des conteneurs
Images de base minimales, utilisateurs non-root, systèmes de fichiers en lecture seule, capacités retirées et analyse d'image — réduisant ce qu'un conteneur compromis peut atteindre.
Un système dans lequel on ne peut pas voir est un système qu'on ne peut pas exploiter.
Une fois qu'une charge est répartie entre fonctions, conteneurs et files sur plus d'un fournisseur, on ne peut pas l'exploiter à l'intuition. L'observabilité est la partie qui transforme un système distribué en quelque chose sur lequel on peut raisonner — métriques pour les tendances, logs pour le détail, traces pour le chemin qu'une requête a pris, et alertes qui notifient sur des symptômes qu'un utilisateur ressentirait réellement.
Je traite la pile d'observabilité comme partie du build, pas comme quelque chose d'ajouté après le premier incident. Les quatre onglets ci-dessous sont les couches que j'instrumente, et l'ordre compte : une métrique pointe le problème, une trace le restreint à un saut, et les logs expliquent ce qui s'est passé sur cette requête exacte.
Des nombres dans le temps, pour que les tendances soient visibles avant de devenir des incidents
Les métriques sont le signal bon marché et toujours actif : taux de requêtes, taux d'erreurs, percentiles de latence, saturation des ressources. C'est ce que lit un autoscaler et ce qui déclenche une alerte, parce qu'elles sont numériques et continues.
J'instrumente les éléments qui correspondent à une expérience utilisateur — la latence qu'une requête subit réellement, le taux d'erreur qu'un client rencontre réellement — plutôt que de simples compteurs au niveau de l'hôte qui semblent sains pendant que le produit échoue.
- Taux de requêtes, taux d'erreurs, percentiles de latence
- Saturation des ressources qui pilote l'autoscaling
- Signaux liés à l'expérience utilisateur, pas seulement des compteurs d'hôte
Le détail que l'on cherche une fois qu'une métrique a indiqué où regarder
Les logs sont structurés et centralisés pour qu'une seule requête puisse être suivie à travers les services qu'elle a touchés. Une métrique me dit que quelque chose ne va pas ; les logs me disent quoi, sur quelle requête, avec quelle entrée.
Les champs structurés comptent plus que le texte libre — un log que l'on peut interroger et agréger vaut bien plus qu'un que l'on ne peut lire que ligne par ligne pendant un incident.
- Structurés, interrogeables, centralisés
- Une seule requête traçable entre les services
- Des champs interrogeables plutôt que des lignes de texte libre
La forme d'une requête au fil de ses passages entre services
Le traçage distribué suit une requête à travers chaque saut — passerelle, service, base de données, file — et montre où le temps est réellement passé. Dans un système réparti entre fonctions, conteneurs et files, c'est la seule réponse honnête à où est-ce lent.
Une trace transforme un vague ça semble lent en un span précis qui possède l'essentiel de la latence, ce qui fait la différence entre deviner et corriger.
- Une requête suivie à travers chaque saut
- Latence attribuée au span qui la possède
- La réponse honnête à où est passé le temps
Des notifications liées à des symptômes que l'utilisateur ressentirait, pas au bruit
Les alertes se déclenchent sur des symptômes — taux d'erreur élevé, latence au-delà d'un seuil, une ressource qui sature — pas sur chaque soubresaut passager. Une alerte qui réveille quelqu'un à 3 h du matin doit correspondre à quelque chose qu'un utilisateur remarquerait réellement.
Le but est un petit nombre d'alertes à fort signal. Trop de notifications à faible signal entraînent les gens à les ignorer, ce qui est pire que de n'en avoir aucune.
- Basées sur les symptômes, liées à un impact visible pour l'utilisateur
- Fort signal plutôt que fort volume
- Des seuils choisis pour qu'une notification signifie quelque chose
Du noyau au cloud, une seule pile.
Hôte, conteneur, pipeline, cloud — lu de bas en haut, le travail est une pile continue plutôt que quatre préoccupations séparées.
Chaque couche repose sur celle d'en dessous. Un hôte Linux durci porte un runtime de conteneurs ; une image immuable tourne sur Kubernetes ; un pipeline CI/CD et l'infrastructure as code rendent l'ensemble reproductible ; et un déploiement multi-cloud le distribue sans se verrouiller sur un seul fournisseur.
Séparées, elles ressemblent à des spécialités distinctes. Exploitées ensemble, elles forment une seule discipline : délibérée à chaque couche, portable entre fournisseurs, et reproductible depuis un dépôt plutôt que depuis la mémoire.
- Hôte Linux au niveau administrateur Réglage du noyau, durcissement sysctl, unités systemd, runtimes de conteneurs et le réseau en dessous — la couche que la plupart héritent, exploitée de façon délibérée.
- Conteneur Docker et Kubernetes Charges empaquetées comme images de conteneur immuables et exécutées sur Kubernetes — GKE, AKS ou EKS — pour que l'unité de déploiement soit la même où qu'elle atterrisse.
- Pipeline CI/CD et infrastructure as code GitHub Actions et GitLab CI construisent et promeuvent l'image ; l'infrastructure déclarée comme du code rend tout l'environnement reproductible plutôt que construit à la main.
- Cloud Multi-cloud, par rôle GCP, Azure et AWS — plus des runtimes edge et des backends gérés — sélectionnés par rôle et orchestrés ensemble, sans qu'aucun fournisseur unique ne possède l'architecture.
Les principes sous la plateforme.
Les fournisseurs et les outils changent avec le projet ; les principes, non. Voici les règles que j'applique que la cible soit GCP, Azure, AWS, l'edge ou un backend géré — la partie qui rend la plateforme reproductible plutôt qu'accidentelle.
Choisir le service le plus fort par rôle
Pour chaque rôle — serving de modèles, le bus d'événements, la base de données, l'edge — je choisis le fournisseur qui le fait le mieux, puis j'orchestre entre eux. Le résultat est un système assemblé à partir des bonnes pièces, pas des plus pratiques.
Ne jamais dépendre d'un seul cloud
Les conteneurs et l'infrastructure as code sont au centre pour que la même charge puisse cibler GCP, Azure ou AWS. Le cloud est une commodité que je peux remplacer, pas une dépendance qui possède le produit.
Construire l'image une fois, la promouvoir inchangée
Une image est construite une seule fois et déplacée à travers chaque porte jusqu'à la production, octet par octet. Ce qui tourne en production est exactement ce qui a passé les tests, pas une reconstruction qui pourrait différer.
Déclarer l'infrastructure, ne jamais la cliquer
Réseaux, clusters, files et bases de données sont déclarés comme du code, planifiés, révisés et appliqués. Personne ne mute la production dans une console, donc le dépôt reste la seule source de vérité.
Durcir l'hôte en dessous
Exploiter Linux au niveau administrateur — réglage du noyau, durcissement sysctl, systemd, runtimes de conteneurs, réseau — signifie que la couche sous l'orchestrateur est délibérée, pas laissée à ses valeurs par défaut.
Rendre le système observable
Métriques, logs et traces font partie du build, pas un ajout après un incident. Un système dans lequel on ne peut pas voir est un système qu'on ne peut pas exploiter.
Choisir le service le plus fort par rôle, garder la charge portable avec des conteneurs et du code, construire l'image une fois, et durcir l'hôte en dessous — tout le reste est détail.
Open to the right work
Si vous avez besoin d'une plateforme qui tourne entre clouds sans appartenir à aucun d'eux, c'est le travail que je fais.
If you are holding a problem that doesn't fit inside one field, that is the conversation I want.