Docker + Laravel = ❤

لارافيل إن دوكر


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


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


  • "أنا أستخدم عامل الرصيف من أجل التنمية المحلية. أضع لارادوك ولا أعرف المشاكل". رائع ، ولكن ماذا عن إطلاق الأتمتة والإنتاج؟
  • "أقوم بجمع حاوية واحدة (متجانسة) استنادًا إلى fedora:latest (~ 230 ميجا بايت) ، وأضع جميع الخدمات (nginx ، و db ، وذاكرة التخزين المؤقت ، وما إلى ذلك) فيها ، وقم بتشغيل كل شيء داخل المشرف." أيضا ممتازة وسهلة البدء ، ولكن ماذا عن إيديولوجية "حاوية واحدة - عملية واحدة"؟ ماذا عن الموازنة وإدارة العمليات؟ ما هو حجم الصورة؟
  • "إليك أجزاء من التهيئة ، الموسم مع مقتطفات من نصوص sh ، وإضافة قيم env سحرية ، واستخدامها." شكراً لكم ، لكن ماذا عن مثال حي واحد على الأقل يمكنني أن أشعله وألعب بالكامل؟

كل ما تقرأه أدناه هو تجربة ذاتية لا تدعي أنها الحقيقة المطلقة. إذا كان لديك إضافات أو مؤشرات على عدم الدقة - مرحبًا بك في التعليقات.


بالنسبة إلى الصبر - رابط إلى المستودع ، استنساخ يمكنك بدء تطبيق Laravel فيه بأمر واحد. كما أنه ليس من الصعب تشغيله على نفس المزرعة ، أو "ربط" الحاويات بشكل صحيح ، أو استخدام إصدار بقالة docker-compose.yml كنقطة انطلاق.

الجزء النظري


ما الأدوات التي سنستخدمها في عملنا ، وما الذي سنركز عليه؟ بادئ ذي بدء ، نحن بحاجة إلى تثبيت على المضيف:


  • docker - في وقت كتابة هذا التقرير ، استخدمت الإصدار 18.06.1-ce
  • docker-compose - يتواءم مع ربط الحاويات وتخزين قيم البيئة اللازمة ؛ الإصدار 1.22.0
  • make - قد تفاجأ ، لكنها تناسب تماما في سياق العمل مع عامل الميناء

يمكنك curl -fsSL get.docker.com | sudo sh docker على أنظمة تشبه debian باستخدام الأمر curl -fsSL get.docker.com | sudo sh curl -fsSL get.docker.com | sudo sh ، لكن docker-compose الأفضل تثبيت docker-compose باستخدام pip ، لأن أحدث الإصدارات تعيش في مستودعاتها ( apt متخلفة كثيرًا ، كقاعدة عامة).

هذا يكمل قائمة التبعيات. ما ستستخدمه للعمل مع شفرة المصدر - phpstorm أو netbeans أو vim dead - الأمر متروك لك.


التالي هو سؤال وجواب مرتجل في سياق تصميم صورة (لا أخاف من هذه الكلمة) :


  • س: الصورة الأساسية - أيهما أفضل للاختيار؟


  • ج: الذي "أرق" ، بلا زخرفة. على أساس alpine (~ 5 ميجا بايت) ، يمكنك جمع كل ما يشتهيه قلبك ، ولكن على الأرجح سيكون عليك اللعب مع تجميع الخدمات من المصدر. كبديل - jessie-slim (~ 30 Mb) . أو استخدم الأكثر استخدامًا في مشروعاتك.


  • س: ما أهمية وزن الصورة؟


  • ج: انخفاض حجم حركة المرور ، انخفاض احتمال حدوث خطأ عند التنزيل (بيانات أقل - احتمال أقل) ، انخفاض في المكان المستهلك. قاعدة "الجاذبية موثوقة" (© "Snatch") لا تعمل بشكل جيد هنا.


  • س: لكن صديقي %friend_name% يقول أن الصورة "المتجانسة" ذات التبعيات %friend_name% هي أفضل طريقة.


  • ج: لنعد فقط. يحتوي التطبيق على 3 تبعيات - PG و Redis و PHP. وأردت اختبار كيف ستتصرف في حزم من إصدارات مختلفة من هذه التبعيات. PG - الإصدارات 9.6 و 10 ، Redis - 3.2 و 4.0 ، PHP - 7.0 و 7.2. في حال كان كل إدمان صورة منفصلة - تحتاج إلى 6 منها ، ولا تحتاج حتى إلى جمعها - كل شيء جاهز ويكمن على hub.docker.com . إذا كانت ، لأسباب إيديولوجية ، "معبأة" جميع التبعيات في حاوية واحدة ، عليك إعادة تجميعها مع الأقلام ... 8 مرات؟ أضف الآن الشرط الذي لا تزال ترغب في تشغيله باستخدام opcache . في حالة التحلل ، هذا ببساطة تغيير في علامات الصور المستخدمة. من السهل تشغيل المونوليث وصيانته ، ولكنه الطريق إلى العدم.


  • س: لماذا المشرف في الحاوية شرير؟


  • ج: لأن PID 1 . إذا كنت لا تريد الكثير من المشكلات المتعلقة بعمليات الزومبي ولديك القدرة على "إضافة السعة" بمرونة عند الضرورة - حاول تشغيل عملية واحدة لكل حاوية. الاستثناء الغريب هو nginx مع عمالها و php-fpm ، الذين لديهم القدرة على إنتاج العمليات ، ولكن عليهم تحمل هذا (علاوة على ذلك ، ليسوا سيئين في الرد على SIGTERM ، بشكل صحيح تمامًا "قتل" عمالهم). من خلال إطلاق جميع الشياطين كمشرف ، تكاد تكون من المؤكد أن تتعامل مع المشاكل. على الرغم من أنه في بعض الحالات ، يصعب الاستغناء عنها ، ولكن هذه استثناءات بالفعل.



بعد أن قررت النهج الرئيسية ، دعنا ننتقل إلى تطبيقنا. يجب أن يكون قادرًا على:


  • web|api - يعطي ثابتًا باستخدام nginx ، nginx محتوى ديناميكيًا باستخدام fpm
  • scheduler - قم بتشغيل مجدول المهام الأصلي
  • queue - معالجة المهام من قوائم الانتظار

مجموعة أساسية يمكن توسيعها إذا لزم الأمر. الآن دعنا ننتقل إلى الصور التي يجب أن نجمعها من أجل أن يقوم تطبيقنا "بالإقلاع" (يتم إعطاء أسماء الرموز الخاصة بهم بين قوسين):


  • PHP + PHP-FPM ( تطبيق ) - البيئة التي سيتم تنفيذ الكود فيها. نظرًا لأن إصدارات PHP و FPM ستكون هي نفسها بالنسبة لنا - فإننا نجمعها في صورة واحدة. لذلك من الأسهل إدارتها باستخدام التكوينات ، وسيكون تكوين الحزم متطابقًا. بالطبع - سيتم تشغيل FPM وعمليات التطبيق في حاويات مختلفة
  • nginx ( nginx ) - هذا لن يزعجك تسليم التكوينات والوحدات الاختيارية لـ nginx - سنقوم بتجميع صورة منفصلة معها. نظرًا لأنها خدمة منفصلة ، فإن لديها ملف إرساء خاص بها وسياقها
  • مصادر التطبيق ( المصادر ) - سيتم تسليم المصدر باستخدام صورة منفصلة ، ورفع volume معهم في حاوية مع التطبيق. الصورة الأساسية هي alpine ، وداخلها لا توجد سوى مصادر مع تبعيات مثبتة ومجمعة باستخدام أصول حزم الويب (أدوات البناء)

إطلاق خدمات تطوير أخرى في حاويات ، hub.docker.com من hub.docker.com ؛ على الإنتاج ، من ناحية أخرى - فهي تعمل على خوادم منفصلة ، مجمعة. كل ما تبقى لنا هو إخبار التطبيق (من خلال البيئة) بالعناوين / المنافذ والتفاصيل التي من الضروري طرقها. الأكثر برودة هو استخدام اكتشاف الخدمة لهذا الغرض ، ولكن ليس في هذا الوقت.


بعد أن قررت في الجزء النظري ، أقترح الانتقال إلى الجزء التالي.


الجزء العملي


أقترح تنظيم الملفات في المستودع على النحو التالي:


 . ├── docker #    -   │  ├── app │  │  ├── Dockerfile │  │  └── ... │  ├── nginx │  │  ├── Dockerfile │  │  └── ... │  └── sources │    ├── Dockerfile │    └── ... ├── src #   │ ├── app │ ├── bootstrap │ ├── config │ ├── artisan │ └── ... ├── docker-compose.yml # Compose-    ├── Makefile ├── CHANGELOG.md └── README.md 

يمكنك التعرف على الهيكل والملفات بالنقر على هذا الرابط .

لإنشاء خدمة ، يمكنك استخدام الأمر:


 $ docker build \ --tag %local_image_name% \ -f ./docker/%service_directory%/Dockerfile ./docker/%service_directory% 

سيكون الاختلاف الوحيد هو تجميع الصورة مع المصادر - لذلك ، ./src ضبط سياق التجميع (الوسيطة المتطرفة) على ./src .


توصي قواعد تسمية الصور في السجل المحلي باستخدام تلك التي يستخدمها %root_directory_name%_%service_name% بشكل افتراضي ، وهي: %root_directory_name%_%service_name% . إذا كان دليل المشروع يسمى my-awesome-project ، وكانت الخدمة تسمى redis ، فمن الأفضل اختيار اسم الصورة (محلي) لاختيار my-awesome-project_redis على التوالي.


لتسريع عملية البناء ، يمكنك إخبار عامل الميناء باستخدام ذاكرة التخزين المؤقت للصورة التي تم تجميعها سابقًا ، ولهذا ، يتم استخدام --cache-from %full_registry_name% ذاكرة التخزين المؤقت --cache-from %full_registry_name% . وبالتالي ، سيظهر برنامج docker docker قبل بدء تعليمات معينة في ملف Dockerfile - هل تغير؟ وإذا لم يكن (التجزئة تتقارب) - ستتخطى التعليمات ، باستخدام الطبقة المعدة بالفعل من الصورة ، والتي ستخبرها باستخدامها كذاكرة تخزين مؤقت. هذا الشيء ليس سيئًا لذا سيعيد بناء العملية ، خاصة إذا لم يتغير شيء :)

انتبه إلى نصوص ENTRYPOINT لتشغيل حاويات التطبيق.

تم جمع صورة البيئة لإطلاق التطبيق (التطبيق) مع الأخذ في الاعتبار حقيقة أنه لن يعمل فقط على الإنتاج ، ولكن أيضًا محليًا ، يحتاج المطورون إلى التفاعل معه بشكل فعال. يجب أن يكون تثبيت وإزالة تبعيات composer ، وإجراء اختبارات unit ، وسجلات tail واستخدام الأسماء المستعارة المألوفة ( php /app/artisanart ، composerc ) بدون أي إزعاج. علاوة على ذلك - سيتم استخدامه أيضًا لإجراء اختبارات unit ومحللات الشفرة الثابتة ( phpstan في حالتنا) على CI. هذا هو السبب في أن ملف Dockerfile الخاص به ، على سبيل المثال ، يحتوي على xdebug تثبيت xdebug ، ولكن الوحدة النمطية نفسها غير ممكّنة (يتم تمكينها فقط باستخدام CI).


أيضًا بالنسبة إلى composer يتم hirak/prestissimo حزمة hirak/prestissimo ، مما يعزز إلى حد كبير تثبيت جميع التبعيات.

عند الإنتاج ، نقوم بتركيب محتويات الدليل /src من الصورة مع المصادر (المصادر) داخلها في دليل /app . من أجل التطوير ، "نقوم بتمرير" الدليل المحلي بمصادر التطبيق ( -v "$(pwd)/src:/app:rw" ).


وهنا يكمن أحد التعقيدات - هذه هي حقوق الوصول إلى الملفات التي تم إنشاؤها من الحاوية. والحقيقة هي أن العمليات التي يتم تشغيلها داخل الحاوية بشكل افتراضي تبدأ من الجذر ( root:root ) ، والملفات التي تم إنشاؤها بواسطة هذه العمليات (ذاكرة التخزين المؤقت ، والسجلات ، والجلسات ، وما إلى ذلك) - أيضًا ، ونتيجة لذلك - ليس لديك أي شيء "محليًا" معهم يمكنك القيام بذلك دون تنفيذ sudo chown -R $(id -u):$(id -g) /path/to/sources .


كحل واحد ، استخدم fixuid ، لكن هذا الحل بسيط. بدت لي أفضل طريقة لإعادة USER_ID المحلي و GROUP_ID داخل الحاوية ، وبدء العمليات بهذه القيم . بشكل افتراضي ، يتم استبدال القيم 1000:1000 (القيم الافتراضية للمستخدم المحلي الأول) بالتخلص من المكالمة $(id -u):$(id -g) ، وإذا لزم الأمر ، يمكنك دائمًا تجاوزها ( $ USER_ID=666 docker-compose up -d ) أو وضع ملف إنشاء عامل .env ملف .env .


أيضًا ، عندما php-fpm إطلاق php-fpm محليًا php-fpm لا تنس تعطيل opcache منه - وإلا فهناك الكثير من "نعم ماذا بحق الجحيم!" سيتم توفيرها لك.


بالنسبة إلى الاتصال "المباشر" بـ redis و postgres ، قمت بطرح منافذ إضافية "خارج" ( 15432 و 15432 على التوالي) ، لذلك لا توجد مشاكل في "الاتصال ومعرفة ما هو وكيف هو حقًا" من حيث المبدأ.


أحتفظ بالحاوية مع تشغيل app اسم الرمز ( --command keep-alive.sh ) لغرض الوصول بسهولة إلى التطبيق.


فيما يلي بعض الأمثلة على حل المشكلات اليومية باستخدام docker-compose :


العمليةالأمر الجاري
قم بتثبيت حزمة composer$ docker-compose exec app composer require package/name
تشغيل phpunit$ docker-compose exec app php ./vendor/bin/phpunit --no-coverage
تثبيت كافة تبعيات العقدة$ docker-compose run --rm node npm install
تثبيت حزمة العقدة$ docker-compose run --rm node npm i package_name
إطلاق إعادة بناء حية للأصول$ docker-compose run --rm node npm run watch

يمكنك العثور على جميع تفاصيل الإطلاق في ملف docker-compose.yml .


تشوي make قيد الحياة!


كتابة نفس الأوامر في كل مرة تصبح مملة بعد المرة الثانية ، وبما أن المبرمجين هم مخلوقات كسولة بطبيعتها ، دعنا ندخل في "الأتمتة" الخاصة بهم. إن الاحتفاظ بمجموعة من النصوص يعد خيارًا ، ولكنه ليس جذابًا مثل ملف واحد ، خاصة وأن تطبيقه في التطوير الحديث لا Makefile حقه.


يمكنك العثور على الدليل الكامل باللغة الروسية على هذا الرابط .

دعنا نرى كيف يبدو الجري في جذر المستودع:


 [user@host ~/projects/app] $ make help Show this help app-pull Application - pull latest Docker image (from remote registry) app Application - build Docker image locally app-push Application - tag and push Docker image into remote registry sources-pull Sources - pull latest Docker image (from remote registry) sources Sources - build Docker image locally sources-push Sources - tag and push Docker image into remote registry nginx-pull Nginx - pull latest Docker image (from remote registry) nginx Nginx - build Docker image locally nginx-push Nginx - tag and push Docker image into remote registry pull Pull all Docker images (from remote registry) build Build all Docker images push Tag and push all Docker images into remote registry login Log in to a remote Docker registry clean Remove images from local registry --------------- --------------- up Start all containers (in background) for development down Stop all started for development containers restart Restart all started for development containers shell Start shell into application container install Install application dependencies into application container watch Start watching assets for changes (node) init Make full application initialization (install, seed, build assets) test Execute application tests Allowed for overriding next properties: PULL_TAG - Tag for pulling images before building own ('latest' by default) PUBLISH_TAGS - Tags list for building and pushing into remote registry (delimiter - single space, 'latest' by default) Usage example: make PULL_TAG='v1.2.3' PUBLISH_TAGS='latest v1.2.3 test-tag' app-push 

انه جيد جدا في الادمان. على سبيل المثال ، لبدء watch ( docker-compose run --rm node npm run watch ) ، تحتاج إلى "رفع" التطبيق - ما عليك سوى تحديد الهدف up باعتباره تابعًا - ولا داعي للقلق بشأن نسيان القيام بذلك قبل الاتصال watch - make نفسه سيفعل كل شيء من أجلك. وينطبق الشيء نفسه على اختبارات التشغيل والتحليلات الثابتة ، على سبيل المثال ، قبل تنفيذ التغييرات - قم make test كل السحر سيحدث لك!


وغني عن القول ، لا داعي للقلق بشأن تجميع الصور وتنزيلها وتحديد --cache-from وكل شيء تقريبًا؟


يمكنك عرض محتويات ملف Makefile على هذا الرابط .


جزء تلقائي


دعنا ننتقل إلى الجزء الأخير من هذه المقالة - هذا هو أتمتة عملية تحديث الصور في Docker Registry. على الرغم من استخدام مثال GitLab CI الخاص بي - لنقل الفكرة إلى خدمة تكامل أخرى ، أعتقد أنها ستكون ممكنة تمامًا.


بادئ ذي بدء ، سنحدد تسمية علامات الصورة المستخدمة:


اسم العلامةالوجهة
latestالصور التي تم جمعها من الفرع master .
حالة الشفرة هي الأحدث ، ولكنها ليست جاهزة بعد للدخول في الإصدار
some-branch-nameالصور التي تم جمعها على الغداء some-branch-name .
وبالتالي ، يمكننا "طرح" التغييرات في أي بيئة تم تنفيذها فقط في إطار غداء محدد حتى قبل دمجها مع الضوء master - يكفي "توسيع" الصور بهذه العلامة.
و - نعم ، التغييرات قد تتعلق بكل من كود وصور جميع الخدمات بشكل عام!
vX.XXفي الواقع ، إصدار التطبيق (يستخدم لنشر إصدار معين)
stableالاسم المستعار ، للعلامة التي تحتوي على أحدث إصدار (استخدم لنشر أحدث إصدار ثابت)

يتم الإصدار عن طريق نشر علامة في vX.XX بتنسيق vX.XX


لتسريع البناء ، يتم استخدام التخزين المؤقت ./src/vendor و ./src/node_modules + --cache-from ذاكرة التخزين المؤقت --cache-from أجل docker build ، ويتكون من المراحل التالية:


اسم المرحلةالوجهة
prepareالمرحلة التحضيرية - تجميع صور جميع الخدمات ما عدا الصورة مع المصدر
testاختبار التطبيق (تشغيل phpunit ، محللات الكود الثابت) باستخدام الصور التي تم جمعها في مرحلة التحضير
buildتثبيت جميع تبعيات composer ( --no-dev ) ، وتجميع assets webpack ، webpack الصورة باستخدام شفرة المصدر بما في ذلك العناصر المستلمة ( vendor/* ، app.js ، app.css )

لقطة شاشة لخطوط الأنابيب


التجميع في الفرع master ينتج push latest العلامات master

في المتوسط ​​، تستغرق جميع مراحل التجميع 4 دقائق ، وهي نتيجة جيدة جدًا (التنفيذ الموازي للمهام هو كل شيء لدينا).


يمكنك التعرف على محتويات التكوين ( .gitlab-ci.yml ) .gitlab-ci.yml في هذا الرابط .


بدلا من الاستنتاج


كما ترى ، تنظيم العمل باستخدام تطبيق php (باستخدام Laravel كمثال) باستخدام Docker ليس صعبًا للغاية. كاختبار ، يمكنك شوكة المستودع ، واستبدال كل tarampampam/laravel-in-docker خاصة بك - جرب كل شيء "مباشر" بنفسك.


للإطلاق المحلي - قم بتشغيل أمرين فقط:


 $ git clone https://gitlab.com/tarampampam/laravel-in-docker.git ./laravel-in-docker && cd $_ $ make init 

ثم افتح http://127.0.0.1:9999 في متصفحك المفضل.


... اغتنام الفرصة


في الوقت الحالي أعمل على مشروع "الرمز التلقائي" لمشروع TL ، ونحن نبحث عن مطوري PHP الموهوبين ومسؤولي النظام (يقع مكتب التطوير في ايكاترينبرج). إذا كنت تعتبر نفسك الأول أو الثاني - اكتب خطاب الموارد البشرية الخاص بنا مع النص "أريد أن أكون فريق تطوير ، استأنف:٪ link_on_summary٪" إلى البريد الإلكتروني hr@avtocod.ru ، فنحن hr@avtocod.ru الانتقال.

Source: https://habr.com/ru/post/ar425101/


All Articles