Aller au contenu principal

App

Disponible

Fonctionnalité déployée et prise en charge. Les champs et commandes décrits ici sont stables au sein de la version v1alpha1.

apiVersiondeploy.stackngo.io/v1alpha1
kindApp
Appliqué parsng apply (manifeste) ou sng app create, patch, scale, restart
Commandes liéessng app list · describe · logs · delete, sng template gen App

À quoi ça sert​

Une App décrit un ensemble de services nommés, comme un docker-compose.yml : une image, des ports, des variables, un volume, une dépendance entre services. La plateforme en fait des conteneurs qui tournent sur votre tenant, un service web exposé en HTTPS sous https://<service>-<app>.<tenant>.dev.stackngo.app/, des tâches ponctuelles (job) ou planifiées (cronjob). Le réseau est fermé par défaut, les conteneurs tournent sans privilège, et l'état de chaque service est lisible dans status.services.

Exemple minimal​

web.yml
apiVersion: deploy.stackngo.io/v1alpha1
kind: App
metadata:
name: web
spec:
services:
web:
image: nginxinc/nginx-unprivileged:1.27
ports: ["8080"]
ingress: { port: 8080 }
sng apply web.yml
sng app describe web # https://web-web.<tenant>.dev.stackngo.app/

Équivalent sans manifeste : sng app create web --image nginxinc/nginx-unprivileged:1.27 --port 8080 --ingress.

Exemple complet commenté​

shop.yml
apiVersion: deploy.stackngo.io/v1alpha1
kind: App
metadata:
name: shop # label DNS, 24 caractères au plus
spec:
imagePullSecrets: [gitlab-registry] # Secrets docker-registry utilisés par tous les services ;
# inutile pour le registre de la plateforme (voir plus bas)
services:
api:
image: registry.gitlab.com/stackngo/shop-api:1.4.2
ports: ["8080"] # "8080" ou "8080:80" (port du Service:port du conteneur), suffixe /udp possible
env:
LOG_LEVEL: info # chaîne
WORKERS: 4 # nombre : converti en "4"
DB_PASSWORD: { secret: db, key: password } # clé d'un Secret, jamais la valeur en clair
envFrom: [shop-api] # toutes les clés du Secret shop-api deviennent des variables
dependsOn: [migrate, db] # attend que migrate soit terminé et db prêt
healthcheck: { http: /healthz, interval: 15s } # sonde readiness + liveness
pullPolicy: always # always, missing ou never
replicas: 2
strategy: { type: rolling, maxSurge: 1, maxUnavailable: 0 } # mise à jour sans coupure
size: xs # 500m CPU / 1Gi réservés, limites au double
ingress: { port: 8080, path: / } # https://api-shop.<tenant>.dev.stackngo.app/
autoscale: { max: 6, cpu: 70 } # de 2 (replicas) à 6 instances, cible 70 % de CPU

migrate:
kind: job # tourne une fois, relancé à chaque changement de sa spec
image: registry.gitlab.com/stackngo/shop-api:1.4.2
command: ["shop", "migrate"] # remplace le CMD de l'image
envFrom: [shop-api]
env:
DB_PASSWORD: { secret: db, key: password }
dependsOn: [db]

cleanup:
kind: cronjob
schedule: "0 3 * * *" # requis pour un cronjob, interdit sinon
image: registry.gitlab.com/stackngo/shop-api:1.4.2
command: ["shop", "gc"]
envFrom: [shop-api]

db:
image: postgres:17
imagePullSecrets: [] # ce service n'utilise aucun Secret de registre
user: "999" # uid numérique non root (runAsUser, runAsGroup, fsGroup)
ports: ["5432"] # joignable par les autres services de l'App : db:5432
env:
PGDATA: /var/lib/postgresql/data/pgdata
POSTGRES_PASSWORD: { secret: db, key: password }
healthcheck: { cmd: ["pg_isready", "-U", "postgres"] }
volumes:
- { path: /var/lib/postgresql/data, size: 10Gi } # volume persistant ; impose 1 réplica

Champs​

Règles de validation croisées​

Le schéma refuse un manifeste qui enfreint une de ces règles, dès sng apply :

  • Le nom de l'App et chaque nom de service sont des labels DNS de 24 caractères au plus (^[a-z](./[-a-z0-9]{0,22}[a-z0-9])?$). Une App compte de 1 à 50 services.
  • schedule est requis pour kind: cronjob et interdit pour les autres kinds.
  • ingress, strategy, replicas, autoscale, allowFrom et restartedAt ne s'appliquent qu'à kind: deployment (le kind par défaut).
  • Un service avec volumes tourne en un seul exemplaire : replicas ≤ 1 et pas d'autoscale.
  • Un service avec volumes qui déclare une strategy doit choisir type: recreate.
  • autoscale.max doit être supérieur ou égal à replicas (1 si absent).
  • healthcheck contient exactement un de http, tcp ou cmd.
  • strategy.maxSurge et strategy.maxUnavailable ne s'appliquent qu'à type: rolling.
  • user est "uid" ou "uid:gid" numériques, uid non nul (root refusé).
  • egress n'accepte que la valeur internet ; size n'accepte que xxxs … xxxl.

D'autres règles sont vérifiées après l'enregistrement. En cas de violation, l'App passe Ready=False avec la raison Invalid :

  • noms de variables d'environnement de la forme ^[A-Za-z_][A-Za-z0-9_.-]*$, valeurs de type chaîne, nombre, booléen ou {secret, key} (null refusé) ;
  • ports uniquement sur un deployment, sans doublon, de la forme "8080", "8080:80" ou "53/udp" (pas d'adresse d'hôte) ;
  • ingress exige un port : ports non vide, et ingress.port présent dans ports ;
  • healthcheck.http exige ports ou healthcheck.port ;
  • allowFrom exige au moins un port déclaré ;
  • strategy: rolling avec maxSurge: 0 et maxUnavailable: 0 est refusée ;
  • dependsOn ne vise ni le service lui-même, ni un service inconnu, ni un cronjob (il n'a pas d'état « terminé »), et ne forme pas de cycle.

L'API refuse en plus, dès sng apply : un ingress sans ports, et un ingress.domain qui n'est pas de la forme <nom>.<tenant>.dev.stackngo.app (voir Exposition et domaines).

App​

App déploie un ensemble de services décrits façon docker compose.

ChampDescriptionDéfautValidation
apiVersion stringdeploy.stackngo.io/v1alpha1
kind stringApp
metadata ObjectMetaSeul metadata.name est à renseigner (nom de la ressource dans votre tenant).
spec AppSpec
status AppStatus

AppSpec​

AppSpec décrit votre application façon docker compose : un ensemble de services nommés.

ChampDescriptionDéfautValidation
imagePullSecrets string arrayImagePullSecrets : noms des secrets d'accès à un registre d'images privé, utilisés par défaut par tous les services.MaxItems: 10
Optional: {}
services object (keys:string, values:Service)Services de l'application, indexés par nom (nom DNS court, ex. api, db).MaxProperties: 50
MinProperties: 1

AppStatus​

AppStatus : état observé de l'application.

ChampDescriptionDéfautValidation
observedGeneration integerDernière version du manifeste prise en compte par la plateforme. Quand elle égale metadata.generation, le statut reflète votre dernier sng apply.Optional: {}
conditions Condition arrayConditions de l'application (Ready, Progressing…), chacune avec son status, sa reason et un message lisible.Optional: {}
services object (keys:string, values:ServiceStatus)État de chaque service de l'application, indexé par son nom.Optional: {}
summary stringSummary : services prêts / total, ex. 3/4.Optional: {}

Service​

Service est un conteneur de l'application : service permanent (deployment), tâche ponctuelle (job) ou tâche planifiée (cronjob).

ChampDescriptionDéfautValidation
kind stringKind : deployment (défaut), job ou cronjob.deploymentEnum: [deployment job cronjob]
Optional: {}
image stringImage du conteneur.MaxLength: 512
MinLength: 1
command string arrayCommand remplace le CMD de l'image (comme command en compose).MaxItems: 64
Optional: {}
entrypoint string arrayEntrypoint remplace l'ENTRYPOINT de l'image.MaxItems: 64
Optional: {}
user stringUser : "uid" ou "uid:gid" numériques sous lesquels tourne le conteneur ; le groupe est aussi
propriétaire des fichiers des volumes. Root (0) est refusé.
Pattern: ^[1-9][0-9]\{0,9\}(:[0-9]\{1,10\})?$
Optional: {}
imagePullSecrets stringImagePullSecrets remplace spec.imagePullSecrets pour ce service ([] = aucun).MaxItems: 10
Optional: {}
pullPolicy stringPullPolicy : always, missing ou never (valeurs compose).Enum: [always missing never]
Optional: {}
env object (keys:string, values:EnvValue)Env : NOM: valeur, ou NOM: {secret: <nom du secret>, key: <clé>}.MaxProperties: 100
Schemaless: {}
Type: object
Optional: {}
envFrom string arrayEnvFrom : secrets de votre tenant dont toutes les clés deviennent des variables d'environnement.MaxItems: 10
Optional: {}
ports IntOrString arrayPorts : "8080" ou "8080:80" (port exposé:port du conteneur), suffixe /udp possible.MaxItems: 20
Optional: {}
volumes Volume arrayVolumes persistants montés dans le conteneur.MaxItems: 10
Optional: {}
dependsOn string arrayDependsOn : services de l'application qui doivent être prêts (deployment) ou terminés (job) avant celui-ci.MaxItems: 20
Optional: {}
init string arrayInit : commande lancée avant chaque démarrage d'un exemplaire du service (même image, même environnement).MaxItems: 64
Optional: {}
healthcheck HealthcheckHealthcheck : test de santé. Un exemplaire en échec ne reçoit plus de trafic ; après retries
échecs consécutifs, son conteneur est redémarré.
Optional: {}
replicas integerReplicas : nombre d'exemplaires du service (deployment uniquement), défaut 1.Maximum: 100
Minimum: 0
Optional: {}
strategy StrategyStrategy de mise à jour (deployment uniquement).Optional: {}
size stringSize : taille du conteneur (réservations ; limites = 2 × réservations). Défaut xxs.Enum: [xxxs xxs xs s m l xl xxl xxxl]
Optional: {}
schedule stringSchedule cron (kind: cronjob uniquement).MaxLength: 128
MinLength: 1
Optional: {}
ingress IngressIngress : exposition publique en HTTP(S) (deployment uniquement).Optional: {}
autoscale AutoscaleAutoscale : ajuste le nombre d'exemplaires selon l'utilisation CPU, entre replicas et autoscale.max.Optional: {}
egress string arrayEgress : sorties ouvertes en plus des services de votre tenant et du DNS. "internet" ouvre
Internet, jamais les réseaux internes de la plateforme.
MaxItems: 1
items:Enum: [internet]
Optional: {}
allowFrom string arrayAllowFrom : Apps du même tenant autorisées à joindre les ports de ce service.MaxItems: 10
items:Pattern: ^[a-z]([-a-z0-9]\{0,22\}[a-z0-9])?$
Optional: {}
restartedAt stringRestartedAt : changer cette valeur redémarre tous les exemplaires du service (sng app restart).MaxLength: 64
Optional: {}

ServiceStatus​

ServiceStatus : état observé d'un service.

ChampDescriptionDéfautValidation
kind stringType du service (service permanent, tâche ponctuelle ou tâche planifiée).
ready booleanReady : deployment disponible et à jour, job terminé, cronjob planifié.
phase stringPhase : Ready, Progressing, Waiting, Failed ou Scheduled.
reason stringRaison courte de l'état courant, utile quand le service n'est pas prêt.Optional: {}
message stringMessage détaillé associé à la raison.Optional: {}
url stringAdresse publique du service lorsqu'il est exposé.Optional: {}

EnvValue​

EnvValue est la valeur d'une variable d'environnement : une chaîne (les nombres et booléens compose sont acceptés et convertis), ou une référence {secret, key}.

ChampDescriptionDéfautValidation
invalid stringRéservé à la plateforme. Ne pas renseigner : une valeur de variable d'environnement s'écrit en texte ou avec secretKeyRef.
raw integer arrayRéservé à la plateforme. Ne pas renseigner.

Volume​

Volume persistant, monté par un seul exemplaire à la fois.

ChampDescriptionDéfautValidation
path stringPath de montage dans le conteneur.MaxLength: 256
Pattern: ^/
size QuantitySize du volume, ex. 10Gi.

Healthcheck​

Healthcheck : exactement un de http, tcp ou cmd.

ChampDescriptionDéfautValidation
http stringHTTP : chemin testé en GET, ex. /healthz.MaxLength: 256
Pattern: ^/
Optional: {}
port integerPort du test http (défaut : premier port du service).Maximum: 65535
Minimum: 1
Optional: {}
tcp integerTCP : port testé en connexion TCP.Maximum: 65535
Minimum: 1
Optional: {}
cmd string arrayCmd : commande exécutée dans le conteneur, code 0 = sain.MaxItems: 32
Optional: {}
interval DurationInterval entre deux tests (défaut 10s).Optional: {}
timeout DurationTimeout d'un test (défaut 5s).Optional: {}
retries integerRetries : échecs consécutifs avant de déclarer le conteneur malade (défaut 3).Maximum: 30
Minimum: 1
Optional: {}
startPeriod DurationStartPeriod : délai de démarrage pendant lequel un échec du test ne redémarre pas le conteneur.Optional: {}

Strategy​

Strategy de mise à jour d'un deployment.

ChampDescriptionDéfautValidation
type stringType : rolling ou recreate.Enum: [rolling recreate]
maxSurge IntOrStringMaxSurge : exemplaires en plus pendant la mise à jour (nombre ou %), défaut 1.Optional: {}
maxUnavailable IntOrStringMaxUnavailable : exemplaires indisponibles tolérés (nombre ou %), défaut 0.Optional: {}

Ingress​

Ingress public du service.

ChampDescriptionDéfautValidation
domain stringDomain : défaut <service>-<app>.<tenant>.<domaine de base>.MaxLength: 253
Pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)+$
Optional: {}
port integerPort du service ciblé (défaut : premier port).Maximum: 65535
Minimum: 1
Optional: {}
path stringPath préfixe, défaut /.MaxLength: 256
Pattern: ^/
Optional: {}

Autoscale​

Autoscale : ajustement automatique du nombre d'exemplaires selon l'utilisation CPU.

ChampDescriptionDéfautValidation
max integerMax : nombre maximal d'exemplaires.Maximum: 100
Minimum: 1
cpu integerCPU : utilisation cible en % des réservations (défaut 80).Maximum: 100
Minimum: 1
Optional: {}

Images du registre de la plateforme. Une image <cluster>.registry.dev.stackngo.fr/<tenant>/… (l'hôte du cluster de l'App, par exemple ovh-gra-0.registry.dev.stackngo.fr) est tirée sans rien déclarer, avec l'accès en lecture du tenant (robot default-pull), tant que ni le service ni l'App ne donnent d'imagePullSecrets. Dès qu'une liste est donnée (même vide, [], au niveau du service), elle remplace cet accès : pour mêler une image d'un registre externe et une image de la plateforme, donnez l'imagePullSecrets du registre externe au service concerné plutôt qu'à l'App. Une image du registre d'un autre cluster est refusée (image … is hosted on the registry of cluster …). Voir Pousser et tirer des images.

Statut et conditions​

status.summary donne le nombre de services prêts sur le total (3/4). status.services.<nom> détaille chaque service : kind, ready, phase, reason, message et url (service exposé).

Phase d'un serviceSignification
Readydeployment disponible et à jour (et, s'il est exposé, certificat HTTPS émis), ou job terminé
Progressingdéploiement en cours, certificat HTTPS en cours d'émission, job en cours d'exécution
Waitingbloqué par une dépendance, un Secret de registre absent ou l'accès au registre pas encore prêt ; rien n'est appliqué
Failedrefus (domaine, conflit, nom de domaine déjà pris, quota) ou échec (job, déploiement qui n'aboutit pas)
Scheduledcronjob planifié (compte comme prêt)
ConditionTrue quandFalse quand
Readytous les services sont Ready ou Scheduledau moins un service ne l'est pas ; reason et message reprennent le service le plus en difficulté (échec, puis attente, puis déploiement)
Progressingau moins un service est en Progressing ou Waiting (raison RollingOut)rien n'est en cours (Reconciled)
Degradedl'App est invalide ou au moins un service est Failedaucun échec (Reconciled)
reasonOùExplicationAction
ReconciledApp, serviceTout est appliqué et prêt—
InvalidApp, serviceRègle vérifiée après l'enregistrement violée (voir ci-dessus) ; le message cite le champCorriger le manifeste, sng apply
DomainNotAllowedserviceingress.domain hors de votre zone, ou nom réservé (status.<zone>)Choisir <nom>.<tenant>.dev.stackngo.app ou retirer domain
HostConflictserviceLe nom d'hôte est déjà servi ailleurs (autre tenant, ou autre App du tenant sur le même chemin)Changer de nom de service, d'App ou de path
NameConflictserviceUn élément du même nom existe déjà dans votre tenant et n'appartient pas à cette AppRenommer le service ou l'App
DNSConflictservice (Failed)Le nom de domaine est déjà publié dans le DNS par quelqu'un d'autre (un enregistrement DNSRecord, par exemple) : Le nom de domaine … est déjà utilisé : l'App n'est pas joignable à cette adresse tant qu'il n'est pas libéré. Les services qui en dépendent démarrent quand mêmeLibérer le nom (supprimer l'enregistrement) ou changer ingress.domain ; le service se rétablit de lui-même
CertificatePendingservice (Progressing)Le service répond, mais son certificat HTTPS n'est pas encore émis : Le certificat HTTPS de l'App est en cours d'émission. Les services qui en dépendent démarrent sans l'attendreAttendre, en général quelques minutes ; pour un domaine apporté, vérifier que la zone est Active
ImagePullSecretNotFoundservice (Waiting)Un Secret de imagePullSecrets n'existe pas ; le service n'est pas créé, re-vérifié toutes les 30 sCréer le Secret ou corriger le nom
RegistryAccessPendingservice (Waiting)L'image vient du registre de la plateforme et l'accès en lecture du tenant n'est pas encore prêt (tenant tout juste créé) : l'accès au registre d'images est en cours de préparation, nouvelle tentative automatiqueAttendre : le service démarre de lui-même
SecretNotFoundservice (Progressing)Un Secret de env / envFrom n'existe pas ; les conteneurs ne démarrent pas tant qu'il manqueCréer le Secret (Injection dans une App)
WaitingForDependencyservice (Waiting)Un service de dependsOn n'est pas encore prêt ou terminéAttendre, ou regarder le service dépendu
DependencyFailedservice (Waiting)Un service de dependsOn est en échecCorriger le service dépendu
QuotaExceededservice (Failed ou Progressing)La taille demandée dépasse le quota de votre offreRéduire size ou replicas, ou changer d'offre
ApplyFailedserviceLa plateforme n'a pas pu appliquer le service ; le message donne l'erreurRelire le message ; contacter le support si elle persiste
RollingOutservice (Progressing)Mise à jour en cours : n/m à jour, k disponiblesAttendre ; sng app logs si ça dure
RolloutFailedservice (Failed)Le déploiement n'a pas abouti dans le délai (conteneurs qui ne démarrent pas)sng app logs, vérifier image, user, healthcheck
JobRunningservice (Progressing)Le job s'exécuteAttendre
JobFailedservice (Failed)Le job a échoué après 3 tentativessng app logs --service <job>, corriger, sng apply

Ce qui se passe quand vous supprimez​

Un service retiré du manifeste s'arrête et n'est plus joignable ; son adresse HTTPS cesse de répondre. Ses volumes sont conservés avec leurs données.

Une App supprimée (sng app delete <app> ou sng kind delete App <app>) : tous ses services s'arrêtent, ses adresses cessent de répondre, ses jobs et cronjobs ne s'exécutent plus. Les volumes sont conservés avec leurs données ; aucun champ deletionPolicy ne permet de les supprimer automatiquement dans cette version. Pour les faire supprimer, demandez-le au support.

À la mise à jour, un deployment suit sa strategy (par défaut rolling : une instance en plus, aucune indisponible), un job dont la spec change est relancé, et sng app restart redémarre les instances d'un service sans rien changer d'autre. Un service en attente (Waiting) ou refusé (Failed) garde sa version précédente si elle existait.

Erreurs fréquentes​

SymptômeCauseCorrection
sng apply refuse : schedule est requis pour kind: cronjob et interdit sinonschedule sur un service qui n'est pas un cronjob, ou cronjob sans scheduleAjouter kind: cronjob ou retirer schedule
sng apply refuse : un service avec volume tourne en un seul exemplairereplicas: 2 ou autoscale sur un service avec volumesRetirer replicas/autoscale, ou sortir l'état du service (base gérée, stockage objet)
sng apply refuse : champs inconnus : spec.services.web.privilegedChamp hors schéma (souvent une clé compose non traduite)Retirer le champ ; voir Depuis docker compose
sng apply refuse : ingress.domain "…" : seul <nom>.<tenant>.dev.stackngo.app est autoriséDomaine hors de votre zoneUtiliser un nom sous votre zone, ou laisser le domaine par défaut
Service Progressing, RollingOut qui ne finit jamais, puis RolloutFailedL'image démarre en root (refusée), ou la sonde healthcheck échoueImage non root ou user: "<uid>" ; vérifier le chemin et le port de la sonde ; sng app logs
Service Waiting, ImagePullSecretNotFoundLe Secret de registre n'existe pas sous ce nomCréer le Secret docker-registry, ou retirer imagePullSecrets (image publique, ou image du registre de la plateforme)
Service Waiting, RegistryAccessPendingImage du registre de la plateforme, accès du tenant pas encore prêtAttendre ; signaler si cela dure plus de quelques minutes
Service Progressing, CertificatePendingCertificat HTTPS en cours d'émissionAttendre ; si cela dure plus d'une heure, contacter le support
Service Failed, DNSConflict : Le nom de domaine … est déjà utiliséUn enregistrement DNS publie déjà ce nomSupprimer l'enregistrement, ou choisir un autre domain
Service Progressing, SecretNotFoundUn Secret de env/envFrom manqueLe créer ; les conteneurs démarrent dès qu'il existe
Service Failed, QuotaExceededsize × replicas dépasse le quota de l'offre (les limites comptent 2 × les réservations)Réduire la taille, ou changer d'offre
Service Failed, HostConflict : le host … est déjà utiliséUn autre tenant sert ce nom d'hôteChoisir un autre nom d'App ou de service

Voir aussi​