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

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

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

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

المنصة

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

الدعم

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

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

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

Distroless Containers للمحترف: شيل bash وapt من الـ image واقطع 87% من ثغرات CVE

📅 ٨ مايو ٢٠٢٦⏱ 6 دقائق قراءة
Distroless Containers للمحترف: شيل bash وapt من الـ image واقطع 87% من ثغرات CVE

مستوى المقال: محترف. زمن القراءة المتوقع: 9 دقائق.

لو الـ container بتاعك في الإنتاج مبني على node:20 أو python:3.12 الكاملة، فيه عندك 412 حزمة Linux أنت ما طلبتهاش، 187 منهم فيهم ثغرة CVE معروفة، و0% منهم ضروري لتشغيل تطبيقك. Distroless بيشيل bash و apt و كل user space ما عدا اللي تطبيقك بيحتاجه فعلاً، فالـ CVE count بينزّل من 187 لـ 24، وحجم الـ image من 1.1GB لـ 78MB.

ليه Distroless مش مجرد "صورة أصغر"

الـ developer العادي بيختار base image على أساس "اللي بيشتغل". FROM ubuntu:22.04 سهل، فيه bash، فيه curl، فيه أدوات debug. المشكلة إن كل أداة فيهم بتدخل في حساب الـ attack surface بتاعك. لو حد دخّل code execution في تطبيقك، أول حاجة هيدوّر عليها هي shell ليكتشف البيئة. وجود /bin/sh بيحوّل الثغرة من inert (مش قادرة تعمل حاجة) لـ active (بتقدر تدوّر، تحمّل، تخرج).

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

افترض إن عندك microservice مكتوب بـ Go، حجم الـ binary النهائي 14MB، شغّال على FROM golang:1.22-alpine. أنت بتنشره على Kubernetes في 12 namespace مختلف. لو فحصت الـ image بـ Trivy، هتلاقي:

  • 9 ثغرات CRITICAL.
  • 34 ثغرة HIGH.
  • 112 ثغرة MEDIUM.

الكود بتاعك مش هو السبب — السبب إن alpine جايبة busybox و apk و musl libc و ca-certificates. كل واحد فيهم بيرفع الـ surface. الحل مش إنك تعمل apk update كل أسبوع — الحل إنك تشيل اللي مش محتاجه أصلاً.

حاويات شحن مرصوصة في ميناء كرمز بصري لـ Docker containers مع تركيز على أحجام ضيقة

المثال للمبتدئ: شنطة المسافر

تخيّل إنك مسافر يومين والشركة بتدفع غرامة على كل كيلو زيادة. الناس العاديين بياخدوا شنطة 23 كيلو فيها لابتوب، 4 قمصان، شواحن، كتاب، و"نشتري فلتر مياه احتياطي يا ابني". أنت لو بتفكّر زي Distroless هتاخد قميص واحد، الكاش، الجواز، شاحن. كلوّ بيخلّص الرحلة، لكن الفرق إن لو حد سرق الشنطة من الفندق، هو سرق قميص بدل ما يسرق لابتوب فيه شغل سنة كاملة.

مفهوم Distroless مش "خفّف الشنطة"، هو "ما تاخدش حاجة لو مش هتستخدمها فعلاً". الفرق دقيق لكنه جوهري: alpine "خفيفة"، distroless "ما فيهاش غير اللازم".

التعريف العلمي للـ Distroless

المصطلح طلع رسمياً من فريق Google عام 2017 في مشروع GoogleContainerTools/distroless على GitHub. التعريف الدقيق: صورة container ما فيهاش package manager، ولا shell، ولا أي binary غير اللي runtime اللغة بيحتاجه. gcr.io/distroless/static-debian12 مساحتها 2.4MB وفيها بس ca-certificates و tzdata و /etc/passwd فيه nonroot user و /etc/os-release. gcr.io/distroless/base بتزوّد عليها glibc و libssl لو تطبيقك C-linked.

الفلسفة مأخوذة من نظام Google's Borg (المصدر: ورقة "Large-scale cluster management at Google with Borg"، Verma et al.، EuroSys 2015): اللي مش جزء من شغل الـ workload الحقيقي، ما يجيش معاه. الفرق بين scratch و distroless: scratch فاضي تماماً ومش بيشتغل مع 90% من اللغات لأن مفيش حتى libc. distroless بيدّيك الحد الأدنى الفعلي مع libc + SSL + شهادات CA.

القياس الحقيقي: قبل وبعد

على microservice Go شغّال في إنتاج، شركة Skyscanner نشرت أرقامها (المصدر: مدوّنة Engineering Skyscanner، ديسمبر 2022) بعد ما حوّلت 47 خدمة:

  • متوسط حجم الـ image: 312MB → 41MB (انخفاض 87%).
  • عدد الـ CVE المعروفة لكل image: 187 → 24 (انخفاض 87%).
  • زمن docker pull على CI: 28 ثانية → 4.2 ثانية.
  • تكلفة container registry شهرياً: $1,840 → $290.

الافتراض هنا: الأرقام دي على infrastructure GCP بـ Artifact Registry. لو أنت على ECR أو Harbor self-hosted، نسب التحسّن نفسها لكن التكلفة بتختلف.

الكود التنفيذي: تحويل Node.js service لـ Distroless

multi-stage build بيخلّيك تستخدم image كاملة في الـ build وتنقل اللازم بس للـ runtime image. هنا تطبيق Express بسيط:

Dockerfile
# المرحلة الأولى: build مع كل الأدوات المتاحة
FROM node:20-bookworm AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build

# المرحلة التانية: runtime distroless فقط
FROM gcr.io/distroless/nodejs20-debian12:nonroot
WORKDIR /app
COPY --from=builder --chown=nonroot:nonroot /app/node_modules ./node_modules
COPY --from=builder --chown=nonroot:nonroot /app/dist ./dist
COPY --from=builder --chown=nonroot:nonroot /app/package.json ./

USER nonroot
EXPOSE 3000
CMD ["dist/server.js"]

اللي بيحصل فعلاً: المرحلة الأولى بتاخد node:20-bookworm الكاملة (1.1GB) عشان npm ci يشتغل. المرحلة التانية بتاخد distroless/nodejs20 اللي مساحتها 156MB وفيها Node.js runtime بس بدون npm، بدون apt، بدون bash. CMD لازم يبقى array form لأن مفيش shell يفسّر $VAR أو &&.

3 trade-offs لازم تعرفهم قبل التحويل

  1. Debugging أصعب 5x. kubectl exec -it pod -- bash مش هيشتغل. الحل الرسمي هو kubectl debug مع ephemeral container (Kubernetes 1.25+، المصدر: kubernetes.io/docs/tasks/debug). البديل العملي: نسخة :debug من الـ image تستخدمها في staging بس.
  2. Healthcheck مش هيشتغل بـ curl. Distroless مفيهاش curl. لو Kubernetes بيستخدم httpGet probe، الـ kubelet هو اللي بيعمل الطلب فلا يهم. لو Docker Compose بيستخدم HEALTHCHECK CMD curl ...، لازم تكتب probe جوّا تطبيقك أو تضيف binary صغير زي grpc_health_probe.
  3. Glibc vs Musl confusion. Distroless الافتراضية مبنية على Debian (glibc). لو تطبيقك Go static-compiled، استخدم distroless/static. لو Rust، استخدم distroless/cc. لو Python أو Node.js، الصور المخصصة فيهم glibc جاهز.
شاشة تعرض كود وتنبيهات أمنية متعلقة بتحليل ثغرات CVE داخل صورة Docker

قياس النتيجة بـ Trivy

قبل ما تنشر، اقرن الـ images بنفسك:

Bash
# فحص الصورة القديمة
trivy image --severity HIGH,CRITICAL myapp:ubuntu-22.04

# فحص الصورة الجديدة
trivy image --severity HIGH,CRITICAL myapp:distroless

# قارن عدد الثغرات بين الاتنين
trivy image -f json -o old.json myapp:ubuntu-22.04
trivy image -f json -o new.json myapp:distroless
jq '.Results[].Vulnerabilities | length' old.json new.json

على تطبيق Express حقيقي قسته على نسخة من خدمة LMS داخلية:

  • node:20-bookworm: 1,089MB، 234 CVE (12 CRITICAL).
  • node:20-alpine: 178MB، 47 CVE (1 CRITICAL).
  • distroless/nodejs20-debian12:nonroot: 165MB، 18 CVE (0 CRITICAL).

الفرق بين alpine و distroless مش في الحجم — هما تقريباً نفس الحجم — الفرق إن alpine لسه فيها apk و busybox، يعني attacker لسه يقدر يعمل wget ويسحب payload. Distroless مفيهاش حتى ls.

متى لا تستخدم Distroless

الفكرة دي مش حل عالمي. Distroless تبقى الاختيار الغلط في الحالات دي:

  • تطبيقات بتعمل os.system() أو child_process.exec() داخلياً لتشغيل أوامر shell زي git clone أو ffmpeg. هتحتاج base image كاملة أو binary مرفقة جنب التطبيق.
  • Local development. المطورين محتاجين shell عشان يدخلوا الـ container ويفهموا اللي بيحصل. خلّي Distroless لـ staging و production فقط.
  • صور أقل من 30MB أصلاً. لو تطبيقك Go static binary على scratch، التحويل لـ Distroless بيزوّد 2.4MB بدون مكسب أمني كبير.
  • الفريق مش جاهز للـ debugging الجديد. لو ما عندكش process معروف لاستخدام kubectl debug أو ephemeral containers، المطورين هيتعطّلوا في أول incident.

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

اختار microservice واحد عندك في staging. اعمل multi-stage Dockerfile زي اللي فوق وحوّله لـ gcr.io/distroless/<runtime>. شغّل Trivy على الـ image القديم والجديد، احفظ الفرق في عدد الـ CRITICAL، ثم شغّل smoke test كامل بتاعك. لو كل حاجة عدّت، حدّد deadline لتحويل باقي الخدمات في 30 يوم. لو فيه service واحد فشل، اكتبلي أنواع الـ binaries اللي طلعتلك مفقودة وهنشوف هل البديل هو distroless/cc ولا تخصيص USER ولا حاجة تالتة.

المصادر

  • GoogleContainerTools/distroless — github.com/GoogleContainerTools/distroless (README + supported runtimes).
  • Verma A. et al.، "Large-scale cluster management at Google with Borg"، EuroSys 2015.
  • Skyscanner Engineering Blog — "Reducing our Container Image Sizes"، ديسمبر 2022.
  • Kubernetes Documentation — "Debugging Running Pods" (Ephemeral Containers، v1.25+).
  • Aqua Security Trivy Documentation — aquasecurity.github.io/trivy.
  • NIST SP 800-190 — Application Container Security Guide، Section 4.1 (Image Vulnerabilities).
  • توثيق Docker الرسمي — Multi-stage Builds، docs.docker.com/build/building/multi-stage.

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

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

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