نشر Laravel Zero Downtime

نشرت: 2022-03-11

عندما يتعلق الأمر بتحديث تطبيق مباشر ، هناك طريقتان مختلفتان بشكل أساسي للقيام بذلك.

في النهج الأول ، نقوم بإجراء تغييرات تدريجية على حالة نظامنا. على سبيل المثال ، نقوم بتحديث الملفات وتعديل خصائص البيئة وتثبيت الضروريات الإضافية وما إلى ذلك. في الطريقة الثانية ، نقوم بتفكيك الأجهزة بالكامل وإعادة بناء النظام بصور جديدة وتكوينات توضيحية (على سبيل المثال ، باستخدام Kubernetes).

نشر Laravel أصبح سهلاً

تغطي هذه المقالة بشكل أساسي التطبيقات الصغيرة نسبيًا ، والتي قد لا تتم استضافتها في السحابة ، على الرغم من أنني سأذكر كيف يمكن لـ Kubernetes مساعدتنا بشكل كبير في عمليات النشر التي تتجاوز سيناريو "عدم وجود سحابة". سنناقش أيضًا بعض المشكلات العامة والنصائح الخاصة بإجراء تحديثات ناجحة قد تكون قابلة للتطبيق في مجموعة من المواقف المختلفة ، وليس فقط مع نشر Laravel.

لأغراض هذا العرض التوضيحي ، سأستخدم مثال Laravel ، لكن ضع في اعتبارك أن أي تطبيق PHP يمكن أن يستخدم نهجًا مشابهًا.

الإصدار

بالنسبة للمبتدئين ، من الأهمية بمكان أن نعرف إصدار الكود المنتشر حاليًا في الإنتاج. يمكن تضمينه في بعض الملفات أو على الأقل في اسم مجلد أو ملف. بالنسبة للتسمية ، إذا اتبعنا الممارسة القياسية للإصدار الدلالي ، فيمكننا تضمين معلومات أكثر من مجرد رقم واحد.

بالنظر إلى إصدارين مختلفين ، يمكن أن تساعدنا هذه المعلومات المضافة في فهم طبيعة التغييرات التي يتم إدخالها بينهما بسهولة.

صورة توضح شرح الإصدارات الدلالية.

يبدأ إصدار الإصدار بنظام التحكم في الإصدار ، مثل Git. لنفترض أننا أعددنا إصدارًا للنشر ، على سبيل المثال ، الإصدار 1.0.3. عندما يتعلق الأمر بتنظيم هذه الإصدارات وتدفقات التعليمات البرمجية ، فهناك أنماط تطوير مختلفة مثل التطوير المستند إلى trunk وتدفق Git ، والتي يمكنك اختيارها أو مزجها بناءً على تفضيلات فريقك وتفاصيل مشروعك. في النهاية ، سننتهي على الأرجح بإصداراتنا الموسومة بالمقابل في فرعنا الرئيسي.

بعد الالتزام ، يمكننا إنشاء علامة بسيطة مثل هذا:

git tag v1.0.3

ثم نقوم بتضمين العلامات أثناء تنفيذ أمر الدفع:

git push <origin> <branch> --tags

يمكننا أيضًا إضافة علامات إلى الالتزامات القديمة باستخدام تجزئاتها.

إحضار ملفات الإصدار إلى وجهتها

يستغرق نشر Laravel وقتًا ، حتى لو كان مجرد نسخ الملفات. ومع ذلك ، حتى لو لم يستغرق الأمر وقتًا طويلاً ، فإن هدفنا هو عدم حدوث أي توقف .

لذلك ، يجب أن نتجنب تثبيت التحديث في مكانه ولا يجب تغيير الملفات التي يتم تقديمها مباشرة. بدلاً من ذلك ، يجب أن ننشر إلى دليل آخر ونجعل التبديل فقط بمجرد أن يصبح التثبيت جاهزًا تمامًا.

في الواقع ، هناك العديد من الأدوات والخدمات التي يمكن أن تساعدنا في عمليات النشر ، مثل Envoyer.io (بواسطة مصمم Laravel.com Jack McDade) ، Capistrano ، Deployer ، إلخ. لم أستخدمها جميعًا في الإنتاج حتى الآن ، لذلك لا يمكنني تقديم توصيات أو كتابة مقارنة شاملة ، ولكن اسمحوا لي أن أعرض الفكرة وراء هذه المنتجات. إذا لم يستطع بعضها (أو جميعها) تلبية متطلباتك ، فيمكنك دائمًا إنشاء البرامج النصية المخصصة لأتمتة العملية بأفضل طريقة تراها مناسبة.

لأغراض هذا العرض التوضيحي ، لنفترض أن تطبيق Laravel الخاص بنا يتم تقديمه بواسطة خادم Nginx من المسار التالي:

/var/www/demo/public

أولاً ، نحتاج إلى دليل لوضع ملفات الإصدار في كل مرة نقوم فيها بعملية النشر. أيضًا ، نحتاج إلى ارتباط رمزي يشير إلى إصدار العمل الحالي. في هذه الحالة ، سيكون /var/www/demo بمثابة رابط رمزي خاص بنا. ستسمح لنا إعادة تعيين المؤشر بتغيير الإصدارات بسرعة.

معالجة ملف نشر Laravel

في حالة تعاملنا مع خادم Apache ، فقد نحتاج إلى السماح بالروابط الرمزية التالية في التكوين:

Options +FollowSymLinks

يمكن أن يكون هيكلنا مثل هذا:

 /opt/demo/release/v0.1.0 /opt/demo/release/v0.1.1 /opt/demo/release/v0.1.2

قد تكون هناك بعض الملفات التي نحتاج إلى استمرارها من خلال عمليات النشر المختلفة ، على سبيل المثال ، ملفات السجل (إذا كنا لا نستخدم Logstash ، من الواضح). في حالة نشر Laravel ، قد نرغب في الاحتفاظ بدليل التخزين وملف تكوين env. يمكننا فصلهم عن الملفات الأخرى واستخدام روابط الرموز الخاصة بهم بدلاً من ذلك.

لجلب ملفات الإصدار الخاصة بنا من مستودع Git ، يمكننا إما استخدام أوامر النسخ أو الأرشفة. يستخدم بعض الأشخاص git clone ، لكن لا يمكنك استنساخ التزام أو علامة معينة. هذا يعني أنه تم جلب المستودع بالكامل ثم تحديد العلامة المحددة. عندما يحتوي المستودع على العديد من الفروع أو تاريخ كبير ، يكون حجمه أكبر بكثير من أرشيف الإصدارات. لذا ، إذا لم تكن بحاجة إلى git repo على الإنتاج على وجه التحديد ، فيمكنك استخدام git archive . هذا يسمح لنا بجلب ملف أرشيف فقط بواسطة علامة محددة. ميزة أخرى لاستخدام الأخير هي أنه يمكننا تجاهل بعض الملفات والمجلدات التي لا ينبغي أن تكون موجودة في بيئة الإنتاج ، على سبيل المثال ، الاختبارات. لهذا ، نحتاج فقط إلى تعيين خاصية تجاهل التصدير في .gitattributes file . في قائمة التحقق من ممارسات التشفير الآمن OWASP ، يمكنك العثور على التوصية التالية: "إزالة رمز الاختبار أو أي وظيفة غير مخصصة للإنتاج ، قبل النشر."

إذا كنا نجلب الإصدار من نظام التحكم في الإصدار المصدر ، فيمكن أن يساعدنا أرشيف git و export-ignore في تلبية هذا المطلب.

دعنا نلقي نظرة على برنامج نصي مبسط (سيحتاج إلى معالجة أفضل للخطأ في الإنتاج):

نشر

 #!/bin/bash # Terminate execution if any command fails set -e # Get tag from a script argument TAG=$1 GIT_REMOTE_URL='here should be a remote url of the repo' BASE_DIR=/opt/demo # Create folder structure for releases if necessary RELEASE_DIR=$BASE_DIR/releases/$TAG mkdir -p $RELEASE_DIR mkdir -p $BASE_DIR/storage cd $RELEASE_DIR # Fetch the release files from git as a tar archive and unzip git archive \ --remote=$GIT_REMOTE_URL \ --format=tar \ $TAG \ | tar xf - # Install laravel dependencies with composer composer install -o --no-interaction --no-dev # Create symlinks to `storage` and `.env` ln -sf $BASE_DIR/.env ./ rm -rf storage && ln -sf $BASE_DIR/storage ./ # Run database migrations php artisan migrate --no-interaction --force # Run optimization commands for laravel php artisan optimize php artisan cache:clear php artisan route:cache php artisan view:clear php artisan config:cache # Remove existing directory or symlink for the release and create a new one. NGINX_DIR=/var/www/public mkdir -p $NGINX_DIR rm -f $NGINX_DIR/demo ln -sf $RELEASE_DIR $NGINX_DIR/demo

لنشر إصدارنا ، يمكننا تنفيذ ما يلي:

deploy.sh v1.0.3

ملاحظة: في هذا المثال ، v1.0.3 هي علامة git لإصدارنا.

ملحن على الإنتاج؟

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

  • يمكن أن يؤدي خطأ الشبكة إلى مقاطعة تنزيل التبعيات.
  • قد لا يتبع بائع المكتبة دائمًا SemVer.

يمكن ملاحظة خطأ في الشبكة بسهولة. حتى أن البرنامج النصي الخاص بنا سيتوقف عن التنفيذ مع وجود خطأ. ولكن قد يكون من الصعب للغاية تحديد التغيير المفاجئ في المكتبة دون إجراء الاختبارات ، وهو ما لا يمكنك القيام به في الإنتاج. أثناء تثبيت التبعيات ، يعتمد Composer و npm والأدوات المماثلة الأخرى على تعيين الإصدار الدلالي – major.minor.patch. إذا رأيت ~ 1.0.2 في composer.json ، فهذا يعني تثبيت الإصدار 1.0.2 أو أحدث إصدار من التصحيح ، مثل 1.0.4. إذا رأيت ^ 1.0.2 ، فهذا يعني تثبيت الإصدار 1.0.2 أو أحدث إصدار ثانوي أو إصدار تصحيح ، مثل 1.1.0. نحن على ثقة من أن بائع المكتبة سيصطدم بالرقم الرئيسي عند إدخال أي تغيير ، ولكن في بعض الأحيان يتم إغفال هذا المطلب أو عدم اتباعه. كانت هناك مثل هذه الحالات في الماضي. حتى إذا قمت بوضع إصدارات ثابتة في composer.json ، فقد تحتوي تبعياتك على ~ و ^ في composer.json.

إذا كان من الممكن الوصول إليه ، في رأيي ، فإن أفضل طريقة هي استخدام مستودع القطع الأثرية (Nexus ، JFrog ، إلخ). سيتم إنشاء بنية الإصدار ، التي تحتوي على جميع التبعيات الضرورية ، مرة واحدة في البداية. سيتم تخزين هذه القطعة الأثرية في مستودع وجلبها لمراحل اختبار مختلفة من هناك. أيضًا ، سيكون هذا هو الإصدار الذي سيتم نشره في الإنتاج ، بدلاً من إعادة إنشاء التطبيق من Git.

التوافق مع التعليمات البرمجية وقاعدة البيانات

السبب في أنني وقعت في حب Laravel للوهلة الأولى هو كيف أولى مؤلفه اهتمامًا وثيقًا بالتفاصيل ، وفكر في راحة المطورين ، وأدرج أيضًا الكثير من أفضل الممارسات في إطار العمل ، مثل عمليات ترحيل قاعدة البيانات.

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

على سبيل المثال ، لنفترض أن لدينا عمود address ونحتاج إلى address1 إلى العنوان 1 address2 . للحفاظ على توافق كل شيء ، قد نحتاج إلى العديد من الإصدارات.

  1. أضف عمودين جديدين في قاعدة البيانات.
  2. قم بتعديل التطبيق لاستخدام الحقول الجديدة كلما أمكن ذلك.
  3. ترحيل بيانات address إلى أعمدة جديدة وإسقاطها.

هذه الحالة هي أيضًا مثال جيد على مدى كون التغييرات الصغيرة أفضل بكثير للنشر. تراجعهم أسهل أيضًا. إذا قمنا بتغيير قاعدة البيانات وقاعدة البيانات لعدة أسابيع أو أشهر ، فقد يكون من المستحيل تحديث نظام الإنتاج دون توقف.

بعض روعة Kubernetes

على الرغم من أن حجم تطبيقنا قد لا يحتاج إلى السحب والعقد و Kubernetes ، إلا أنني ما زلت أود أن أذكر كيف تبدو عمليات النشر في K8s. في هذه الحالة ، لا نقوم بإجراء تغييرات على النظام ، بل نعلن ما نود تحقيقه وما الذي يجب تشغيله على عدد النسخ المتماثلة. بعد ذلك ، يتأكد Kubernetes من أن الحالة الفعلية تطابق الحالة المطلوبة.

عندما يكون لدينا إصدار جديد جاهز ، فإننا نبني صورة بها ملفات جديدة ، ونضع علامة على الصورة بالإصدار الجديد ، ونمررها إلى K8s. سوف يقوم الأخير بتدوير صورتنا بسرعة داخل كتلة. سوف ينتظر قبل أن يصبح التطبيق جاهزًا بناءً على فحص الجاهزية الذي نقدمه ، ثم يعيد توجيه حركة المرور بشكل غير ملحوظ إلى التطبيق الجديد ويقتل التطبيق القديم. يمكننا بسهولة تشغيل عدة إصدارات من تطبيقنا مما يتيح لنا تنفيذ عمليات نشر باللونين الأزرق / الأخضر أو ​​الكناري باستخدام عدد قليل من الأوامر.

إذا كنت مهتمًا ، فهناك بعض العروض الرائعة في الحديث "9 خطوات إلى رائعة مع Kubernetes بواسطة Burr Sutter."

الموضوعات ذات الصلة: المصادقة الكاملة للمستخدم والتحكم في الوصول - برنامج تعليمي لـ Laravel Passport ، Pt. 1