CONTAINER APPS · دليل CDNCTL
ترحيل مشروع Docker متعدد الخدمات، من البداية للنهاية مع cdnctl
يشرح هذا الدليل خطوة بخطوة كيفية أخذ مشروع بأسلوب docker-compose متعدد الخدمات (خدمات ويب/API، عُمّال خلفية، قاعدة بيانات، ذاكرة تخزين مؤقت وناقل رسائل) وتشغيله على منصة الحاويات المُدارة CDN.com.tr — بالكامل من سطر أوامر cdnctl. استبدل القيم النائبة (<account-uuid>، أسماء الصور، المنافذ) بقيمك الخاصة.
إذا كان لديك بالفعل ملف docker-compose.yml، يمكنك تخطي خطوات الإنشاء والربط اليدوية أدناه: تُحلل المنصة ملف compose الخاص بك وتقترح خطة جاهزة للتأكيد. راجع دليل استيراد Docker Compose أو مرجع cdnctl compose. تبقى الخطوات الكاملة هنا الطريقة لضبط دقيق أو ترحيل أي شيء لا يغطيه استيراد compose.
1 · المفاهيم
- الحساب = المشروع. يمكن لحساب واحد أن يضم عدة تطبيقات حاوية؛ باقتك هي ما يحدد عدد التطبيقات التي يمكنك تشغيلها.
- التطبيق = صورة حاوية واحدة (خدمة). لكل تطبيق منفذ، فحص سلامة، موارد، متغيرات بيئة وأسرار، ويمكن أن يتوسع إلى N نسخة.
- الإضافات المُدارة هي خلفيات ذات حالة —
postgres(PostgreSQL/TimescaleDB) وmysqlوredisوnats(JetStream) — تربطها بتطبيق. تُوفَّر كل إضافة للتطبيق الذي تفعّلها عليه وتعرض اسم مضيف ثابتًا داخل الكتلة (in-cluster). تُحقن تفاصيل اتصالها — المضيف، المنفذ، المستخدم، قاعدة البيانات وكلمة المرور المُولَّدة — مباشرة في ذلك التطبيق كمتغيرات بيئة وأسرار (مثلDATABASE_URL،REDIS_URL،NATS_URL)، جاهزة للاستخدام. تُولَّد كلمة المرور لك ولا تُعرض مجددًا أبدًا، لذا لا تبني سلاسل الاتصال يدويًا للتطبيق المالك. - الشبكة الخاصة واكتشاف الخدمات. تشترك التطبيقات في حسابك في شبكة خاصة. يمكن الوصول إلى كل تطبيق من تطبيقاتك الأخرى باسم التطبيق — تمامًا مثل اسم خدمة docker-compose — على
http://<app-name>:<port>(ننشر لقبًا (alias) داخليًا في DNS يساوي اسم تطبيقك). فإذا كان ملف compose الخاص بك يتحدث إلىhttp://hot-data-store:8082، سمِّ ذلك التطبيقhot-data-storeبالمنفذ8082وسيعمل تلقائيًا. يمكن للتطبيقات الوصول إلى الإنترنت لكن ليس إلى الخدمات الخاصة لعملاء آخرين. - تُعاد مضيفات الإضافات إليك. عند تفعيل إضافة، يُبلغ
cdnctl container addons listعن مضيفها الثابت داخل الكتلة (حقلhost). هذا هو<db-host>/<redis-host>/<nats-host>المستخدم في سلاسل الاتصال أدناه — لست بحاجة لتخمينه. - النشر للعامة. يمكن نشر أي تطبيق على نطاق فرعي لـ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 وادفعها إلى سجل يمكن للمنصة سحبه منه. إذا كانت المستودعات خاصة، سجّل بيانات اعتماد سحب في الحساب:
# 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 · وفّر الإضافات المُدارة (قاعدة بيانات، ذاكرة تخزين مؤقت، ناقل رسائل)
تُربط كل إضافة بتطبيق، لذا أنشئ ذلك التطبيق أولاً (الخطوة 5) ثم فعّل الإضافات عليه. تُوفَّر الإضافة لذلك التطبيق ويُحقن اتصالها فيه تلقائيًا — لا تُجمّع الرابط ولا تنسخ كلمة مرور:
# 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>
من الآن فصاعدًا تحتوي بيئة ذلك التطبيق بالفعل على قيم جاهزة للاستخدام — تُشير إليها، لا تبنيها:
# 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 يدويًا للتطبيق المالك؛ فهي بالفعل سر عليه.
مشاركة خلفية بين التطبيقات. يمكن الوصول إلى الخلفيات غير المحمية بكلمة مرور — NATS، وRedis كما هي مُعدة هنا — من تطبيقاتك الأخرى أيضًا: خذ host من addons list واضبط NATS_URL/REDIS_URL كمتغير بيئة في كل مستهلك (الخطوة 5). قاعدة بيانات SQL المحمية بكلمة مرور مختلفة: بما أن كلمة المرور تُحقن فقط في التطبيق المالك للإضافة (ولا تُعرض أبدًا)، لا تحاول إعادة استخدامها من تطبيق ثانٍ. بدلاً من ذلك، إما دع تطبيقًا واحدًا يملك قاعدة البيانات وتستدعيه التطبيقات الأخرى عبر واجهتك البرمجية، أو شغّل صورة قاعدة بيانات خاصة بك بكلمة مرور تضبطها بنفسك. مضيفات التطبيق إلى التطبيق هي فقط أسماء تطبيقاتك (اللقب بأسلوب docker-compose من الخطوة 1)، لذا تُحل روابط مثل http://hot-data-store:8082 دون رفع أي ملف docker-compose.yml.
5 · أنشئ كل خدمة واربطها
أنشئ تطبيقًا واحدًا لكل خدمة. الترتيب هو: أنشئ التطبيق ← فعّل إضافاته (الخطوة 4) ← انشر (الخطوة 6). تفعيل إضافة يعيد نشر التطبيق مع حقن الاتصال، لذا أنشئ هنا أولاً التطبيق المالك للإضافة، ثم شغّل أوامر الخطوة-4 عليه. اختر فحص السلامة المناسب لكل نوع خدمة:
--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 مشفرة ولا تُوضع أبدًا في إعدادات نصية عادية أو سجلات. حدّث البيئة/الأسرار لاحقًا باستخدام cdnctl container apps update --account <account-uuid> --app <app-uuid> --env-json ... --secrets-json ...
6 · انشر، تحقق من الحالة والسجلات
انشر كل تطبيق (انشر الخدمة التي تُهيئ مخطط قاعدة البيانات أولاً، إذا كانت تطبيقاتك تُشغّل عمليات ترحيل عند الإقلاع):
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
يحصل كل تطبيق على نطاق فرعي ثابت واحد؛ انشر بقدر ما تحتاج من الخدمات — حساب واحد، أسماء مضيف عامة متعددة. لاستخدام نطاقك الخاص بدلاً من ذلك، أضفه كنقطة تسليم (delivery point) ووجّهه إلى التطبيق (نطاقك ← CDN ← الخدمة).
انشر فقط الخدمات التي يجب أن تكون عامة. يبقى عُمّال الخلفية وقواعد البيانات خاصة. أضف مصادقة إلى أي خدمة HTTP تنشرها.
8 · قابلية الرصد (لوحة واحدة موحدة)
شغّل مجموعة أدوات المراقبة الخاصة بك كتطبيقات عادية: مُجمّع مقاييس يفحص خدماتك، بالإضافة إلى لوحة معلومات تنشرها للعامة. أبقِ المُجمّع داخليًا وانشر لوحة المعلومات فقط:
# 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>
تعطيل إضافة بيانات يحتفظ بقرصها افتراضيًا؛ حذف البيانات يتطلب تأكيدًا صريحًا. حذف تطبيق يزيل أيضًا نطاقه الفرعي العام.