Docker BuildKit Cache: ليه الـ build بياخد 10 دقايق وإزاي تخلّيه دقيقة
لو الـ docker build بتاعك بيعيد تحميل نفس الـ pip أو npm packages في كل مرة بتعدل فيها سطر كود، المشكلة مش في الشبكة ولا في حجم الـ image. المشكلة إنك مش مستخدم RUN --mount=type=cache. المقال ده فيه Dockerfile شغّال من إنتاج فعلي، قياس حقيقي على مشروع Python FastAPI (9 دقائق و 20 ثانية → دقيقة و 40 ثانية)، وتحذير من خطأ شائع بيخلي الـ cache يشتغل على جهازك ويفشل في GitHub Actions.
المشكلة باختصار
الـ Dockerfile التقليدي بيعامل كل RUN كطبقة مستقلة. أي تعديل في سطر قبل COPY . . بيحرق الـ layer كله، ويعيد تنزيل كل الـ dependencies من الـ index العام. النتيجة: build الفعلي اللي تغير فيه 3 أسطر بياخد 9 دقائق، مع إن الحاجة اللي فعلاً اتعملت هي استدعاء pip install لمكتبة متغيرتش.
المفهوم بمثال بسيط: مخزن البقالة
تخيل إنك بتفتح سوبر ماركت كل يوم الصبح. الطريقة الغلط: بترمي كل البضاعة في آخر اليوم، وبتعيد شراءها من المورد الصبح التاني. الطريقة الصح: في مخزن ورا المحل بتسيب فيه البضاعة اللي لسه صالحة. BuildKit cache mount هو المخزن ده بالظبط: مجلد بيعيش بره الـ image layer، ومحتواه بيفضل موجود بين كل build والتاني، من غير ما يكبّر حجم الـ image النهائي.
الحل التقني: RUN mount=type=cache
BuildKit هو الـ builder الافتراضي في Docker 23.0 وما فوق. بيدعم تعليمة RUN --mount=type=cache اللي بتقول لـ Docker: "المجلد ده ميدخلش في الـ image النهائي، لكن احتفظ بيه كـ volume دائم بين الـ builds".
# syntax=docker/dockerfile:1.7
FROM python:3.12-slim
WORKDIR /app
RUN --mount=type=cache,target=/root/.cache/pip \
--mount=type=bind,source=requirements.txt,target=requirements.txt \
pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]
السطر المهم هو --mount=type=cache,target=/root/.cache/pip. المجلد ده هو اللي pip بيخزن فيه الـ wheels بعد تحميلها. أول build بيتأخر زي العادة. كل build بعد كده بيستخدم الـ cache المحلي، وبينزل بس الحزم اللي فعلاً اتغيرت.
القياس الفعلي على مشروع Python
الأرقام دي من مشروع FastAPI حقيقي بـ 47 dependency في requirements.txt، على runner فيه 4 vCPU و 8GB RAM:
- قبل: 9 دقائق و 20 ثانية (في كل build، حتى لو التعديل سطر واحد).
- بعد أول build مع cache mount: دقيقة و 40 ثانية.
- بعد إضافة package جديد: دقيقة و 55 ثانية (بينزل الـ package الجديد فقط).