أتمتة التطبيقات

أنشئ التطبيقات واضبطها وانشرها واحذفها من الطرفية أو من سكربت، مع شرح كل خيار.

كل ما تفعله وحدة التحكّم بالتطبيق تفعله الـ CLI عبر الخيارات. تشرح هذه الصفحة إنشاء تطبيق، وتعديله بعد ذلك، والأوامر التي ستشغّلها يوميًا. أمّا خطّ النشر المحيط بها فانظر النشر من CI/CD.

تسمية الأشياء

يُشار إلى التطبيق بمعرّفه النصّي (slug) أو باسمه أو بـ id الخاص به. وتصلح أيضًا بادئة فريدة من الـ slug أو الـ id، وهذا مريح في الطرفية وخطِر في السكربت: فـ api ستطابق api-gateway لو كان الوحيد. على السكربتات أن تستخدم apps get --exact حين تسأل عن وجود شيء ما.

يمكن أن تأتي الخيارات قبل الاسم أو بعده: isogrid apps deploy shop --wait وisogrid apps deploy --wait shop أمر واحد.

وتُحدَّد المناطق والأحجام والشبكات والخزائن والمجموعات والأعضاء بالطريقة نفسها: بالـ id أو الـ slug أو الاسم الظاهر في وحدة التحكّم. وحين لا يطابق الاسم شيئًا أو يطابق أكثر من شيء، تسرد رسالة الخطأ الخيارات الصحيحة.

إنشاء تطبيق

isogrid apps create --name NAME --provider github|gitlab|registry --url REPO_URL|IMAGE_REF
    [--cluster REGION] [--tier SIZE] [--network NETWORK] [--organization ORG]
    [--branch main] [--dockerfile Dockerfile] [--context .] [--port N] [--replicas N] [--public]
    [--env KEY=VALUE]... [--env-file PATH] [--secret KEY=VALUE]... [--secrets-file PATH]
    [--vault-secret VAULT/KEY[:TARGET]]... [--slug SLUG] [--wait] [--follow] [--timeout 20m] [--json]

لا يلزم إلا --name و--url. ولكل ما عداهما قيمة افتراضية معقولة.

من أين تأتي الشيفرة

--provider هو github أو gitlab أو registry. يمكنك إغفاله حين يدلّ عليه العنوان: المضيف github.com يعني GitHub، والمضيف الذي يحتوي على gitlab يعني GitLab. أمّا GitLab مستضاف ذاتيًا باسم آخر، أو صيغة owner/repo المجرّدة، فتحتاج إليه.

--url يقبل ما ستلصقه عادةً. في GitHub، كل هذه تشير إلى المستودع نفسه:

acme/api
https://github.com/acme/api
https://github.com/acme/api.git
git@github.com:acme/api.git

وفي GitLab، بما في ذلك النسخ المستضافة ذاتيًا والمشاريع داخل مجموعات متداخلة:

https://gitlab.com/acme/api
https://gitlab.example.com/platform/backend/api
git@gitlab.example.com:platform/backend/api.git

العنوان المنسوخ من صفحة فرع يبني ذلك الفرع: https://github.com/acme/api/tree/release/2.0 و https://gitlab.com/acme/api/-/tree/staging يحدّدان الفرع ما لم يقل --branch غير ذلك. ودون أيّهما يكون الفرع main.

يجب أن يكون المستودع ممّا ربطته مؤسّستك، وممّا مُنحت حقّ الوصول إليه. يعرض isogrid repos list الأمرين؛ انظر مستودعات Git.

من السجلّ، يكون --url مرجعًا لصورة:

api:1.2
acme/api:1.2
registry.example.com:5000/acme/api:1.2

الوسم الافتراضي latest، و--tag يتجاوز الوسم الموجود في المرجع. و--image acme/api:1.2 اختصار لـ --provider registry --url acme/api:1.2. يجب أن تكون الصورة قد دُفعت مسبقًا، إلى مساحة أسماء يمكنك النشر منها.

كيف يُبنى

للمستودع:

الخيار الافتراضي
--branch من العنوان، وإلا main الفرع الذي يتبعه كل بناء.
--dockerfile Dockerfile مسار ملف Dockerfile، انطلاقًا من جذر المستودع.
--context . مجلّد سياق البناء.

أين يعمل، وبأي حجم

--cluster هو المنطقة. يمكنك إغفاله حين لا تكون مفتوحة لك إلا منطقة واحدة؛ وإلا سردت رسالة الخطأ المناطق التي يمكنك اختيارها.

--tier هو نوع النسخة. افتراضيه أصغر ما في المنطقة. يسرد isogrid apps tiers <app> الأنواع بعد أن يوجد التطبيق؛ وقبل ذلك يعرض نموذج الإنشاء في وحدة التحكّم القائمة نفسها.

--network افتراضيه شبكة مؤسّستك الخاصة في تلك المنطقة. سمِّ شبكة أخرى لتضع التطبيق بجانب شيء يحتاج إلى الوصول إليه بشكل خاص.

الثلاثة تقبل slug أو اسمًا أو id.

--port هو المنفذ الذي تستمع عليه الحاوية. دونه يُستخدم المنفذ الذي تعلنه الصورة، و8080 إن لم تعلن شيئًا.

--replicas عدد النسخ المطلوب تشغيلها، 1 افتراضيًا. انظر نشر تطبيق لتعرف لماذا تعني النسخة الواحدة انقطاعًا قصيرًا في كل عملية نشر.

--public ينشر التطبيق على عنوان عام. دونه لا يحصل على عنوان؛ ويمكنك نشره لاحقًا بـ apps update --public.

--slug يحدّد المعرّف المستخدم في العناوين والأوامر. يُشتقّ من الاسم إن أغفلته.

الإعدادات والأسرار

هناك ثلاثة أنواع من القيم، ونوع ما تستخدمه يحدّد من يستطيع قراءتها لاحقًا.

متغيّرات البيئة — --env KEY=VALUE (قابل للتكرار) و--env-file PATH. إعدادات عادية، يقرؤها كل من يرى التطبيق. وحين يرد المفتاح نفسه في الاثنين، يفوز الخيار: الملف يحمل القيم الافتراضية، وسطر الأوامر هو الاستثناء.

أسرار الحاوية — --secret KEY=VALUE و--secrets-file PATH. تُكتب في مخزن الأسرار قبل أول نشر ولا تُعرض مجدّدًا أبدًا، لأحد، بأي أمر أو واجهة API.

أسرار الخزائن — --vault-secret VAULT/KEY[:TARGET]. تربط مفتاحًا موجودًا أصلًا في إحدى خزائنك بالتطبيق. فـ payments/stripe-key:STRIPE_KEY يعرض المفتاح stripe-key من الخزنة payments باسم STRIPE_KEY؛ ودون :TARGET يُشتقّ الاسم من المفتاح. لا يُنسخ شيء: يكفي تدوير القيمة في الخزنة.

تستخدم الملفات صيغة dotenv المعتادة:

# comments and blank lines are ignored
LOG_LEVEL=info
export REGION=eu            # "export" is allowed, and so is an inline comment
GREETING='taken literally, $HOME included'
TLS_KEY="-----BEGIN PRIVATE KEY-----
spans several lines until the closing quote
-----END PRIVATE KEY-----"

لا يوجد توسيع للمتغيّرات: علامة $ في القيمة تبقى $. أسماء المتغيّرات حروف وأرقام وشرطات سفلية، ولا تبدأ برقم، والأسماء التي تبدأ بـ ISOGRID_ محجوزة للمنصّة. ويمكن أن تحتوي أسماء الأسرار أيضًا على . و-، فـ tls.key اسم صالح. ويُبلَّغ عن أي خطأ مع اسم الملف ورقم السطر قبل إرسال أي شيء.

انتظار النتيجة

دون --wait، يعود create بمجرّد أن يوجد التطبيق ويُدرَج أول بناء أو نشر له في قائمة الانتظار. وهذا لا يعني أنه يعمل.

--wait ينتظر حتى ينتهي البناء (إن وُجد) والنشر، ويخرج برمز غير الصفر إن فشل أيّهما. --follow يفعل الشيء نفسه مع طباعة سجلّات البناء والنشر. --timeout يحدّ مدّة الانتظار؛ وافتراضيه 20m.

يفشل الانتظار — برمز الخروج 1 — حين:

  • يفشل البناء؛
  • يفشل النشر؛
  • لا تصبح النسخة الجديدة سليمة أبدًا فيُتراجَع إلى السابقة، حتى وإن ظلّ التطبيق يخدم الطلبات؛
  • يُنشر التطبيق لكنه يبقى دون عدد نسخه المطلوب لأكثر من ثلاث دقائق، ما يعني أنها تنهار؛
  • تنتهي المهلة. يستمرّ العمل على المنصّة؛ لا يتوقّف إلا الانتظار. تحقّق منه بـ isogrid apps status.

يطبع --json التطبيق كما أعادته واجهة API، على المخرج القياسي، بعد انتهاء الأمر. أمّا التقدّم فيُكتب على مخرج الأخطاء القياسي، فلا يختلط الاثنان أبدًا.

أمثلة

مستودع، مع القيم الافتراضية لكل ما له قيمة افتراضية:

isogrid apps create --name shop --url https://github.com/acme/shop --wait

مشروع GitLab على مضيفك الخاص، مع فرع محدّد وحجم ونسختين:

isogrid apps create \
  --name billing-api \
  --provider gitlab \
  --url https://git.acme.internal/platform/billing/api \
  --branch release \
  --cluster eu-west --tier medium --replicas 2 \
  --env-file deploy/production.env \
  --secrets-file deploy/production.secrets.env \
  --vault-secret payments/stripe-key:STRIPE_KEY \
  --public --wait

صورة دفعتها مسبقًا:

isogrid apps create --name worker --image acme/worker:2024.10.1 --cluster eu-west --wait

تعديل تطبيق

isogrid apps update <app> [--name N] [--branch B] [--dockerfile P] [--context D] [--url URL|IMAGE] [--tag T]
    [--port N] [--replicas N] [--tier SIZE] [--cluster REGION] [--network NETWORK]
    [--env KEY=VALUE]... [--unset-env KEY]... [--env-file PATH] [--replace-env]
    [--secret KEY=VALUE]... [--secrets-file PATH] [--unset-secret KEY]... [--replace-secrets]
    [--public | --private] [--deploy [--ref REF]] [--wait] [--follow] [--json]

لا يتغيّر إلا ما تعطيه. الأمر الذي لن يغيّر شيئًا يقول ذلك ويخرج بالرمز 0، ما يجعل تشغيل update في كل خطّ نشر آمنًا.

متغيّرات البيئة: تُدمج ما لم تقل غير ذلك

الخيار الأثر
--env KEY=VALUE، --env-file PATH يضيف تلك المفاتيح أو يستبدل قيمها. ويُبقي البقية.
--unset-env KEY يزيل مفتاحًا واحدًا.
--replace-env يجعل القيم المعطاة هي البيئة كلّها. ويُزال كل ما عداها.

--replace-env دون قيم يُفرغ البيئة. استخدمه عن قصد.

يطبع isogrid apps env <app> البيئة الحالية على هيئة ملف dotenv، فتكون الدورة الكاملة:

isogrid apps env shop > shop.env
# edit shop.env
isogrid apps update shop --env-file shop.env --replace-env

الأسرار: بالطريقة نفسها، دون أن تُعرض القيم أبدًا

الخيار الأثر
--secret KEY=VALUE، --secrets-file PATH يضيف تلك الأسرار أو يستبدلها.
--unset-secret KEY يزيل سرًّا واحدًا.
--replace-secrets يجعل المجموعة المعطاة هي الأسرار الوحيدة. يحتاج إلى قيمة واحدة على الأقل.

يسرد isogrid apps secrets <app> الأسماء. ولا يعيد أي أمر قيمة.

متى يسري التغيير

تعيد المنصّة نشر التطبيق العامل حين تتغيّر بيئته، أو حين يتغيّر حجمه (--tier، --replicas). لا حاجة إلى أن تطلب ذلك.

كل ما عدا ذلك يسري في النشر التالي. مرّر --deploy ليحدث ذلك الآن:

  • في التطبيق المبني من git، يبني --deploy الفرع عند آخر إيداع فيه وينشر النتيجة. ويبني --ref فرعًا أو وسمًا أو SHA إيداع بعينه هذه المرّة فقط؛ وتظلّ عمليات البناء اللاحقة تتبع فرع التطبيق.
  • في الصورة، يعيد --deploy نشر الصورة والوسم المضبوطين عليه.

تعمل --wait و--follow و--timeout كما في create. ومع --wait حين لا يكون هناك ما يُنشر، يقول الأمر إنه لا شيء يُنتظر.

الصور والمستودعات

في تطبيق مبني من صورة، ينقل --tag إلى وسم آخر من الصورة نفسها، و--url إلى صورة أخرى في سجلّك. وهكذا ينشر خطّ النشر الذي يبني صوره بنفسه كلّ صورة منها:

isogrid apps update worker --tag "$GIT_SHA" --deploy --wait

لا يمكن تغيير مستودع التطبيق. في تطبيق مبني من git، لا يجوز لـ --url إلا أن يعيد ذكر المستودع نفسه، مثلًا لالتقاط فرع من عنوان /tree/BRANCH. للبناء من مستودع مختلف، أنشئ تطبيقًا جديدًا.

أمثلة

فعّل سجلّات التصحيح؛ وستعيد المنصّة نشره:

isogrid apps update shop --env LOG_LEVEL=debug --wait

دوّر بيانات اعتماد وأزل أخرى قديمة:

isogrid apps update shop --secret DATABASE_PASSWORD="$NEW_PASSWORD" --unset-secret LEGACY_TOKEN

اتبع فرعًا جديدًا وابنه الآن:

isogrid apps update shop --branch release/3.0 --deploy --wait

ابنِ إيداعًا محدّدًا بعينه، وهذا ما يفعله خطّ النشر:

isogrid apps update shop --deploy --ref 3f2c1ab --wait

نشر ملف stack

يُنشر ملف docker-compose.yml أو docker stack كما هو. تصبح كل خدمة فيه تطبيقًا واحدًا باسم <stack>-<service>:

isogrid stack preview SOURCE [--stack NAME] [--region REGION] [--var KEY=VALUE]... [--var-file PATH] [--json]
isogrid stack apply   SOURCE [--stack NAME] [--region REGION] [--var KEY=VALUE]... [--var-file PATH]
    [--size [FILE:]SERVICE=TIER]... [--registry [FILE:]SERVICE=NAME]... [--network [FILE:]SERVICE=NAME]...
    [--placement [FILE:]SERVICE=CONSTRAINT]... [--port [FILE:]SERVICE=N]... [--skip [FILE:]SERVICE]...
    [--no-deploy] [--wait] [--follow] [--timeout 20m] [--json]

SOURCE:  -f FILE|DIR ...                        (repeatable; - reads standard input)
         --repo REPO [--provider github|gitlab] [--ref REF] --path PATH ...   (--path repeatable)

مثال قصير، فيه كتلة سجلّات مشتركة وتحديث تدريجي:

x-logging: &logging
  driver: json-file
  options:
    max-size: "20m"
    max-file: "5"

services:
  api:
    image: registry.gitlab.com/fraus/api:${IMAGE_TAG}
    env_file: [api.env]
    ports: ["8000:8000"]
    logging: *logging
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
        failure_action: rollback
        monitor: 30s
      restart_policy:
        condition: on-failure
      placement:
        constraints: ["node.labels.zone == a"]

  worker:
    image: registry.gitlab.com/fraus/worker:${IMAGE_TAG}
    logging: *logging

انشره، وانتظر حتى تعمل كل خدمة:

isogrid stack apply -f stack.yml --stack fraus --var IMAGE_TAG=1.4.2 --wait

ينشئ هذا fraus-api وfraus-worker. تطبيق الملف نفسه مرّة أخرى بقيمة --stack نفسها يحدّث هذين التطبيقين ويعيد نشرهما، لذا يمكن أن يبقى الملف في مستودعك ويُطبَّق من خطّ النشر. اسم الـ stack معرّف (slug) بأحرف صغيرة لا يتجاوز 40 حرفًا.

شغّل isogrid stack preview أوّلًا. لا يغيّر شيئًا، ويعرض لكل خدمة ما إذا كان تطبيقها سيُنشأ أو يُحدَّث أو يُتخطّى، والصورة والسجلّ الذي يسحبها، والحجم، والشبكة، والتوزيع الذي سيُطبَّق إلى جانب ما يطلبه الملف، وإعدادات النشر والسجلّات، والتحذيرات. الخيار -f - يقرأ الملف من الدخل القياسي.

قيم ${VAR} في الملف تُملأ من --var KEY=VALUE و--var-file (ملف dotenv)، والخيار هو الغالب. وتعمل ${VAR:-default} كما في compose. أي متغيّر يستعمله الملف ولم يعطه أحد يوقف apply قبل أن يتغيّر أي شيء.

من مستودع متّصل

لا يلزم أن يكون الملف على جهازك. يحدّد --repo و--path ملفًّا في مستودع GitHub أو GitLab وصلته مؤسّستك، وتقرؤه المنصّة بصلاحية ذلك الاتصال نفسه: لا يُستنسخ شيء محلّيًّا ولا يمرّ أي رمز عبر الـ CLI:

isogrid stack apply --repo gitlab:global_fraus/deployment-config \
  --path stacks/prod/login.yml --stack prod-login

يُكتب --repo على شكل owner/name (‏GitHub، وهو الافتراضي) أو group/project؛ اكتب gitlab:group/project، أو أضف --provider gitlab، لـ GitLab. ويصلح رابط المستودع أيضًا. يقرأ --ref فرعًا أو وسمًا أو إيداعًا بدل الفرع الافتراضي. تذكر المعاينة المرجع الذي قرأته، ويقرأ apply المرجع نفسه، فلا يغيّر دفعٌ بينهما ما راجعته. لا يُجمع بين -f و--repo.

مجلّد كامل

يقبل -f مجلّدًا أيضًا، فينشر كل ملف *.yml و*.yaml فيه مباشرة، بترتيب الأسماء (لا تُقرأ المجلّدات الفرعية). ويمكن تكرار -f، وكذلك --path. كل ملف stack مستقلّ:

isogrid stack apply -f stacks/prod --stack prod --var IMAGE_TAG=1.4.2 --wait

مع عدّة ملفات يصبح --stack بادئة: يصير stacks/prod/login.yml الـ stack prod-login، وstacks/prod/billing.yml الـ stack prod-billing (يُحوَّل اسم الملف إلى أحرف صغيرة، ويصير كل ما ليس حرفًا أو رقمًا -، ولا يتجاوز الاسم كاملًا 40 حرفًا). ومن دون --stack تكون البادئة اسم المجلّد الذي فيه الملف، فتنتج هنا الأسماء نفسها. ومع ملف واحد يبقى --stack اسم الـ stack كما كان.

تُعايَن الملفات أوّلًا، ثم تُطبَّق واحدًا بعد الآخر، كلٌّ تحت عنوانه. الملف الذي يفشل (متغيّر ناقص، أو خدمة فشلت) لا يوقف الملفات الأخرى؛ ويطبع الأمر عدد ما طُبِّق ويخرج بالرمز 1 إن فشل أيٌّ منها. وينتظر --wait تطبيقات الملفات كلّها ضمن مهلة --timeout الواحدة.

ما يُطبَّق وما لا يُطبَّق

من الملف
image، deploy.replicas، environment تُطبَّق.
command، entrypoint، user، healthcheck، stop_grace_period تُطبَّق على الحاوية وتظهر في المعاينة. healthcheck: {disable: true} يعطّل فحص الصورة نفسها.
ports، expose منفذ الحاوية هو الذي يُوجَّه إليه التطبيق، و--port يستبدله. المنافذ المنشورة لا تُفتح على العُقد: يبقى التطبيق خاصًّا حتى تجعله عامًّا.
deploy.update_config، rollback_config، restart_policy تُطبَّق: التوازي، والتأخير، والترتيب، والإجراء عند الفشل، ومدّة المراقبة، وشرط إعادة التشغيل.
logging مع المشغّل json-file يُطبَّق بوصفه حفظًا لسجلّات التطبيق، مع max-size وmax-file. المشغّلات الأخرى تُتجاهل.
deploy.resources.limits تظهر في المعاينة. الحجم نوع مثيل: أصغر ما في المنطقة ما لم يحدّد --size غيره.
deploy.placement.constraints لا تُحفظ إلا إذا كان القيد أحد خيارات التوزيع في المنطقة، المسرودة في آخر المعاينة. اختر أحدها بـ --placement.
env_file لا يُقرأ. يبقى الملف على جهازك، ويطبع apply الأمر الذي يضبط متغيّراته بعد ذلك، مثل isogrid apps update fraus-api --env-file api.env. ضع الأسرار في --secrets-file بدلًا منه.
volumes لا تُركَّب. استعمل قاعدة بيانات مُدارة أو تخزين كائنات للبيانات التي يجب أن تبقى بعد إعادة النشر.
cap_add لا يُطبَّق. تعمل الحاويات بالصلاحيات (capabilities) الافتراضية.

الخدمة التي فيها build: ولا image لها تُتخطّى: ابنها من مستودعها تطبيقًا مستقلًّا، أو ادفع الصورة واذكر اسمها. توضع الخدمات على شبكة مؤسّستك في المنطقة، حيث يصل بعضها إلى بعض باسم الخدمة، و--network يضع إحداها في مكان آخر.

خيارات لكل خدمة

الخيار
--size SERVICE=TIER نوع المثيل، بالاسم أو المعرّف.
--registry SERVICE=NAME أيّ سجلّاتك يسحب الصورة، ويلزم حين يخدم أكثر من واحد مضيفها.
--network SERVICE=NAME شبكة غير شبكة مؤسّستك.
--placement SERVICE=CONSTRAINT أحد خيارات التوزيع في المنطقة، كما هو مكتوب أو بتسميته. قابل للتكرار.
--port SERVICE=N المنفذ الذي تستمع عليه الحاوية.
--skip SERVICE استبعاد الخدمة هذه المرّة.

الخيار الذي يسمّي خدمة ليست في الملف، أو خيارًا لا تعرضه المنطقة، يفشل قبل إرسال أي شيء، ويسرد ما كان صالحًا.

مع عدّة ملفات، يسري --size web=Large على الخدمة web في كل ملف فيه خدمة بهذا الاسم. أضف قبله اسم ملف، بامتداده أو من دونه، لتستهدف ملفًّا واحدًا فقط: --size login:web=Large، --skip billing.yml:worker.

--wait ينتظر كل تطبيق أُنشئ أو حُدِّث، ضمن مهلة --timeout واحدة لها جميعًا، ويخرج بالرمز 1 إن فشل أي نشر أو أُعيد إلى الإصدار السابق، تمامًا كما يفعل apps deploy --wait. ويخرج apply بالرمز 1 أيضًا حين يتعذّر تطبيق أي خدمة؛ أمّا الخدمات الأخرى فتُطبَّق مع ذلك، ويُطبع سبب كل فشل. الخيار --no-deploy يحفظ التطبيقات دون نشرها.

سجلّات حاويات أخرى

إلى جانب سجلّ المنصّة نفسها، يمكن للتطبيقات أن تنشر صورًا من GitLab وGitHub وDocker Hub وAzure وGoogle وAWS وQuay، أو من أي سجلّ يتكلّم الواجهة القياسية. احفظ بيانات الاعتماد مرّة واحدة لكل مؤسّسة:

isogrid registries list
isogrid registries add --name NAME --kind KIND [--host HOST] --username USER --password-stdin [--no-verify]
isogrid registries update <registry> [--name NAME] [--username USER] [--password-stdin]
isogrid registries test <registry>
isogrid registries remove <registry> [--yes]

قيمة --kind هي dockerhub أو gitlab أو github أو azure أو google أو aws أو quay أو generic. ويمكن ترك --host للأنواع التي لها مضيف واحد معروف.

كلمة المرور أو الرمز المميّز لا تكون خيارًا أبدًا. تُقرأ من الدخل القياسي مع --password-stdin، وإلا فمن متغيّر البيئة ISOGRID_REGISTRY_PASSWORD، كي لا تنتهي في سجلّ أوامر الـ shell ولا في سجلّ CI. استعمل رمزًا مميّزًا للقراءة من الصور بدلًا من كلمة مرور حساب:

# GitLab: a deploy token with read_registry
echo "$GITLAB_DEPLOY_TOKEN" | isogrid registries add --name gitlab --kind gitlab \
  --username gitlab+deploy-token-1 --password-stdin

# A self-hosted GitLab
echo "$TOKEN" | isogrid registries add --name gitlab-acme --kind gitlab \
  --host registry.git.acme.internal --username deployer --password-stdin

# GitHub Container Registry: a token with read:packages
echo "$GHCR_TOKEN" | isogrid registries add --name ghcr --kind github --username acme-bot --password-stdin

# Docker Hub: an access token
echo "$DOCKERHUB_TOKEN" | isogrid registries add --name hub --kind dockerhub --username acme --password-stdin

# Azure Container Registry
echo "$ACR_PASSWORD" | isogrid registries add --name acr --kind azure \
  --host acme.azurecr.io --username acme-pull --password-stdin

تُفحص بيانات الاعتماد لدى السجلّ قبل حفظها، ما لم تمرّر --no-verify. ويفحصها test من جديد ويخرج بالرمز 1 حين يرفضها السجلّ. ولا يمكن حذف سجلّ ما دامت تطبيقات تنشر منه.

ثم انشر صورة منه بمرجعها الكامل:

isogrid apps create --name web --external-image registry.gitlab.com/acme/web:1.4 --registry gitlab --wait
isogrid apps update web --external-image registry.gitlab.com/acme/web:1.5 --registry gitlab --deploy --wait

الصورة العامّة لا تحتاج إلى سجلّ: --external-image nginx:1.27. ويبقى --image و--url لصور سجلّ المنصّة. أمّا ملف الـ stack فيختار السجلّ وحده بحسب مضيف الصورة، ولا يطلب --registry إلا حين يخدم ذلك المضيفَ أكثرُ من سجلّ لديك.

أوامر الاستخدام اليومي

isogrid apps list                          # source, status, size and address of each
isogrid apps get <app> [--exact]           # one application in detail
isogrid apps status <app>                  # copies running and wanted, and why they differ
isogrid apps build <app> [--ref REF]       # build from git and deploy the result
isogrid apps deploy <app>                  # redeploy the current image, without building
isogrid apps stop <app>                    # scale to zero
isogrid apps start <app>                   # and back
isogrid apps scale <app> [--replicas N] [--tier SIZE]
isogrid apps tiers <app>                   # sizes its region offers, with prices
isogrid apps logs <app> [--follow]         # the deployment log
isogrid apps logs <app> --search QUERY     # search what the application printed
isogrid apps env <app>                     # environment variables, as a dotenv file
isogrid apps secrets <app>                 # secret names, never values
isogrid apps delete <app> [--yes]

build وdeploy مختلفان. يجلب build الشيفرة الجديدة ويبنيها وينشر ما بناه. أمّا deploy (ويُسمّى أيضًا redeploy) فيعيد تشغيل التطبيق من الصورة التي لديه، ولا يلتقط شيئًا جديدًا من git. كلاهما يقبل --wait و--follow و--timeout.

النشر الفاشل يُتراجَع عنه إلى النسخة التي كانت تعمل، فإعادة محاولة deploy آمنة: أسوأ الاحتمالات أن يستمرّ التطبيق كما كان. ومع --wait يُحتسب التراجع فشلًا.

status يقرأ حالة العنقود الآن، لا آخر ما سجّلته المنصّة. وحين تنقص النسخ، يذكر السبب، ويسرد النسخ الأخيرة مع العقدة والخطأ. إنه أول أمر تشغّله حين يكون التطبيق قائمًا لكنه غير سليم.

get --exact لا يطابق إلا slug أو اسمًا أو id كاملًا، ويخرج بالرمز 1 حين لا يوجد تطبيق كهذا. تستخدمه السكربتات لاختبار الوجود:

if isogrid apps get shop --exact --json > /dev/null 2>&1; then
  echo "shop exists"
fi

delete يسأل أولًا. ودون طرفية يسأل فيها، كما في خطّ النشر، يرفض برمز الخروج 2 ما لم تمرّر --yes، فيكون كل حذف غير مراقَب قد كتبه أحدهم صراحةً.

البحث في السجلّات

يطبع isogrid apps logs <app> سجلّ النشر: ما فعلته المنصّة لنشر التطبيق. أمّا لتصفّح ما طبعه التطبيق نفسه، فابحث فيه:

isogrid apps logs <app> --search QUERY [--since 1h] [--limit 500] [--json]

يُطبع كل تطابق على الشكل timestamp [replica] LEVEL message. الاستعلام قائمة شروط يجب أن تتحقّق كلّها:

الشرط يطابق
timeout الأسطر التي فيها الكلمة
"connection reset" العبارة بعينها
-healthcheck الأسطر التي ليست فيها الكلمة
/5\d\d/ تعبيرًا نمطيًا
level:error مستوى: error أو warn أو info أو debug
replica:2 أسطر النسخة الثانية
isogrid apps logs api --search 'level:error -healthcheck' --since 1h
isogrid apps logs api --search '"payment failed" replica:2' --limit 50

يقبل --since مدّة (30m، 1h) أو وقتًا بصيغة RFC 3339. و--limit هو أقصى عدد من الأسطر المُعادة، حتى 2000؛ وحين يطابق أكثر من ذلك تُحفظ الأحدث ويقول الأمر ذلك. تذهب التطابقات إلى المخرج القياسي والملاحظات إلى مخرج الأخطاء، فلا يتلقّى > errors.txt إلا الأسطر.

من دون حفظ السجلّات لا يمكن البحث إلا فيما تحمله النسخ العاملة حاليًا، وكل إعادة نشر تبدأ من الصفر. يخبرك الأمر حين يكون الحفظ متوقّفًا. فعّله من إعدادات التطبيق في وحدة التحكّم، أو بكتلة logging من نوع json-file في ملف stack.

رموز الخروج

الرمز المعنى
0 نجاح، بما في ذلك update لم يكن لديه ما يغيّره.
1 فشل شيء ما: خطأ من واجهة API، أو بناء فاشل، أو نشر فاشل أو متراجَع عنه مع --wait، أو انتهاء المهلة، أو عدم وجود تطابق لـ apps get --exact.
2 استُخدم الأمر بشكل خاطئ: خيار مجهول، أو وسيط ناقص، أو خيارات متعارضة، أو delete دون --yes ودون طرفية.

حالات حدّية يحسن معرفتها قبل أن تصادفها

"is not a repository you have been given access to". المستودع مربوط، لكن عبر حساب شخص آخر، ولم يفتحه لك. انظر مستودعات Git.

نجح البناء وفشل --wait مع ذلك. بُنيت النسخة الجديدة ثم لم تصبح سليمة، فأُعيدت السابقة. الموقع يعمل؛ أمّا الشيفرة الجديدة فلا. اقرأ isogrid apps logs <app>.

يقول --tag إنه خاصّ بتطبيقات الصور. التطبيق مبني من git، وصوره توسَم بعمليات بنائه. استخدم --deploy --ref بدلًا منه.

لم يُزِل update --env-file ما حذفته من الملف. الدمج هو السلوك الافتراضي. أضف --replace-env حين يكون الملف هو البيئة كلّها.

وصلت قيمة تحتوي على $ دون تغيير. هذا مقصود. لا يُوسَّع شيء، لأن كلمة مرور تتغيّر بصمت أسوأ بكثير.