أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

أتمتة تنظيف Docker على السيرفر — سكربت أسبوعي يوفّر 40GB كل شهر

📅 ٢٠ أبريل ٢٠٢٦⏱ 6 دقائق قراءة
أتمتة تنظيف Docker على السيرفر — سكربت أسبوعي يوفّر 40GB كل شهر

أتمتة تنظيف Docker على السيرفر — سكربت أسبوعي يوفّر 40GB كل شهر

لو شغّلت df -h على سيرفر فيه Docker شغّال من 3 شهور، غالبًا هتلاقي 40% على الأقل من الـ disk ضايع في images قديمة و volumes يتيمة. السكربت اللي في المقال ده بيفضّي المساحة دي أسبوعيًا بدون ما تفقد أي container شغّال.

ركايب سيرفرات في داتا سنتر تمثل المساحة المستهلكة بسبب Docker images وvolumes المتراكمة

المشكلة باختصار

Docker بيحتفظ بكل image بنيتها، كل container وقف ومتشالش، وكل volume مش متربط بأي حاجة شغّالة. لو بتبني الـ image عشر مرات في اليوم (CI، تجارب محلية، hotfixes)، المساحة بتتراكم بسرعة رهيبة. على سيرفر CI عندي بيبني 20 مرة يوميًا، الـ disk بيمتلي في حوالي شهر من غير ما تلاحظ.

مثال بسيط جدًا للمبتدئين

تخيّل إنك كل ما تطبخ وجبة، بترمي الصحون في الحوض بدل ما تغسلها. أول يومين الموضوع عادي. بعد أسبوع، المطبخ كله مش قابل للاستخدام، والحوض بيفيض. Docker بالظبط كده — كل build بيسيب "صحون" (images قديمة، containers واقفة، caches، volumes يتيمة) لو متشالتش، بتبوّظ المطبخ. الفرق إن هنا "المطبخ" هو الـ disk بتاع السيرفر، ولمّا يمتلي بتوقع production.

الشرح العلمي

Docker معماريًا مبني على layered filesystem. كل docker build بينتج layers جديدة، والقديمة متبقاش مربوطة بأي tag أو container لكن مش بتتمسح تلقائيًا. نفس الحاجة بتحصل مع:

  • Exited containers: كل docker run بدون --rm بيسيب container جثّة لما يخلّص شغله.
  • Dangling images: images اتبنت وبعدين اتبنى فوقها tag جديد — الـ tag انتقل، لكن الـ image الأصلي لسه على الـ disk.
  • Anonymous volumes: لو container كان بيستخدم volume من غير اسم، لمّا تشيل الـ container الـ volume بيفضل موجود للأبد.
  • BuildKit cache: مجرد الـ build cache لوحده ممكن يوصل 30-50GB على سيرفر CI نشط.

ليه الحل اليدوي مش كفاية

الأمر المشهور docker system prune -a بيبان إنه يحل المشكلة. في الواقع بيعمل 3 مشاكل:

  1. بيمسح images هتحتاجها في البناء القادم — زي node:20 أو postgres:16. نتيجة: أول build بعدها بياخد 3-5 دقايق زيادة عشان يسحب الـ base images من الـ registry تاني.
  2. بيلمس volumes لو زوّدت --volumes — ممكن تمسح volume فيه data إنتاج بالغلط لو مش مربوط حاليًا بأي container شغّال.
  3. مبيتشرطش وقت — لو شغّلته الساعة 10 الصبح وحد بيعمل deploy في نفس اللحظة، بتخاطر بـ race condition على الـ images.

البديل: سكربت يتحكم بالظبط في إيه يتمسح وإيه يتساب، ويشتغل في وقت هادي (فجر الأحد مثلًا)، ويبعت لك تنبيه بالنتيجة.

terminal بيعرض سكربت bash لتنظيف Docker images وvolumes بشكل دوري

السكربت الأسبوعي الكامل

الأدوات: docker + bash + cron + webhook Slack للتنبيه. احفظ الملف في /usr/local/bin/docker-cleanup.sh:

Bash
#!/usr/bin/env bash
set -euo pipefail

LOG="/var/log/docker-cleanup.log"
SLACK_WEBHOOK="${SLACK_WEBHOOK:-}"
KEEP_DAYS=14

exec >> "$LOG" 2>&1
echo "=== cleanup started: $(date -Iseconds) ==="

before=$(df -BG / | awk 'NR==2 {print $4}' | tr -d 'G')

# 1. containers وقفت من أكثر من 24 ساعة
docker container prune -f --filter "until=24h"

# 2. images dangling + images غير مستخدمة أقدم من 14 يوم
docker image prune -af --filter "until=${KEEP_DAYS}*24h"

# 3. builder cache (BuildKit)
docker builder prune -f --filter "until=${KEEP_DAYS}*24h" --keep-storage 10GB

# 4. volumes يتيمة — بس اللي مفيهاش label keep=true
docker volume ls -qf dangling=true | while read -r vol; do
  label=$(docker volume inspect -f '{{index .Labels "keep"}}' "$vol" 2>/dev/null || echo "")
  if [[ "$label" != "true" ]]; then
    docker volume rm "$vol" || true
  fi
done

after=$(df -BG / | awk 'NR==2 {print $4}' | tr -d 'G')
freed=$((after - before))

msg="Docker cleanup done — freed ${freed}GB (was ${before}G, now ${after}G)"
echo "$msg"

if [[ -n "$SLACK_WEBHOOK" ]]; then
  curl -sS -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"$msg\"}" "$SLACK_WEBHOOK" >/dev/null
fi

خلّيه قابل للتنفيذ:

Bash
sudo chmod +x /usr/local/bin/docker-cleanup.sh

جدولته في cron

Bash
# افتح crontab
sudo crontab -e

# أضف السطر ده (يوم الأحد 4 الفجر)
0 4 * * 0 SLACK_WEBHOOK=https://hooks.slack.com/services/XXX /usr/local/bin/docker-cleanup.sh

حماية الـ volumes المهمة

لو عندك volume فيه data إنتاج (postgres، redis persistence، uploaded files)، علّمه بـ label وقت الإنشاء:

Bash
docker volume create --label keep=true pg_prod_data

السكربت بيتخطّاه تلقائيًا بسبب الشرط label == "true".

ملاحظة مهمة: Docker مش بيدعم إضافة label لـ volume موجود بدون إعادة إنشاء. لو عندك volumes قديمة مش معلّمة، الحل الآمن: اربط الـ volume بـ container شغّال دايمًا (مثلًا حطّه في docker-compose.yml مع restart: always). ساعتها مش هيظهر أصلًا في dangling=true.

الأرقام من سيرفر حقيقي

على سيرفر إنتاج بيعمل CI builds 15-20 مرة يوميًا (stack: Node.js + PostgreSQL + Redis):

  • قبل السكربت: 92GB مستهلك من 100GB disk.
  • أول تشغيل: حرّر 52GB (معظمها images قديمة من 6 شهور لـ Node.js 16 كانت بتترص من كل build قديم).
  • من الأسبوع الثاني: بيحرّر 8-12GB كل أحد.
  • على مدار شهر: متوسط 40GB محرّرة، مع ثبات الاستهلاك حوالين 55-65GB بدل ما يتجاوز 90GB.

trade-offs ومتى مش مناسب

المكسب: مش هتحتاج تتذكر تفضّي السيرفر يدويًا. بتكسب ~10-15 دقيقة شهريًا من شغل idle، والأهم إنك بتتفادى الكابوس بتاع disk full وسط deploy.

الثمن:

  1. أول build بعد الـ cleanup ممكن يطول دقيقة-دقيقتين لأن base images هتتسحب من الـ registry تاني.
  2. لو Slack webhook بتاعك معلّق أو URL غلط، مفيش تنبيه = ممكن تنسى الموضوع تمامًا. ضيف healthcheck خارجي (زي healthchecks.io) لو السكربت نفسه مهم عندك.
  3. --keep-storage 10GB في BuildKit ممكن يكون كبير أو صغير حسب حجم سيرفرك. ابدأ بـ 10 وعدّل لو شايف الـ cache بيمتلي أو الـ builds بطيئة.
  4. السكربت مش بيتعامل مع images معلّمة بـ tag لكن مش مستخدمة حاليًا — هتفضل موجودة. ده مقصود عشان متكسرش rollback.

الافتراضات اللي الحل مبني عليها: Docker 23.0+ (BuildKit هو الـ default). لو عندك Docker أقدم، شيل سطر docker builder prune لأنه هيفشل. السكربت كمان بيفترض إن Docker data موجودة على الـ partition الرئيسية (/). لو عندك /var/lib/docker mounted على partition منفصل، غيّر الـ df -BG / لـ df -BG /var/lib/docker.

متى لا تستخدم هذه الطريقة

  • سيرفر فيه image واحد بيشتغل شهور بدون builds — مفيش تراكم أصلًا، السكربت overkill.
  • بيئة فيها compliance تتطلب audit log لكل حاجة اتمسحت — السكربت مبيحتفظش بـ manifest للـ images المحذوفة؛ هتحتاج تزوّد logging أعمق.
  • Kubernetes cluster — في K8s، إدارة الـ image garbage collection شغل kubelet نفسه عبر imageGCHighThresholdPercent و imageGCLowThresholdPercent. تشغيل السكربت ده على node فيه kubelet بيشتغل = مخاطرة إنك تحذف images الـ kubelet لسه محتاجها.
  • Docker Swarm نشط بـ services دايمًا scaling — استخدم فلاتر أكثر تحفظًا (until=30*24h) لأن الـ services بتحتاج الـ images جاهزة أي لحظة.

الخطوة التالية

شغّل السكربت يدويًا دلوقتي مرة واحدة قبل ما تحطه في cron:

Bash
sudo bash /usr/local/bin/docker-cleanup.sh
tail -f /var/log/docker-cleanup.log

لو توفّر أقل من 5GB في أول تشغيل، معناه السيرفر نظيف أصلًا والأتمتة مش أولوية — دوّر على سبب تاني لامتلاء الـ disk (logs، uploads، backups محلية). لو توفّر 20GB+، ضيفه للـ cron فورًا وروح نام.

المصادر

  • Docker Docs — docker system prune
  • Docker Docs — docker builder prune (BuildKit cache)
  • Docker Docs — Volumes & labels
  • Kubernetes Docs — Image Garbage Collection
  • Slack Incoming Webhooks

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة