الرئيسيةمن أناالدوراتالمدونةسوق الأوامرالمناهج والباقاتالشركاء

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • سوق الأوامر
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
DevOps بالعربي

صورة Docker بتاعتك 850 ميجا؟ نزّلها لـ 18 ميجا بالـ Multi-Stage Build

متوسط12 أغسطس 20265 دقائق قراءة
صورة Docker بتاعتك 850 ميجا؟ نزّلها لـ 18 ميجا بالـ Multi-Stage Build

المستوى المطلوب: متوسط. بيفترض المقال إنك بتعرف تكتب Dockerfile بسيط وتشغّل docker build. لو مبتدئ تمامًا، هتلاقي كل مفهوم متشروح بمثال قبل التفاصيل التقنية.

سطرين في نهاية الـ Dockerfile بيقدروا ينزّلوا صورة خدمتك من ~850 ميجابايت إلى ~18 ميجابايت، من غير ما تلمس سطر واحد في كود التطبيق. النتيجة: نشر أسرع، سحب أخف على كل عقدة، وسطح هجوم أصغر بكتير.

ليه صورة Docker بتتضخّم وإزاي تصغّرها بالـ Multi-Stage Build

المشكلة باختصار

الصورة الضخمة مش مجرد رقم وحش في docker images. هي بتكلّفك في تلات حتات: كل deploy بيسحب جيجابايتات على كل عقدة فيبطّأ الـ rollout، تخزين الصور في الـ registry بيتراكم ويكلّف فلوس، وأهم حاجة: كل أداة بناء وكل مكتبة تطوير بتفضل جوه الصورة النهائية هي باب محتمل لثغرة أمنية. الصورة اللي فيها compiler وgit وحزم dev هي هدف أكبر من صورة فيها ملف تنفيذي واحد بس.

خزائن خوادم في مركز بيانات ترمز لتكلفة تخزين ونقل صور Docker الضخمة

ليه الصورة بتكبر أصلًا؟ الطبقات هي السبب

خلّينا نبدأ بمثال بسيط. تخيّل طاهي بيحضّر طبق واحد. عشان يجهّزه استعمل مطبخ كامل: أفران، خلاطات، عشرات التوابل، وأكياس دقيق. لما يقدّم الطبق للزبون، هو مش بيديله المطبخ كله بأدواته وفوضاه، بيديله الطبق النهائي في صحن نظيف بس. الصورة أحادية المرحلة (single-stage) بتعمل العكس: بتحزم المطبخ كله مع الطبق.

علميًا: صورة Docker مبنية من طبقات (layers) فوق بعض. كل تعليمة RUN وCOPY وADD في الـ Dockerfile بتنتج طبقة جديدة تتراكم على اللي قبلها. لو بنيت تطبيق Go بصورة golang:1.22، الصورة دي لوحدها فيها الـ Go toolchain كامل (compiler، أدوات، مكتبات) بحجم يقارب 800 ميجابايت. بعد ما تبني، الملف التنفيذي بتاعك ممكن يكون 15 ميجابايت بس، لكن الـ 800 ميجا بتاعة أدوات البناء بتفضل معاك في الطبقات للأبد. انت بتشحن المطبخ عشان تقدّم طبق.

الحل: افصل مرحلة البناء عن مرحلة التشغيل

الـ Multi-Stage Build بيخلّيك تكتب أكتر من مرحلة (stage) في نفس الـ Dockerfile. مرحلة أولى فيها كل أدوات البناء، ومرحلة أخيرة فاضية تقريبًا تنسخ منها الناتج النهائي بس. Docker بيرمي كل الطبقات الوسيطة ويحتفظ بالمرحلة الأخيرة فقط كصورة نهائية.

ده Dockerfile حقيقي لتطبيق Go، قابل للنسخ:

Dockerfile
# المرحلة 1: البناء — فيها كل الأدوات
FROM golang:1.22 AS build
WORKDIR /src

# انسخ ملفات الاعتماديات الأول لوحدها
COPY go.mod go.sum ./
RUN go mod download

# بعدين انسخ الكود وابنِ
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server ./cmd/server

# المرحلة 2: التشغيل — صورة شبه فاضية
FROM gcr.io/distroless/static-debian12
COPY --from=build /app/server /server
USER nonroot:nonroot
ENTRYPOINT ["/server"]

لاحظ ترتيب السطور في مرحلة البناء، دي أهم خدعة في الكاش. انسخ go.mod وgo.sum ونزّل الاعتماديات قبل ما تنسخ باقي الكود. Docker بيخزّن كل طبقة في كاش، ولو محتوى طبقة ما اتغيّرش بيعيد استخدامها. لما تعدّل سطر في كودك بس، طبقة go mod download بتفضل من الكاش وما بتتنزّلش الاعتماديات من الأول. لو عكست الترتيب (نسخت الكود قبل الاعتماديات)، أي تعديل بسيط هيبطّل الكاش ويعيد تحميل كل حاجة، فالبناء ياخد دقايق بدل ثواني.

القياس: الأرقام قبل وبعد

بعد ما تبني، قِس بنفسك:

Bash
# ابنِ الصورة
docker build -t api:multistage .

# شوف الحجم
docker images api

# شوف أكبر الطبقات ومين اللي بيكبّر الصورة
docker history api:multistage

النتيجة النموذجية على تطبيق Go صغير: صورة golang:1.22 أحادية المرحلة بتطلع حوالي 850 ميجابايت. نفس التطبيق بالـ Multi-Stage على distroless/static بيطلع حوالي 18 ميجابايت. ده تقليل يقارب 98%. سحب صورة 18 ميجا على 10 عقد أسرع بمراحل من سحب 850 ميجا، والفرق ده بيتضاعف مع كل نشر في اليوم. ملف .dockerignore بيكمّل الحكاية، بيمنع نسخ حاجات مالهاش لازمة تكبّر سياق البناء:

.git
node_modules
*.md
Dockerfile
.dockerignore
tmp/

الـ trade-offs: بتكسب إيه وبتخسر إيه

مفيش حل مجاني بالكامل. صور distroless وscratch مفيهاش shell ولا bash، فمش هتقدر تعمل docker exec -it وتدخل تتفرّج جوه الحاوية بسهولة. المكسب: سطح هجوم أصغر بشكل جذري. الخسارة: تصحيح الأخطاء وقت الأزمة بيبقى أصعب. الحل الوسط: استخدم وسم distroless:debug اللي فيه shell بسيط لبيئات الاختبار بس.

كمان، CGO_ENABLED=0 بيبني ملف تنفيذي static مستقل، وده اللي بيسمحلك تستخدم distroless/static أو حتى scratch. لكن لو تطبيقك بيعتمد على مكتبات C أصلية (زي بعض drivers قواعد البيانات)، هتحتاج base image فيها libc زي distroless/base، والحجم هيكبر شوية. الـ trade-off هنا: أصغر حجم مقابل توافق أقل مع الاعتماديات الأصلية.

الافتراض إن تطبيقك compiled (Go أو Rust). للغات المفسَّرة زي Node.js أو Python، مش هتوصل 18 ميجا، لكن نفس المبدأ بيشتغل: مرحلة تبني الـ node_modules أو تجهّز الـ virtualenv، ومرحلة نهائية على node:20-slim أو python:3.12-slim تنسخ الناتج بس، فتنزّل من ~1.1 جيجا لـ ~150 ميجا.

متى لا تستخدم هذه الطريقة

مش كل صورة محتاجة multi-stage. لو الـ base image بتاعتك صغيرة أصلًا ومفيش خطوة بناء حقيقية (سكربت بسيط على alpine)، التعقيد الزيادة مش هيجيب مكسب يُذكر. وفي بيئة التطوير المحلية اللي بتحتاج فيها أدوات وshell جوه الحاوية باستمرار، صورة كاملة أريَح. الـ Multi-Stage بيلمع لما يكون عندك خطوة بناء واضحة وصورة نهائية بتتنشر مرات كتير.

الخطوة التالية

افتح الـ Dockerfile الحالي بتاعك وشغّل docker history <image>. دوّر على أكبر طبقة، غالبًا هتلاقيها أدوات البناء أو الـ dev dependencies. ابدأ بفصل مرحلة البناء عن مرحلة التشغيل بالقالب اللي فوق، أعد البناء، وقارن docker images قبل وبعد. لو الحجم ما نزلش، شوف إيه اللي بتنسخه في المرحلة الأخيرة وتأكد إنك مش بتجرّ معاك حاجات المرحلة الأولى.

المصادر

  • توثيق Docker الرسمي عن الـ Multi-Stage Builds: https://docs.docker.com/build/building/multi-stage/
  • أفضل ممارسات كتابة Dockerfile (ترتيب الطبقات والكاش): https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
  • كيفية عمل build cache في Docker: https://docs.docker.com/build/cache/
  • مشروع Distroless من Google (صور تشغيل بأقل سطح هجوم): https://github.com/GoogleContainerTools/distroless

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة