النشر باستخدام Docker#
يوفر استخدام Docker لنشر تطبيق Vapor الخاص بك عدة فوائد:
يمكن تشغيل تطبيقك المُحوّل إلى حاوية Docker بشكل موثوق باستخدام الأوامر نفسها على أي منصة تعمل بها خدمة Docker Daemon – أي Linux (CentOS و Debian و Fedora و Ubuntu) و macOS و Windows.
يمكنك استخدام docker-compose أو ملفات Kubernetes manifests لتنسيق الخدمات المتعددة اللازمة للنشر الكامل (مثل Redis و Postgres و nginx وغيرها).
من السهل اختبار قدرة تطبيقك على التوسّع أفقيًا، حتى محليًا على جهاز التطوير الخاص بك.
سيتوقف هذا الدليل قبل شرح كيفية نقل تطبيقك المُحوّل إلى حاوية إلى الخادم. أبسط عملية نشر تتضمن تثبيت Docker على خادمك وتشغيل الأوامر نفسها التي كنت ستشغّلها على جهاز التطوير لتشغيل تطبيقك.
عادةً ما تختلف عمليات النشر الأكثر تعقيدًا ومتانة باختلاف حل الاستضافة الذي تعتمده؛ فالعديد من الحلول الشائعة مثل AWS يوفّر دعمًا مدمجًا لـ Kubernetes وحلول قواعد بيانات مخصّصة مما يجعل من الصعب كتابة أفضل الممارسات بطريقة تنطبق على جميع عمليات النشر.
ومع ذلك، فإن استخدام Docker لتشغيل حزمة الخادم بالكامل محليًا لأغراض الاختبار قيّم للغاية للتطبيقات الخادمية الكبيرة والصغيرة على حد سواء. بالإضافة إلى ذلك، تنطبق المفاهيم الموصوفة في هذا الدليل بشكل عام على جميع عمليات النشر باستخدام Docker.
الإعداد#
ستحتاج إلى إعداد بيئة التطوير الخاصة بك لتشغيل Docker واكتساب فهم أساسي لملفات الموارد التي تُعِدّ حزم Docker.
تثبيت Docker#
ستحتاج إلى تثبيت Docker في بيئة التطوير الخاصة بك. يمكنك العثور على معلومات لأي منصة في قسم المنصات المدعومة من نظرة عامة على Docker Engine. إذا كنت تستخدم Mac OS، يمكنك الانتقال مباشرةً إلى صفحة تثبيت Docker for Mac.
إنشاء القالب#
نقترح استخدام قالب Vapor كنقطة بداية. إذا كان لديك تطبيق بالفعل، فقم ببناء القالب كما هو موضح أدناه في مجلد جديد كمرجع أثناء تحويل تطبيقك الحالي إلى حاوية – يمكنك نسخ الموارد الأساسية من القالب إلى تطبيقك وتعديلها قليلاً كنقطة انطلاق.
أنشئ تطبيق Vapor جديدًا باستخدام
vapor new my-dockerized-appواتّبع المطالبات لتفعيل الميزات ذات الصلة أو تعطيلها. ستؤثّر إجاباتك على هذه المطالبات في كيفية إنشاء ملفات موارد Docker.
موارد Docker#
من المفيد، سواء الآن أو في المستقبل القريب، أن تتعرّف على نظرة عامة على Docker. ستشرح النظرة العامة بعض المصطلحات الأساسية التي يستخدمها هذا الدليل.
يحتوي قالب تطبيق Vapor على موردَين أساسيين خاصين بـ Docker: ملف Dockerfile وملف docker-compose.
Dockerfile#
يخبر ملف Dockerfile كيفية بناء صورة لتطبيقك المُحوّل إلى حاوية. تحتوي تلك الصورة على الملف التنفيذي لتطبيقك وجميع الاعتماديات اللازمة لتشغيله. يستحق المرجع الكامل أن يبقى مفتوحًا عندما تعمل على تخصيص ملف Dockerfile الخاص بك.
يتألف ملف Dockerfile المُنشأ لتطبيق Vapor الخاص بك من مرحلتين. تبني المرحلة الأولى تطبيقك وتُعِدّ منطقة تخزين مؤقت تحتوي على النتيجة. أما المرحلة الثانية فتُعِدّ أساسيات بيئة تشغيل آمنة، وتنقل كل ما في منطقة التخزين المؤقت إلى مكانه في الصورة النهائية، وتضبط نقطة دخول وأمرًا افتراضيَّين يُشغّلان تطبيقك في وضع الإنتاج على المنفذ الافتراضي (8080). يمكن تجاوز هذا الإعداد عند استخدام الصورة.
ملف Docker Compose#
يحدّد ملف Docker Compose الطريقة التي ينبغي بها لـ Docker بناء خدمات متعددة في علاقتها ببعضها البعض. يوفّر ملف Docker Compose في قالب تطبيق Vapor الوظائف اللازمة لنشر تطبيقك، ولكن إذا أردت معرفة المزيد فعليك الرجوع إلى المرجع الكامل الذي يحتوي على تفاصيل حول جميع الخيارات المتاحة.
ملاحظة
إذا كنت تخطّط في نهاية المطاف لاستخدام Kubernetes لتنسيق تطبيقك، فإن ملف Docker Compose ليس ذا صلة مباشرة. ومع ذلك، فإن ملفات Kubernetes manifests متشابهة من الناحية المفاهيمية، بل توجد مشاريع تهدف إلى نقل ملفات Docker Compose إلى Kubernetes manifests.
سيحدّد ملف Docker Compose في تطبيق Vapor الجديد خدمات لتشغيل تطبيقك، وتشغيل الترحيلات (migrations) أو التراجع عنها، وتشغيل قاعدة بيانات كطبقة استمرارية لتطبيقك. ستختلف التعريفات الدقيقة تبعًا لقاعدة البيانات التي اخترت استخدامها عند تشغيل vapor new.
لاحظ أن ملف Docker Compose الخاص بك يحتوي على بعض متغيرات البيئة المشتركة قرب أعلى الملف. (قد يكون لديك مجموعة مختلفة من المتغيرات الافتراضية تبعًا لما إذا كنت تستخدم Fluent أم لا، وأي مشغّل Fluent قيد الاستخدام إن كنت تستخدمه.)
x-shared_environment: &shared_environment
LOG_LEVEL: ${LOG_LEVEL:-debug}
DATABASE_HOST: db
DATABASE_NAME: vapor_database
DATABASE_USERNAME: vapor_username
DATABASE_PASSWORD: vapor_password
سترى هذه المتغيرات مُدرجة في خدمات متعددة أدناه باستخدام صيغة مرجع YAML وهي <<: *shared_environment.
المتغيرات DATABASE_HOST و DATABASE_NAME و DATABASE_USERNAME و DATABASE_PASSWORD مُدرجة بقيم ثابتة في هذا المثال، بينما سيأخذ LOG_LEVEL قيمته من البيئة التي تُشغّل الخدمة أو سيعود إلى 'debug' إذا لم يكن هذا المتغير مُعرّفًا.
ملاحظة
إدراج اسم المستخدم وكلمة المرور بقيم ثابتة مقبول للتطوير المحلي، ولكن ينبغي عليك تخزين هذه المتغيرات في ملف أسرار (secrets) لنشر الإنتاج. إحدى طرق التعامل مع هذا في الإنتاج هي تصدير ملف الأسرار إلى البيئة التي تُشغّل عملية النشر واستخدام أسطر مثل التالي في ملف Docker Compose:
DATABASE_USERNAME: ${DATABASE_USERNAME}
هذا يمرّر متغير البيئة إلى الحاويات كما هو مُعرّف من قِبل المضيف.
أمور أخرى ينبغي الانتباه إليها:
تُعرّف اعتماديات الخدمات بواسطة مصفوفات
depends_on.تُكشف منافذ الخدمات للنظام الذي يُشغّل الخدمات باستخدام مصفوفات
ports(بصيغة<host_port>:<service_port>).يُعرّف
DATABASE_HOSTبأنهdb. هذا يعني أن تطبيقك سيصل إلى قاعدة البيانات عبرhttp://db:5432. يعمل هذا لأن Docker سيُشغّل شبكة تستخدمها خدماتك، وسيوجّه نظام DNS الداخلي على تلك الشبكة الاسمdbإلى الخدمة المسمّاة'db'.يُتجاوز توجيه
CMDفي ملف Dockerfile في بعض الخدمات باستخدام مصفوفةcommand. لاحظ أن ما يُحدَّد بواسطةcommandيُشغَّل مقابلENTRYPOINTفي ملف Dockerfile.في وضع Swarm (المزيد حول هذا أدناه)، ستُعطى الخدمات افتراضيًا نسخة واحدة (instance)، ولكن خدمتَي
migrateوrevertمُعرّفتان بـdeployreplicas: 0بحيث لا تبدآن افتراضيًا عند تشغيل Swarm.
البناء#
يخبر ملف Docker Compose كيفية بناء تطبيقك (باستخدام ملف Dockerfile في المجلد الحالي) وكيفية تسمية الصورة الناتجة (my-dockerized-app:latest). الأخيرة هي في الواقع مزيج من اسم (my-dockerized-app) ووسم (latest) حيث تُستخدم الوسوم لإصدار صور Docker.
لبناء صورة Docker لتطبيقك، شغّل
docker compose build
من المجلد الجذر لمشروع تطبيقك (المجلد الذي يحتوي على docker-compose.yml).
ستلاحظ أنه يجب إعادة بناء تطبيقك واعتمادياته حتى لو كنت قد بنيتها سابقًا على جهاز التطوير الخاص بك. يجري بناؤها في بيئة بناء Linux التي يستخدمها Docker، لذا فإن نواتج البناء من جهاز التطوير الخاص بك غير قابلة لإعادة الاستخدام.
عند الانتهاء، ستجد صورة تطبيقك عند تشغيل
docker image ls
التشغيل#
يمكن تشغيل حزمة خدماتك مباشرةً من ملف Docker Compose، أو يمكنك استخدام طبقة تنسيق مثل وضع Swarm أو Kubernetes.
مستقل (Standalone)#
أبسط طريقة لتشغيل تطبيقك هي بدؤه كحاوية مستقلة. سيستخدم Docker مصفوفات depends_on للتأكد من بدء أي خدمات معتمَد عليها أيضًا.
أولاً، نفّذ:
docker compose up app
ولاحظ أن كلتا الخدمتين app و db تبدآن.
يستمع تطبيقك على المنفذ 8080، وكما هو مُعرّف في ملف Docker Compose، يصبح متاحًا على جهاز التطوير الخاص بك عبر http://localhost:8080.
هذا التمييز في تعيين المنافذ مهم جدًا لأنه يمكنك تشغيل أي عدد من الخدمات على المنافذ نفسها إذا كانت كلها تعمل في حاوياتها الخاصة وتكشف كل منها منافذ مختلفة للجهاز المضيف.
زُر http://localhost:8080 وسترى It works!، لكن زُر http://localhost:8080/todos وستحصل على:
{"error":true,"reason":"Something went wrong."}
ألقِ نظرة على مخرجات السجلات في الطرفية التي شغّلت فيها docker compose up app وسترى:
[ ERROR ] relation "todos" does not exist
بالطبع! نحتاج إلى تشغيل الترحيلات على قاعدة البيانات. اضغط Ctrl+C لإيقاف تطبيقك. سنبدأ التطبيق مرة أخرى، لكن هذه المرة بـ:
docker compose up --detach app
الآن سيبدأ تطبيقك في وضع “منفصل” (في الخلفية). يمكنك التحقق من ذلك بتشغيل:
docker container ls
حيث سترى كلاً من قاعدة البيانات وتطبيقك يعملان في حاويات. يمكنك حتى التحقق من السجلات بتشغيل:
docker logs <container_id>
لتشغيل الترحيلات، نفّذ:
docker compose run migrate
بعد تشغيل الترحيلات، يمكنك زيارة http://localhost:8080/todos مرة أخرى، وستحصل على قائمة فارغة من المهام (todos) بدلاً من رسالة خطأ.
مستويات السجل (Log Levels)#
تذكّر مما سبق أن متغير البيئة LOG_LEVEL في ملف Docker Compose سيُوَرَّث من البيئة التي تبدأ فيها الخدمة إن كان متاحًا.
يمكنك تشغيل خدماتك بـ
LOG_LEVEL=trace docker-compose up app
للحصول على تسجيل بمستوى trace (الأكثر تفصيلاً). يمكنك استخدام متغير البيئة هذا لضبط التسجيل إلى أي مستوى متاح.
سجلات جميع الخدمات#
إذا حدّدت خدمة قاعدة البيانات صراحةً عند تشغيل الحاويات، فسترى سجلات كل من قاعدة بياناتك وتطبيقك.
docker-compose up app db
إيقاف الحاويات المستقلة#
الآن بعد أن أصبح لديك حاويات تعمل “منفصلة” عن صدفة (shell) المضيف، ستحتاج إلى إخبارها بالإيقاف بطريقة ما. من المفيد معرفة أنه يمكن طلب إيقاف أي حاوية قيد التشغيل بـ
docker container stop <container_id>
لكن أسهل طريقة لإيقاف هذه الحاويات تحديدًا هي
docker-compose down
مسح قاعدة البيانات#
يُعرّف ملف Docker Compose وحدة تخزين db_data للإبقاء على قاعدة بياناتك بين التشغيلات. هناك طريقتان لإعادة تعيين قاعدة بياناتك.
يمكنك إزالة وحدة التخزين db_data في الوقت نفسه الذي تُوقف فيه حاوياتك بـ
docker-compose down --volumes
يمكنك رؤية أي وحدات تخزين تُبقي البيانات حاليًا بـ docker volume ls. لاحظ أن اسم وحدة التخزين عادةً ما يكون له بادئة my-dockerized-app_ أو test_ تبعًا لما إذا كنت تعمل في وضع Swarm أم لا.
يمكنك إزالة وحدات التخزين هذه واحدة تلو الأخرى بـ، على سبيل المثال
docker volume rm my-dockerized-app_db_data
يمكنك أيضًا تنظيف جميع وحدات التخزين بـ
docker volume prune
فقط كن حذرًا حتى لا تحذف عن طريق الخطأ وحدة تخزين تحتوي على بيانات كنت تريد الاحتفاظ بها!
لن يسمح لك Docker بإزالة وحدات التخزين المستخدمة حاليًا بواسطة حاويات قيد التشغيل أو متوقّفة. يمكنك الحصول على قائمة بالحاويات قيد التشغيل بـ docker container ls، ويمكنك رؤية الحاويات المتوقّفة أيضًا بـ docker container ls -a.
وضع Swarm#
وضع Swarm واجهة سهلة الاستخدام عندما يكون لديك ملف Docker Compose جاهز وتريد اختبار كيفية توسّع تطبيقك أفقيًا. يمكنك قراءة كل ما يتعلق بوضع Swarm في الصفحات المتفرّعة من النظرة العامة.
أول ما نحتاجه هو عقدة مدير (manager node) لـ Swarm الخاص بنا. شغّل
docker swarm init
بعد ذلك سنستخدم ملف Docker Compose الخاص بنا لتشغيل حزمة (stack) باسم 'test' تحتوي على خدماتنا
docker stack deploy -c docker-compose.yml test
يمكننا رؤية كيف تعمل خدماتنا بـ
docker service ls
ينبغي أن تتوقع رؤية 1/1 نسخة لخدمتَي app و db، و 0/0 نسخة لخدمتَي migrate و revert.
نحتاج إلى استخدام أمر مختلف لتشغيل الترحيلات في وضع Swarm.
docker service scale --detach test_migrate=1
ملاحظة
لقد طلبنا للتو من خدمة قصيرة العمر أن تتوسّع إلى نسخة واحدة. ستتوسّع بنجاح، وتعمل، ثم تخرج. ومع ذلك، سيتركها ذلك بـ 0/1 نسخة قيد التشغيل. هذا ليس بالأمر المهم إلى أن نرغب في تشغيل الترحيلات مرة أخرى، لكن لا يمكننا أن نطلب منها “التوسّع إلى نسخة واحدة” إذا كانت بالفعل عند تلك القيمة. من غرائب هذا الإعداد أنه في المرة القادمة التي نريد فيها تشغيل الترحيلات ضمن بيئة تشغيل Swarm نفسها، نحتاج أولاً إلى تقليص الخدمة إلى 0 ثم إعادة توسيعها إلى 1.
المكافأة على عنائنا في سياق هذا الدليل القصير هي أنه يمكننا الآن توسيع تطبيقنا إلى أي قيمة نريدها لاختبار مدى تعامله الجيد مع تنازع قاعدة البيانات والأعطال والمزيد.
إذا أردت تشغيل 5 نسخ من تطبيقك بشكل متزامن، نفّذ
docker service scale test_app=5
بالإضافة إلى مشاهدة Docker يوسّع تطبيقك، يمكنك التحقق من أن 5 نسخ تعمل فعلاً بالتحقق مرة أخرى من docker service ls.
يمكنك عرض (ومتابعة) سجلات تطبيقك بـ
docker service logs -f test_app
إيقاف خدمات Swarm#
عندما تريد إيقاف خدماتك في وضع Swarm، تفعل ذلك بإزالة الحزمة التي أنشأتها سابقًا.
docker stack rm test
عمليات نشر الإنتاج#
كما ذُكر في الأعلى، لن يتطرّق هذا الدليل إلى تفاصيل كثيرة حول نشر تطبيقك المُحوّل إلى حاوية إلى الإنتاج، لأن الموضوع كبير ويتباين بشكل كبير تبعًا لخدمة الاستضافة (AWS و Azure وغيرها)، والأدوات (Terraform و Ansible وغيرها)، والتنسيق (Docker Swarm و Kubernetes وغيرها).
ومع ذلك، فإن التقنيات التي تتعلّمها لتشغيل تطبيقك المُحوّل إلى حاوية محليًا على جهاز التطوير الخاص بك قابلة للنقل إلى حد كبير إلى بيئات الإنتاج. سيقبل نسخة خادم مُعدّة لتشغيل خدمة Docker daemon جميع الأوامر نفسها.
انسخ ملفات مشروعك إلى خادمك، واتصل بالخادم عبر SSH، وشغّل أمر docker-compose أو docker stack deploy لتشغيل الأمور عن بُعد.
بدلاً من ذلك، اضبط متغير البيئة المحلي DOCKER_HOST ليشير إلى خادمك وشغّل أوامر docker محليًا على جهازك. من المهم ملاحظة أنه مع هذا النهج، لا تحتاج إلى نسخ أي من ملفات مشروعك إلى الخادم لكنك تحتاج إلى استضافة صورة Docker الخاصة بك في مكان يمكن لخادمك سحبها منه.