Aller au contenu principal

Payload CMS sur GKE Autopilot

Payload CMS sur GKE Autopilot

Payload CMS est un CMS headless et un framework applicatif natif TypeScript, orienté code, construit directement sur Next.js — non pas un produit SaaS hébergé, mais une bibliothèque installée dans votre propre application Next.js. Le contenu est modélisé au moyen de « Collections » typées définies dans payload.config.ts, et Payload génère une interface d'administration ainsi que des API REST, GraphQL et Local à partir de cette même configuration. Ce module déploie une véritable application Payload sur GKE Autopilot au-dessus du socle App_GKE, qui provisionne et gère l'infrastructure Google Cloud et Kubernetes partagée.

Ce guide se concentre sur les services cloud utilisés par ce déploiement et sur la manière de les explorer et de les exploiter depuis la console Google Cloud et la ligne de commande. Pour les mécanismes communs à toutes les applications GKE — Workload Identity, ingress, autoscaling, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC Service Controls, sauvegardes et cycle de vie du déploiement — reportez-vous au guide du socle App_GKE plutôt que de les répéter ici.


1. Vue d'ensemble​

Payload s'exécute sous la forme d'un pod Node.js (Next.js) sur GKE Autopilot. Il n'existe aucune image Docker officielle de Payload — ce module construit, via Cloud Build, une véritable application de démarrage vérifiée localement à partir des sources (un modèle create-payload-app vierge utilisant l'adaptateur PostgreSQL). Le déploiement relie un ensemble ciblé de services Google Cloud :

FonctionnalitéService Google CloudRemarques
CalculGKE AutopilotPods Node.js (Next.js standalone), avec autoscaling horizontal
BuildCloud BuildConstruit l'application de démarrage Payload fournie à partir de Payload_Common/scripts/Dockerfile — aucune image préconstruite n'existe à télécharger
Base de donnéesCloud SQL for PostgreSQL 15Obligatoire — l'adaptateur Postgres de Payload est utilisé ; MySQL/MongoDB ne sont pas raccordés
Stockage d'objetsAucunAucun bucket n'est provisionné ; les médias téléversés vont sur le disque local et éphémère du conteneur
SecretsSecret ManagerPAYLOAD_SECRET généré automatiquement ; mot de passe de la base de données
IngressCloud Load BalancingLoadBalancer externe par défaut, domaine personnalisé + certificat géré en option

Valeurs par défaut judicieuses à connaître d'emblée :

  • PostgreSQL 15 est obligatoire et le schéma n'est pas créé au démarrage. Démarrer le serveur compilé sur une base de données neuve ne crée aucune table — le job d'initialisation payload-migrate applique le schéma via la CLI payload migrate, à l'aide d'un fichier de migration pré-généré intégré à l'image.
  • container_image_source est fixé à "custom". Rien ne peut être déployé sans une exécution Cloud Build — le module construit toujours Payload_Common/scripts/ à partir des sources.
  • Les sondes de santé ciblent /admin, et non / ou une route d'API. /admin sert le formulaire de connexion/de création du premier utilisateur de Payload et renvoie un 200 sans authentification ; les routes REST/GraphQL de Payload exigent une authentification et ne conviennent pas comme cibles de sonde.
  • Aucun bucket de stockage n'est provisionné. Les médias téléversés sont écrits sur le disque local du conteneur et ne survivent pas à un redémarrage de pod ni à un redéploiement.
  • enable_redis et les variables associées du groupe 21 sont déclarées mais inertes. Elles ne sont pas transmises à Payload_Common, qui ne dispose d'aucun raccordement Redis.
  • service_type vaut LoadBalancer par défaut. La vérification en conditions réelles de ce déploiement a utilisé ClusterIP uniquement parce que le quota d'adresses IP statiques IN_USE_ADDRESSES du projet cible était épuisé au moment du déploiement — un choix opérationnel propre à ce déploiement, et non une valeur par défaut du module. Revenez à LoadBalancer (ou réservez une IP statique) dès que le quota est disponible.
  • Le premier utilisateur administrateur est créé manuellement. Payload ne dispose d'aucune CLI non interactive pour cela — visiter /admin avec une collection users vide affiche un formulaire d'inscription.
  • Au moins 1 réplica est maintenu par défaut (min_instance_count = 1).

2. Services Google Cloud et comment les explorer​

Toutes les commandes supposent que vous avez exécuté gcloud container clusters get-credentials <cluster> --region <region> --project <project> et que PROJECT, REGION et NAMESPACE sont définis. L'espace de noms et les autres identifiants sont indiqués dans les sorties du déploiement.

A. GKE Autopilot — la charge de travail Payload​

Les pods Payload sont planifiés sur Autopilot, qui facture le CPU et la mémoire réellement demandés par les pods.

  • Console : Kubernetes Engine → Workloads → sélectionnez la charge de travail Payload pour voir les pods, les révisions et les événements. Kubernetes Engine → Services & Ingress affiche l'IP externe.
  • CLI :
    kubectl get pods,svc -n "$NAMESPACE"
    kubectl logs -n "$NAMESPACE" deploy/<service-name> --tail=100

Consultez App_GKE pour savoir comment Autopilot, le scaling et le type de charge de travail (Deployment ou StatefulSet) sont gérés.

B. Cloud Build — construction de l'image Payload​

Comme aucune image Payload officielle n'existe, chaque déploiement (et chaque redéploiement après une modification du Dockerfile ou des sources) déclenche une exécution Cloud Build sur Payload_Common/scripts/.

  • Console : Cloud Build → History.
  • CLI :
    gcloud builds list --project "$PROJECT" --limit=10
    gcloud builds log <build-id> --project "$PROJECT"

C. Cloud SQL for PostgreSQL 15​

Payload stocke toutes les données de l'application (Collections, utilisateurs, métadonnées des documents téléversés) dans une instance gérée Cloud SQL for PostgreSQL 15. Les pods y accèdent de façon privée via le sidecar Cloud SQL Auth Proxy sur un socket Unix ; aucune IP publique n'est exposée. Au premier déploiement, db-init crée la base de données et le rôle, puis payload-migrate applique le schéma.

  • Console : SQL → sélectionnez l'instance pour les connexions, les sauvegardes, les flags et les métriques.
  • CLI :
    gcloud sql instances list --project "$PROJECT"
    gcloud sql instances describe <instance-name> --project "$PROJECT"
    gcloud sql connect <instance-name> --user=<db-user> --database=<db-name> --project "$PROJECT"

Le nom de l'instance, le nom de la base de données, l'utilisateur et le secret Secret Manager contenant le mot de passe figurent tous dans les sorties. Consultez App_GKE pour le modèle de connexion, les sauvegardes automatiques et la rotation des mots de passe.

D. Secret Manager​

Un secret cryptographique est généré automatiquement et stocké dans Secret Manager : PAYLOAD_SECRET (utilisé pour signer les propres jetons de session/d'authentification de Payload). Le mot de passe de la base de données est géré séparément par le socle.

  • Console : Security → Secret Manager.
  • CLI :
    gcloud secrets list --project "$PROJECT"
    gcloud secrets versions access latest --secret=<secret-name> --project "$PROJECT"

Consultez App_GKE pour l'intégration Secret Store CSI et la rotation.

E. Réseau et entrée​

Par défaut, la charge de travail est exposée via une IP externe Cloud Load Balancing (service_type = LoadBalancer). Un domaine personnalisé avec un certificat géré par Google peut être activé, et une IP statique peut être réservée afin que l'adresse survive aux redéploiements.

  • Console : Network services → Load balancing ; VPC network → IP addresses.
  • CLI :
    kubectl get svc -n "$NAMESPACE"
    gcloud compute addresses list --project "$PROJECT"

Si le déploiement a été configuré avec service_type = "ClusterIP" (par exemple parce que le quota d'IP statiques était épuisé au moment du déploiement), accédez plutôt à l'application depuis l'intérieur du cluster :

kubectl port-forward -n "$NAMESPACE" svc/<service-name> 18080:3000
curl -s http://localhost:18080/admin -o /dev/null -w '%{http_code}\n'

Consultez App_GKE pour les domaines personnalisés, Cloud CDN et les détails sur les IP statiques.

F. Cloud Logging et Monitoring​

Les flux stdout/stderr des pods sont envoyés à Cloud Logging ; les métriques GKE et Cloud SQL sont envoyées à Cloud Monitoring. Des tests de disponibilité et des règles d'alerte sont disponibles en option.

  • Console : Logging → Logs Explorer ; Monitoring → Dashboards / Alerting.
  • CLI :
    gcloud logging read 'resource.type="k8s_container" AND resource.labels.namespace_name="'"$NAMESPACE"'"' \
    --project "$PROJECT" --limit 50

3. Comportement de l'application Payload​

  • Configuration de la base de données au premier déploiement. db-init (avec postgres:15-alpine) se connecte via le Cloud SQL Auth Proxy et crée de manière idempotente le rôle et la base de données de l'application.
  • La migration du schéma est un job distinct et dépendant. payload-migrate (depends_on_jobs = ["db-init"]) exécute ./node_modules/.bin/payload migrate depuis une copie complète /app/cli de node_modules + des sources TypeScript intégrée à l'image — le runtime Next.js standalone allégé qui sert le trafic n'inclut ni la CLI Payload ni ses dépendances. Sur GKE, le Cloud SQL Auth Proxy s'exécute comme sidecar natif ; le script de migration lui signale de s'arrêter via http://localhost:9091/quitquitquit une fois les migrations terminées.
  • PAYLOAD_SECRET doit être considéré comme immuable après le premier démarrage. Il signe les jetons de session/d'authentification de Payload ; sa rotation invalide toutes les sessions actives.
  • Chemin de vérification d'état. Les sondes de démarrage et de vivacité ciblent /admin — la route de l'interface d'administration de Payload, qui renvoie un 200 sans authentification dès que le serveur Node.js et la connexion à la base de données sont prêts. Prévoyez plusieurs minutes au premier démarrage pour que le job payload-migrate se termine avant que le service ne soit censé servir du contenu réel.
  • Premier compte administrateur. Payload ne dispose d'aucune commande CLI pour créer le premier utilisateur administrateur de manière non interactive. Visitez $SERVICE_URL/admin (ou utilisez kubectl port-forward en cas de ClusterIP) — avec une collection users vide, Payload affiche un formulaire d'inscription pour créer le premier administrateur. Il s'agit d'une étape manuelle et unique de l'opérateur.
  • Les médias téléversés ne sont pas conservés. Aucun bucket de stockage n'est provisionné ; les fichiers téléversés sont écrits sur le disque local du conteneur et sont perdus au prochain redémarrage de pod ou redéploiement.
  • service_type peut nécessiter une bascule manuelle. S'il est exposé en ClusterIP en raison de contraintes de quota d'IP, l'application n'est accessible que via kubectl port-forward/kubectl exec jusqu'à ce qu'il soit rebasculé en LoadBalancer (ou qu'une IP statique soit réservée) et réappliqué.
  • Inspecter l'exécution des jobs :
    kubectl get jobs -n "$NAMESPACE"
    kubectl logs -n "$NAMESPACE" job/<job-name>

4. Variables de configuration​

Les variables sont regroupées exactement comme elles apparaissent sur la plateforme de déploiement. Seuls les paramètres propres à Payload ou notables pour celui-ci sont listés ; toutes les autres entrées sont héritées de App_GKE avec leur comportement et leurs valeurs par défaut standard.

Groupe 1 — Projet et identité​

VariableValeur par défautDescription
project_id(obligatoire)Projet Google Cloud cible.
regionus-central1Région de la charge de travail et des ressources régionales.

Groupe 2 — Environnement de déploiement​

VariableValeur par défautDescription
tenant_iddemoSuffixe court qui rend les noms de ressources uniques par environnement.

Groupe 3 — Identité de l'application​

VariableValeur par défautDescription
application_namepayloadNom de base des ressources. Ne pas modifier après le premier déploiement.
application_display_namePayload CMSNom lisible affiché dans la Console. Texte résiduel provenant de la source clonée du module — remplacez-le par Payload CMS au moment du déploiement ; il est purement cosmétique.
application_versionlatestÉtiquette de suivi du déploiement intégrée à l'image via l'argument de build Cloud Build application_version.

Groupe 4 — Exécution et mise à l'échelle​

VariableValeur par défautDescription
deploy_applicationtrueDéfinissez false pour provisionner uniquement l'infrastructure.
container_image_sourcecustomFixe — il n'existe aucune image Payload préconstruite à déployer.
min_instance_count / max_instance_count1 / 3Nombre minimal de réplicas maintenus actifs ; GKE ne réduit pas à zéro.
container_port3000Valeur par défaut de Next.js.
container_resources{ cpu_limit="1000m", memory_limit="1Gi" }Payload (Next.js) a besoin de marge pour le serveur standalone ainsi que pour l'empreinte TypeScript/CLI du job de migration ; envisagez d'augmenter la mémoire en production.
enable_cloudsql_volumetrueSidecar Cloud SQL Auth Proxy pour les connexions par socket.
enable_image_mirroringtrueMet en miroir l'image construite dans Artifact Registry.

Groupe 5 — Variables d'environnement et secrets​

VariableValeur par défautDescription
environment_variables{}Paramètres supplémentaires non secrets. Ne définissez pas PAYLOAD_SECRET ni DATABASE_URL ici — les deux sont calculés automatiquement.
secret_environment_variables{}Correspondance variable d'environnement → nom du secret Secret Manager.

Groupe 6 — Backend GKE et cluster​

VariableValeur par défautDescription
service_typeLoadBalancerMode d'exposition du Service Kubernetes. Définissez ClusterIP si le quota d'IP statiques du projet est épuisé ; accédez alors via kubectl port-forward.
workload_typenull (se résout en Deployment)Payload n'a pas besoin de PVC par pod par défaut.
session_affinityNoneAucune exigence de session persistante pour cette application de démarrage minimale.

Groupe 11 — Cloud Storage et Artifact Registry​

VariableValeur par défautDescription
enable_gcs_storagefalseDéclarée dans variables.tf avec une description évoquant un adaptateur de stockage GCS compatible S3, mais non transmise à Payload_Common — sans effet. La sortie storage_buckets de Payload_Common vaut toujours [].
gcs_volumes[]Réellement transmise — montages de volumes GCS Fuse, si vous souhaitez raccorder vous-même un stockage persistant.

Groupe 13 — Système de fichiers (NFS) et jobs​

VariableValeur par défautDescription
initialization_jobs[]Laissez vide pour utiliser la chaîne intégrée db-init → payload-migrate.
enable_nfsfalseNon requis — les données propres à Payload résident dans Postgres, pas sur NFS.

Groupe 14 — Observabilité et santé​

VariableValeur par défautDescription
startup_probe_configHTTP /admin, délai de 120 s, période de 15 s, 40 tentativesFenêtre totale d'environ 12 minutes pour que les migrations du premier démarrage se terminent.
health_check_configHTTP /admin, délai de 30 sSonde de vivacité.

Groupe 15 — Backend de base de données​

VariableValeur par défautDescription
database_typePOSTGRES_15Fixe ; Payload requiert PostgreSQL.
application_database_name / application_database_userpayload / payloadNom de la base PostgreSQL et utilisateur de l'application. Immuables après le premier déploiement.

Groupe 19 — Domaine personnalisé, IP statique et réseau​

VariableValeur par défautDescription
reserve_static_iptrueIP externe stable d'un redéploiement à l'autre — utile une fois service_type = LoadBalancer rétabli après un éventuel repli temporaire sur ClusterIP.

Groupe 21 — Cloud Armor et cache Redis​

VariableValeur par défautDescription
enable_redis / redis_host / redis_port / redis_authtrue / "" / 6379 / ""Inertes. Déclarées dans variables.tf (avec une description affirmant que Payload v0.4+ requiert Redis) mais jamais transmises à Payload_Common, qui ne dispose d'aucun raccordement Redis. Les définir n'a aucun effet.

5. Sorties​

Ces valeurs sont renvoyées à l'issue d'un déploiement réussi et constituent le moyen le plus rapide de localiser et d'explorer les ressources en cours d'exécution.

SortieDescription
service_nameNom du Service Kubernetes.
namespaceEspace de noms dans lequel s'exécute la charge de travail.
service_cluster_ipClusterIP interne au cluster.
service_external_ipIP externe du LoadBalancer (lorsqu'une IP statique est réservée ou que service_type = LoadBalancer).
service_urlURL d'accès à Payload.
database_instance_nameNom de l'instance Cloud SQL.
database_name / database_userNom / utilisateur de la base de données de l'application.
database_password_secretSecret Secret Manager contenant le mot de passe de la base.
database_host / database_portPoint de terminaison de la base (127.0.0.1 via l'Auth Proxy) / port.
storage_bucketsToujours vide — aucun bucket n'est provisionné.
container_image / container_registryImage construite et dépôt Artifact Registry.
initialization_jobs / db_import_jobNoms des jobs de configuration et d'import (facultatif).
deployment_id / tenant_id / resource_prefixIdentifiants de nommage.
project_id / project_numberIdentifiants du projet.
kubernetes_readyIndique si le cluster/la charge de travail est prêt.
monitoring_enabled / monitoring_notification_channelsÉtat du monitoring et canaux.
cicd_enabled / cicd_configurationÉtat et détails du CI/CD.
vpc_sc_enabled / vpc_sc_perimeter_name / vpc_sc_dry_run_modeÉtat de VPC-SC.
audit_logging_enabled / artifact_registry_cmek_enabledÉtat de la journalisation d'audit et de CMEK.

6. Pièges de configuration et valeurs par défaut judicieuses​

Risque : Critique (perte de données / panne / sécurité) — Élevé (service dégradé) — Moyen (coût ou dégradation partielle) — Faible (mineur).

ParamètreValeur judicieuseRisqueConséquence en cas d'erreur
PAYLOAD_SECRET (généré automatiquement)Ne jamais effectuer de rotation après le premier démarrageCritiqueSa rotation invalide toutes les sessions actives et oblige tous les utilisateurs à se reconnecter.
application_database_name / application_database_userÀ définir une seule foisCritiqueImmuables après le premier déploiement ; les renommer recrée la base/l'utilisateur et détruit toutes les données.
startup_probe_config / délai de payload-migrateConserver la fenêtre complète d'environ 12 minutesÉlevéSi la fenêtre de la sonde est raccourcie en deçà du temps nécessaire à payload-migrate, le pod peut être marqué comme défaillant avant la fin de la migration du schéma, car les deux s'exécutent en parallèle au lieu que la sonde attende le job.
Persistance des médias/téléversementsAjouter un véritable adaptateur de stockage avant toute utilisation en productionÉlevéSans bucket de stockage raccordé, tous les médias téléversés résident sur le disque local du conteneur et sont perdus à chaque redémarrage de pod ou redéploiement.
service_typeLoadBalancer (par défaut)ÉlevéS'il reste en ClusterIP (par exemple après un repli dû au quota), l'application n'est pas accessible de l'extérieur tant qu'il n'est pas rebasculé.
enable_gcs_storageNe pas compter sur ce paramètreMoyenDéclaré mais non transmis à Payload_Common — l'activer ne provisionne ni ne raccorde aucun stockage.
enable_redis / redis_*Ne pas compter sur ces paramètresMoyenDéclarés mais non transmis à Payload_Common, qui ne dispose d'aucun raccordement Redis — les définir n'a aucun effet.
Création du premier administrateurÀ effectuer rapidement après le déploiementMoyenTant que le premier administrateur n'a pas été créé via le formulaire d'inscription /admin, l'instance ne possède aucun utilisateur authentifié.
container_image_sourceLaisser à customFaibleIl n'existe aucune image Payload préconstruite ; définir prebuilt sans container_image valide fait échouer le déploiement.

Pour le comportement du socle évoqué tout au long de ce guide — IAM et Workload Identity, autoscaling, ingress et certificats, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC-SC, sauvegardes et mise en miroir des images — consultez App_GKE. La configuration applicative propre à Payload partagée avec la variante Cloud Run est décrite dans Payload_Common.

Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.