Docker: تحسين الصور وأفضل الممارسات
آخر تحديث: 2026-08-26
تحسين الصور ليس فقط لتقليل حجمها — الصور الأصغر تعني نشرًا أسرع، تكاليف تخزين أقل، وسطح هجوم أصغر.
1. ما ستتعلمه
- استراتيجيات تحسين عدد طبقات الصورة
- نصائح لدمج أوامر RUN
- تنظيف التبعيات وإدارة التخزين المؤقت
- استخدام أدوات فحص Dockerfile
- تطوير طرق تحسين التخزين المؤقت
2. قصة حقيقية لمهندس CI
(1) نقطة الألم: وقت بناء CI ارتفع من 8 إلى 25 دقيقة
ارتفع وقت بناء CI لبوب فجأة من 8 إلى 25 دقيقة. عند التحقيق، اكتشف أن كل بناء كان يعيد تثبيت جميع التبعيات — لأن COPY . . كان موضوعًا قبل RUN pip install، مما تسبب في انتهاء صلاحية ذاكرة تثبيت التبعيات المؤقتة مع كل تغيير في الكود. الانتظار لمدة 20 دقيقة قلل بشكل كبير من كفاءة التطوير.
(2) حلول تحسين التخزين المؤقت
بإعادة ترتيب تعليمات Dockerfile ونقل COPY package.json قليل التغيير إلى البداية، تمكنا من استخدام التخزين المؤقت للطبقات لتقليل وقت البناء إلى 3 دقائق.
# قبل: تغيير الكود يبطل ذاكرة pip المؤقتة
COPY . .
RUN pip install -r requirements.txt
# بعد: معدل تغيير التبعيات << معدل تغيير الكود
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
(3) الفوائد: وقت البناء انخفض من 25 إلى 3 دقائق
ببساطة بإعادة ترتيب سطرين من الكود، انخفض وقت البناء من 25 إلى 3 دقائق (عند إصابة التخزين المؤقت)، مما أدى إلى زيادة 8 أضعاف في كفاءة خط أنابيب CI.
3. الاستراتيجيات الرئيسية لتحسين الصور
(1) إطار المقارنة قبل وبعد
graph TB
subgraph Before["قبل التحسين"]
B1["FROM ubuntu:22.04<br/>77 ميجابايت"] --> B2["RUN apt-get install<br/>300 ميجابايت"]
B2 --> B3["COPY . .<br/>(مع) node_modules"]
B3 --> B4["RUN npm install<br/>200 ميجابايت"]
B4 --> B5["النهائي: 1.2 جيجابايت"]
end
subgraph After["بعد التحسين"]
A1["FROM node:20-alpine<br/>135 ميجابايت"] --> A2["COPY package.json"]
A2 --> A3["RUN npm ci<br/>50 ميجابايت"]
A3 --> A4["COPY . .<br/>(بدون) node_modules"]
A4 --> A5["النهائي: 180 ميجابايت"]
end
(2) نظرة عامة على استراتيجيات التحسين
| الاستراتيجية | الفعالية | صعوبة التنفيذ |
|---|---|---|
| التبديل إلى صورة أساسية (alpine/slim) | الحجم ↓50–80% | ⭐ |
| بناء متعدد المراحل | الحجم ↓60–90% | ⭐⭐ |
| RUN دمج + مسح الذاكرة المؤقتة | الحجم ↓20–40% | ⭐ |
| ضبط ترتيب COPY | سرعة البناء ↑3–8x | ⭐ |
| .dockerignore | السياق ↓30–90% | ⭐ |
| فحص hadolint | اكتشاف المشكلات المحتملة | ⭐⭐ |
4. دمج أوامر RUN
(1) مبدأ الدمج
كل أمر RUN ينشئ طبقة جديدة. ادمج العمليات المرتبطة في أمر RUN واحد ونظف الملفات المؤقتة في نفس الطبقة.
▶ مثال: دمج الأوامر (الصعوبة: ⭐⭐)
# سيء: 3 طبقات منفصلة، ذاكرة apt المؤقتة تبقى في الصورة
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y nginx
# جيد: طبقة واحدة، ذاكرة مؤقتة نظيفة في نفس الطبقة
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
nginx && \
rm -rf /var/lib/apt/lists/*
(2) تأثير --no-install-recommends
| الخيار | محتوى التثبيت | توفير المساحة النموذجي |
|---|---|---|
| الافتراضي | الحزم الرئيسية + الحزم الموصى بها + الحزم المقترحة | خط الأساس |
--no-install-recommends |
الحزمة الرئيسية + التبعيات فقط | ↓30–50% |
5. تحسين معدل إصابة التخزين المؤقت
(1) آلية التخزين المؤقت للطبقات
عندما يبني Docker صورة، إذا بقيت التعليمات وسياق الطبقة دون تغيير، يُعاد استخدام التخزين المؤقت. بمجرد انتهاء صلاحية ذاكرة طبقة مؤقتة، يجب إعادة بناء جميع الطبقات اللاحقة.
graph TB
L1["FROM python:3.12<br/>✅ إصابة الذاكرة المؤقتة"] --> L2["WORKDIR /app<br/>✅ إصابة الذاكرة المؤقتة"]
L2 --> L3["COPY requirements.txt<br/>✅ إصابة الذاكرة المؤقتة (بدون تغيير)"]
L3 --> L4["RUN pip install<br/>✅ إصابة الذاكرة المؤقتة"]
L4 --> L5["COPY . .<br/>❌ فقدان الذاكرة المؤقتة (تغير الكود)"]
L5 --> L6["CMD python app.py<br/>❌ إعادة بناء"]
▶ مثال: مقارنة البناء مع وبدون --no-cache (الصعوبة: ⭐⭐)
# بناء عادي: يستخدم التخزين المؤقت حيثما أمكن
docker build -t myapp:1.0 .
# إجبار إعادة البناء بدون أي تخزين مؤقت
docker build --no-cache -t myapp:1.0 .
# استخدام صورة سابقة كمصدر تخزين مؤقت (تحسين CI)
docker build --cache-from=myapp:latest -t myapp:1.0 .
(2) تحسين ترتيب تعليمات Dockerfile
# ============================================
# Dockerfile محسن: معدل إصابة عالي للتخزين المؤقت
# ============================================
FROM node:20-alpine
WORKDIR /app
# 1. الأقل تغييرًا: بيانات الحزمة الوصفية
COPY package*.json ./
# 2. تثبيت التبعيات (مخزنة مؤقتًا ما لم يتغير package.json)
RUN npm ci --only=production
# 3. الأكثر تغييرًا: كود التطبيق
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
| تغييرات السيناريو | الإصدار المحسن | الإصدار غير المحسن |
|---|---|---|
| تغيير الكود فقط | ✅ npm ci يستخدم التخزين المؤقت (5 ثوانٍ) | ❌ npm ci يعيد البناء (3 دقائق) |
| تحديث إصدار التبعية | ✅ إعادة بناء بـ npm ci (مطلوب) |
❌ إعادة بناء بـ npm ci (مثل أعلاه) |
| تعديل Dockerfile | ✅ إعادة بناء الطبقات المتأثرة فقط | ❌ إعادة بناء الكل |
6. أدوات تحليل الصور
▶ مثال: تحليل طبقات الصورة بـ dive (الصعوبة: ⭐⭐)
dive هي أداة تفاعلية لتحليل طبقات الصورة تُظهر الملفات التي أُضيفت أو عُدلت أو حُذفت في كل طبقة.
# تثبيت dive (Linux)
wget https://github.com/wagoodman/dive/releases/download/v0.12.0/dive_0.12.0_linux_amd64.deb
sudo dpkg -i dive_0.12.0_linux_amd64.deb
# تحليل صورة
dive nginx:latest
▶ مثال: فحص Dockerfile بـ hadolint (الصعوبة: ⭐⭐)
hadolint هي أداة فحص لـ Dockerfiles تتحقق من انتهاكات أفضل الممارسات.
# تثبيت hadolint (Linux)
wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
chmod +x /usr/local/bin/hadolint
# فحص Dockerfile
hadolint Dockerfile
Dockerfile:3 DL3013 warning: Pin versions in pip. Instead of `pip install <package>` use `pip install <package>==<version>`
Dockerfile:5 DL3059 info: Do not use `--no-cache-dir` in pip install. It is redundant when `--cache-from` is used.
Dockerfile:7 DL3008 warning: Pin versions in apt-get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
7. مقارنة كاملة لـ Dockerfile قبل وبعد التحسين
(1) أمثلة تحسين تطبيق Node.js
# ============================================
# قبل: Dockerfile غير محسن (1.2 جيجابايت)
# ============================================
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["npm", "start"]
# ============================================
# بعد: Dockerfile محسن (180 ميجابايت)
# ============================================
# 1. قاعدة Alpine بدلاً من Node الكامل
FROM node:20-alpine
WORKDIR /app
# 2. نسخ بيانات التبعيات الوصفية أولاً (تحسين التخزين المؤقت)
COPY package*.json ./
# 3. تبعيات الإنتاج فقط
RUN npm ci --only=production && \
npm cache clean --force
# 4. نسخ الكود المصدري (الأكثر تغييرًا)
COPY . .
# 5. التشغيل كمستخدم غير root
RUN addgroup -S appgroup && \
adduser -S appuser -G appgroup && \
chown -R appuser:appgroup /app
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
(2) مقارنة نتائج التحسين
| المقياس | قبل التحسين | بعد التحسين | التحسن |
|---|---|---|---|
| حجم الصورة | 1.2 جيجابايت | 180 ميجابايت | 6.7x ↓ |
| وقت البناء (تغييرات الكود) | 3 دقائق | 5 ثوانٍ | 36x ↓ |
| وقت البناء (تغييرات التبعيات) | 3 دقائق | 45 ثانية | 4x ↓ |
| الثغرات الأمنية | 200+ | 30 | 6.7x ↓ |
| درجة CIS Benchmark | 45 | 85 | +40 |
8. سياسات التثبيت الخاصة بكل لغة
| اللغة | التبعيات | أمر التثبيت | أمر التنظيف |
|---|---|---|---|
| Node.js | package*.json |
npm ci --only=production |
npm cache clean --force |
| Python | requirements.txt |
pip install --no-cache-dir |
(مدمج --no-cache-dir) |
| Go | go.mod go.sum |
go mod download |
go clean -modcache |
| Java | pom.xml / build.gradle |
mvn dependency:resolve |
تنظيف ذاكرة .m2 المؤقتة |
| Ruby | Gemfile Gemfile.lock |
bundle install --without dev |
تنظيف ذاكرة .bundle المؤقتة |
9. مثال كامل: تحسين شامل لتطبيق Node.js
# ============================================
# Dockerfile إنتاج Node.js محسن بالكامل
# الميزات: alpine، متعدد المراحل، تخزين مؤقت، غير root
# ============================================
# ---------- المرحلة 1: البناء ----------
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---------- المرحلة 2: الإنتاج ----------
FROM node:20-alpine
WORKDIR /app
# نسخ تبعيات الإنتاج فقط
COPY package*.json ./
RUN npm ci --only=production && \
npm cache clean --force
# نسخ التطبيق المبني من مرحلة البناء
COPY --from=builder /app/dist ./dist
# الأمان: مستخدم غير root
RUN addgroup -S appgroup && \
adduser -S appuser -G appgroup && \
chown -R appuser:appgroup /app
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
# بناء والتحقق
docker build -t node-app:optimized .
# مقارنة الأحجام
docker images node-app
# تحليل بـ dive
dive node-app:optimized
# فحص بـ hadolint
hadolint Dockerfile
❓ أسئلة شائعة
apt-get clean لماذا يقلل حجم الصورة؟apt-get clean وحده غير فعال تقريبًا (الأمر نفسه ينطبق على rm). يجب استخدامه داخل نفس RUN: apt-get update && apt-get install && rm -rf /var/lib/apt/lists/*. --no-install-recommends أكثر فعالية من apt-get clean.docker build: Using cache يشير إلى إصابة، و Step 3/7 : RUN xxx يشير إلى إعادة بناء. يمكنك أيضًا استخدام --progress=plain لعرض السجلات التفصيلية. في CI، docker build --cache-from=app:latest يسمح بإعادة استخدام التخزين المؤقت من البناء السابق.--no-cache متى يجب استخدامه؟--no-cache أثناء التطوير اليومي، لأنه سيجعل كل بناء يبدأ من الصفر.docker scout quickview <صورة> (أداة Docker الرسمية) أو trivy image <صورة> (مفتوحة المصدر). Trivy هي أداة فحص الصور مفتوحة المصدر الأكثر شيوعًا، قادرة على اكتشاف CVEs المعروفة في حزم نظام التشغيل وتبعيات اللغة. نوصي بدمج خطوة فحص في خط أنابيب CI.📖 ملخص
- جوهر تحسين الصور: تقليل عدد الطبقات + التنظيف في نفس الطبقة + اختيار صورة أساسية أصغر
- مبدأ دمج RUN: دمج العمليات المرتبطة في RUN واحد؛ التثبيت والتنظيف في نفس الطبقة
- تحسين التخزين المؤقت: وضع التعليمات التي نادرًا ما تتغير في البداية (COPY ملفات التبعيات)، والتي تتغير كثيرًا في النهاية (COPY الكود المصدري)
--no-install-recommendsيقلل حجم التثبيت بنسبة 30–50%- استخدم
diveلتحليل كفاءة طبقات الصورة، وhadolintللتحقق من أفضل ممارسات Dockerfile - عملية التحسين الكاملة: صورة Alpine الأساسية → بناء متعدد المراحل → تحسين التخزين المؤقت → غير root → فحص الصحة
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): استخدم
diveلتحليل كل طبقة من صورة موجودة (مثلnginx:latest)، مسجلاً أي طبقة هي الأكبر وما الملفات التي أضيفت. - تمرين متقدم (الصعوبة: ⭐⭐): استخدم hadolint لفحص Dockerfiles من الدروس 7–9 وصحح جميع اقتراحات مستوى warning.
- تحدٍ (الصعوبة: ⭐⭐⭐): قارن أحجام الصور لنفس تطبيق Python المبني باستخدام الصور الأساسية alpine و debian و slim، وحلل أسباب الاختلافات في الحجم.