البناء متعدد المراحل والتحسين
صغّر الصور وافصل البناء عن وقت التشغيل
البناء متعدد المراحل والتحسين درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.
لماذا البناء متعدد المراحل
تحتوي الصورة ذات المرحلة الواحدة على Composer واعتماديات البناء وترويسات التطوير ومجلد tests/ ضمن بيئة الإنتاج. تتيح لكم عمليات البناء متعددة المراحل التجميع والتثبيت في مرحلة builder كبيرة، ثم نسخ العناصر النهائية فقط إلى مرحلة runtime صغيرة.
والنتيجة: صور أصغر، وسطح هجوم أقل، وعمليات سحب أسرع، ومن دون شحن المصرّفات إلى الإنتاج.
المراحل المسماة
يبدأ كل FROM ... AS name مرحلة جديدة. ويمكن للمراحل اللاحقة استخدام COPY --from=name لنسخ الملفات من المراحل السابقة. تصبح المرحلة النهائية وحدها صورتكم؛ أما المراحل الوسيطة فتُهمَل، مع بقائها مخزّنة مؤقتًا.
# Stage 1: dependencies
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist --ignore-platform-reqs
# Stage 2: runtime
FROM php:8.3-fpm-alpine AS runtime
WORKDIR /app
COPY --from=vendor /app/vendor ./vendor
COPY . .فصل اعتماديات البناء
يتطلب تجميع الامتدادات autoconf وgcc وترويسات التطوير، ولا ينتمي أيٌّ منها إلى بيئة التشغيل. استخدموا المثبّت في مرحلة builder، ثم انسخوا ملفات .so المجمّعة وملف ini المطابق في conf.d إلى مرحلة تشغيل نظيفة.
FROM php:8.3-fpm-alpine AS ext-builder
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions redis igbinary opcache intl
FROM php:8.3-fpm-alpine AS runtime
# Copy compiled extensions + their enable configs
COPY --from=ext-builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=ext-builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ترتيب التخزين المؤقت للطبقات
يخزّن Docker الطبقات مؤقتًا من الأعلى إلى الأسفل، ويبطل صلاحية كل ما يقع أسفل الطبقة التي تغيّرت. رتّبوا العناصر من الأقل إلى الأكثر تغيّرًا:
- القاعدة والامتدادات (نادرًا)
composer.lockوالتثبيت (أحيانًا)- مصدر التطبيق (مع كل commit)
- تفريغ التحميل التلقائي (مع كل commit)
وهذا يعني أن تغييرًا يقتصر على الشيفرة سيعيد استخدام طبقة vendor المخزّنة مؤقتًا بالكامل.
# BAD: copying all source before composer install
# busts the vendor layer on every code change
COPY . .
RUN composer install
# GOOD: lock first, then source
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-autoloader
COPY . .
RUN composer dump-autoload --optimizeحوامل التخزين المؤقت في BuildKit
باستخدام BuildKit (DOCKER_BUILDKIT=1) يمكنكم تركيب ذاكرة تخزين مؤقتة دائمة تستمر بين عمليات البناء من دون أن تنتهي داخل الصورة. وهذا مثالي لذاكرة Composer المؤقتة العامة، إذ تتخطى عمليات البناء المتكررة إعادة تنزيل الحزم.
# syntax=docker/dockerfile:1
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN --mount=type=cache,target=/tmp/composer-cache \
COMPOSER_CACHE_DIR=/tmp/composer-cache \
composer install --no-dev --prefer-distقياس حجم الصورة
افحصوا توزيع الطبقات للعثور على التضخم. يعرض docker history الحجم الذي أضافه كل توجيه، بينما تعرض أدوات مثل dive المساحة المهدرة. الهدف هو الوصول إلى مرحلة تشغيل بلا مصرّفات ولا Composer ولا اعتماديات تطوير.
# Compare sizes
docker images myapp
# Per-layer contribution
docker history --no-trunc --format '{{.Size}}\t{{.CreatedBy}}' myapp:latest
# Deep inspection of wasted bytes
dive myapp:latestتقليص المرحلة النهائية
يجب ألا تحتوي مرحلة التشغيل على Composer أو البرنامج النصي لمثبّت الامتدادات أو مجموعة اختباراتكم. انسخوا vendor والمصدر من مراحل البناء، ولا تشغّلوا composer مطلقًا في المرحلة النهائية. أزيلوا المثبّت بعد استخدامه إذا اضطررتم إلى تشغيله هناك.
FROM php:8.3-fpm-alpine AS runtime
WORKDIR /app
# bring extensions + vendor in from builders — no Composer here
COPY --from=ext-builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=ext-builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=vendor /app/vendor ./vendor
COPY . .
USER www-data
CMD ["php-fpm"]حدود Distroless وScratch
لا يستطيع PHP العمل على scratch الفارغ تمامًا، إذ يحتاج إلى libc ومكتبات مشتركة. والحد العملي الأدنى هو alpine (المبني على musl) أو Debian مصغّر بأسلوب distroless. يُعد Alpine الأصغر حجمًا، لكن انتبهوا إلى المكتبات الأصلية التي تتوقع glibc؛ وإذا واجهتم أخطاء segmentation fault مع NSS أو ICU، فعودوا إلى php:8.3-fpm-bookworm.
# Smallest practical PHP runtime
FROM php:8.3-fpm-alpine
# If musl causes native-lib issues (e.g., some ICU edge cases),
# the glibc Debian slim variant is the safe fallback:
# FROM php:8.3-fpm-bookwormاستهداف المراحل
يمكن لملف Dockerfile واحد خدمة بيئتي التطوير والإنتاج باستخدام --target. أضيفوا مرحلة dev فوق مرحلة التشغيل، وأعيدوا فيها اعتماديات Composer الخاصة بالتطوير وXdebug؛ وابنوا باستخدام --target=runtime للإنتاج و--target=dev محليًا.
FROM runtime AS dev
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions xdebug @composer
USER root
RUN composer install # includes dev deps
# Build prod: docker build --target runtime -t app:prod .
# Build dev: docker build --target dev -t app:dev .التفكير في أحجام الطبقات
يساعدكم نموذج ذهني سريع على توقّع سلوك التخزين المؤقت. يحاكي هذا المقطع من CLI الخطأ الشائع المتمثل في نمو الطبقة بلا حدود مقارنةً بطبقة ذات حجم محدد، ويوضح سبب أهمية دمج التنظيف في تعليمة RUN نفسها.
<?php
// Simulate layer sizes (MB) for two strategies
$installSteps = [120, 8, 8, 8];
// Separate RUN per step keeps temp files in layers
$separate = array_sum($installSteps);
// Single RUN with cleanup removes temp files before commit
$combined = max($installSteps); // peak, then cleaned
echo "Separate layers total: {$separate} MB\n";
echo "Combined+cleanup: {$combined} MB\n";
echo 'Saved: ' . ($separate - $combined) . " MB\n";
?>الدمج والتنظيف في RUN واحدة
تُعد كل RUN طبقةً، وحذف الملفات في طبقة لاحقة لا يقلل حجم الصورة، لأن الطبقات السابقة لا تزال تحتوي على وحدات البايت. ثبّتوا الملفات واستخدموها ونظّفوها ضمن RUN واحدة، حتى لا تُسجّل الملفات المؤقتة في الصورة.
RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS && \
pecl install redis && \
docker-php-ext-enable redis && \
apk del .build-deps && \
rm -rf /tmp/pear /var/cache/apk/*فحص سريع
لماذا يجب إزالة اعتماديات البناء في تعليمة RUN نفسها التي ثبّتها؟
مراجعة
تُبقي عمليات البناء متعددة المراحل المصرّفات واعتماديات التطوير خارج بيئة الإنتاج. تعلّمتم تسمية المراحل ونسخ العناصر باستخدام COPY --from، وترتيب الطبقات من الأقل إلى الأكثر تغيّرًا لتحسين التخزين المؤقت، واستخدام حوامل التخزين المؤقت في BuildKit لصالح Composer، وقياس الحجم باستخدام docker history/dive، والاختيار الواعي بين alpine وglibc، واستهداف مراحل التطوير والإنتاج، ودمج التثبيت والتنظيف في RUN واحدة.
الأسئلة الشائعة
هل درس «البناء متعدد المراحل والتحسين» مجاني؟
نعم — نص درس «البناء متعدد المراحل والتحسين» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.
ماذا ستتعلم في «البناء متعدد المراحل والتحسين»؟
صغّر الصور وافصل البناء عن وقت التشغيل تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟
لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «البناء متعدد المراحل والتحسين»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟
نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- وضع تطبيق PHP في حاوية
- البناء متعدد المراحل والتحسين
- Docker Compose للبيئات المحلية
- التكامل والنشر المستمران باستخدام GitHub Actions