Docker: تحسين الصور وأفضل الممارسات

آخر تحديث: 2026-08-26

تحسين الصور ليس فقط لتقليل حجمها — الصور الأصغر تعني نشرًا أسرع، تكاليف تخزين أقل، وسطح هجوم أصغر.

1. ما ستتعلمه



2. قصة حقيقية لمهندس CI

(1) نقطة الألم: وقت بناء CI ارتفع من 8 إلى 25 دقيقة

ارتفع وقت بناء CI لبوب فجأة من 8 إلى 25 دقيقة. عند التحقيق، اكتشف أن كل بناء كان يعيد تثبيت جميع التبعيات — لأن COPY . . كان موضوعًا قبل RUN pip install، مما تسبب في انتهاء صلاحية ذاكرة تثبيت التبعيات المؤقتة مع كل تغيير في الكود. الانتظار لمدة 20 دقيقة قلل بشكل كبير من كفاءة التطوير.

(2) حلول تحسين التخزين المؤقت

بإعادة ترتيب تعليمات Dockerfile ونقل COPY package.json قليل التغيير إلى البداية، تمكنا من استخدام التخزين المؤقت للطبقات لتقليل وقت البناء إلى 3 دقائق.

DOCKERFILE
# قبل: تغيير الكود يبطل ذاكرة 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) إطار المقارنة قبل وبعد

100%
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 واحد ونظف الملفات المؤقتة في نفس الطبقة.

▶ مثال: دمج الأوامر (الصعوبة: ⭐⭐)

DOCKERFILE
# سيء: 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 صورة، إذا بقيت التعليمات وسياق الطبقة دون تغيير، يُعاد استخدام التخزين المؤقت. بمجرد انتهاء صلاحية ذاكرة طبقة مؤقتة، يجب إعادة بناء جميع الطبقات اللاحقة.

100%
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 (الصعوبة: ⭐⭐)

BASH
# بناء عادي: يستخدم التخزين المؤقت حيثما أمكن
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
# ============================================
# 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 هي أداة تفاعلية لتحليل طبقات الصورة تُظهر الملفات التي أُضيفت أو عُدلت أو حُذفت في كل طبقة.

BASH
# تثبيت 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
💡 نصيحة: واجهة dive تعرض: ① الحجم ودرجة الكفاءة لكل طبقة على اليسار؛ ② شجرة الملفات المضافة لكل طبقة على اليمين. العلامات الحمراء تشير إلى مساحة مهدرة (ملفات تم استبدالها أو حذفها بواسطة طبقات لاحقة).

▶ مثال: فحص Dockerfile بـ hadolint (الصعوبة: ⭐⭐)

hadolint هي أداة فحص لـ Dockerfiles تتحقق من انتهاكات أفضل الممارسات.

BASH
# تثبيت 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
💻 المخرجات:

TEXT 📖 للعرض فقط
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
# ============================================
# قبل: Dockerfile غير محسن (1.2 جيجابايت)
# ============================================
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["npm", "start"]
DOCKERFILE
# ============================================
# بعد: 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
# ============================================
# 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"]
BASH
# بناء والتحقق
docker build -t node-app:optimized .

# مقارنة الأحجام
docker images node-app

# تحليل بـ dive
dive node-app:optimized

# فحص بـ hadolint
hadolint Dockerfile

❓ أسئلة شائعة

س هل كل RUN يزيد حجم الصورة؟
ج نعم، حتى لو كان RUN يحذف الملفات فقط — لأن الطبقة الجديدة تسجل "عملية الحذف"، بينما الطبقة الأساسية لا تزال موجودة. لذلك، يجب إكمال "التثبيت + التنظيف" داخل نفس RUN، بحيث تكون الملفات المؤقتة التي أنشئت أثناء التثبيت وعملية الحذف موجودة في نفس الطبقة.
س 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.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): استخدم dive لتحليل كل طبقة من صورة موجودة (مثل nginx:latest)، مسجلاً أي طبقة هي الأكبر وما الملفات التي أضيفت.
  2. تمرين متقدم (الصعوبة: ⭐⭐): استخدم hadolint لفحص Dockerfiles من الدروس 7–9 وصحح جميع اقتراحات مستوى warning.
  3. تحدٍ (الصعوبة: ⭐⭐⭐): قارن أحجام الصور لنفس تطبيق Python المبني باستخدام الصور الأساسية alpine و debian و slim، وحلل أسباب الاختلافات في الحجم.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%