قلّل حجم Docker image قبل ما تزود سرعة السيرفر
مستوى القارئ: متوسط
لو الـ Docker image عندك كبيرة، هتكسب أسرع deploy وأقل وقت pull قبل ما تفكر في سيرفر أقوى.
المشكلة باختصار
الطريقة الشائعة الغلط إنك تبني التطبيق وتشغله في نفس الـ image. الطريقة دي بتفشل لما أدوات البناء، مثل compiler أو SDK أو ملفات cache، تفضل موجودة في الإنتاج. النتيجة إن الـ image تكبر، والـ deploy يبطأ، وسطح الهجوم الأمني يزيد.
الافتراض إن عندك خدمة API صغيرة أو متوسطة بتطلع image بين 700MB و1GB، وبتعمل deploy أكثر من مرة يوميًا على Kubernetes أو VM عادي. لو عندك 10 nodes وكل node لازم يعمل pull لصورة 940MB، فأنت بتنقل حوالي 9.4GB في كل rollout. لو نزلت الصورة إلى 312MB، النقل يبقى 3.1GB تقريبًا. الفرق 6.3GB في rollout واحد.
الفكرة الأساسية: افصل build عن runtime
ركز: الـ runtime مش محتاج كل اللي استخدمته أثناء البناء. Docker بتسمي ده multi-stage build. بتستخدم أكثر من FROM داخل نفس Dockerfile. مرحلة تبني، ومرحلة نهائية تنسخ الناتج فقط. Docker بتوضح في مستنداتها إنك تقدر تنسخ artifacts من مرحلة لمرحلة وتسيب الأدوات غير المطلوبة خارج الصورة النهائية.
مثال بسيط: تطبيق Go محتاج صورة فيها Go SDK وقت البناء. لكن وقت التشغيل محتاج binary فقط. لو شحنت الـ SDK في الإنتاج، أنت شحنت مئات الميجابايت بلا سبب. نفس الفكرة تنطبق على Node.js لما تبني assets ثم تشغل production dependencies فقط، أو Java لما تبني JAR في JDK ثم تشغله على JRE أخف.
قِس الأول بدل ما تخمّن
قبل أي تعديل، شوف أكبر الطبقات. استخدم docker image history. الأمر ده يعرض تاريخ بناء الصورة وحجم كل طبقة حسب Docker CLI reference.
docker image history --no-trunc my-api:current
docker images my-api:current
لو لقيت طبقة apt-get install build-essential حجمها 350MB، أو npm install داخل صورة production بيضيف 220MB، يبقى عندك هدف واضح. أفضل طريقة هنا إنك تكتب الرقم قبل التعديل وبعده، بدل ما تقول الصورة اتحسنت وخلاص.
Dockerfile عملي يقلل الحجم
ده مثال Node.js مبسط. الفكرة مش إنك تنسخه كما هو لكل مشروع، لكن تفهم مكان الفصل بالظبط.
# syntax=docker/dockerfile:1
FROM node:22-bookworm AS build
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
RUN npm run build
FROM node:22-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
COPY --from=build /app/dist ./dist
CMD ["node", "dist/server.js"]