2.2تحسين البناء : الطبقات والذاكرة المؤقتة والبناء متعدد المراحل
الأساسي في 30 ثانية
- الترتيب يقود الذاكرة المؤقتة : يعيد Docker تشغيل الطبقات حتى أول طبقة معدلة — الثابت أولا، والمتغير بعده.
- متعدد المراحل = نبني ضخما ونسلّم نحيفا : نجمّع في مرحلة مجهزة، ولا نسلّم إلا القابل للتشغيل — COPY --from=build.
- قِس قبل التحسين : يثبت docker images وdocker history الكسب — استهدف أقل من 200 Mo لتطبيق ويب.
حالة مستمرة — إدير والبناء الذي كان يعيد كل شيء
تطبيق إدير المغلف (2.1) يعمل، لكن كل فاصلة معدلة تعيد تشغيل npm install ويستمر البناء دقائق ؛ وتتجاوز الصورة الغيغابايت. أعاد إدير الترتيب : الاعتماديات الثابتة أولا (package*.json + التثبيت)، ثم الكود المتغير — فلم يعد تغيير الكود يعيد التثبيت. ثم انتقل إلى متعدد المراحل : مرحلة مجهزة تجمّع، ومرحلة slim لا تستقبل إلا النتيجة عبر COPY --from=build. والحكم بالأرقام : من 1 Go إلى نحو 150 Mo. وفي 2.3، سيرقّم إدير هذه الصورة النحيفة ؛ وفي 2.5 سيقوّيها.
تتمة الخيط الرابط 2.1 : تطبيق إدير أصبح سريع البناء ونحيف التسليم — ويستمر في 2.3 و2.4 و2.5.
1. الذاكرة المؤقتة : الترتيب يغيّر كل شيء
يعيد Docker تشغيل الطبقات بالترتيب ويعيد استخدام الذاكرة المؤقتة حتى أول طبقة معدلة : فكل ما بعدها يُعاد تشغيله. لذلك نضع الثابت أولا (الاعتماديات : COPY package*.json ثم التثبيت) والمتغير بعده (الكود : COPY . .). وعكس الترتيب يعيد تحميل كل شيء عند كل فاصلة معدلة. ينسخ إدير البيانات الوصفية ويثبّت قبل الكود : فتغيير الكود لا يعيد إلا الطبقات الأخيرة.
2. متعدد المراحل : نبني ضخما ونسلّم نحيفا
نجمّع في صورة مجهزة (مرحلة « build » بعدة تطوير كاملة) ثم ننسخ النتيجة وحدها في صورة تشغيل خفيفة (COPY --from=build). فتبقى عدة التطوير والمصادر والذاكرات المؤقتة على الرصيف : ولا تحتوي الصورة النهائية إلا الضروري للتشغيل. وكل FROM تفتح مرحلة ؛ ولا نسلّم إلا الأخيرة. يبني إدير في صورة كاملة ويسلّم في node:20-slim.
3. قِس قبل التحسين
يعطي docker images الحجم، ويعطي docker history الوزن لكل طبقة : فنحسّن ما نقيسه. وهدف واقعي : تطبيق ويب تحت 200 Mo (قاعدة slim + متعدد المراحل). وملف .dockerignore كامل (node_modules و.git وdist) يخفف السياق المرسل إلى المحرك. وتحت العتبة، نطارد الاعتماديات غير الضرورية، لا البايتات. يدوّن إدير الحجم والمدة قبل/بعد : بالأرقام الداعمة، لا بالحدس.
هل تعلم؟
ترتيب الأسطر هو وقت حياة.
🔎 مثال ملموس — 🔎 مثال ملموس — Node : من 1 Go إلى 150 Mo
ملف Dockerfile الخاص بإدير، بنسخة محسنة ومقاسة :
# المرحلة 1 : البناء
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# المرحلة 2 : التنفيذ الخفيف
FROM node:20-slim
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
EXPOSE 3000
CMD ["node", "dist/server.js"]
القراءة : تبقى عدة التطوير والمصادر في مرحلة البناء، المهملة بعدها ؛ ولا تحتوي المرحلة النهائية إلا القابل للتشغيل.
الدرس : يثبت docker history أي الطبقات ذابت — قِس قبل التصفيق.
مفردات القسم
- الذاكرة المؤقتة للبناء (Cache)
- إعادة استخدام الطبقات غير المعدلة بين بناءين : فيُعاد كل شيء انطلاقا من أول طبقة معدلة.
مثال بسيط: لم يعد إدير يعيد npm install عندما يتغير كوده وحده.
ما ليس كذلك: صورة نهائية أصغر.
رد فعل: الثابت أولا للتخبيء.
- البناء متعدد المراحل (Multi-stage)
- عدة تعليمات FROM : نبني في مرحلة مجهزة ولا نسلّم إلا المرحلة الأخيرة النحيفة.
مثال بسيط: يجمّع إدير في node:20 ثم يسلّم في node:20-slim.
ما ليس كذلك: مجرد إعادة ترتيب للطبقات.
رد فعل: نبني ضخما ونسلّم نحيفا.
- COPY --from
- النسخ من مرحلة سابقة (لا من السياق) : فلا نسترجع إلا النتيجة المجمّعة.
مثال بسيط: ينسخ إدير /app/dist من مرحلة البناء إلى المرحلة slim.
ما ليس كذلك: COPY كلاسيكي من السياق.
رد فعل: لا تسلّم إلا القابل للتشغيل.
- history
- تفصيل صورة طبقة بطبقة (docker history) : وزن كل طبقة ومصدرها.
مثال بسيط: يقارن إدير history قبل/بعد ليثبت الكسب.
ما ليس كذلك: الحجم الإجمالي وحده (docker images).
رد فعل: قِس لكل طبقة قبل التحسين.
في التطبيق — عمليا — صهّر صورة
- ابنِ تطبيقك « الساذج » (كله في كتلة واحدة) ودوّن الحجم + المدة.
- أعد الترتيب : الاعتماديات أولا، ثم الكود ؛ وأعد البناء بعد تغيير الكود.
- حوّل إلى متعدد المراحل (بناء مجهّز، وتشغيل slim) وقِس مجددا.
- قارن history قبل/بعد واحتفظ بالوصفة الرابحة.
نص جاهز — برّر التحسين (30 ثانية)
« قبلا، كان كل تعديل يعيد كل التثبيت وكانت الصورة تتجاوز الغيغابايت. والآن الاعتماديات مخبأة، ولا يُعاد إلا الكود، ومتعدد المراحل لا يسلّم إلا القابل للتشغيل : نحو 150 Mo. والدليل : docker images وhistory قبل/بعد. أسرع في البناء، وأخف في النشر. »
يُقال مع عرض قياسي إدير، بالأرقام الداعمة.
قالب جاهز للنسخ — Dockerfile متعدد المراحل لتطبيق Node
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-slim
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
EXPOSE 3000
CMD ["node", "dist/server.js"]
ذاكرة ميتة
COPY . . في السطر الأول : كل بناء يعيد كل شيء — فيعيد إدير الاعتماديات قبل الكود.
عدة مسلّمة
صورة إنتاج بعدة التطوير الكاملة : ينتقل إدير إلى متعدد المراحل ولا يسلّم إلا dist.
سياق ثقيل
سياق 800 Mo : يكتب إدير .dockerignore (node_modules و.git وdist) فيتنفس البناء.
تحسين عمياني
« أخف، أحس بذلك » : فيُخرج إدير docker history ويثبت الكسب طبقة بطبقة.
✓ ممارسات جيدة
- الاعتماديات أولا، ثم الكود، دائما.
- .dockerignore كامل (node_modules و.git وdist).
- سلّم في slim، وابنِ في الكاملة.
✗ أخطاء شائعة
- COPY . . في السطر الأول (ذاكرة ميتة).
- تسليم عدة التطوير في الإنتاج.
- التحسين دون قياس (history/images).
النقاط الأساسية
- ترتيب الطبقات يقود الذاكرة المؤقتة (الثابت أولا، والمتغير بعده).
- متعدد المراحل : لا نسلّم إلا القابل للتشغيل (COPY --from=build).
- نقيس : docker images (الحجم) + docker history (لكل طبقة).
- .dockerignore كامل : فلا يجب أن يحمل السياق غير الضروري.
تحدي 48 ساعة — طبِّق غدا
على تطبيقك المغلف (2.1) : دوّن الحجم + المدة، وأعد ترتيب الاعتماديات ثم الكود، وحوّل إلى متعدد المراحل، ثم قارن history قبل/بعد.
- أستطيع ترتيب Dockerfile لأجل الذاكرة المؤقتة (الثابت ثم المتغير).
- أعرف كتابة بناء متعدد المراحل لا يسلّم إلا القابل للتشغيل.
- أقيس عبر images + history قبل إعلان أي كسب.
أسئلة شائعة
لماذا يعيد بنائي npm install في كل مرة؟
لأن COPY . . يسبق RUN npm ci : فأدنى ملف معدَّل يُبطل الذاكرة المؤقتة منذ تلك الطبقة. انسخ package*.json وثبّت قبل الكود : فلن يعيد التثبيت إلا تغيير الاعتماديات.
هل يبطئ متعدد المراحل البناء؟
قليلا (مرحلتان للبناء)، لكنه يقسّم الحجم المسلَّم وسطح الهجوم. وفي CI مع الذاكرة المؤقتة، تكون الكلفة الإضافية هامشية أمام الكسب في النشر والأمان.
لبناء سريع، أين تضع COPY . . (الكود)؟