CONTAINER APPS · GUIDE CDNCTL
Migrer un projet Docker multi-services, de bout en bout avec cdnctl
Ce guide explique comment prendre un projet multi-services de style docker-compose (services web/API, workers en arrière-plan, une base de données, un cache et un bus de messages) et l'exécuter sur la plateforme de conteneurs managés CDN.com.tr — entièrement depuis la ligne de commande cdnctl. Remplacez les valeurs d'exemple (<account-uuid>, noms d'images, ports) par les vôtres.
Si vous avez déjà un docker-compose.yml, vous pouvez sauter les étapes manuelles de création et de connexion ci-dessous : la plateforme analyse votre fichier compose et propose un plan prêt à confirmer. Voir le guide d'import Docker Compose ou la référence compose de cdnctl. Les étapes de bout en bout ci-dessous restent le moyen d'affiner ou de migrer tout ce que l'import compose ne couvre pas.
1 · Concepts
- Compte = projet. Un compte peut contenir plusieurs container apps ; votre forfait détermine combien d'applications vous pouvez exécuter.
- App = une image de conteneur (un service). Chaque app a un port, un contrôle de santé, des ressources, des variables d'environnement et des secrets, et peut être mise à l'échelle sur N réplicas.
- Les add-ons managés sont des backends avec état —
postgres(PostgreSQL/TimescaleDB),mysql,redisetnats(JetStream) — que vous rattachez à une app. Chaque addon est provisionné pour l'app sur laquelle vous l'activez et expose un hostname stable au sein du cluster. Ses détails de connexion — host, port, utilisateur, base de données et le mot de passe généré — sont injectés directement dans cette app en tant que variables d'environnement et secrets (par ex.DATABASE_URL,REDIS_URL,NATS_URL), prêts à l'emploi. Le mot de passe est généré pour vous et n'est jamais réaffiché, donc vous ne construisez pas de chaîne de connexion à la main pour l'app propriétaire. - Réseau privé et service discovery. Les apps de votre compte partagent un réseau privé. Chaque app est accessible depuis vos autres apps par son nom d'app — exactement comme un nom de service docker-compose — à l'adresse
http://<app-name>:<port>(nous publions un alias DNS interne égal à votre nom d'app). Donc si votre fichier compose s'adresse àhttp://hot-data-store:8082, nommez cette apphot-data-storeavec le port8082et cela fonctionne directement. Les apps peuvent atteindre internet mais pas les services privés des autres clients. - Les hosts des addons vous sont retournés. Quand vous activez un addon, son host stable au sein du cluster est rapporté par
cdnctl container addons list(le champhost). C'est ce<db-host>/<redis-host>/<nats-host>qui est utilisé dans les chaînes de connexion ci-dessous — vous n'avez pas à le deviner. - Exposition publique. Toute app peut être publiée sur un sous-domaine CDN.com.tr ou sur votre propre domaine. Chaque service exposé obtient son propre hostname.
2 · Configurez cdnctl
Authentifiez-vous une fois avec votre token API du panneau (ou faites login avec vos identifiants) :
cdnctl configure --endpoint https://cdn.com.tr --token <your-api-token>
# verify
cdnctl accounts list
cdnctl container apps list --account <account-uuid>
3 · Générez et publiez les images, enregistrez un identifiant de pull
Générez chaque service pour linux/amd64 et poussez-le vers un registre que la plateforme peut pull. Si les dépôts sont privés, enregistrez un identifiant de pull sur le compte :
# build & push (one image per service)
docker buildx build --platform linux/amd64 -t your-registry/app-web:1.0.0 --push .
docker buildx build --platform linux/amd64 -t your-registry/app-worker:1.0.0 --push .
# register pull credentials (token is stored encrypted, never returned)
cdnctl container registry-credentials create --account <account-uuid> \
--name registry --registry-url https://index.docker.io/v1/ \
--username <user> --password <token>
cdnctl container registry-credentials list --account <account-uuid>
N'intégrez jamais de secrets dans les images. Utilisez un .dockerignore pour exclure .env, les artefacts de build et les données VCS.
4 · Provisionnez les add-ons managés (DB, cache, bus de messages)
Chaque addon est rattaché à une app ; vous créez donc d'abord cette app (étape 5), puis vous activez les addons dessus. L'addon est provisionné pour cette app et sa connexion y est injectée automatiquement — vous n'assemblez pas l'URL et ne copiez pas de mot de passe :
# enable the backends your owner app needs (app created in step 5):
cdnctl container addons enable-postgres --account <account-uuid> --app <app-uuid> --storage-mb 10240
cdnctl container addons enable-redis --account <account-uuid> --app <app-uuid>
cdnctl container addons enable-nats --account <account-uuid> --app <app-uuid> --storage-mb 5120
cdnctl container addons list --account <account-uuid> --app <app-uuid>
Désormais, l'environnement de cette app contient déjà des valeurs prêtes à l'emploi — vous les référencez, vous ne les construisez pas :
# auto-injected into the app the addon is enabled on:
DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USER # env
DATABASE_PASSWORD, DATABASE_URL # secrets (full postgres:// URL)
REDIS_URL # redis://<redis-host>:6379/0
NATS_URL # nats://<nats-host>:4222
cdnctl container addons list rapporte, à titre de référence, le host stable, le port, l'utilisateur et le nom de base de données de chaque addon (par ex. host: ca-…-postgres) — le mot de passe généré n'est jamais retourné. Vous n'avez donc pas besoin d'écrire DATABASE_URL à la main pour l'app propriétaire ; c'est déjà un secret sur celle-ci.
Partager un backend entre applications. Les backends sans mot de passe — NATS, et Redis tel que configuré ici — sont aussi accessibles depuis vos autres apps : récupérez le host depuis addons list et définissez NATS_URL/REDIS_URL en env sur chaque consommateur (étape 5). Une base de données SQL protégée par mot de passe est différente : comme le mot de passe n'est jamais injecté que dans l'app propriétaire de l'addon (et jamais affiché), n'essayez pas de le réutiliser depuis une seconde app. À la place, laissez soit une app posséder la base de données et faites en sorte que les autres l'appellent, elle, via votre API, soit exécutez votre propre image de base de données avec un mot de passe que vous définissez vous-même. Les hosts d'app à app sont simplement vos noms d'app (l'alias de style docker-compose de l'étape 1), donc des URL comme http://hot-data-store:8082 se résolvent sans qu'aucun docker-compose.yml ne soit téléversé.
5 · Créez et connectez chaque service
Créez une app par service. L'ordre est : créer l'app → activer ses addons (étape 4) → déployer (étape 6). Activer un addon redéploie l'app avec la connexion injectée ; créez donc d'abord ici l'app propriétaire de l'addon, puis exécutez les commandes de l'étape 4 sur celle-ci. Choisissez le bon contrôle de santé selon le type de service :
--healthcheck-type http(par défaut) pour les services HTTP — définissez--healthcheck /votre/chemin.--healthcheck-type tcppour les services TCP sans endpoint HTTP.--healthcheck-type nonepour les workers en arrière-plan qui n'écoutent pas.
# app-web OWNS the postgres/redis/nats addons you enable in step 4, so DATABASE_URL,
# REDIS_URL and NATS_URL are injected for you — you only add your own secrets here:
cdnctl container apps create --account <account-uuid> \
--name app-web --image your-registry/app-web --tag 1.0.0 \
--port 8080 --healthcheck-type http --healthcheck /health \
--metrics-port 9090 --metrics-path /metrics \
--registry-credential <cred-uuid> \
--secrets-json '{"API_KEY":"<secret>"}'
# a background worker that shares the message bus. It does NOT own the addons, so point it
# at the passwordless NATS host from `addons list` (there is no DB password to copy):
cdnctl container apps create --account <account-uuid> \
--name app-worker --image your-registry/app-worker --tag 1.0.0 \
--healthcheck-type none --registry-credential <cred-uuid> \
--env-json '{"NATS_URL":"nats://<nats-host>:4222"}' \
--secrets-json '{"API_KEY":"<secret>"}'
Les secrets passés avec --secrets-json sont stockés comme des secrets Kubernetes chiffrés et ne sont jamais placés dans une configuration en clair ou dans des logs. Mettez à jour env/secrets plus tard avec cdnctl container apps update --account <account-uuid> --app <app-uuid> --env-json ... --secrets-json ...
6 · Déployez, vérifiez le statut et les logs
Déployez chaque app (déployez d'abord le service qui initialise le schéma de la base de données, si vos apps exécutent des migrations au démarrage) :
cdnctl container apps deploy --account <account-uuid> --app <app-uuid>
cdnctl container apps status --account <account-uuid> --app <app-uuid>
cdnctl container apps wait --account <account-uuid> --app <app-uuid> --status running --timeout 300
cdnctl container apps logs --account <account-uuid> --app <app-uuid> --tail 100
cdnctl container apps diagnose --account <account-uuid> --app <app-uuid>
7 · Exposez des services publiquement
Donnez à un service son propre sous-domaine CDN.com.tr (le HTTPS est géré pour vous) :
cdnctl container apps expose --account <account-uuid> --app <app-uuid>
# returns the app's public_subdomain, e.g. https://<uid>.cdn.com.tr
Chaque app obtient un sous-domaine immuable ; exposez autant de services que nécessaire — un seul compte, de nombreux hostnames publics. Pour utiliser votre propre domaine à la place, ajoutez-le comme point de livraison et pointez-le vers l'app (votre domaine → CDN → le service).
N'exposez que les services qui doivent être publics. Les workers en arrière-plan et les bases de données restent privés. Ajoutez une authentification à tout service HTTP que vous publiez.
8 · Observabilité (vue unifiée)
Exécutez votre stack de monitoring comme des apps ordinaires : un collecteur de métriques qui scrape vos services, plus un dashboard que vous exposez publiquement. Gardez le collecteur interne et n'exposez que le dashboard :
# collector (internal): build an image with your scrape config baked in
cdnctl container apps create --account <account-uuid> --name metrics \
--image your-registry/metrics --tag 1.0.0 --port 9090 --healthcheck-type http --healthcheck /-/healthy \
--registry-credential <cred-uuid>
cdnctl container apps deploy --account <account-uuid> --app <metrics-app-uuid>
# dashboard: wire it to the collector via env, then expose it
cdnctl container apps create --account <account-uuid> --name dashboard \
--image your-registry/dashboard --tag 1.0.0 --port 3000 --healthcheck-type http --healthcheck /api/health \
--registry-credential <cred-uuid> \
--env-json '{"METRICS_URL":"http://<metrics-host>:80"}' --secrets-json '{"ADMIN_PASSWORD":"<secret>"}'
cdnctl container apps deploy --account <account-uuid> --app <dashboard-app-uuid>
cdnctl container apps expose --account <account-uuid> --app <dashboard-app-uuid>
9 · Cycle de vie et rollback
cdnctl container apps scale --account <account-uuid> --app <app-uuid> --replicas 0 # stop
cdnctl container apps restart --account <account-uuid> --app <app-uuid>
cdnctl container apps delete --account <account-uuid> --app <app-uuid> # removes app + its subdomain
cdnctl container addons disable-postgres --account <account-uuid> --app <app-uuid> # keeps data
cdnctl container addons disable-postgres --account <account-uuid> --app <app-uuid> --delete-data --confirmation <app-name>
Désactiver un addon de données conserve son volume par défaut ; supprimer les données nécessite une confirmation explicite. Supprimer une app retire aussi son sous-domaine public.