كتابة ملفات Dockerfile
كتابة ملفات Dockerfile
يُعدّ ملف Dockerfile المصدرَ الوحيد للحقيقة فيما يخص طريقة تجميع صورة الحاوية. كل صورة إنتاجية في الشركات الجادة — سواء كانت Node API أو Python ML worker أو Go microservice — تبدأ من هنا. كتابة Dockerfile بشكل رديء تعني بنيات CI بطيئة، وصور ضخمة، وسلوك غير متوقع في وقت التشغيل، وسطح أمني لا يمكن تفسيره. أما كتابتها بشكل صحيح فتعني اختراقات ذاكرة التخزين المؤقت في 10 ثوان، وصور نهائية بحجم 20 ميغابايت فقط، وبنيات قابلة للتكرار على جهاز كل مطور وعلى كل CI runner.
يستعرض هذا الدرس كل تعليمة مهمة، ويوضح سبب كون ترتيب الطبقات اعتباراً رئيسياً، ويُريك كيف تبدو ملفات Dockerfile الصديقة للتخزين المؤقت في الإنتاج الفعلي.
التعليمات الأساسية
FROM — اختيار الصورة الأساسية
FROM هي دائماً التعليمة الأولى. تحدد الصورة الأساسية التي تُبنى صورتك فوقها. كل تعليمة لاحقة تضيف طبقة جديدة فوق تلك الأساس.
في الشركات الكبرى، ثلاث قواعد تحكم FROM:
- دائماً ثبّت digest محدد أو على الأقل وسماً محدداً — لا تستخدم
FROM node:latestأبداً. الوسوم العائمة تكسر قابلية التكرار بصمت عندما ينشر المصدر صورة جديدة. - فضّل الصور الأساسية الرسمية المصغّرة:
node:22-alpine،python:3.12-slim،golang:1.22-bookworm. متغيرات Alpine وslim أصغر بكثير من الصور الكاملة المبنية على Debian. - للثنائيات المُجمَّعة ثابتياً (Go، Rust)، فضّل
FROM scratch— المرحلة النهائية لا تحتوي على أي سطح نظام تشغيل.
COPY — إحضار الملفات إلى الصورة
COPY <src> <dest> تنسخ الملفات من سياق البناء (نظام ملفاتك المحلي) إلى طبقة الصورة. ADD موجودة أيضاً لكن يجب تجنبها إلا إذا احتجت تحديداً لاستخراج tar التلقائي أو جلب URL البعيد — كلاهما مصدر مشاكل. استخدم COPY افتراضياً.
الأعلام المهمة التي ستستخدمها فعلياً في الإنتاج:
--chown=user:group— تحديد الملكية في خطوة واحدة بدلاً من تعليمةRUN chownمنفصلة (التي ستنشئ طبقة إضافية).--from=builder— النسخ من مرحلة أخرى في بناء متعدد المراحل (يُغطى في درس لاحق).
COPY . . ترسل مجلد عملك بالكامل إلى سياق البناء — بما يشمل .git/، وnode_modules/، وملفات الاختبار، وملفات .env المحلية. ملف .dockerignore جيد يُقلص نقل سياق البناء من غيغابايتات إلى كيلوبايتات.RUN — تنفيذ خطوات البناء
RUN تُنفّذ أمر shell أثناء البناء وتحفظ النتيجة كطبقة جديدة. كل تعليمة RUN هي مفتاح تخزين مؤقت. أهم قاعدة في الإنتاج: اجمع الأوامر المترابطة في تعليمة RUN واحدة، خاصة عند تثبيت الحزم وتعديلها وتنظيفها — لأنك لو قسّمتها على عدة تعليمات RUN، ستجمّد الملفات الوسيطة في الطبقات السابقة حتى بعد حذفها.
الشكل shell مقابل الشكل exec: RUN apt-get update هو شكل shell (يعمل عبر /bin/sh -c). يمكنك أيضاً استخدام شكل exec: RUN ["apt-get", "update"]. شكل shell أكثر قراءة للأوامر المتسلسلة؛ شكل exec يتجنب تفسير shell ويُفضَّل في CMD وENTRYPOINT.
CMD وENTRYPOINT — تعريف سلوك التشغيل
هاتان التعليمتان مصدر ارتباك دائم. القاعدة بسيطة بعد استيعابها:
ENTRYPOINT— الملف التنفيذي الثابت الذي يعمل دائماً. يحدد ما تكونه الحاوية.CMD— الوسائط الافتراضية التي تُمرَّر إلى ENTRYPOINT (أو، إن لم يكن هناك ENTRYPOINT، الأمر الافتراضي للتشغيل). يحدد الإعدادات الافتراضية المعقولة التي يمكن تجاوزها عندdocker run.
كلاهما يقبل شكل shell وشكل exec. استخدم دائماً شكل exec (["executable", "arg1"]) لـ CMD وENTRYPOINT. شكل shell يُغلّف العملية في /bin/sh -c، مما يجعلها PID 2 بدلاً من PID 1 — وهذا يعني أن الإشارات (SIGTERM، SIGINT) من Docker أو Kubernetes لا تُسلَّم أبداً لعمليتك، مما يُسبب إيقاف تشغيل غير نظيف ونشرات متداول بطيئة.
docker run، تُستبدل CMD بالكامل — لا تُدمج. صمّم واجهة الوسائط الخاصة بك وفق ذلك.ترتيب الطبقات وملفات Dockerfile الصديقة للتخزين المؤقت
تخزين Docker المؤقت للبناء مُقيَّد بنص التعليمة ومحتوى أي ملفات مشار إليها. بمجرد إلغاء صلاحية طبقة، يجب إعادة بناء كل طبقة لاحقة. هذا يعني أن ترتيب الطبقات قرار مهم للأداء، وليس قرار جماليات فقط.
القاعدة الذهبية: رتّب التعليمات من الأقل تغيراً إلى الأكثر تغيراً.
ملف Dockerfile إنتاجي متكامل (Node.js API)
فيما يلي ملف Dockerfile كامل وحقيقي يدمج كل المبادئ السابقة. ادرس الترتيب والتعليقات:
RUN adduser + USER ربح أمني في سطرين فقط.تعليمات مفيدة أخرى
WORKDIR /app— تحدد مجلد العمل للتعليمات اللاحقة. فضّلها علىRUN cd /appالذي لا يستمر.ENV KEY=value— تضع متغيرات بيئة متاحة في وقت البناء والتشغيل معاً. استخدمها لـNODE_ENV=production،PYTHONUNBUFFERED=1، إلخ. لا تستخدم ENV للأسرار — فهي مدمجة في الصورة ومرئية عبرdocker history.ARG— متغير وقت البناء فقط، لا يستمر في الصورة. آمن للأشياء كأرقام الإصدار:ARG APP_VERSION=1.0.0.EXPOSE 3000— يوثّق المنفذ الذي تستمع إليه الحاوية. لا ينشر المنفذ فعلياً؛ ذلك يحدث عندdocker run -pأو في Docker Compose. اعتبره بيانات وصفية للمشغّلين.LABEL— إرفاق بيانات وصفية (المطوّر، الإصدار، git SHA). مفيد لـdocker inspectوأنظمة الجرد الآلية.
--cache-from / --cache-to أو cache-to=type=gha في GitHub Actions للحفاظ على ذاكرة الطبقات المؤقتة بين تشغيلات pipeline — وهذا غالباً أكبر تسريع متاح لـ CI.ملخص
ملف Dockerfile الاحترافي يُحدَّد بأربع عادات: ثبّت صورتك الأساسية، اجمع أوامر RUN لتطوي الطبقات، رتّب التعليمات من الأقل تغيراً إلى الأكثر، واستخدم دائماً شكل exec لـ ENTRYPOINT وCMD. كل انحراف له تكلفة حقيقية — سواء في حجم الصورة، أو وقت البناء، أو موثوقية التشغيل. اجعل هذه المبادئ هي الافتراضية، لا الاستثناء.