Cloud

Plateformes Cloud

Le déploiement cloud est l'art de choisir la quantité d'infrastructure à gérer. D'un VPS brut à un PaaS managé jusqu'à Kubernetes, le compromis reste le même : le contrôle et le coût face au temps passé à maintenir le système en vie.

intermediate14 min readUpdated 16 sept. 2026
Dockerfile
dockerfile
# Dockerfile
FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:22-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
Spectre
VPS, PaaS, conteneurs, serverless
Managé signifie
Patchs, scaling et sauvegardes
Configuration
Variables d'environnement, pas de fichiers
État
Instances stateless, stores externes
Health checks
Sondes de liveness et de readiness
Rollback
Redéployer la release précédente

Pourquoi c'est important

Ce que le cloud vous apporte réellement

L'infrastructure devient le travail de quelqu'un d'autre

La plateforme gère les patchs du noyau, termine le TLS, équilibre le trafic et redémarre les instances en panne, pour que votre équipe passe son temps sur le produit plutôt que sur le système d'exploitation.

Une image, n'importe quel hôte

Un conteneur construit une seule fois s'exécute de la même manière sur un ordinateur portable, un PaaS ou un orchestrateur managé, ce qui rend le déploiement portable et limite les surprises.

Scaling et observabilité intégrés

Ajoutez des instances quand le trafic augmente, consultez les logs et les métriques en un seul endroit, et laissez les health checks supprimer une instance défectueuse avant que les utilisateurs ne s'en aperçoivent.

Le tableau complet

Les trois couches d'un déploiement

Vous packagez l'application sous forme d'image, vous la confiez à une plateforme qui l'exécute, et vous conservez tout l'état dans des stores managés à côté.

L'image

Package

L'application et son runtime sont figés dans une image immuable, taguée par commit, que chaque environnement exécute sans modification.

La plateforme

Exécution

Un PaaS, un service de conteneurs managé ou un cluster planifie l'image, la redémarre en cas d'échec et la scale selon la charge.

La couche de données

Persistance

L'état réside en dehors de l'instance dans des services managés PostgreSQL, Redis et du stockage d'objets, afin que les instances puissent être remplacées à tout moment.

Flux

Du commit à la release en production

C'est le chemin parcouru par un changement une fois que la CI est au vert, et les mêmes étapes s'appliquent que la plateforme soit Fly.io, Cloud Run ou Kubernetes.

  1. 1

    Construire l'image

    Le pipeline construit une image multi-stage taguée avec le commit, en excluant les outils de build de la couche d'exécution.

  2. 2

    Push vers un registry

    L'image est poussée vers un registry et référencée par un digest immuable qui ne change jamais.

  3. 3

    Provisionner ou mettre à jour le service

    La plateforme est informée du nouveau digest, soit par un appel API, un fichier de config ou un manifest appliqué au cluster.

  4. 4

    Exécuter les migrations

    Les migrations de schéma rétrocompatibles sont exécutées une seule fois, avant que la nouvelle version ne commence à servir le trafic.

  5. 5

    Déployer la nouvelle version

    De nouvelles instances démarrent aux côtés des anciennes, et le trafic n'est basculé que lorsqu'elles sont déclarées saines (healthy).

  6. 6

    Passer un health check

    Une sonde de readiness confirme que l'instance peut atteindre sa base de données et ses dépendances avant de recevoir des requêtes.

  7. 7

    Basculer le trafic et garder l'ancienne version

    La release précédente reste disponible afin qu'un rollback soit un simple changement de routage plutôt qu'une reconstruction.

Le guide complet

Plateformes Cloud: Tout ce que vous devez savoir

Le spectre du déploiement

Le “cloud” n’est pas un bloc monolithique. Il existe tout un spectre selon le niveau de contrôle que vous avez sur la machine, et chaque produit se situe quelque part sur cet axe.

Un serveur privé virtuel (VPS) est une machine que vous louez et administrez. Vous installez le runtime, configurez un reverse proxy, gérez les certificats TLS et maintenez l’OS à jour. C’est une solution économique, flexible et que vous pouvez casser librement. C’est le choix idéal lorsque vous avez besoin d’un runtime spécifique ou que vous souhaitez comprendre chaque couche technique.

Un platform as a service (PaaS) tel que Railway, Render ou Fly.io prend votre code ou votre image et l’exécute. Vous déclarez un port et un health check ; la plateforme gère le TLS, le routage, les redémarrages et le scaling. C’est par là que la plupart des petites équipes devraient commencer.

Les conteneurs managés tels que AWS ECS, Google Cloud Run ou Azure Container Apps exécutent une image de conteneur avec plus de paramètres qu’un PaaS : networking, rôles IAM, règles d’autoscaling. En termes de charge opérationnelle, ils se situent entre un PaaS et un cluster.

Les fonctions serverless telles que AWS Lambda ou Cloudflare Workers exécutent une fonction par requête, redescendent à zéro (scale to zero) et facturent à l’invocation. Elles sont excellentes pour des tâches ponctuelles et brèves, mais maladroites pour les connexions longue durée et les charges CPU lourdes.

Kubernetes est un orchestrateur polyvalent. Il offre le contrôle le plus poussé mais aussi la surface d’attaque et de gestion la plus vaste. Son utilisation est justifiée lorsque vous gérez de nombreux services avec une équipe plateforme ; c’est disproportionné pour une seule API.

La meilleure stratégie consiste à commencer par les solutions managées et à évoluer vers plus de contrôle uniquement lorsqu’une limitation concrète apparaît. L’erreur la plus courante est d’adopter Kubernetes pour un seul service simplement parce que cela semble être le choix le plus “professionnel”.

Option Vous gérez Idéal pour Points de vigilance
VPS OS, runtime, proxy, TLS Contrôle total, runtimes personnalisés Patchs, basculement (failover), astreintes
PaaS Uniquement l’application Petites équipes, itération rapide Moins de contrôle, coût par unité
Conteneurs managés Image, IAM, règles de scaling Services nécessitant des réglages fins Plus de configuration qu’un PaaS
Serverless Une fonction à la fois Tâches ponctuelles et brèves Cold starts et limites de temps
Kubernetes Tout ce qui est au-dessus du kernel Nombreux services, équipes plateforme Charge opérationnelle réelle

Ce que le terme « managé » vous apporte réellement

Le mot managé cache une liste de tâches qui cessent d’être les vôtres.

  • Le patching. Le kernel, le runtime et l’image de base sont mis à jour sans que vous ayez à planifier de fenêtre de maintenance.
  • Le TLS. Les certificats sont émis et renouvelés automatiquement, et le HTTP est redirigé vers le HTTPS.
  • Le scaling. Des instances sont ajoutées lorsque le CPU ou le nombre de requêtes franchit un seuil, et supprimées lorsqu’il redescend.
  • La santé et les redémarrages. Un processus qui plante ou qui échoue à son health check est remplacé sans intervention humaine.
  • Les logs et les métriques. La sortie est collectée de manière centralisée et interrogeable, au lieu de résider dans un fichier sur une machine où vous devez vous connecter en SSH.
  • Les sauvegardes. Les bases de données managées effectuent des snapshots et supportent la récupération à un instant T (point-in-time recovery).

Ce que vous sacrifiez, c’est le contrôle et une partie de l’efficacité financière. Vous ne pouvez pas optimiser le kernel, installer des paquets système arbitraires ou grappiller le dernier dollar d’une machine. Pour la plupart des équipes, c’est un bon compromis : l’alternative est une rotation d’astreinte pour une infrastructure dans laquelle vous n’êtes pas spécialisés.

L’exception, ce sont les données. PostgreSQL, Redis et le stockage d’objets en version managée en valent presque toujours la peine. Mettre en place soi-même des sauvegardes fiables, le failover et la réplication est un projet en soi, et la version managée est généralement moins coûteuse que les heures d’ingénierie qu’elle permet d’économiser.

La philosophie des Twelve-Factor

La méthodologie “Twelve-Factor App” est une ancienne checklist qui décrit encore avec précision le déploiement cloud-native. Trois de ses facteurs sont particulièrement importants au quotidien.

La configuration dans l’environnement. Tout ce qui diffère d’un déploiement à l’autre — URLs de base de données, clés API, feature flags — provient de variables d’environnement, et non de fichiers commités dans le dépôt. C’est ce qui permet à une seule image de s’exécuter dans n’importe quel environnement.

DATABASE_URL=postgres://user:pass@host:5432/app
REDIS_URL=redis://host:6379
PORT=8080

Des processus sans état (stateless). Une instance ne conserve aucun état durable. Elle peut être démarrée, arrêtée, dupliquée et détruite librement. Les sessions, les uploads et les caches résident dans des services externes.

Les logs comme flux d’événements. L’application écrit des lignes structurées vers stdout et ne fait rien d’autre. C’est la plateforme qui les collecte, les stocke et permet de les rechercher. Pas de fichiers de logs à rotationner, pas de disque à remplir.

Un quatrième facteur mérite d’être souligné : la disposabilité. Démarrer rapidement et s’arrêter proprement. Un processus qui met une minute à booter ralentit le scaling et les déploiements ; un processus qui ignore SIGTERM abandonne les requêtes en cours.

Créer une image de conteneur légère

Une bonne image de production est légère, reproductible et s’exécute avec un utilisateur non-root. Le build multi-étape (multi-stage build) permet d’obtenir ces trois critères.

FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:22-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]

L’étape de build installe tout, compile, puis supprime les dépendances de développement. L’étape de runtime ne copie que node_modules, dist et le manifeste. TypeScript, les frameworks de test et les fichiers sources n’atteignent jamais la production.

USER node abandonne les privilèges root, afin qu’un processus compromis ne puisse pas facilement s’élever en privilèges. NODE_ENV=production désactive les comportements de développement et active les optimisations du framework. Fixez l’image de base sur une variante spécifique, et privilégiez bookworm-slim ou une image distroless plutôt qu’une installation Debian complète pour réduire la surface d’attaque.

Ajoutez un .dockerignore pour que le contexte de build reste léger :

node_modules
.git
dist
.env

Une image plus petite est téléchargée plus rapidement, ce qui réduit directement les temps de démarrage à froid (cold starts) et les délais de déploiement.

Configuration de l’environnement et secrets

La configuration doit être injectée, jamais intégrée en dur. La même image doit pouvoir s’exécuter en staging et en production, seul l’environnement changeant.

Les plateformes diffèrent selon le mécanisme utilisé, mais la structure reste la même. Un fichier de configuration déclare les valeurs non sensibles et un gestionnaire de secrets (secret store) conserve les données confidentielles.

[env]
  NODE_ENV = "production"
  LOG_LEVEL = "info"

[[services]]
  internal_port = 8080

Les secrets sont définis séparément et ne sont jamais commités :

fly secrets set DATABASE_URL=postgres://...
fly secrets set STRIPE_SECRET_KEY=sk_live_...

Sur Kubernetes, les secrets arrivent sous forme de variables d’environnement ou de fichiers montés :

envFrom:
  - secretRef:
      name: api-secrets

Deux bonnes pratiques permettent de sécuriser tout cela. Premièrement, validez la configuration au démarrage et provoquez une erreur explicite si un élément manque, plutôt que de planter lors de la première requête qui en aura besoin. Deuxièmement, privilégiez l’accès basé sur l’identité : une identité de charge de travail (workload identity) ou un rôle d’instance permet à l’application de récupérer des identifiants éphémères sans aucun secret statique.

import { z } from "zod";

const Env = z.object({
  DATABASE_URL: z.string().url(),
  REDIS_URL: z.string().url(),
  PORT: z.coerce.number().default(3000),
  LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
});

const parsed = Env.safeParse(process.env);

if (!parsed.success) {
  console.error("invalid configuration", parsed.error.flatten().fieldErrors);
  process.exit(1);
}

export const config = parsed.data;

Le processus refuse de démarrer si l’environnement est incomplet, ce qui transforme toute une catégorie de surprises au runtime en un échec de déploiement immédiat et évident. Ne loggez jamais l’environnement lui-même : une seule ligne de debug qui affiche process.env peut compromettre tous les secrets détenus par le service.

Déploiements sans interruption et health checks

Un déploiement qui rejette des requêtes est un déploiement que les utilisateurs remarquent. Les déploiements sans interruption (zero-downtime) reposent sur deux piliers : démarrer les nouvelles instances avant d’arrêter les anciennes, et savoir quand une nouvelle instance est réellement prête.

La Readiness signifie que l’instance peut traiter du trafic. Elle s’est connectée à la base de données, a préchargé ses caches et a terminé son démarrage. C’est seulement à ce moment-là que le load balancer doit lui router des requêtes.

La Liveness signifie que l’instance est toujours saine. Si elle échoue répétitivement, la plateforme la tue et la remplace.

app.get("/healthz", async (req, res) => {
  try {
    await db.query("select 1");
    res.status(200).json({ status: "ok" });
  } catch {
    res.status(503).json({ status: "unhealthy" });
  }
});

Gardez vos health checks légers et honnêtes. Vérifier la base de données est raisonnable ; appeler cinq services en aval ne l’est pas, car cela transforme une micro-coupure ailleurs en une boucle de redémarrage. Si une dépendance est optionnelle, rapportez un état sain et dégradez le service gracieusement.

L’arrêt progressif (graceful shutdown) est l’autre partie de l’équation. Lors de la réception de SIGTERM, arrêtez d’accepter de nouvelles connexions, terminez les requêtes en cours, fermez le pool de connexion à la base de données et quittez le processus. Accordez à la plateforme un délai de grâce légèrement supérieur à la requête la plus lente.

process.on("SIGTERM", () => {
  server.close(async () => {
    await db.end();
    await redis.quit();
    process.exit(0);
  });
  setTimeout(() => process.exit(1), 15_000).unref();
});

Le timeout sert de filet de sécurité : si un élément reste bloqué, le processus s’arrête tout de même avant l’expiration du délai de grâce de la plateforme, évitant ainsi que celle-ci ne doive recourir à SIGKILL.

Mise à l’échelle horizontale et statelessness

Le scaling horizontal consiste à exécuter plusieurs copies d’une même instance. Cela ne fonctionne que si les instances sont interchangeables, ce qui exige qu’aucune d’entre elles ne détienne d’état unique.

L’état doit être confié à des services conçus à cet effet :

  • Les sessions dans Redis, et non en mémoire ou uniquement via un cookie signé.
  • Les uploads dans un stockage d’objets tel que S3 ou R2, et non sur le disque local.
  • Les caches dans Redis ou un CDN, et non dans une map locale au processus.
  • Les tâches de fond (background jobs) dans une file d’attente avec des workers dédiés, et non via un timer à l’intérieur du processus web.

Une fois l’état externalisé, le scaling n’est plus qu’une question de chiffres. La plateforme ajoute des instances lors des pics de charge et les supprime lorsque celle-ci redescend, et n’importe quelle instance peut répondre à n’importe quelle requête.

L’autoscaling nécessite un signal. Le CPU est le choix par défaut le plus courant, mais pour les services Node.js limités par les E/S (I/O-bound), la concurrence des requêtes ou la profondeur de la file d’attente reflètent souvent mieux la charge. Scalez sur la métrique qui prédit réellement la saturation, et définissez toujours un nombre minimum d’instances pour que le service survive à un pic de trafic pendant le boot des nouvelles instances.

N’oubliez pas le problème des connexions : chaque instance ouvre son propre pool de base de données. Vingt instances avec un pool de vingt nécessitent quatre cents connexions, ce que PostgreSQL n’accordera pas. Limitez la taille du pool par instance et placez un pooler devant la base de données.

Postgres et Redis managés

La base de données est l’élément de la stack le moins adapté à une gestion manuelle, mais c’est paradoxalement celui qu’on est le plus tenté d’auto-héberger. Un service Postgres managé vous offre des sauvegardes automatisées, la récupération à un instant T (point-in-time recovery), un serveur de secours en cas de panne (failover standby) et souvent des réplicas de lecture, le tout sans aucune charge opérationnelle.

Deux règles permettent de maintenir le système sain. Premièrement, dimensionnez vos connexions avec soin. Configurez max sur le pool avec un petit nombre par instance et utilisez un pooler tel que PgBouncer pour les déploiements multi-instances. Deuxièmement, exécutez vos migrations comme une étape de release, et non au démarrage de l’application. Si chaque instance effectue la migration au lancement, un déploiement progressif (rolling deploy) exécutera la même migration cinq fois simultanément.

# run once, before the new version starts
npm run db:migrate

Un service Redis managé est l’endroit idéal pour stocker les sessions, les compteurs de limitation de débit (rate-limit) et les files d’attente de tâches (job queues). Activez la persistance si les données sont critiques, et considérez l’instance comme une dépendance partagée dont la latence affecte chaque requête. Gardez-la dans la même région que l’application ; un saut inter-régions à chaque lecture de cache est une taxe qui se fera sentir.

DNS, TLS et domaines personnalisés

Le DNS associe un nom à la plateforme, et le TLS rend la connexion fiable. Les deux sont désormais largement automatisés, mais les détails restent importants.

Pointez un enregistrement A ou ALIAS vers l’adresse de la plateforme, ou un CNAME vers son hostname. Utilisez un TTL court pendant la migration afin de pouvoir corriger rapidement une éventuelle erreur, puis augmentez-le. Gardez le domaine apex et l’hôte www cohérents, en redirigeant l’un vers l’autre pour éviter que les liens et les cookies ne soient fragmentés entre deux origines.

Le TLS est émis et renouvelé automatiquement par la plupart des plateformes. Forcez le HTTPS, activez le HSTS une fois que vous êtes certain de votre configuration, et assurez-vous que l’application fasse confiance aux headers de transfert du proxy afin qu’elle génère des URLs https et détecte la véritable IP du client.

proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;

Lorsque vous gérez votre propre reverse proxy, le guide Nginx détaille ces headers, la terminaison TLS et la configuration upstream. Une mauvaise configuration peut entraîner des boucles de redirection et des limiteurs de débit (rate limiters) qui considèrent que chaque requête provient de l’IP du proxy.

Observabilité et alertes

On ne peut pas exploiter ce que l’on ne voit pas. Trois types de signaux couvrent la majorité des besoins.

Les Logs sont des événements structurés. Émettez du JSON avec un niveau, un message et un request id afin qu’ils puissent être filtrés et corrélés. Écrivez sur stdout et laissez la plateforme les collecter.

console.log(JSON.stringify({ level: "info", msg: "request", id, path, ms }));

Les Metrics sont des nombres évoluant dans le temps : taux de requêtes, taux d’erreurs, percentiles de latence, CPU, mémoire et profondeur de file d’attente. Suivez les quatre signaux d’or — latence, trafic, erreurs et saturation — pour chaque service.

Les Traces suivent une requête à travers différents services et rendent visible un fan-out lent. Elles deviennent d’autant plus importantes que le nombre de services augmente ; pour une seule API, de bons logs et metrics suffisent généralement.

Configurez vos alertes sur les symptômes ressentis par les utilisateurs, et non sur les causes. Une augmentation du taux d’erreurs ou une latence p95 dépassant un seuil justifie de réveiller quelqu’un. Un CPU élevé qui n’a pas affecté les requêtes est une note pour le dashboard, pas une alerte critique. Chaque alerte doit être exploitable, sinon vous habituez vos équipes à les ignorer.

Sensibilisation aux coûts

Les factures cloud augmentent dans les interstices entre les décisions. Les coupables habituels sont prévisibles.

L’Egress est souvent la plus grande surprise. Les données sortant du fournisseur sont facturées, parfois lourdement, tandis que les données entrantes sont généralement gratuites. Servez vos assets volumineux via un CDN, compressez les réponses et gardez les services qui communiquent fréquemment dans la même région.

Les ressources inactives sont faciles à oublier : une base de données de staging laissée en cours d’exécution, des volumes non attachés, d’anciens snapshots, ou des instances surdimensionnées conservées « au cas où ». Réduisez la taille des environnements hors production en dehors des heures de travail.

L’over-provisioning gaspille de l’argent dans l’autre sens. Ajustez la taille de vos instances en mesurant l’utilisation réelle, et laissez l’autoscaling gérer les pics au lieu de payer pour le pire scénario 24h/24.

Compromis du Serverless. Les fonctions qui passent à zéro sont peu coûteuses au repos, mais peuvent devenir onéreuses sous une charge constante comparées à un petit container actif en permanence. Modélisez les deux options si le trafic est prévisible.

Configurez une alerte budgétaire sur le compte. Une facture qui double en silence est bien pire qu’une notification indiquant qu’un seuil a été franchi.

Infrastructure as Code

Passer par une console est acceptable pour le premier déploiement, mais devient un risque par la suite. L’Infrastructure as Code (IaC) enregistre l’état souhaité dans des fichiers, rendant ainsi les environnements reproductibles, révisables et récupérables.

Terraform et OpenTofu décrivent les ressources de manière déclarative. Pulumi utilise de véritables langages de programmation. Les manifestes Kubernetes et les fichiers de configuration PaaS sont des formes simplifiées de ce même concept. L’outil importe moins que la pratique : la définition réside dans le contrôle de version et est appliquée par un pipeline.

resource "aws_db_instance" "main" {
  engine                  = "postgres"
  instance_class          = "db.t4g.small"
  allocated_storage       = 50
  backup_retention_period = 7
}

Deux règles rendent l’IaC sécurisée. Gardez l’état (state) à distance et verrouillé pour éviter que deux personnes n’appliquent des modifications conflictuelles. Révisez les plans avant l’application, car un plan prévoyant la destruction d’une base de données doit alerter un humain, et non s’exécuter silencieusement.

Pour un service PaaS unique, un fly.toml ou un render.yaml commité dans le dépôt constitue déjà de l’infrastructure as code. Commencez par là et adoptez un outil complet lorsque la surface d’infrastructure s’agrandira.

Rollbacks et forward fixes

Chaque déploiement doit prévoir un chemin de retour. Puisque l’artéfact est immuable et tagué par commit, effectuer un rollback revient à redéployer le digest précédent.

fly releases
fly deploy --image ghcr.io/me/app@sha256:previous

Sur Kubernetes, un rollout undo permet de revenir à la révision précédente. Sur un PaaS, la plupart des plateformes conservent une liste des releases et vous permettent d’en redéployer une en un clic.

Les rollbacks sont rapides, mais pas toujours suffisants. Si une migration de base de données a déjà été exécutée et a modifié le schéma, l’ancien code doit toujours être capable de le lire. C’est pourquoi les migrations doivent être rétrocompatibles pendant au moins une release : ajoutez des colonnes avant de les utiliser, cessez d’utiliser des colonnes avant de les supprimer, et ne combinez jamais un changement destructif avec le code qui en dépend dans le même déploiement.

Lorsqu’un rollback est impossible — par exemple, une migration de données irréversible — la solution est le “forward fix” : livrez la correction rapidement, en suivant la même discipline de pipeline, plutôt que de modifier la production manuellement.

Réseau, régions et latence

L’emplacement de vos instances et de vos données détermine la réactivité perçue de votre application. Une requête qui doit traverser un océan pour atteindre la base de données engendre au moins cent millisecondes de latence avant même que le moindre traitement ne commence, et aucune optimisation du code ne pourra supprimer ce délai.

Gardez l’application, la base de données et le cache dans la même région. C’est la décision la plus efficace pour réduire la latence, et elle est gratuite. Placez vos assets statiques derrière un CDN pour qu’ils soient servis depuis un point de présence proche de l’utilisateur, et ne laissez que les requêtes dynamiques remonter jusqu’à l’origine.

N’exposez jamais votre base de données sur l’internet public. Utilisez le réseau privé de votre plateforme afin que seuls les services du même réseau puissent y accéder, et gérez les accès via des groupes de sécurité ou des règles de firewall plutôt que par un port ouvert. Cela élimine toute une catégorie d’attaques et améliore généralement la latence.

Le multi-région est une étape dont la plupart des produits n’ont pas besoin. Cela multiplie les coûts, complexifie la gestion de la base de données et introduit un délai de réplication (replication lag), où un utilisateur écrivant dans une région et lisant dans une autre peut voir des données obsolètes. Commencez avec une seule région et un CDN, mesurez la latence réelle ressentie par vos utilisateurs, et ne vous étendez que lorsqu’une audience spécifique le justifie.

Tester un déploiement avant qu’il ne soit visible par les utilisateurs

La production ne doit pas être le premier endroit où un changement est exécuté. Quelques couches de tests supplémentaires permettent de détecter les erreurs que les tests unitaires ne peuvent pas identifier.

Les environnements de preview déploient une instance complète pour chaque pull request, permettant ainsi au reviewer de tester concrètement le changement. Les plateformes qui les supportent transforment la revue de code : on ne se contente plus de lire un diff, on utilise la fonctionnalité.

Le staging reflète la configuration de la production — même moteur de base de données, même structure de variables d’environnement, même proxy — mais sans les données de production. Son rôle est de détecter le type de bug qui n’apparaît que lorsque les composants réels sont connectés entre eux.

Les smoke tests sont exécutés immédiatement après chaque déploiement et testent le chemin critique : l’endpoint de santé (health check), une connexion, une lecture et une écriture. Ce n’est pas une suite de tests complète, mais une confirmation rapide que la release est opérationnelle.

#!/usr/bin/env bash
set -euo pipefail

BASE="${1:-https://app.example.com}"

curl -fsS "$BASE/healthz" >/dev/null
curl -fsS "$BASE/api/version" | grep -q '"sha"'
echo "smoke test passed"

Les feature flags permettent de découpler le déploiement de la mise en production (release). Le code est déployé “à l’obscur”, puis un flag l’active pour un seul utilisateur, puis pour un certain pourcentage, et enfin pour tout le monde. Si un problème survient, le flag est désactivé en quelques secondes, sans nécessiter de nouveau déploiement.

Le load testing avant un lancement permet d’identifier le premier goulot d’étranglement : connexions, CPU ou requête lente. Testez avec une concurrence réaliste et surveillez la base de données, et pas seulement l’API.

Bonnes pratiques

  • Commencez par des services managés et ne passez au contrôle manuel que lorsqu’une limitation apparaît.
  • Construisez une image multi-stage légère et exécutez-la avec un utilisateur non-root.
  • Injectez toute la configuration via l’environnement ; ne l’intégrez jamais directement dans l’image.
  • Gardez chaque instance stateless ; placez les sessions, les uploads et les caches dans des stores managés.
  • Exposez un endpoint de readiness et de liveness léger.
  • Gérez SIGTERM et fermez les connexions avant de quitter.
  • Exécutez les migrations comme une étape de release, et assurez leur rétrocompatibilité.
  • Limitez le nombre de connexions à la base de données par instance et utilisez un pool devant Postgres.
  • Taguez les releases par digest et gardez la version précédente prête pour un rollback.
  • Écrivez des logs structurés vers stdout et configurez des alertes sur les symptômes visibles par l’utilisateur.
  • Définissez l’infrastructure dans le versionnement et examinez les plans avant de les appliquer.
  • Configurez une alerte budgétaire et surveillez l’egress.

Erreurs courantes

  • Adopter Kubernetes pour un seul service et passer tout son roadmap à gérer le cluster.
  • Stocker les sessions en mémoire, puis se demander pourquoi les utilisateurs sont déconnectés à chaque déploiement.
  • Écrire les uploads sur le disque local et les perdre lorsque l’instance est remplacée.
  • Intégrer la configuration de l’environnement directement dans l’image et devoir la reconstruire pour chaque environnement.
  • Lancer les migrations au démarrage de l’application, ce qui provoque leur exécution multiple lors d’un rolling deploy.
  • Scaler les instances sans ajuster la limite de connexions à la base de données.
  • Faire aveuglément confiance aux headers de proxy, ou ne pas les transférer du tout.
  • Laisser la taille par défaut du pool de connexions à la base de données sur tout un parc d’instances.
  • Configurer des alertes sur le CPU au lieu de se baser sur les erreurs et la latence ressenties par les utilisateurs.
  • Oublier les ressources de sortie (egress) et les ressources inactives jusqu’à la première facture surprise.
  • Ne pas avoir de stratégie de rollback, ou en avoir une qui échoue car une migration n’était pas compatible.
  • Gérer la production manuellement via une console, sans aucun historique des modifications effectuées.

Et après ?

Ce guide constitue la destination finale de l’artéfact que le CI/CD construit et déploie. L’image elle-même est issue du guide Docker, qui traite en profondeur des builds multi-étapes et de la mise en cache des couches. Si vous gérez vous-même la terminaison TLS ou le routage du trafic, le guide Nginx présente la configuration du proxy, et le guide Linux explique le fonctionnement de l’hôte et du shell que vous automatisez. Ensemble, ils couvrent tout le parcours, depuis le commit d’un changement jusqu’à une release opérationnelle et observable.

En pratique

Packager, configurer, surveiller, livrer

Les quatre artefacts qui décrivent presque n'importe quel déploiement cloud.

Dockerfile
FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:22-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]

Plateforme managée vs serveurs auto-gérés

Une plateforme managée échange un peu de contrôle et un léger surcoût unitaire pour s'affranchir de la gestion des patchs, du TLS, du failover et de la planification de la capacité.

Managé
# the platform owns the host, TLS and restarts
fly deploy
fly scale count 3
fly logs
Auto-géré
# you own the OS, TLS renewal and failover
ssh prod-01
apt-get update && apt-get upgrade -y
certbot renew
systemctl restart nginx

Instances stateless vs état local persistant

Tout ce qui est stocké en mémoire ou sur disque local disparaît au prochain déploiement et est invisible pour les autres instances derrière le load balancer.

Préférer
app.post("/login", async (req, res) => {
  const sid = randomUUID();
  await redis.set(`sess:${sid}`, JSON.stringify(user), "EX", 3600);
  res.cookie("sid", sid, { httpOnly: true, secure: true });
});
Éviter
const sessions = new Map<string, User>();

app.post("/login", async (req, res) => {
  sessions.set(randomUUID(), user);
  // lost on redeploy, not shared between instances
});

Compromis

De quel niveau de plateforme avez-vous réellement besoin ?

Chaque couche que vous cessez de gérer est une couche que vous ne comprenez plus. Choisissez le point du spectre qui correspond à votre équipe et à votre trafic.

Strengths

  • Les plateformes managées éliminent les tâches ingrates

    Les certificats TLS, les patchs OS, l'agrégation de logs et les redémarrages cessent d'être des tickets. Une petite équipe livre beaucoup plus lorsqu'elle n'a pas à gérer des serveurs.

  • Les conteneurs garantissent la portabilité

    Une image qui tourne sur un hôte de conteneurs tournera sur un autre ; passer d'un PaaS à un cluster est donc un changement de config plutôt qu'une réécriture.

  • Les services de données managés en valent la peine

    Les sauvegardes automatisées, la récupération point-in-time, le failover et les réplicas de lecture sont difficiles à implémenter correctement. Un PostgreSQL managé est généralement moins cher que l'ingénieur qui devrait le maintenir.

Trade-offs

  • Le serverless a des cold starts et des limites

    Les fonctions qui scalent à zéro sont économiques au repos mais lentes lors de la première requête. Les tâches de longue durée, les websockets et les charges CPU lourdes conviennent mieux aux conteneurs.

  • Kubernetes a une réelle courbe d'apprentissage

    Un cluster est un système distribué que vous devez désormais opérer. Pour un service unique, un PaaS est presque toujours le meilleur choix jusqu'à ce que l'échelle impose autre chose.

  • La commodité du managé a un prix

    L'egress, la facturation à la requête et les ressources inactives s'accumulent discrètement. Les services managés coûtent plus cher par unité qu'un serveur auto-géré, et la facture le reflète.

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre Cloud Platforms ?

Notre tutoriel interactif vous guide à travers Cloud Platforms pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.