المستوى المطلوب: متوسط. هذا المقال يفترض أنك كتبت Dockerfile من قبل وشغّلت docker build مرة واحدة على الأقل. لست بحاجة لخبرة عميقة، لكن يجب أن تكون عارفًا الفرق بين الـ image والـ container.
Docker Build بطيء؟ السبب في ترتيب سطور الـ Dockerfile وليس في السيرفر
لو الـ docker build عندك بياخد 4 دقائق كل مرة تعدّل فيها سطر كود واحد، انت بتدفع تكلفة وقت مالهاش لزمة. ترتيب صحيح لسطور الـ Dockerfile مع تفعيل BuildKit ينزّلان نفس البناء لـ 12 ثانية — بدون ما تغيّر سطرًا واحدًا في كود التطبيق نفسه.
المشكلة باختصار
كل مرة تعدّل سطرًا في الكود وتعمل build، يعيد Docker تثبيت كل الـ dependencies من الصفر. على مشروع Node فيه 840 حزمة، ده معناه أن npm install يشتغل من البداية في كل بناء. النتيجة: دورة feedback بطيئة، وخط CI/CD يستهلك دقائق غالية، ومطوّر يقعد يبصّ في الشاشة بدل ما يشتغل. المشكلة ليست في قوة السيرفر — المشكلة أن Docker لا يجد شيئًا يعيد استخدامه من البناء السابق.
ليه الـ build يعيد نفسه: مفهوم الطبقات (Layers)
قبل التعريف العلمي، خد مثالًا بسيطًا. تخيّل أنك تجهّز نفس الساندوتش كل يوم بنفس الترتيب: عيش، ثم جبنة، ثم طماطم، ثم ملح. لو حافظت على الترتيب ده، تقدر تجهّز العيش والجبنة مقدمًا وتسيبهم جاهزين على الرف. بس لو قررت تبدأ بالملح قبل العيش، تضطر تبدأ الطبق كله من أوله في كل مرة، لأن كل خطوة تعتمد على اللي قبلها.
Docker يفكّر بنفس الطريقة بالظبط. كل تعليمة في الـ Dockerfile (زي FROM أو COPY أو RUN) تنتج طبقة (layer)، وهي فرق (diff) في نظام الملفات يُسجَّل بشكل منفصل. Docker يخزّن كل طبقة في الـ build cache. عند إعادة البناء، يمشي Docker على التعليمات بالترتيب: طول ما التعليمة وكل اللي قبلها لم يتغيّروا، يأخذ الطبقة الجاهزة من الـ cache بدل ما ينفّذها. أول ما تتغيّر تعليمة واحدة، ينكسر الـ cache عندها، وكل التعليمات التالية تُنفَّذ من جديد إجباريًا. ده اسمه cache invalidation، والقاعدة الأساسية: الـ cache يعمل بالتسلسل، مش بالقطعة المعزولة.
الخطأ اللي يكسر الـ Cache: ترتيب السطور
خد الـ Dockerfile الشائع ده:
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
المشكلة في السطر COPY . . — هو ينسخ كل ملفات المشروع. أي تعديل في أي ملف source، حتى لو مسافة أو تعليق، يغيّر محتوى الطبقة دي. وبما أن RUN npm install جاية بعدها مباشرة، ينكسر الـ cache بتاعها تلقائيًا، فيعيد npm تثبيت 840 حزمة في كل بناء. الافتراض الخاطئ هنا أن "نسخ الكود" و"تثبيت الحزم" خطوة واحدة. هما خطوتان بإيقاع تغيير مختلف تمامًا: الكود يتغيّر عشرات المرات في اليوم، والـ dependencies تتغيّر مرة كل أسبوعين تقريبًا.