
في هذه المقالة ، سأتحدث عن تجربتي في "تغليف" تطبيق 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/artisan
→ art
، composer
→ c
) بدون أي إزعاج. علاوة على ذلك - سيتم استخدامه أيضًا لإجراء اختبارات 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
الانتقال.