Docker: أساسيات Dockerfile
آخر تحديث: 2026-08-26
Dockerfile هو "الكود المصدري" للصورة — بكتابة Dockerfile، يمكنك بناء صورة تطبيق قابلة للتكرار بأمر واحد.
1. ما ستتعلمه
- الصيغة الأساسية وهيكل Dockerfile
- سياسة اختيار الصورة الأساسية باستخدام عبارة "FROM"
- RUN: أفضل الممارسات لتنفيذ أوامر البناء
- الفرق بين CMD و ENTRYPOINT
- إعداد السياق و .dockerignore
2. قصة حقيقية لمطور Python
(1) نقطة الألم: الاضطرار إلى إعداد البيئة يدويًا مع كل نشر
كتبت أليس تطبيق ويب بلغة Python. في كل مرة تنشره، تحتاج إلى تثبيت Python يدويًا، وإعداد بيئة افتراضية، وتثبيت التبعيات، ونسخ الكود. تحتاج إلى تكرار هذه العملية ثلاث مرات — مرة لخادم الاختبار، ومرة لبيئة ما قبل الإنتاج، ومرة لخادم الإنتاج — بإجمالي ثلاث تكرارات، كل منها مع اختلافات طفيفة.
(2) حلول أتمتة Dockerfile
قال بوب: "اكتب Dockerfile، 'احزم' تطبيقك في صورة، وبعد ذلك ستتمكن من تشغيله في أي مكان بأمر واحد."
# Dockerfile بسيط لتطبيق Flask
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
(3) الفائدة: ابنِ مرة واحدة، شغّل في أي مكان
بعد أن قامت أليس ببناء الصورة باستخدام docker build -t myapp:1.0 .، عملت البيئات الثلاث جميعها على نفس الصورة، مما قلل وقت النشر من 30 دقيقة إلى 3 دقائق وقضى على تناقضات البيئة.
3. الهيكل الأساسي لـ Dockerfile
Dockerfile هو ملف نصي عادي يحتوي على تسلسل من التعليمات لبناء صورة. كل تعليمة تبني طبقة صورة.
graph LR
DF["Dockerfile<br/>تسلسل التعليمات"] -->|docker build| IMG["الصورة<br/>تراكب للقراءة فقط"]
IMG -->|docker run| CTN["الحاوية<br/>طبقة قابلة للكتابة + طبقة للقراءة فقط"]
(1) توقيت تنفيذ التعليمات
| التوقيت | الأمر | الوصف |
|---|---|---|
| أثناء البناء | FROM / RUN / COPY / ADD / ARG |
إنشاء طبقة الصورة |
| وقت التشغيل | CMD / ENTRYPOINT / ENV / EXPOSE / USER |
يحدد سلوك الحاوية |
(2) قالب Dockerfile الأساسي
# 1. الصورة الأساسية
FROM python:3.12-slim
# 2. تعيين دليل العمل
WORKDIR /app
# 3. نسخ ملف التبعيات أولاً (تحسين التخزين المؤقت)
COPY requirements.txt .
# 4. تثبيت التبعيات
RUN pip install --no-cache-dir -r requirements.txt
# 5. نسخ كود التطبيق
COPY . .
# 6. تعريف الأمر الافتراضي
CMD ["python", "app.py"]
4. FROM: اختيار صورة أساسية
FROM هي التعليمة الأولى في Dockerfile؛ تحدد الصورة الأساسية للبناء.
(1) استراتيجية اختيار الصورة الأساسية
| الاستراتيجية | الصورة | الحجم | حالة الاستخدام |
|---|---|---|---|
| صورة اللغة الرسمية | python:3.12-slim |
155 ميجابايت | مشروع Python |
| متغير Alpine | python:3.12-alpine |
50 ميجابايت | مساحة القرص منخفضة جدًا |
| بناء متعدد المراحل | golang:1.22 → alpine |
12 ميجابايت | لغات Go/Rust المترجمة |
| نظام تشغيل بسيط | debian:bookworm-slim |
74 ميجابايت | يتطلب بيئة مخصصة |
▶ مثال: أبسط Dockerfile (الصعوبة: ⭐)
# Dockerfile بسيط: يطبع hello فقط
FROM alpine:3.19
CMD ["echo", "Hello from Docker!"]
# بناء وتشغيل
docker build -t hello:1.0 .
docker run --rm hello:1.0
Hello from Docker!
5. RUN: تنفيذ أمر البناء
RUN ينفذ أمرًا أثناء البناء، ويكتب النتيجة في طبقة صورة جديدة.
(1) صيغتان
| الصيغة | الكتابة | الميزات |
|---|---|---|
| صيغة Shell | RUN apt-get install nginx |
ينفذ /bin/sh -c افتراضيًا؛ يدعم الأنابيب |
| وضع Exec | RUN ["apt-get", "install", "nginx"] |
تنفيذ مباشر بدون تشغيل shell |
▶ مثال: RUN لتثبيت التبعيات (الصعوبة: ⭐⭐)
# أفضل ممارسة: دمج أوامر RUN لتقليل الطبقات
FROM debian:bookworm-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
nginx && \
rm -rf /var/lib/apt/lists/*
apt-get المتعددة في RUN واحد لتقليل عدد طبقات الصورة. rm -rf /var/lib/apt/lists/* ينظف ذاكرة APT المؤقتة لتقليل حجم الصورة.
(2) مبادئ دمج سلسلة RUN
| الطريقة | النتيجة | ملاحظات |
|---|---|---|
RUN متعددة |
كل إدخال ينشئ طبقة | طبقات كثيرة، حجم ملف كبير |
دمج RUN && |
طبقة واحدة لكل مسار | طبقات قليلة، حجم مضغوط |
| مسح الذاكرة المؤقتة | rm -rf apt/lists |
يمسح الذاكرة المؤقتة داخل نفس المستوى |
| تنظيف منفصل | RUN rm التالي |
غير صالح — الطبقة السابقة قد تصلبت |
# سيء: ينشئ طبقتين، ذاكرة apt المؤقتة مخبوزة في الطبقة 1
RUN apt-get update
RUN apt-get install -y nginx
# جيد: ينشئ طبقة واحدة، ذاكرة مؤقتة نظيفة في نفس الطبقة
RUN apt-get update && \
apt-get install -y nginx && \
rm -rf /var/lib/apt/lists/*
6. CMD و ENTRYPOINT
كل من CMD و ENTRYPOINT يحددان الأوامر التي سيتم تنفيذها عند بدء الحاوية، لكنهما يتصرفان بشكل مختلف.
(1) مقارنة أوامر البدء الثلاثة
| البُعد | CMD |
ENTRYPOINT |
|---|---|---|
| الغرض | توفير أوامر افتراضية | تعريف نقطة دخول ثابتة |
| يمكن تجاوزه | معامل docker run يتجاوز مباشرة |
يتطلب --entrypoint للتجاوز |
| الدمج | يمكن استخدامه مع ENTRYPOINT | يمكن استخدامه مع وسائط سطر أوامر CMD |
| متعدد | فقط الأخير يسري | فقط الأخير يسري |
(2) الصيغ الثلاث لـ CMD
| الصيغة | الكتابة | مستوى التوصية | الوصف |
|---|---|---|---|
| صيغة Exec | CMD ["python", "app.py"] |
⭐⭐⭐ | تنفيذ مباشر؛ الإشارات تمر بشكل صحيح |
| صيغة Shell | CMD python app.py |
⭐ | كعملية فرعية لـ /bin/sh -c، SIGTERM لا يمر |
| صيغة المعاملات | CMD ["--port", "8080"] |
⭐⭐ | تستخدم مع ENTRYPOINT |
▶ مثال: الفرق بين CMD و ENTRYPOINT (الصعوبة: ⭐⭐)
# Dockerfile مع CMD: يمكن تجاوز الأمر بسهولة
FROM alpine:3.19
CMD ["echo", "Hello default"]
# افتراضي: يشغل CMD
docker run --rm test-cmd
# المخرجات: Hello default
# تجاوز CMD بأمر مخصص
docker run --rm test-cmd echo "Custom message"
# المخرجات: Custom message
# Dockerfile مع ENTRYPOINT: الأمر يبقى، الوسائط تُلحق
FROM alpine:3.19
ENTRYPOINT ["echo"]
CMD ["Hello default"]
# افتراضي: يشغل ENTRYPOINT + CMD
docker run --rm test-entry
# المخرجات: Hello default
# إلحاق وسائط (لا يتجاوز ENTRYPOINT)
docker run --rm test-entry "Custom message"
# المخرجات: Custom message
# تجاوز ENTRYPOINT (نادرًا ما نحتاجه)
docker run --rm --entrypoint sh test-entry -c "ls /"
▶ مثال: دمج ENTRYPOINT + CMD (الصعوبة: ⭐⭐⭐)
هذه هي أفضل ممارسة — ENTRYPOINT يحدد البرنامج المراد تشغيله، و CMD يوفر الوسائط الافتراضية:
# نمط Entrypoint + CMD
FROM python:3.12-slim
WORKDIR /app
COPY app.py .
ENTRYPOINT ["python", "app.py"]
CMD ["--host", "0.0.0.0", "--port", "5000"]
# افتراضي: يستخدم وسائط CMD
docker run --rm myapp
# يكافئ: python app.py --host 0.0.0.0 --port 5000
# تجاوز الوسائط فقط
docker run --rm myapp --port 8080
# يكافئ: python app.py --port 8080
7. إعداد السياق و .dockerignore
(1) إعداد السياق
المعامل الأخير لـ docker build، .، لا يشير إلى "الدليل الحالي"، بل إلى سياق البناء — يرسل Docker Client جميع الملفات في هذا الدليل إلى Daemon.
graph LR
CTX["سياق البناء<br/>(. جدول محتويات الدليل)"] -->|إرسال ملف| D["Docker Daemon"]
D -->|Dockerfile في COPY/ADD| IMG["طبقات الصورة"]
node_modules في الدليل، سيتم إرسال 1 جيجابايت إلى daemon أثناء البناء (حتى لو لم يقم Dockerfile بـ COPY لها). يمكن لـ .dockerignore استبعاد الملفات غير الضرورية.
▶ مثال: كيف يعمل .dockerignore (الصعوبة: ⭐⭐)
# .dockerignore - استبعاد الملفات من سياق البناء
node_modules
.git
__pycache__
*.pyc
.env
Dockerfile
docker-compose*.yml
README.md
.vscode
(2) لماذا يجب أن تأتي تعليمة COPY لملفات التبعيات قبل تعليمة COPY للكود المصدري؟
# جيد: معدل تغيير التبعيات < معدل تغيير الكود
# عندما يتغير الكود، تستخدم طبقة التبعيات التخزين المؤقت
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# سيء: أي تغيير في الملف يبطل طبقة pip install
COPY . .
RUN pip install -r requirements.txt
| سيناريو التغيير | الإصدار الجيد | الإصدار السيء |
|---|---|---|
| تعديل الكود فقط | ✅ استخدام ذاكرة pip المؤقتة (ثوانٍ) | ❌ إعادة بناء pip (دقائق) |
| تغيير التبعيات | ✅ إعادة بناء طبقة pip (مطلوب) | ❌ إعادة بناء طبقة pip (كما أعلاه) |
8. المعاملات الشائعة لـ docker build
| المعامل | الوظيفة | مثال |
|---|---|---|
-t |
اسم الصورة: الوسم | -t myapp:1.0 |
-f |
تحديد مسار Dockerfile | -f Dockerfile.prod . |
--build-arg |
تمرير معاملات البناء | --build-arg VERSION=2.0 |
--no-cache |
عدم استخدام التخزين المؤقت | --no-cache |
--target |
البناء حتى مرحلة محددة | --target builder |
--platform |
تحديد المنصة المستهدفة | --platform linux/arm64 |
▶ مثال: بناء صورة وعرض سجل الطبقات (الصعوبة: ⭐⭐)
# بناء مع وسم
docker build -t myapp:1.0 .
# عرض طبقات الصورة
docker history myapp:1.0
9. مثال كامل: كتابة Dockerfile لتطبيق Flask
# ============================================
# Dockerfile لتطبيق ويب Flask
# يوضح: FROM، WORKDIR، COPY، RUN، CMD
# ============================================
# استخدام صورة Python الرسمية slim
FROM python:3.12-slim
# تعيين دليل العمل داخل الحاوية
WORKDIR /app
# نسخ ملف التبعيات أولاً (تحسين التخزين المؤقت)
COPY requirements.txt .
# تثبيت التبعيات (تنظيف الذاكرة المؤقتة في نفس الطبقة)
RUN pip install --no-cache-dir -r requirements.txt
# نسخ الكود المصدري للتطبيق
COPY . .
# كشف منفذ التطبيق (توثيق فقط)
EXPOSE 5000
# تشغيل تطبيق Flask
CMD ["python", "app.py"]
# بناء الصورة
docker build -t flask-app:1.0 .
# تشغيل الحاوية
docker run -d -p 5000:5000 --name my-flask flask-app:1.0
# اختبار التطبيق
curl http://localhost:5000
# عرض حجم الصورة وطبقاتها
docker images flask-app
docker history flask-app:1.0
# docker images flask-app
REPOSITORY TAG IMAGE ID SIZE
flask-app 1.0 a1b2c3d4e5f6 180MB
# docker history flask-app:1.0
IMAGE CREATED CREATED BY SIZE
a1b2c3d4e5f6 5 seconds ago CMD ["python" "app.py"] 0B
<missing> 5 seconds ago COPY . . 2.5kB
<missing> 5 seconds ago RUN pip install --no-cache-dir... 45MB
<missing> 5 seconds ago COPY requirements.txt . 58B
<missing> 5 seconds ago WORKDIR /app 0B
❓ أسئلة شائعة
python app.py)، بينما يوفر CMD وسائط افتراضية (مثل --port 5000). الوسائط الممررة إلى docker run تتجاوز CMD ولكن ليس ENTRYPOINT، مما يتيح إعداد "ملف تنفيذي ثابت + وسائط مرنة".. في نهاية أمر docker build يحدد دليل سياق البناء. يقوم Docker client بتجميع جميع الملفات في هذا الدليل وإرسالها إلى daemon. أوامر COPY و ADD في Dockerfile يمكنها فقط الإشارة إلى الملفات داخل سياق البناء. استخدم .dockerignore لاستبعاد الملفات غير الضرورية، مما يسرع البناء ويقلل حجم سياق البناء.COPY؛ ③ تصحيح تفاعلي — docker run -it <آخر-طبقة-ناجحة> bash ادخل آخر طبقة صورة ناجحة لاستكشاف الأخطاء يدويًا.\ لمتابعة السطر و && لربط الأوامر. هذه هي الطريقة القياسية لكتابة Dockerfile، مما يقلل عدد الطبقات مع الحفاظ على قابلية القراءة. مثال: RUN apt-get update && \ + apt-get install -y nginx && \ + rm -rf /var/lib/apt/lists/*.📖 ملخص
- Dockerfile هو "الكود المصدري" للصورة؛ كل تعليمة تولد طبقة للقراءة فقط
- FROM: اختر صورة أساسية: "slim" يوفر توافقًا جيدًا، بينما "alpine" هو الأصغر لكنه قد يواجه مشاكل توافق
- استخدم أمر دمج RUN لتقليل عدد الطبقات، ونظف الذاكرة المؤقتة داخل كل طبقة لتقليل حجم الملف
- CMD يوفر أمرًا افتراضيًا (يمكن تجاوزه)، بينما ENTRYPOINT يحدد نقطة دخول ثابتة (يصعب تجاوزها)
- دمج ENTRYPOINT + CMD هو أفضل ممارسة: برنامج ثابت + معاملات مرنة
- COPY ملفات التبعيات قبل الكود المصدري، واستخدم التخزين المؤقت للطبقات لتسريع البناء
📝 تمارين
- مسألة أساسية (الصعوبة: ⭐): اكتب Dockerfile لتطبيق Node.js، باستخدام
node:20-alpineكصورة أساسية، وnpm installلتثبيت التبعيات، وnode server.jsلبدء التطبيق. - مسألة متقدمة (الصعوبة: ⭐⭐): ابنِ صورة وشغّل حاوية. استخدم
docker historyلتحليل حجم كل طبقة في الصورة، وحدد أكبر طبقة، واشرح السبب. - تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ ملف .dockerignore لاستبعاد node_modules و .git، وقارن الفرق في حجم سياق البناء مع وبدون ملف .dockerignore (تلميح: ابحث عن سطر "Sending build context to Docker daemon" في مخرجات
docker build).