أتمتة تنظيف Docker: كيف تحرر عشرات الجيجابايت بسكربت cron واحد
لو الـ VPS بتاعك امتلأ فجأة، الـ deploy وقف الساعة 2 الفجر، وبتلاقي 60% من المساحة مشغولة في /var/lib/docker — الحل مش docker system prune -a -f العمياء. هتخسر cache مهم وتعيد بناء صور هتحتاجها بكرة. السكربت اللي هنا بينظف بأمان ووفّر على سيرفر إنتاج واحد 44GB في أسبوع.
المشكلة باختصار
Docker بيترك ورا كل عملية build أو run خمس أنواع من المخلفات: طبقات صور قديمة (image layers)، حاويات متوقفة (stopped containers)، volumes يتيمة (dangling volumes)، build cache بيكبر بلا حدود، وlogs بتتراكم. على سيرفر CI/CD أو staging، ده بيوصل بسهولة لـ 40–70GB في الشهر. لما الـ disk يمتلئ، الـ Docker daemon بيقع والحاويات بتفشل تبدأ من جديد.
تبسيط المفهوم أولًا: إيه هي الـ dangling images؟
تخيّل إنك بتعمل مسودّات لرسمة. كل مرة تعدّل حاجة، بتحتفظ بالنسخة القديمة على جنب "تحسّبًا". بعد 20 تعديل، عندك 20 نسخة، بس اللي فعلًا بتستخدمه هو الأخير. الـ dangling images هي بالظبط المسودّات دي: صور Docker قديمة من builds سابقة، مفيهاش tag دلوقتي، ومحدش بيستخدمها — بس لسه ماخدة مساحة.
علميًا: الـ dangling image هي أي image بـ tag <none>، يعني لا docker run ولا docker build بيشاور عليها. بتتولد لما تبني نفس الـ tag مرتين: البناء الأول بيبقى يتيم والتاني ياخد الـ tag. وعلى سيرفر CI بيبني كل commit، الأعداد بتتضاعف بسرعة.
ليه docker system prune -a خطر
docker system prune -a --volumes بيمسح كل image مش مستخدم حاليًا، حتى لو هتحتاجها خلال ساعة. على سيرفر CI بيبني نفس base images يوميًا (Node, Python, Postgres)، ده معناه إعادة تنزيل وإعادة بناء كل مرة. بناء كان بياخد 45 ثانية يبقى 3 دقائق.
بدل ما نعمل تنظيف مدمّر، الـ trade-off الذكي هو: احذف اللي أقدم من أسبوع، احتفظ بالـ cache الحديث. بتوفر 80% من المساحة، وبتخسر أقل من 15% من سرعة البناء.
السكربت الآمن — تنظيف مدروس بالـ filters
#!/usr/bin/env bash
set -euo pipefail
LOG="/var/log/docker-cleanup.log"
THRESHOLD_GB=10
echo "=== Docker cleanup $(date -Iseconds) ===" >> "$LOG"
# 1. احذف حاويات متوقفة أقدم من 24 ساعة
docker container prune -f --filter "until=24h" >> "$LOG" 2>&1
# 2. احذف الصور الـ dangling (بدون tag)
docker image prune -f >> "$LOG" 2>&1
# 3. احذف الصور غير المستخدمة الأقدم من 7 أيام
docker image prune -a -f --filter "until=168h" >> "$LOG" 2>&1
# 4. احذف volumes يتيمة
docker volume prune -f >> "$LOG" 2>&1
# 5. نظف build cache أقدم من 3 أيام، احتفظ بـ 5GB
docker builder prune -f --filter "until=72h" --keep-storage 5GB >> "$LOG" 2>&1
# 6. تنبيه لو المساحة لسه ضعيفة
FREE_GB=$(df -BG /var/lib/docker | awk 'NR==2 {gsub("G",""); print $4}')
if [ "$FREE_GB" -lt "$THRESHOLD_GB" ]; then
echo "WARN: only ${FREE_GB}GB free after cleanup" | tee -a "$LOG"
fi
echo "free: ${FREE_GB}GB" >> "$LOG"