CONTAINER APPS · GUÍA DE CDNCTL
Migre un proyecto Docker multiservicio, de principio a fin con cdnctl
Esta guía recorre el proceso de tomar un proyecto multiservicio estilo docker-compose (servicios web/API, workers en segundo plano, una base de datos, una caché y un bus de mensajes) y ejecutarlo en la plataforma de contenedores gestionados de CDN.com.tr — completamente desde la línea de comandos cdnctl. Reemplace los valores de marcador de posición (<account-uuid>, nombres de imagen, puertos) con los suyos.
Si ya tiene un docker-compose.yml, puede omitir los pasos manuales de creación y conexión de abajo: la plataforma analiza su archivo compose y propone un plan listo para confirmar. Consulte la guía de importación de Docker Compose o la referencia de compose de cdnctl. Los pasos de extremo a extremo aquí siguen siendo la forma de ajustar o migrar cualquier cosa que la importación de compose no cubra.
1 · Conceptos
- Cuenta = proyecto. Una cuenta puede contener muchas container apps; su paquete determina cuántas apps puede ejecutar.
- App = una imagen de contenedor (un servicio). Cada app tiene un puerto, un health check, recursos, variables de entorno y secrets, y puede escalar a N réplicas.
- Los complementos gestionados (managed addons) son backends con estado —
postgres(PostgreSQL/TimescaleDB),mysql,redisynats(JetStream) — que usted conecta a una app. Cada addon se aprovisiona para la app en la que lo habilita y expone un hostname estable dentro del clúster. Sus detalles de conexión — host, puerto, usuario, base de datos y la contraseña generada — se inyectan directamente en esa app como variables de entorno y secrets (p. ej.DATABASE_URL,REDIS_URL,NATS_URL), listos para usar. La contraseña se genera por usted y nunca se vuelve a mostrar, así que no construye las cadenas de conexión a mano para la app propietaria. - Red privada y service discovery. Las apps de su cuenta comparten una red privada. Cada app es accesible desde sus otras apps por su nombre de app — exactamente igual que un nombre de servicio de docker-compose — en
http://<nombre-app>:<puerto>(publicamos un alias DNS interno = su nombre de app). Así que si su archivo compose habla conhttp://hot-data-store:8082, llame a esa apphot-data-storecon el puerto8082y simplemente funciona. Las apps pueden salir a internet pero no acceder a los servicios privados de otros clientes. - Los hosts de los addons se le devuelven. Cuando habilita un addon, su host estable dentro del clúster lo reporta
cdnctl container addons list(el campohost). Ese es el<db-host>/<redis-host>/<nats-host>usado en las cadenas de conexión de abajo — no lo adivina. - Exposición pública. Cualquier app puede publicarse en un subdominio de CDN.com.tr o en su propio dominio. Cada servicio expuesto obtiene su propio hostname.
2 · Configure cdnctl
Autentíquese una vez con su token de API del panel (o haga login con sus credenciales):
cdnctl configure --endpoint https://cdn.com.tr --token <your-api-token>
# verify
cdnctl accounts list
cdnctl container apps list --account <account-uuid>
3 · Compile y publique imágenes, registre una credencial de pull
Compile cada servicio para linux/amd64 y súbalo a un registro que la plataforma pueda usar para hacer pull. Si los repos son privados, registre una credencial de pull en la cuenta:
# 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>
Nunca incorpore secrets dentro de las imágenes. Use un .dockerignore para excluir .env, artefactos de build y datos de VCS.
4 · Aprovisione complementos gestionados (DB, caché, bus de mensajes)
Cada addon se conecta a una app, así que primero crea esa app (paso 5) y luego habilita los addons en ella. El addon se aprovisiona para esa app y su conexión se inyecta automáticamente — usted no arma la URL ni copia una contraseña:
# 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>
A partir de ahora el entorno de esa app ya contiene valores listos para usar — usted los referencia, no los construye:
# 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 reporta el host estable, el puerto, el usuario y el nombre de base de datos de cada addon (p. ej. host: ca-…-postgres) a modo de referencia — la contraseña generada nunca se devuelve. Así que no necesita escribir a mano DATABASE_URL para la app propietaria; ya es un secret en ella.
Compartir un backend entre apps. Los backends sin contraseña — NATS, y Redis tal como está configurado aquí — también pueden alcanzarse desde sus otras apps: tome el host de addons list y configure NATS_URL/REDIS_URL como env en cada consumidor (paso 5). Una base de datos SQL protegida con contraseña es distinta: como la contraseña solo se inyecta en la app que posee el addon (y nunca se muestra), no intente reutilizarla desde una segunda app. En su lugar, deje que una app sea la dueña de la base de datos y que las demás la llamen a ella a través de su API, o ejecute su propia imagen de base de datos con una contraseña que usted mismo defina. Los hosts entre apps son simplemente sus nombres de app (el alias estilo docker-compose del paso 1), así que URLs como http://hot-data-store:8082 se resuelven sin haber subido ningún docker-compose.yml.
5 · Cree y conecte cada servicio
Cree una app por servicio. El orden es: cree la app → habilite sus addons (paso 4) → despliegue (paso 6). Habilitar un addon redespliega la app con la conexión inyectada, así que cree aquí primero la app propietaria del addon, y luego ejecute los comandos del paso 4 contra ella. Elija el health check correcto según el tipo de servicio:
--healthcheck-type http(por defecto) para servicios HTTP — configure--healthcheck /su/ruta.--healthcheck-type tcppara servicios TCP sin un endpoint HTTP.--healthcheck-type nonepara workers en segundo plano que no escuchan.
# 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>"}'
Los secrets pasados con --secrets-json se almacenan como Kubernetes secrets cifrados y nunca se colocan en configuración en texto plano ni en logs. Actualice env/secrets más tarde con cdnctl container apps update --account <account-uuid> --app <app-uuid> --env-json ... --secrets-json ...
6 · Despliegue, verifique el estado y los logs
Despliegue cada app (despliegue primero el servicio que inicializa el esquema de la base de datos, si sus apps ejecutan migraciones al arrancar):
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 · Exponga servicios públicamente
Dele a un servicio su propio subdominio de CDN.com.tr (el HTTPS se gestiona por usted):
cdnctl container apps expose --account <account-uuid> --app <app-uuid>
# returns the app's public_subdomain, e.g. https://<uid>.cdn.com.tr
Cada app obtiene un subdominio inmutable; exponga tantos servicios como necesite — una cuenta, muchos hostnames públicos. Para usar su propio dominio en su lugar, agréguelo como delivery point y apúntelo a la app (su dominio → CDN → el servicio).
Exponga solo los servicios que deban ser públicos. Los workers en segundo plano y las bases de datos permanecen privados. Agregue autenticación a cualquier servicio HTTP que publique.
8 · Observabilidad (un solo panel de control)
Ejecute su stack de monitoreo como apps normales: un recolector de métricas que hace scrape de sus servicios, más un dashboard que expone públicamente. Mantenga el recolector interno y exponga solo el 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 · Ciclo de vida y 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>
Deshabilitar un addon de datos conserva su volumen por defecto; eliminar los datos requiere una confirmación explícita. Eliminar una app también elimina su subdominio público.