CONTAINER APPS · РУКОВОДСТВО CDNCTL
Миграция многосервисного Docker-проекта, от начала до конца, с cdnctl
Это руководство шаг за шагом показывает, как взять многосервисный проект в стиле docker-compose (веб/API-сервисы, фоновые воркеры, база данных, кеш и шина сообщений) и запустить его на управляемой контейнерной платформе CDN.com.tr — целиком из командной строки cdnctl. Замените значения-заполнители (<account-uuid>, имена образов, порты) на свои собственные.
Если у вас уже есть docker-compose.yml, вы можете пропустить ручные шаги создания и связывания ниже: платформа разбирает ваш compose-файл и предлагает готовый к подтверждению план. См. руководство по импорту Docker Compose или справочник cdnctl compose. Пошаговые инструкции здесь остаются способом тонкой настройки или переноса всего, что не покрывает импорт compose.
1 · Концепции
- Аккаунт = проект. Один аккаунт может содержать множество container app; сколько приложений вы можете запускать, определяет ваш пакет.
- App = один образ контейнера (сервис). У каждого app есть порт, health check, ресурсы, переменные env и секреты; он может масштабироваться до N реплик.
- Управляемые аддоны — это stateful-бэкенды —
postgres(PostgreSQL/TimescaleDB),mysql,redisиnats(JetStream) — которые вы подключаете к app. Каждый аддон предоставляется для того app, на котором вы его включаете, и предоставляет стабильное имя хоста внутри кластера. Его данные подключения — хост, порт, пользователь, база данных и сгенерированный пароль — внедряются напрямую в это app как env-переменные и секреты (напр.DATABASE_URL,REDIS_URL,NATS_URL), готовые к использованию. Пароль генерируется за вас и никогда не отображается снова, так что вы не собираете строку подключения вручную для владеющего app. - Приватная сеть и service discovery. Приложения в вашем аккаунте разделяют приватную сеть. Каждое app доступно из ваших других app по его имени app — точно как имя сервиса docker-compose — по адресу
http://<имя-app>:<порт>(мы публикуем внутренний псевдоним DNS = имя вашего app). Так что если ваш compose-файл обращается кhttp://hot-data-store:8082, назовите это apphot-data-storeс портом8082, и оно просто заработает. App могут выходить в интернет, но не к приватным сервисам других клиентов. - Хосты аддонов возвращаются вам. Когда вы включаете аддон, его стабильный хост внутри кластера сообщается командой
cdnctl container addons list(полеhost). Это и есть<db-host>/<redis-host>/<nats-host>, используемые в строках подключения ниже — вам не нужно их угадывать. - Публичный доступ. Любое app можно опубликовать на субдомене CDN.com.tr или на вашем собственном домене. Каждый опубликованный сервис получает собственное имя хоста.
2 · Настройте cdnctl
Аутентифицируйтесь один раз своим токеном API панели (или выполните login со своими учётными данными):
cdnctl configure --endpoint https://cdn.com.tr --token <your-api-token>
# verify
cdnctl accounts list
cdnctl container apps list --account <account-uuid>
3 · Соберите и опубликуйте образы, зарегистрируйте учётные данные для загрузки
Соберите каждый сервис под linux/amd64 и запушьте в registry, который платформа сможет забрать. Если репозитории приватные, зарегистрируйте учётные данные для загрузки в аккаунте:
# 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>
Никогда не встраивайте секреты в образы. Используйте .dockerignore, чтобы исключить .env, артефакты сборки и данные VCS.
4 · Предоставьте управляемые аддоны (БД, кеш, шина сообщений)
Каждый аддон подключается к app, поэтому сначала вы создаёте это app (шаг 5), а затем включаете на нём аддоны. Аддон предоставляется для этого app, и его подключение внедряется в него автоматически — вы не собираете URL и не копируете пароль:
# 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>
С этого момента окружение этого app уже содержит готовые к использованию значения — вы ссылаетесь на них, а не собираете их:
# 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 сообщает стабильный host, порт, пользователя и имя базы данных каждого аддона (напр. host: ca-…-postgres) для справки — сгенерированный пароль никогда не возвращается. Так что вам не нужно вручную писать DATABASE_URL для владеющего app — он уже является секретом на нём.
Общий доступ к бэкенду между приложениями. Бэкенды без пароля — NATS и Redis в этой конфигурации — доступны и из ваших других app: возьмите host из addons list и задайте env NATS_URL/REDIS_URL на каждом потребителе (шаг 5). SQL-база данных, защищённая паролем, отличается: поскольку пароль внедряется только в то app, которое владеет аддоном (и никогда не показывается), не пытайтесь повторно использовать её из второго app. Вместо этого либо пусть одно app владеет базой данных, а остальные обращаются к нему через ваш API, либо запустите собственный образ базы данных с паролем, который вы сами задаёте. Хосты между app — это просто ваши имена app (псевдоним в стиле docker-compose из шага 1), так что URL вроде http://hot-data-store:8082 разрешаются без загрузки docker-compose.yml.
5 · Создайте и свяжите каждый сервис
Создайте одно app на сервис. Порядок такой: создать app → включить его аддоны (шаг 4) → задеплоить (шаг 6). Включение аддона передеплоивает app с внедрённым подключением, поэтому сначала создайте здесь app-владельца аддона, а затем выполните команды шага 4 для него. Выберите правильный health check для каждого типа сервиса:
--healthcheck-type http(по умолчанию) для HTTP-сервисов — задайте--healthcheck /ваш/путь.--healthcheck-type tcpдля TCP-сервисов без HTTP-эндпоинта.--healthcheck-type noneдля фоновых воркеров, которые ничего не слушают.
# 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>"}'
Секреты, переданные через --secrets-json, хранятся как зашифрованные секреты Kubernetes и никогда не попадают в открытую конфигурацию или логи. Обновляйте env/секреты позже с помощью cdnctl container apps update --account <account-uuid> --app <app-uuid> --env-json ... --secrets-json ...
6 · Задеплойте, проверьте статус и логи
Задеплойте каждое app (сначала задеплойте сервис, инициализирующий схему базы данных, если ваши app выполняют миграции при запуске):
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 · Опубликуйте сервисы вовне
Дайте сервису собственный субдомен CDN.com.tr (HTTPS настраивается за вас):
cdnctl container apps expose --account <account-uuid> --app <app-uuid>
# returns the app's public_subdomain, e.g. https://<uid>.cdn.com.tr
Каждое app получает один неизменяемый субдомен; публикуйте столько сервисов, сколько нужно — один аккаунт, много публичных имён хостов. Чтобы использовать собственный домен, добавьте его как delivery point и направьте на app (ваш домен → CDN → сервис).
Публикуйте только те сервисы, которые должны быть публичными. Фоновые воркеры и базы данных остаются приватными. Добавляйте аутентификацию к любому публикуемому HTTP-сервису.
8 · Наблюдаемость (единая панель)
Запускайте свой стек мониторинга как обычные app: сборщик метрик, который опрашивает ваши сервисы, плюс дашборд, который вы публикуете вовне. Держите сборщик внутренним и публикуйте только дашборд:
# 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 · Жизненный цикл и откат
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>
Отключение аддона данных по умолчанию сохраняет его том; удаление данных требует явного подтверждения. Удаление app также удаляет его публичный субдомен.