Docker Multi-Stage Builds للمبتدئين: نزّل صورة Node.js من 1.2GB لـ 95MB
مستوى المقال: مبتدئ. هذا الشرح موجّه لمن بنى Dockerfile واحد من قبل، ولاحظ أن صورة الإنتاج كبيرة بشكل غير منطقي مقارنة بحجم الكود الفعلي.
لو الـ image بتاع تطبيق Node.js عندك بيوصل 1.2GB وأنت كل اللي محتاجه شوية ملفات JavaScript جاهزة، Multi-Stage Builds بتنزّل الحجم لـ 95MB بتعديل في خمس سطور داخل Dockerfile. مفيش تغيير في الكود، ومفيش أداة جديدة، ومفيش CI خاص.
المشكلة باختصار
الـ Dockerfile التقليدي بياخد كل أدوات البناء معاه للإنتاج. الـ npm cache، والـ devDependencies، والـ TypeScript compiler، وملفات الـ source قبل الترجمة. كل ده في الإنتاج بيشغّل مساحة، بيبطّأ الـ pull من الـ registry، وبيكبر سطح الهجوم الأمني.
الـ Multi-Stage Build بيفصل "مرحلة البناء" عن "مرحلة التشغيل" داخل نفس الـ Dockerfile. الناتج: صورة إنتاج فيها بس اللي محتاجه التطبيق وقت ما يشتغل، من غير الأدوات اللي ما عادش لها لزمة بعد البناء.
المثال البسيط: المطبخ والطبق
تخيّل مطعم بيقدّم بيتزا. المطبخ فيه فرن، عجّانة، خلاطات، أكياس دقيق، وثلاجات بمكونات خام. لمّا الزبون بيطلب بيتزا، النادل ما بياخدش معاه الفرن والعجانة للترابيزة. بياخد الطبق فقط.
الـ Dockerfile العادي بيوصّل الفرن والعجانة للترابيزة. الـ Multi-Stage Build بيوصّل الطبق فقط. نفس الأكل، تجربة أنظف، ومساحة أقل على الترابيزة.
التعريف الدقيق
Multi-Stage Build هو Dockerfile فيه أكثر من تعليمة FROM. كل FROM بيبدأ مرحلة (stage) جديدة بـ base image مختلف وله اسم اختياري بـ AS <name>. التعليمة COPY --from=<stage> بتنقل ملفات محددة من مرحلة لمرحلة تانية. الصورة النهائية اللي بتتنشر هي آخر مرحلة فقط، مع أي ملفات اتنقلت إليها بـ COPY --from. كل المراحل الوسطية بتتمسح من النتيجة النهائية.
قبل: Dockerfile بمرحلة واحدة
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD ["node", "dist/index.js"]
الحجم الناتج لتطبيق Express متوسط مع TypeScript و 40 dependency: حوالي 1.2GB.
المشكلة: الصورة فيها node_modules الكاملة بـ devDependencies (TypeScript, eslint, jest)، الـ source قبل الترجمة، الـ npm cache بحوالي 200MB، وكل أدوات أنظمة Debian اللي جوه image الـ node:20 الكامل.