تبقى Docker في 2026 المعيار الفعلي لحوسبة التطبيقات في حاويات، وPodman منافسها بلا daemon بتوافق 95%. للمطور وإداري النظم في منطقة MENA، إتقان الأنماط المتقدمة لـ Docker (multi-stage builds، volumes مسمّاة، شبكات مخصّصة، healthchecks، سياق أمني) يصنع الفرق بين بنية تحتية هشّة وأخرى جاهزة للإنتاج. هذا الدليل العملي 2026.
يغطي هذا الدليل العام الأنماط المتقدمة. المقالات المرافقة تفصّل: multi-stage builds محسَّن، Docker مقابل Podman، Docker Compose في الإنتاج، Docker rootless والأمان.
Docker في 2026
- Docker Desktop: ترخيص مدفوع للشركات > 250 موظفاً أو > 10 مليون USD رقم أعمال
- Docker Engine: مفتوح المصدر Apache 2.0، مجاني بلا حدود
- BuildKit: باني حديث مع cache موزّع وbuilds متوازية
- Docker Compose v2 (مدمج في docker CLI)
- Docker Hub: registry عام، خطة مجانية محدودة (200 سحب كل 6 ساعات للمجهولين)
Podman
- توافق Docker بـ 95%:
alias docker=podmanيعمل غالباً - بلا daemon: لا عملية root دائمة
- Rootless افتراضياً: حاويات تحت مستخدم عادي
- Pods (مجموعات حاويات تتقاسم الشبكة) — مفهوم من Kubernetes
- توليد YAML Kubernetes من pods
Multi-stage build محسَّن
# Dockerfile multi-stage لتطبيق Node.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && \
cp -R node_modules /tmp/prod_node_modules
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
USER node
COPY --from=builder --chown=node:node /tmp/prod_node_modules ./node_modules
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/package.json ./
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health',(r)=>process.exit(r.statusCode===200?0:1))"
CMD ["node", "dist/server.js"]
الصورة النهائية ~150 ميغا بدل 1 جيغا في النهج الساذج. انظر درسنا multi-stage.
Volumes مسمّاة مقابل bind mounts
- Bind mount:
-v /host/path:/container/path— مشاركة مجلد المضيف. عملي في التطوير. - Named volume:
-v mydata:/data— Docker يدير التخزين. قابل للنسخ الاحتياطي، نقل، ولا يلوث المضيف. - tmpfs:
--tmpfs /tmp— RAM فقط، مثالي لـ caches مؤقتة
شبكات مخصّصة
# إنشاء شبكة خاصة
docker network create app-net
# تشغيل خدمات متعددة عليها
docker run -d --network app-net --name api -p 3000:3000 myapp:latest
docker run -d --network app-net --name db postgres:16
# api تستطيع حلّ 'db' عبر DNS الداخلي
Healthchecks
حاسم لـ zero-downtime deployments. المنسّق (Coolify، Swarm، K8s) ينتظر نجاح healthcheck قبل قطع الحاوية القديمة.
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
السياق الأمني
- USER غير root: لا تشغّل أبداً كـ root داخل الحاوية
- –read-only: حاوية بنظام ملفات للقراءة فقط، باستثناء volumes
- –cap-drop=ALL –cap-add=NET_BIND_SERVICE: إسقاط كل القدرات
- –security-opt=no-new-privileges: منع تصعيد الامتيازات
- فحص الصور بـ trivy أو Snyk قبل الدفع للإنتاج
Cache BuildKit
DOCKER_BUILDKIT=1 docker build --cache-from type=registry,ref=registry/myapp:cache -t myapp:latest .
Cache موزّع عبر registry: builds CI أسرع، الفريق يتشارك الـ cache.
لماذا تذهب Docker / Podman أبعد من المستوى الأساسي في 2026
في 2026، حوسبة تطبيق في حاوية لم تعد كفاءة نادرة: كل مطور مبتدئ في الرياض، الدار البيضاء أو دبي كتب Dockerfile من قبل. ما يميز الملف الكبير هو إتقان التحسينات التي تنزل بالصورة من 1 جيغا إلى 80 ميغا، تُحصّن وقت التشغيل، وتمنع تسرّب الأسرار. هذا الدرس يستهدف هذا المستوى المتوسط-المتقدم: multi-stage builds، شبكات مخصّصة، أمان (rootless، capabilities، seccomp)، والتحوّل من Docker إلى Podman في السياقات التي يطرح فيها daemon root مشكلة.
عملياً، ستخرج بـ: Dockerfile multi-stage يُنتج صورة نهائية 80 ميغا لتطبيق Node.js، شبكة bridge مخصّصة لعزل خدماتك، حاوية rootless مع Podman، وثلاث تقنيات لفحص الأمان (Trivy، Docker Scout، Syft). ما يكفي لتقديم حصيلة تقنية مقنعة في مقابلة DevOps في الرياض، دبي أو القاهرة بأجر يوم بين 200 و400 USD لمهمة DevOps متقدمة.
الخطوة 1: Multi-stage build لصورة Node.js مصغّرة
صورة Node ساذجة على أساس node:20 تزن 1.1 جيغا. بـ multi-stage build وصورة distroless من Google، ننزل إلى 80 ميغا. المبدأ: stage build كامل (يجمّع TypeScript، يثبّت dev dependencies)، ثم stage runtime صغير جداً ينسخ فقط الأقطاب الضرورية.
# Stage 1: build
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: runtime
FROM gcr.io/distroless/nodejs22-debian12
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
CMD ["dist/index.js"]
ابنِ بـ docker build -t myapp:slim . ثم docker images myapp. المخرَج المنتظر يعرض حجماً حول 80-120 ميغا حسب المشروع. إن رأيت 500+ ميغا بعدُ، تحقق أن stage runtime لا يرث عرَضاً من node:22 بدل distroless.
الخطوة 2: تحسين cache طبقات Docker
cache Docker مبني على ترتيب التعليمات. قاعدة ذهبية: انسخ أولاً الملفات التي تتغير نادراً (package.json، requirements.txt)، ثم الكود المصدر. وإلا كل تعديل للكود يُبطل cache التبعيات والـ build يأخذ 5 دقائق بدل 5 ثوانٍ.
# سيئ: COPY . . قبل npm ci
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN npm ci
# كل تعديل في src/ يعيد تحميل node_modules
# جيد: package.json أولاً
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# node_modules في cache طالما لم يتغيّر package.json
المخرَج المنتظر في build ثانٍ: السطر RUN npm ci يجب أن يعرض CACHED. إن أعاد التنفيذ، فالـ cache مكسور — تحقق من ترتيب COPY.
الخطوة 3: شبكة bridge مخصّصة
شبكة bridge الافتراضية لـ Docker مشترَكة بين كل الحاويات بلا عزل وبلا حلّ DNS بالاسم. لمشروع متعدد الخدمات (app + db + cache)، bridge مخصّص لا غنى عنه. الحاويات تتحلّل فيه باسم خدمتها.
docker network create app-net
docker run -d --name postgres --network app-net -e POSTGRES_PASSWORD=secret postgres:16
docker run -d --name myapp --network app-net -e DATABASE_URL=postgresql://postgres:secret@postgres:5432/db myapp:slim
تحقّق من الاتصال بـ docker exec myapp ping -c 1 postgres. المخرَج المنتظر: 1 packets transmitted, 1 received, 0% packet loss. إن أخفق ping، فالحاويتان ليستا على الشبكة نفسها أو DNS الداخلي لـ Docker معطّل.
الخطوة 4: الأمان — runtime غير root وcapabilities
افتراضياً، حاوية Docker تشتغل كـ root من الداخل. ثغرة في التطبيق قد تُضخَّم حينئذٍ إذا سمحت CVE بالهروب. الحلّ: مستخدم غير root وإسقاط capabilities Linux غير الضرورية.
FROM node:22-alpine
RUN addgroup -S app && adduser -S app -G app
USER app
WORKDIR /home/app
COPY --chown=app:app package*.json ./
RUN npm ci --omit=dev
COPY --chown=app:app . .
CMD ["node", "index.js"]
شغّل بـ docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --security-opt=no-new-privileges myapp. تحقّق بـ docker exec myapp id: المخرَج يجب أن يُظهر uid=100(app) gid=101(app)، أبداً uid=0(root).
الخطوة 5: فحص الثغرات بـ Trivy
Trivy من Aqua Security يفحص صورة في ثوان ويسرد CVE حسب الخطورة. صار المعيار الفعلي في 2026، مدمجاً في معظم خطوط GitHub Actions وGitLab CI وForgejo Actions.
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image myapp:slim
المخرَج يسرد CVE حسب الفئة (CRITICAL، HIGH، MEDIUM، LOW). لـ build إنتاجي، ارفض الدمج إن رفع Trivy أي CVE CRITICAL أو HIGH غير قابلة للترقيع. نصيحة: استخدم --severity HIGH,CRITICAL --exit-code 1 في الخط لإفشال الـ build تلقائياً.
الخطوة 6: التحوّل إلى Podman للـ rootless الأصلي
Podman هو البديل المتوافق مع Docker الذي تروّج له Red Hat. الفرق الرئيسي: لا daemon، وكل الحاويات تشتغل rootless افتراضياً. للخوادم المشتركة (مختبر، إنتاج متعدد المستأجرين)، إنه حجّة أمنية كبرى. CLI متوافق: alias docker=podman ومعظم الأوامر تشتغل بلا تغيير.
sudo apt install -y podman
podman run --rm hello-world
podman build -t myapp:slim .
podman run -d --name myapp -p 8080:8080 myapp:slim
المخرَج المنتظر: hello-world يعرض الرسالة الكلاسيكية، ثم build وrun لتطبيقك يعملان بلا sudo. لا daemon Docker يشتغل — تحقّق بـ systemctl status docker الذي يجب أن يُرجع could not be found.
الخطوة 7: توليد SBOM بـ Syft
SBOM (Software Bill of Materials) يسرد كل مكوّنات صورة. مطلوب من مدقّقي الأمن (ISO 27001، شهادات PCI-DSS) وبعض العملاء enterprise في الخليج والمغرب العربي الذين يشترطون التتبّع. Syft من Anchore يولّده بـ JSON أو SPDX أو CycloneDX.
syft myapp:slim -o cyclonedx-json > sbom.json
cat sbom.json | jq '.components | length'
المخرَج المنتظر: عدد يمثّل مجموع المكوّنات (حزم npm، مكتبات نظام). احفظ هذا SBOM مع كل إصدار: عند CVE مستقبلية على مكتبة تابعة، تستطيع تتبّع أي إصدارات تطبيقك مصابة فوراً.
الخطوة 8: Pod متعدد الحاويات مع Podman
Podman الأصلي يُدير pods (مجموعات حاويات تتقاسم نفس namespace الشبكي)، مثل Kubernetes لكن محلياً. عملي لنشر ثلاثي app + db + cache على VPS متواضع دون تثبيت K8s.
podman pod create --name webpod -p 8080:8080
podman run -d --pod webpod --name redis redis:7-alpine
podman run -d --pod webpod --name app myapp:slim
تحقّق بـ podman pod ps: الـ pod يجب أن يعرض 3 حاويات (الـ pod نفسه + redis + app). التطبيق يرى redis على localhost:6379 لأنهما يتقاسمان نفس namespace الشبكي. لتصدير pod إلى manifest Kubernetes جاهز للنشر: podman generate kube webpod > webpod.yaml.
ملحق: دروس من هجرة Docker إلى Podman
ثلاثة فخاخ شائعة عند هجرة stack إنتاجي من Docker إلى Podman. الفخ 1: الحاويات rootless لا تستطيع ربط المنافذ المحظورة (< 1024) بلا capability خاصة. الحلّ: اربط على 8080 وضع reverse proxy عبر Caddy أو Nginx. الفخ 2: volumes المسمّاة لـ Docker غير قابلة للنقل إلى Podman. الحلّ: استخدم bind mounts صريحة على مسارات مطلقة.
الفخ 3: الصور الخاصة (registry GitLab، Forgejo) تطلب مصادقة مختلفة. podman login يخزّن الرمز في $XDG_RUNTIME_DIR/containers/auth.json بدل ~/.docker/config.json. تحقّق من هذا المسار إن أخفقت سحوبك بـ 401 Unauthorized.
ملحق: قائمة فحص الإنتاج لصورك
قبل دفع صورة إلى registry ونشرها في الإنتاج، تحقّق منهجياً من 8 نقاط: (1) Dockerfile multi-stage مع صورة distroless أو Alpine في runtime؛ (2) USER غير root صريح؛ (3) --cap-drop=ALL و--security-opt=no-new-privileges في runtime؛ (4) متغيّرات حساسة عبر secrets Docker أو متغيّرات بيئة محقونة في runtime، لا مكتوبة في Dockerfile؛ (5) HEALTHCHECK معلَن للسماح بالتعافي التلقائي؛ (6) labels OCI معيارية (org.opencontainers.image.source، .version، .created)؛ (7) صورة مفحوصة بـ Trivy، صفر CVE CRITICAL أو HIGH؛ (8) SBOM Syft مرفق بالإصدار.
ملحق: seccomp وملفات AppArmor
درجة فوق --cap-drop=ALL، نقيّد استدعاءات النظام بـ seccomp. Docker يوفر ملفاً افتراضياً يحجب 44 syscall خطيراً. للتعمّق، نكتب ملفاً مخصّصاً لا يسمح إلا بالـ syscalls التي يستخدمها التطبيق فعلاً. أداة عملية: strace -c node index.js تسرد syscalls الفعلية، نترجمها إلى JSON seccomp.
لـ AppArmor (جانب Ubuntu / Debian)، Docker يُحمّل docker-default تلقائياً. تحقّق بـ aa-status | grep docker-default. لملف مخصّص أصرم، ولّده بـ aa-genprof في وضع learning، ثم حمّله عبر --security-opt=apparmor=mon-profil. الجمع بين seccomp + AppArmor + capabilities يعطي sandbox قريباً من gVisor دون خسارة الأداء.
سلسلة دروس Docker / Podman
- Multi-stage builds: تحسين صور Docker
- Docker مقابل Podman: مقارنة نزيهة 2026
- Docker Compose في الإنتاج
- Docker rootless وأمان الحاويات