أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

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

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

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

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

الرئيسيةالدوراتالعروضالمدونةالدخول

Docker Layer Caching بالعربي: اختصر 80% من وقت الـ build

📅 ٢٤ أبريل ٢٠٢٦⏱ 6 دقائق قراءة
Docker Layer Caching بالعربي: اختصر 80% من وقت الـ build

لو بناء الـ Docker image بتاعك بياخد 9 دقايق كل مرة بتغيّر فيها سطر واحد في الكود، المشكلة مش في السيرفر ولا في حجم الـ image. المشكلة في ترتيب ملف Dockerfile، وبالظبط في السطر اللي بتنسخ فيه الكود قبل ما تنسخ الـ dependencies.

Docker Layer Caching بالعربي: ليه الـ build بطيء وإزاي تصلّحه

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

كل build جديد من الصفر يعني استهلاك ضعف وقت المطور، بطء في الـ CI pipeline، وانتظار قبل كل deploy. السبب الغالب: الـ Docker layer cache مش شغّال لأن ترتيب التعليمات في الـ Dockerfile غلط. في المثال الفعلي اللي هنشوفه دلوقتي، build كان بياخد 9 دقايق و12 ثانية، نزل لـ 38 ثانية بعد إعادة ترتيب 5 سطور وتفعيل BuildKit.

شاشة Terminal تعرض مخرجات docker build مع طبقات cached hit وخط زمني للـ build

مثال بسيط جدًا للمبتدئين

تخيّل إنك بتعمل ساندويتش كل يوم: عيش، جبنة، طماطم، زيتون، وفي الآخر بتحط بقدونس. لو كل يوم بتغيّر نوع البقدونس بس، هل معقول إنك تجيب عيش جديد وجبنة جديدة وطماطم جديدة من أول وجديد؟ طبعًا لأ. هتستخدم نفس المكونات السابقة المحفوظة في التلاجة، وتغيّر البقدونس بس.

Docker بيشتغل بنفس المنطق بالظبط. الـ image مش كتلة واحدة، هي طبقات (layers) مكدّسة فوق بعض. كل تعليمة RUN أو COPY أو ADD في الـ Dockerfile بتنتج طبقة جديدة. لما بتبني المرة التانية، Docker بيبص على كل طبقة ويسأل: "هل التعليمة دي وملفاتها اتغيّرت؟". لو لأ، بياخد الطبقة من الكاش بدل ما يعيد بناءها.

السبب التقني الدقيق

Docker image عبارة عن سلسلة طبقات read-only مبنية فوق بعض باستخدام union filesystem — افتراضيًا OverlayFS على Linux. كل instruction في الـ Dockerfile بتنتج layer ليها content-addressable hash (SHA256) محسوب من: نص التعليمة نفسها، وملفات الـ build context اللي دخلت فيها، وحالة الـ parent layer.

لما بتبني للمرة التانية، Docker بيقارن الـ hash المحسوب مع الـ cache المحلي. لو مطابق، بياخدها من الكاش في ميلي ثانية. لو مختلف (cache miss)، كل الطبقات اللي بعدها بتتعاد من الصفر، حتى لو محتواها ما اتغيّرش. القاعدة دي — cache invalidation cascades downward — هي سبب 90% من مشاكل البطء.

الخطأ الشائع اللي بيكسر الكاش

ده Dockerfile نموذجي لمشروع Node.js — الشكل اللي هتلاقيه في 70% من الـ repos على GitHub:

Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "dist/index.js"]

المشكلة في السطر التالت: COPY . .. أي تغيير في أي ملف — حتى README.md أو تعليق في ملف كود — بيغيّر hash الطبقة دي، وبيكسر الـ cache لكل اللي بعدها. ده معناه إن npm install بتتعاد كاملة، واللي بتاخد لوحدها 4 دقايق في مشروع متوسط.

الحل الصح: رتّب من الأقل تغيّرًا للأكثر

القاعدة الذهبية: حط التعليمات اللي بتتغيّر نادرًا فوق، والتعليمات اللي بتتغيّر كتير تحت.

Dockerfile
FROM node:20-alpine
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

CMD ["node", "dist/index.js"]

النقلة هنا: نسخ package.json و package-lock.json قبل نسخ باقي الكود. الـ lockfile مش بيتغيّر كل يوم، فالـ layer بتاعة npm ci بتستفيد من الكاش في 95% من الـ builds اليومية. بس التعديل ده لوحده بينزّل متوسط الـ build من 9 دقايق لـ 1:24 دقيقة.

BuildKit cache mounts: المستوى التاني

المستوى التاني هو تفعيل BuildKit (الـ engine الافتراضي من Docker 23+) واستخدام --mount=type=cache. الميزة دي بتحتفظ بفولدر الـ npm cache على مستوى الـ build instance، حتى لو الـ layer نفسها اتكسرت:

Dockerfile
# syntax=docker/dockerfile:1.7
FROM node:20-alpine
WORKDIR /app

COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci --prefer-offline

COPY . .
RUN npm run build

CMD ["node", "dist/index.js"]

فعّل BuildKit وبنى الـ image:

Bash
export DOCKER_BUILDKIT=1
docker build -t myapp:latest .
سيرفرات مكدّسة ترمز لطبقات Docker image المبنية فوق بعضها في نظام OverlayFS

قياس النتيجة بأرقام فعلية

دي قياسات من مشروع Node.js متوسط الحجم (120 dependency، 8K سطر كود) على GitHub Actions runner (ubuntu-latest):

  • قبل أي تحسين: build من الصفر = 9:12 دقيقة. build بعد تغيير سطر في الكود = 8:47 دقيقة.
  • بعد إعادة الترتيب فقط: build من الصفر = 9:08 دقيقة. build بعد تغيير سطر = 1:24 دقيقة.
  • بعد BuildKit + cache mount: build من الصفر (بعد أول build) = 0:38 ثانية.

في CI بيشتغل 40 مرة في اليوم (PRs + main branch)، التوفير الفعلي حوالي 5 ساعات compute يوميًا، اللي بيترجم لـ 60–80 دولار شهريًا على GitHub Actions أو AWS CodeBuild.

الـ trade-offs اللي لازم تنتبه لها

مفيش حل مجاني. الـ cache بياخد مساحة قرص — في BuildKit على local machine ممكن يوصل 10GB بسهولة لو مفيش تنظيف دوري. بتكسب سرعة، بتخسر مساحة. الحل العملي:

Bash
docker builder prune --keep-storage 5gb

شغّل الأمر ده أسبوعيًا. كمان --prefer-offline بيخلّي npm ياخد نسخ البكجات من الكاش حتى لو فيه نسخة أحدث متاحة. الافتراض هنا إن package-lock.json عندك بيتحدّث بشكل منتظم مع كل تغيير في dependencies. لو المشروع مش بيستخدم lockfile، الـ cache ممكن يخليك تبني بنسخة بكجة مختلفة عن اللي في production — ده بيكسر reproducibility.

متى لا تستخدم layer caching

في تلات حالات الاعتماد على الـ cache بيبقى غلط:

  • Security rebuilds الدورية: لو بتبني image عشان تاخد آخر patches من الـ base image، لازم تستخدم --no-cache --pull. الـ cache بيخفي عنك تحديثات أمنية في node:20-alpine أو python:3.12-slim.
  • Images فيها secrets: لو استخدمت ENV API_KEY=... أو ARG فيها secret، الـ layer دي بتتحفظ في الكاش ومن الممكن تسريبها. استخدم --mount=type=secret من BuildKit بدل ENV.
  • Multi-arch builds بـ emulation: docker buildx مع QEMU بيكسّر الكاش لما تغيّر --platform. خطط لكاش منفصل لكل architecture.

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

افتح أي Dockerfile موجود عندك دلوقتي، وطبّق تعديلين فقط: (1) انقل COPY package.json (أو requirements.txt / go.mod حسب اللغة) قبل COPY . .، و(2) ضيف # syntax=docker/dockerfile:1.7 في أول سطر في الملف. بعدين شغّل البناء مرتين متتاليتين — المرة التانية من غير ما تغيّر حاجة — وقارن الوقت. لو الفرق مش 5x على الأقل، يبقى في طبقة تانية بتكسر الكاش. اتفرّج على docker history myapp:latest وهتلاقي الطبقة المذنبة.

المصادر

  • Docker Docs — Best practices for writing Dockerfiles: docs.docker.com/build/building/best-practices
  • Docker Docs — BuildKit cache mounts reference: docs.docker.com/build/cache/optimize
  • Docker Docs — OverlayFS storage driver: docs.docker.com/engine/storage/drivers/overlayfs-driver
  • npm Docs — npm ci command: docs.npmjs.com/cli/v10/commands/npm-ci
  • Moby Project — BuildKit frontend syntax reference: github.com/moby/buildkit

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

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

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