App
Fonctionnalité déployée et prise en charge. Les champs et commandes décrits ici sont stables au sein de la version v1alpha1.
apiVersion | deploy.stackngo.io/v1alpha1 |
kind | App |
| Appliqué par | sng apply (manifeste) ou sng app create, patch, scale, restart |
| Commandes liées | sng 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
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é
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. scheduleest requis pourkind: cronjobet interdit pour les autres kinds.ingress,strategy,replicas,autoscale,allowFrometrestartedAtne s'appliquent qu'àkind: deployment(le kind par défaut).- Un service avec
volumestourne en un seul exemplaire :replicas≤ 1 et pas d'autoscale. - Un service avec
volumesqui déclare unestrategydoit choisirtype: recreate. autoscale.maxdoit être supérieur ou égal àreplicas(1 si absent).healthcheckcontient exactement un dehttp,tcpoucmd.strategy.maxSurgeetstrategy.maxUnavailablene s'appliquent qu'àtype: rolling.userest"uid"ou"uid:gid"numériques, uid non nul (root refusé).egressn'accepte que la valeurinternet;sizen'accepte quexxxs…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}(nullrefusé) ; portsuniquement sur undeployment, sans doublon, de la forme"8080","8080:80"ou"53/udp"(pas d'adresse d'hôte) ;ingressexige un port :portsnon vide, etingress.portprésent dansports;healthcheck.httpexigeportsouhealthcheck.port;allowFromexige au moins un port déclaré ;strategy: rollingavecmaxSurge: 0etmaxUnavailable: 0est refusée ;dependsOnne vise ni le service lui-même, ni un service inconnu, ni uncronjob(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.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
apiVersion string | deploy.stackngo.io/v1alpha1 | ||
kind string | App | ||
metadata ObjectMeta | Seul 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.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
imagePullSecrets string array | ImagePullSecrets : 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.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
observedGeneration integer | Derniè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 array | Conditions 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 string | Summary : 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).
| Champ | Description | Défaut | Validation |
|---|---|---|---|
kind string | Kind : deployment (défaut), job ou cronjob. | deployment | Enum: [deployment job cronjob] Optional: {} |
image string | Image du conteneur. | MaxLength: 512 MinLength: 1 | |
command string array | Command remplace le CMD de l'image (comme command en compose). | MaxItems: 64 Optional: {} | |
entrypoint string array | Entrypoint remplace l'ENTRYPOINT de l'image. | MaxItems: 64 Optional: {} | |
user string | User : "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 string | ImagePullSecrets remplace spec.imagePullSecrets pour ce service ([] = aucun). | MaxItems: 10 Optional: {} | |
pullPolicy string | PullPolicy : 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 array | EnvFrom : secrets de votre tenant dont toutes les clés deviennent des variables d'environnement. | MaxItems: 10 Optional: {} | |
ports IntOrString array | Ports : "8080" ou "8080:80" (port exposé:port du conteneur), suffixe /udp possible. | MaxItems: 20 Optional: {} | |
volumes Volume array | Volumes persistants montés dans le conteneur. | MaxItems: 10 Optional: {} | |
dependsOn string array | DependsOn : services de l'application qui doivent être prêts (deployment) ou terminés (job) avant celui-ci. | MaxItems: 20 Optional: {} | |
init string array | Init : commande lancée avant chaque démarrage d'un exemplaire du service (même image, même environnement). | MaxItems: 64 Optional: {} | |
healthcheck Healthcheck | Healthcheck : 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 integer | Replicas : nombre d'exemplaires du service (deployment uniquement), défaut 1. | Maximum: 100 Minimum: 0 Optional: {} | |
strategy Strategy | Strategy de mise à jour (deployment uniquement). | Optional: {} | |
size string | Size : taille du conteneur (réservations ; limites = 2 × réservations). Défaut xxs. | Enum: [xxxs xxs xs s m l xl xxl xxxl] Optional: {} | |
schedule string | Schedule cron (kind: cronjob uniquement). | MaxLength: 128 MinLength: 1 Optional: {} | |
ingress Ingress | Ingress : exposition publique en HTTP(S) (deployment uniquement). | Optional: {} | |
autoscale Autoscale | Autoscale : ajuste le nombre d'exemplaires selon l'utilisation CPU, entre replicas et autoscale.max. | Optional: {} | |
egress string array | Egress : 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 array | AllowFrom : 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 string | RestartedAt : changer cette valeur redémarre tous les exemplaires du service (sng app restart). | MaxLength: 64 Optional: {} |
ServiceStatus
ServiceStatus : état observé d'un service.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
kind string | Type du service (service permanent, tâche ponctuelle ou tâche planifiée). | ||
ready boolean | Ready : deployment disponible et à jour, job terminé, cronjob planifié. | ||
phase string | Phase : Ready, Progressing, Waiting, Failed ou Scheduled. | ||
reason string | Raison courte de l'état courant, utile quand le service n'est pas prêt. | Optional: {} | |
message string | Message détaillé associé à la raison. | Optional: {} | |
url string | Adresse 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}.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
invalid string | Réservé à la plateforme. Ne pas renseigner : une valeur de variable d'environnement s'écrit en texte ou avec secretKeyRef. | ||
raw integer array | Réservé à la plateforme. Ne pas renseigner. |
Volume
Volume persistant, monté par un seul exemplaire à la fois.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
path string | Path de montage dans le conteneur. | MaxLength: 256 Pattern: ^/ | |
size Quantity | Size du volume, ex. 10Gi. |
Healthcheck
Healthcheck : exactement un de http, tcp ou cmd.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
http string | HTTP : chemin testé en GET, ex. /healthz. | MaxLength: 256 Pattern: ^/ Optional: {} | |
port integer | Port du test http (défaut : premier port du service). | Maximum: 65535 Minimum: 1 Optional: {} | |
tcp integer | TCP : port testé en connexion TCP. | Maximum: 65535 Minimum: 1 Optional: {} | |
cmd string array | Cmd : commande exécutée dans le conteneur, code 0 = sain. | MaxItems: 32 Optional: {} | |
interval Duration | Interval entre deux tests (défaut 10s). | Optional: {} | |
timeout Duration | Timeout d'un test (défaut 5s). | Optional: {} | |
retries integer | Retries : échecs consécutifs avant de déclarer le conteneur malade (défaut 3). | Maximum: 30 Minimum: 1 Optional: {} | |
startPeriod Duration | StartPeriod : 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.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
type string | Type : rolling ou recreate. | Enum: [rolling recreate] | |
maxSurge IntOrString | MaxSurge : exemplaires en plus pendant la mise à jour (nombre ou %), défaut 1. | Optional: {} | |
maxUnavailable IntOrString | MaxUnavailable : exemplaires indisponibles tolérés (nombre ou %), défaut 0. | Optional: {} |
Ingress
Ingress public du service.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
domain string | Domain : 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 integer | Port du service ciblé (défaut : premier port). | Maximum: 65535 Minimum: 1 Optional: {} | |
path string | Path préfixe, défaut /. | MaxLength: 256 Pattern: ^/ Optional: {} |
Autoscale
Autoscale : ajustement automatique du nombre d'exemplaires selon l'utilisation CPU.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
max integer | Max : nombre maximal d'exemplaires. | Maximum: 100 Minimum: 1 | |
cpu integer | CPU : 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 service | Signification |
|---|---|
Ready | deployment disponible et à jour (et, s'il est exposé, certificat HTTPS émis), ou job terminé |
Progressing | déploiement en cours, certificat HTTPS en cours d'émission, job en cours d'exécution |
Waiting | bloqué par une dépendance, un Secret de registre absent ou l'accès au registre pas encore prêt ; rien n'est appliqué |
Failed | refus (domaine, conflit, nom de domaine déjà pris, quota) ou échec (job, déploiement qui n'aboutit pas) |
Scheduled | cronjob planifié (compte comme prêt) |
| Condition | True quand | False quand |
|---|---|---|
Ready | tous les services sont Ready ou Scheduled | au moins un service ne l'est pas ; reason et message reprennent le service le plus en difficulté (échec, puis attente, puis déploiement) |
Progressing | au moins un service est en Progressing ou Waiting (raison RollingOut) | rien n'est en cours (Reconciled) |
Degraded | l'App est invalide ou au moins un service est Failed | aucun échec (Reconciled) |
reason | Où | Explication | Action |
|---|---|---|---|
Reconciled | App, service | Tout est appliqué et prêt | — |
Invalid | App, service | Règle vérifiée après l'enregistrement violée (voir ci-dessus) ; le message cite le champ | Corriger le manifeste, sng apply |
DomainNotAllowed | service | ingress.domain hors de votre zone, ou nom réservé (status.<zone>) | Choisir <nom>.<tenant>.dev.stackngo.app ou retirer domain |
HostConflict | service | Le 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 |
NameConflict | service | Un élément du même nom existe déjà dans votre tenant et n'appartient pas à cette App | Renommer le service ou l'App |
DNSConflict | service (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ême | Libérer le nom (supprimer l'enregistrement) ou changer ingress.domain ; le service se rétablit de lui-même |
CertificatePending | service (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'attendre | Attendre, en général quelques minutes ; pour un domaine apporté, vérifier que la zone est Active |
ImagePullSecretNotFound | service (Waiting) | Un Secret de imagePullSecrets n'existe pas ; le service n'est pas créé, re-vérifié toutes les 30 s | Créer le Secret ou corriger le nom |
RegistryAccessPending | service (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 automatique | Attendre : le service démarre de lui-même |
SecretNotFound | service (Progressing) | Un Secret de env / envFrom n'existe pas ; les conteneurs ne démarrent pas tant qu'il manque | Créer le Secret (Injection dans une App) |
WaitingForDependency | service (Waiting) | Un service de dependsOn n'est pas encore prêt ou terminé | Attendre, ou regarder le service dépendu |
DependencyFailed | service (Waiting) | Un service de dependsOn est en échec | Corriger le service dépendu |
QuotaExceeded | service (Failed ou Progressing) | La taille demandée dépasse le quota de votre offre | Réduire size ou replicas, ou changer d'offre |
ApplyFailed | service | La plateforme n'a pas pu appliquer le service ; le message donne l'erreur | Relire le message ; contacter le support si elle persiste |
RollingOut | service (Progressing) | Mise à jour en cours : n/m à jour, k disponibles | Attendre ; sng app logs si ça dure |
RolloutFailed | service (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 |
JobRunning | service (Progressing) | Le job s'exécute | Attendre |
JobFailed | service (Failed) | Le job a échoué après 3 tentatives | sng 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ôme | Cause | Correction |
|---|---|---|
sng apply refuse : schedule est requis pour kind: cronjob et interdit sinon | schedule sur un service qui n'est pas un cronjob, ou cronjob sans schedule | Ajouter kind: cronjob ou retirer schedule |
sng apply refuse : un service avec volume tourne en un seul exemplaire | replicas: 2 ou autoscale sur un service avec volumes | Retirer replicas/autoscale, ou sortir l'état du service (base gérée, stockage objet) |
sng apply refuse : champs inconnus : spec.services.web.privileged | Champ 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 zone | Utiliser un nom sous votre zone, ou laisser le domaine par défaut |
Service Progressing, RollingOut qui ne finit jamais, puis RolloutFailed | L'image démarre en root (refusée), ou la sonde healthcheck échoue | Image non root ou user: "<uid>" ; vérifier le chemin et le port de la sonde ; sng app logs |
Service Waiting, ImagePullSecretNotFound | Le Secret de registre n'existe pas sous ce nom | Créer le Secret docker-registry, ou retirer imagePullSecrets (image publique, ou image du registre de la plateforme) |
Service Waiting, RegistryAccessPending | Image du registre de la plateforme, accès du tenant pas encore prêt | Attendre ; signaler si cela dure plus de quelques minutes |
Service Progressing, CertificatePending | Certificat HTTPS en cours d'émission | Attendre ; 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 nom | Supprimer l'enregistrement, ou choisir un autre domain |
Service Progressing, SecretNotFound | Un Secret de env/envFrom manque | Le créer ; les conteneurs démarrent dès qu'il existe |
Service Failed, QuotaExceeded | size × 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ôte | Choisir un autre nom d'App ou de service |
Voir aussi
- Guides : Depuis docker compose, Services, jobs et cronjobs, Variables et secrets, Stockage, Exposition et domaines, Healthchecks et scaling, Tailles et ressources, Réseau, Opérations.
- CLI :
sng app, flags globaux et commandes de l'état désiré. - Autres kinds :
SSMSecret(secrets injectés),DNSZone(domaines apportés, Bêta).