Aller au contenu principal

Depuis docker compose

Disponible

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

La conversion automatique d'un fichier compose n'est pas encore proposée en ligne de commande : ce guide donne la correspondance à appliquer à la main. Elle reprend exactement les règles du convertisseur de la plateforme.

Un manifeste App se lit comme un docker-compose.yml : des services nommés, une image, des ports, des variables, des volumes, des dépendances. Ce guide vous donne la correspondance champ par champ pour passer de l'un à l'autre, et liste ce qui ne se traduit pas.

Prérequis​

  • Une session ouverte et un tenant choisi : sng sso login, sng tenant use.
  • Vos images construites et poussées dans un registre joignable depuis la plateforme (build: n'est pas traduit).
  • Pour une image privée : un Secret docker-registry sur votre tenant (Variables et secrets).

Étapes​

1. Partir du gabarit​

sng template gen App shop
créé /home/vous/shop.yml (kind=App)

Le fichier contient un service web commenté ; remplacez-le par vos services.

2. Traduire service par service​

Prenons ce docker-compose.yml :

docker-compose.yml
name: shop
services:
api:
image: registry.gitlab.com/stackngo/shop-api:1.4.2
command: shop serve --port 8080
ports: ["8080"]
environment:
LOG_LEVEL: info
WORKERS: 4
env_file: [.env]
depends_on:
migrate: { condition: service_completed_successfully }
db: { condition: service_healthy }
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"]
interval: 15s
retries: 5
deploy:
replicas: 2
resources:
reservations: { cpus: "0.25", memory: 256M }
update_config: { order: start-first }
migrate:
image: registry.gitlab.com/stackngo/shop-api:1.4.2
command: ["shop", "migrate"]
depends_on: [db]
db:
image: postgres:17
user: "999"
expose: ["5432"]
environment:
POSTGRES_PASSWORD: changeme
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
volumes:
pgdata:

Sa traduction :

shop.yml
apiVersion: deploy.stackngo.io/v1alpha1
kind: App
metadata:
name: shop # name du projet compose
spec:
imagePullSecrets: [gitlab-registry] # registre privé : Secret à créer sur le tenant
services:
api:
image: registry.gitlab.com/stackngo/shop-api:1.4.2
command: ["shop", "serve", "--port", "8080"]
ports: ["8080"]
env:
LOG_LEVEL: info
WORKERS: 4
envFrom: [api-env] # env_file → Secret api-env, à créer
dependsOn: [db, migrate] # migrate attendu « terminé » → migrate devient un job
healthcheck:
cmd: ["wget", "-qO-", "http://127.0.0.1:8080/healthz"]
interval: 15s
retries: 5
replicas: 2
size: xs # 0.25 CPU / 256M → plus petite taille qui couvre
strategy: { type: rolling, maxSurge: 1, maxUnavailable: 0 } # order: start-first
ingress: { port: 8080 } # compose n'a pas d'équivalent : à ajouter
migrate:
kind: job # attendu en service_completed_successfully
image: registry.gitlab.com/stackngo/shop-api:1.4.2
command: ["shop", "migrate"]
dependsOn: [db]
db:
image: postgres:17
user: "999"
ports: ["5432"] # expose → ports (joignable en db:5432 par les autres services)
env:
POSTGRES_PASSWORD: { secret: db, key: password } # jamais la valeur en clair
healthcheck: { cmd: ["sh", "-c", "pg_isready -U postgres"] }
volumes:
- { path: /var/lib/postgresql/data, size: 10Gi } # volume nommé → volume persistant, taille à choisir

3. Créer les Secrets attendus​

Le manifeste référence gitlab-registry, api-env et db. Créez-les avant ou juste après l'apply : voir Injection dans une App. Tant qu'un Secret d'environnement manque, le service reste en Progressing avec la raison SecretNotFound ; un Secret de registre manquant bloque le service en Waiting (ImagePullSecretNotFound).

4. Appliquer et suivre​

sng apply shop.yml
sng app describe shop
App : shop
Statut : en cours
Message : api : attend : migrate
SERVICE KIND IMAGE PHASE URL DETAIL
api deployment registry.gitlab.com/stackngo/shop-api:1.4.2 Waiting - WaitingForDependency attend : migrate
db deployment postgres:17 Ready - Reconciled 1/1 prêts
migrate job registry.gitlab.com/stackngo/shop-api:1.4.2 Progressing - JobRunning job en cours

Vérification​

sng app describe shop affiche Statut : prête et, pour api, l'URL https://api-shop.<tenant>.dev.stackngo.app/. curl -I sur cette URL répond HTTP/2 200.

Correspondance champ par champ​

composeAppRemarque
name (projet)metadata.nameminuscules, _ et . deviennent -, 24 caractères au plus
nom de serviceclé de spec.servicesmême normalisation ; cache_store → cache-store
imageimagerequis
build—ignoré : construisez et poussez l'image, puis renseignez image
commandcommandforme liste ; une chaîne compose est découpée comme par le shell
entrypointentrypoint
user: "999", "1000:1000"userseulement un uid numérique non root ; user: redis est ignoré
environment: K: venv: K: vvaleur recopiée ; ${VAR} n'est pas interpolé (remplacez la valeur)
environment: K: (sans valeur)—ignoré : la valeur venait de votre machine
env_file: [.env]envFrom: [<service>-env]le fichier n'est pas recopié : créez le Secret <service>-env avec son contenu
valeur qui ressemble à un secret (PASSWORD, TOKEN, KEY…)env: K: { secret: s, key: k }mettez-la dans un Secret
ports: "8080", "80:8080", "53:53/udp"portspublié:cible ; host_ip ignoré ; une plage (8000-8010) est refusée
expose: ["5432"]ports: ["5432"]même chose : un port déclaré est joignable par les autres services de l'App
volumes: nom:/cheminvolumes: [{ path: /chemin, size: 1Gi }]volume persistant ; taille à ajuster (1Gi par défaut) ; impose un seul réplica
volumes: ./fichier:/chemin (bind), tmpfs—ignorés : seuls les volumes nommés deviennent des volumes
depends_on: [db]dependsOn: [db]la condition service_healthy est implicite (le service doit être prêt)
depends_on: x: { condition: service_completed_successfully }dependsOn: [x] et x devient kind: job
healthcheck.test: ["CMD", …]healthcheck.cmd: […]
healthcheck.test: ["CMD-SHELL", "…"]healthcheck.cmd: ["sh", "-c", "…"]
interval, timeout, start_period, retriesinterval, timeout, startPeriod, retriesretries borné à 1–30
healthcheck: disable: true ou test: ["NONE"]—pas de sonde
pull_policy: always / missing, if_not_present / neverpullPolicy: always / missing / never
deploy.replicas: nreplicas: n0–100 ; ramené à 1 si le service a un volume
deploy.resources.reservations (ou limits à défaut)sizeplus petite taille dont les réservations couvrent CPU et mémoire ; au-delà de xxxl, xxxl
deploy.update_config: order: start-firststrategy: { type: rolling, maxSurge: <parallelism ou 1>, maxUnavailable: 0 }pas de strategy si le service a un volume (il est en recreate)
deploy.update_config: order: stop-first (ou absent)strategy: { type: rolling, maxSurge: 0, maxUnavailable: <parallelism ou 1> }
profiles—choisissez les services à garder
—ingressaucun équivalent compose : à ajouter sur le service web
—egress: [internet]compose laisse sortir vers Internet ; ici c'est fermé par défaut
—sizedéfaut xxs si vous ne précisez rien

Ce qui n'est pas supporté​

Ces clés compose n'ont pas d'équivalent et sont écartées, le plus souvent parce qu'elles contredisent la sécurité par défaut (conteneur sans privilège, réseau fermé) :

  • privileged, cap_add, network_mode, pid, devices, tmpfs ;
  • secrets montés en fichier et la section secrets: : utilisez env: { secret, key } ou envFrom ;
  • configs et la section configs: ;
  • build, les bind mounts, les plages de ports, host_ip ;
  • restart, container_name, networks, labels, logging, extra_hosts : non repris (le redémarrage est toujours automatique, le nom du service sert de nom DNS, le réseau est celui de l'App).
Deux changements de comportement à anticiper

Non-root. Vos conteneurs tournent sans privilège. Une image qui démarre en root (nginx, postgres sans user) est refusée : prenez une variante non root (nginxinc/nginx-unprivileged) ou donnez user: "<uid>".

Réseau fermé. Aucune sortie vers Internet sans egress: [internet] sur le service concerné. Entre services d'une même App, seuls les ports déclarés sont joignables.

Pour aller plus loin​

  • Un service compose nommé web dans l'App shop est joignable par les autres services en web:<port>, comme en compose : c'est le nom du service qui compte, pas celui de l'App.
  • Les tailles et leur calcul depuis deploy.resources : Tailles et ressources.
  • Les règles qui peuvent refuser le manifeste : page App de la référence des manifestes.

Voir aussi​