Docker من الصفر إلى الاحتراف: 100 مثال مع Dockerfile وCompose وCI/CD

Docker من الصفر إلى الاحتراف: 100 مثال مع Dockerfile وCompose وCI/CD

تجمع Docker التطبيق وكل ما يحتاجه داخل حاوية، فتعمل البرمجيات نفسها بالطريقة نفسها على الحاسوب المحمول والخادم والسحابة. تأخذك هذه المقالة من أول حاوية إلى إعداد يراعي بيئة الإنتاج عبر 100 مثال. ستتعلم واجهة سطر أوامر Docker، والصور والسجلات، وكتابة Dockerfile مع بناء متعدد المراحل، والأحجام والشبكات، وDocker Compose لتطبيقات متعددة الحاويات، والإعدادات والأسرار، وحدود الموارد وفحوص الصحة، وأمان الحاويات، وتصحيح الأخطاء، وخطوط CI/CD، ونظرة أولى على Kubernetes وSwarm. ملاحظة صريحة: لم يكن Docker مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة، لذلك لم تُنفَّذ الأمثلة المئة هنا. كُتبت كل الأوامر والملفات وفق الصيغة الرسمية لـ Docker وتم التحقق منها بالقراءة، ويجب أن تشغّلها بنفسك قبل الاعتماد عليها.

ما الذي تحتاجه

Docker Desktop على Windows أو macOS، أو Docker Engine على Linux مع إضافة Docker Compose (يجب أن يعمل الأمر docker compose version). يُفضَّل محرر مثل Visual Studio Code مع إضافة Docker. تكفي معرفة أساسية بسطر الأوامر للبدء. تستخدم بعض الأمثلة مشروع Python أو Node.js نموذجياً، وبعضها يستخدم سجلاً خاصاً، لذلك استخدم عناصر نائبة مثل registry.example.com ولا تضع كلمات مرور حقيقية في الأوامر.

كيفية استخدام المثال

تستخدم أمثلة bash واجهة Docker CLI وDocker Compose. تُحفظ أمثلة Dockerfile في ملف اسمه Dockerfile داخل مجلد المشروع. تُحفظ أمثلة YAML في compose.yaml أو في ملف Kubernetes المذكور في النص.

الموضوع 1: البدء مع Docker

الحاوية عملية معزولة لها نظام ملفات وشبكة خاصان بها، وتُبنى من صورة. تعلم المفاهيم الأساسية، وتحقق من التثبيت، وشغّل أولى حاوياتك.

الصور والحاويات والسجلات

الصورة قالب للقراءة فقط مكوّن من طبقات. الحاوية نسخة تعمل من الصورة. السجل مثل Docker Hub أو Azure Container Registry يخزن الصور حتى تتمكن الأجهزة الأخرى من تنزيلها.

لماذا تفيد الحاويات

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

التحقق من التثبيت

شغّل docker version لعرض معلومات العميل والخادم. إذا لم يظهر سطر الخادم فمعنى ذلك أن Docker Desktop أو خدمة Docker لا تعمل بعد، فشغّلها قبل تنفيذ أي أمر حاويات.

رسوم توضيحية

كيف تنقل Docker التطبيق من Dockerfile إلى حاوية تعمل: يُبنى Dockerfile إلى صورة، وتُرفع الصورة إلى سجل، ثم يسحبها المضيف ويشغّلها كحاويات
كيف تنقل Docker التطبيق من Dockerfile إلى حاوية تعمل: يُبنى Dockerfile إلى صورة، وتُرفع الصورة إلى سجل، ثم يسحبها المضيف ويشغّلها كحاويات

مثال 1: التحقق من تثبيت Docker

تأكد من أن العميل يستطيع الوصول إلى محرك Docker.

docker --version
docker info --format "server: {{.ServerVersion}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 2: تشغيل hello-world

أول حاوية لك، تُنزَّل وتعمل بأمر واحد.

docker run hello-world

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 3: سحب صورة

تنزيل الصورة دون تشغيل حاوية.

docker pull nginx:1.27

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 4: عرض الصور المحلية

معرفة الصور المخزنة على هذا الجهاز.

docker images
docker image ls --filter "reference=nginx"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 5: فحص صورة

قراءة بنية الصورة ونظام التشغيل الخاص بها.

docker image inspect nginx:1.27 --format "arch={{.Architecture}} os={{.Os}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 6: فحص استخدام القرص

معرفة المساحة التي تأخذها الصور والحاويات والأحجام.

docker system df

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 2: تشغيل الحاويات وإدارتها

يُنشئ الأمر docker run حاوية من صورة. ستنشر المنافذ، وتقرأ السجلات، وتنفذ أوامر داخل الحاوية، وتنظف الحاويات التي لم تعد تحتاجها.

وضع الخلفية والوضع التفاعلي

-d يشغّل الحاوية في الخلفية، و-it يربطها بالطرفية. الخيار --rm يحذف الحاوية عند توقفها، وهو مناسب للتجارب القصيرة.

المنافذ والأسماء

الصيغة -p مضيف:حاوية تنشر منفذاً، فالأمر 8080:80 يوجّه المنفذ 8080 على المضيف إلى المنفذ 80 داخل الحاوية. الخيار --name يعطي الحاوية اسماً واضحاً تستخدمه في الأوامر اللاحقة.

دورة الحياة والتنظيف

تحتفظ الحاويات المتوقفة ببياناتها حتى تحذفها. استخدم docker ps -a لعرضها وdocker rm لحذفها، ونظّف بانتظام على الأجهزة التي تبني كثيراً.

مثال 7: تشغيل حاوية في الخلفية

تشغيل حاوية منفصلة وعرضها.

docker run -d --name web nginx:1.27
docker ps

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 8: نشر منفذ

الوصول إلى خادم الويب داخل الحاوية من المضيف.

docker run -d --name web-8080 -p 8080:80 nginx:1.27
curl http://localhost:8080

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 9: تشغيل صدفة مؤقتة

استخدام --rm لحذف الحاوية عند الخروج.

docker run -it --rm alpine:3.20 sh

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 10: عرض الحاويات

إظهار الحاويات العاملة والمتوقفة.

docker ps
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 11: قراءة سجلات الحاوية

عرض آخر الأسطر أو متابعة المخرجات مباشرة.

docker logs --tail 50 web
docker logs -f web

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 12: تشغيل أمر داخل حاوية

فتح صدفة أو تنفيذ أمر واحد داخل حاوية عاملة.

docker exec -it web sh
docker exec web cat /etc/nginx/nginx.conf

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 13: الإيقاف والبدء وإعادة التشغيل

التحكم في دورة حياة الحاوية.

docker stop web
docker start web
docker restart web

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 14: حذف الحاويات

حذف الحاويات التي لم تعد تحتاجها.

docker rm -f web web-8080
docker container prune -f

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 3: الصور والسجلات

للصورة اسم ووسم وdigest. الوسم يمكن أن يتغير أما الـ digest فلا يتغير، لذلك تحتاج إليهما معاً لوضع الوسم على البرمجيات ومشاركتها وإعادة إنتاجها بدقة.

الوسوم والـ digest

latest مجرد وسم يتحرك كلما حدّثه الناشر. أما الـ digest، وهو هاش طويل يبدأ بـ sha256، فيحدد صورة واحدة بعينها، وهذه هي الطريقة لجعل النشر قابلاً لإعادة الإنتاج.

الدفع إلى سجل

سجّل الدخول، ثم أعطِ الصورة وسماً يتضمن عنوان السجل الكامل وادفعها. تستطيع الأجهزة الأخرى حينها سحب الصورة نفسها بالاسم. لا تدفع أبداً صوراً تحتوي على أسرار.

قراءة سجل الصورة

يعرض docker history كل طبقة والتعليمة التي أنشأتها وحجمها. وهو يساعد في العثور على طبقات كبيرة بشكل غير طبيعي والتأكد من عدم كتابة كلمة مرور داخل الصورة.

مثال 15: وسم صورة

إعطاء الصورة اسماً وإصداراً خاصين بك.

docker tag nginx:1.27 mycompany/nginx:1.0.0
docker images mycompany/nginx

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 16: دفع صورة إلى سجل

رفع الصورة حتى تتمكن الأجهزة الأخرى من سحبها.

docker login registry.example.com
docker tag mycompany/nginx:1.0.0 registry.example.com/team/nginx:1.0.0
docker push registry.example.com/team/nginx:1.0.0

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 17: الدفع إلى Azure Container Registry

تسجيل الدخول بواجهة Azure CLI ثم الدفع إلى السجل.

az acr login --name <registry-name>
docker tag mycompany/nginx:1.0.0 <registry-name>.azurecr.io/nginx:1.0.0
docker push <registry-name>.azurecr.io/nginx:1.0.0

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 18: تشغيل سجل محلي

تشغيل سجل خاص على جهازك للاختبار.

docker run -d -p 5000:5000 --restart unless-stopped --name registry registry:2
docker tag nginx:1.27 localhost:5000/nginx:1.27
docker push localhost:5000/nginx:1.27

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 19: تثبيت الصورة بالـ digest

الـ digest بصمة لا تتغير، على عكس الوسم.

docker pull nginx:1.27
docker image inspect nginx:1.27 --format "{{index .RepoDigests 0}}"
docker run -d nginx@sha256:<digest-from-the-line-above>

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 20: قراءة طبقات الصورة

معرفة الأمر الذي أنشأ كل طبقة وحجمها.

docker history nginx:1.27
docker history --no-trunc --format "{{.Size}}\t{{.CreatedBy}}" nginx:1.27

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 4: كتابة Dockerfile

Dockerfile وصفة نصية. كل تعليمة تُنشئ طبقة، لذلك يحدد ترتيب التعليمات ومحتواها الحجم وسرعة إعادة البناء وما يعمل عند بدء الحاوية.

التعليمات الأساسية

تختار FROM صورة الأساس. تضيف COPY وRUN الملفات والأوامر. يحدد WORKDIR المجلد للتعليمات اللاحقة. تحدد CMD وENTRYPOINT ما تشغّله الحاوية.

الفرق بين ARG وENV

يوجد ARG فقط أثناء بناء الصورة. أما ENV فيبقى داخل الصورة ويكون مرئياً للحاوية العاملة. استخدم ARG لاختيارات البناء وENV للإعدادات التي يقرأها البرنامج.

البناء والوسم

يرسل الأمر docker build -t الاسم:الوسم . المجلد إلى أداة البناء. تُستخدم فقط الملفات التي ينسخها Dockerfile، لذلك أبقِ سياق البناء صغيراً بملف .dockerignore.

مثال 21: Dockerfile بسيط

أبسط وصفة مفيدة: صورة أساس وأمر واحد.

FROM alpine:3.20
CMD ["echo", "Hello from a container"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 22: بناء الصورة وتشغيلها

تحويل Dockerfile إلى صورة وتشغيلها.

docker build -t hello-image:1.0 .
docker run --rm hello-image:1.0

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 23: Dockerfile لتطبيق Python

تثبيت المتطلبات ونسخ الكود إلى صورة خفيفة.

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 24: Dockerfile لتطبيق Node.js

تثبيت حزم الإنتاج وكشف المنفذ.

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 25: ARG وENV

ضبط قيم وقت البناء ومتغيرات البيئة وقت التشغيل.

FROM alpine:3.20
ARG APP_VERSION=dev
ENV APP_VERSION=$APP_VERSION
ENV APP_PORT=8080
CMD ["sh", "-c", "echo version=$APP_VERSION port=$APP_PORT"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 26: تمرير معاملات البناء

تغيير الإصدار وقت البناء دون تعديل الملف.

docker build --build-arg APP_VERSION=1.4.2 -t vars-image:1.4.2 .
docker run --rm -e APP_PORT=9000 vars-image:1.4.2

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 27: WORKDIR وCOPY

تحديد مجلد العمل ونسخ الملفات إلى الصورة.

FROM alpine:3.20
WORKDIR /srv/site
COPY ./site/ ./
# ADD can unpack archives, but COPY is clearer for plain files
RUN ls -la /srv/site

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 28: ENTRYPOINT وCMD

اختيار البرنامج الثابت ووسائطه الافتراضية.

FROM alpine:3.20
RUN apk add --no-cache curl
ENTRYPOINT ["curl", "-s"]
CMD ["https://example.com"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 5: أفضل الممارسات للصور

الصور الجيدة صغيرة، وقابلة لإعادة الإنتاج، وسريعة البناء، وآمنة للتشغيل. تغطي البنايات متعددة المراحل وترتيب الطبقات وتثبيت الإصدارات والمستخدم غير root وفحوص الصحة معظم الممارسات.

البناء متعدد المراحل

تحتوي مرحلة البناء على المترجمات والأدوات، وتنسخ المرحلة النهائية النتيجة فقط. تبقى الصورة النهائية صغيرة ولا تحتوي على الشيفرة المصدرية أو أدوات البناء التي قد يستغلها المهاجم.

تخزين الطبقات مؤقتاً

تعيد Docker استخدام الطبقة عندما لا تتغير تعليمتها ومدخلاتها، وتعيد بناء كل ما يليها بعد أول تغيير. انسخ ملفات الاعتماديات وثبّتها قبل نسخ الشيفرة المصدرية.

المستخدمون والإصدارات والفحوص

شغّل التطبيق بمستخدم رقمي غير root، وثبّت إصدارات صور الأساس، وأضف وسوم المالك والإصدار، واستخدم HEALTHCHECK حتى تستطيع Docker الإبلاغ عن أن التطبيق يعمل فعلاً.

مثال 29: استبعاد الملفات بـ .dockerignore

إبقاء الأسرار وبيانات Git والحزم خارج البناء.

cat > .dockerignore <<'EOF'
.git
node_modules
*.log
.env
EOF
docker build -t app:1.0 .

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 30: بناء متعدد المراحل لـ Go

التجميع في صورة كبيرة وشحن البرنامج المترجم فقط.

FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod ./
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app .

FROM gcr.io/distroless/static-debian12
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 31: بناء متعدد المراحل لواجهة أمامية

بناء الملفات ثم تقديمها عبر Nginx.

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 32: التشغيل بمستخدم غير root

تقليل الضرر إذا تعرض التطبيق لهجوم.

FROM python:3.12-slim
RUN useradd --create-home --uid 10001 appuser
WORKDIR /home/appuser/app
COPY --chown=appuser:appuser . .
USER appuser
CMD ["python", "app.py"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 33: تثبيت إصدارات الصور الأساسية

تجنب المفاجآت من وسم latest المتغير.

# Avoid "latest": it changes without warning
FROM python:3.12.5-slim-bookworm
# Pin system packages when the build must be reproducible
RUN apt-get update && apt-get install -y --no-install-recommends curl=8.* \
 && rm -rf /var/lib/apt/lists/*

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 34: ترتيب الطبقات للاستفادة من الذاكرة المؤقتة

نسخ ملفات الحزم أولاً ليبقى البناء سريعاً.

FROM node:20-alpine
WORKDIR /app
# Dependencies change less often than source code, so copy them first
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["npm", "start"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 35: إضافة وسوم للصورة

تسجيل العنوان والإصدار والمالك داخل الصورة.

FROM alpine:3.20
LABEL org.opencontainers.image.title="sample-api" \
      org.opencontainers.image.version="1.4.2" \
      org.opencontainers.image.maintainer="[email protected]"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 36: HEALTHCHECK

إخبار Docker بأن التطبيق يعمل بشكل سليم.

FROM nginx:1.27-alpine
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget -qO- http://localhost/ || exit 1

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 6: الأحجام والبيانات الدائمة

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

الأحجام المسماة

يديرها Docker ويخزنها خارج الحاوية. استخدمها لقواعد البيانات، فالبيانات تبقى عند حذف الحاوية، ويمكن أخذ نسخة احتياطية منها بحاوية مساعدة.

ربط المجلدات

يربط bind mount مجلداً من حاسوبك بالحاوية. وهو مفيد أثناء التطوير لأن تغييرات الشيفرة تظهر فوراً، لكنه يمنح الحاوية وصولاً مباشراً إلى ملفات المضيف، لذلك استخدم :ro إذا كانت القراءة فقط مطلوبة.

النسخ الاحتياطي والبيانات المؤقتة

انسخ الحجم احتياطياً بتشغيل حاوية صغيرة تركّب الحجم وتكتب ملف tar. استخدم tmpfs للملفات المؤقتة التي يجب أن تعيش في الذاكرة وتختفي عند توقف الحاوية.

مثال 37: إنشاء حجم مسمى

حفظ البيانات التي يجب أن تبقى بعد حذف الحاوية.

docker volume create app-data
docker volume ls
docker volume inspect app-data

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 38: استخدام حجم مسمى مع قاعدة بيانات

حفظ بيانات PostgreSQL بأمان عند إعادة تشغيل الحاوية.

docker run -d --name db -e POSTGRES_PASSWORD=<strong-password> \
  -v app-data:/var/lib/postgresql/data postgres:16
docker exec db psql -U postgres -c "select 1"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 39: ربط مجلد من المضيف

مشاركة مجلد من جهازك أثناء التطوير.

docker run --rm -it -v "$(pwd)/site:/usr/share/nginx/html:ro" -p 8081:80 nginx:1.27-alpine

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 40: ربط للقراءة فقط

السماح للحاوية بقراءة الإعدادات دون تعديلها.

docker run --rm -v /srv/config:/etc/app:ro alpine:3.20 ls -l /etc/app

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 41: نسخ احتياطي لحجم

أرشفة بيانات الحجم في ملف tar بحاوية مساعدة.

docker run --rm -v app-data:/data -v "$(pwd):/backup" alpine:3.20 \
  tar czf /backup/app-data-backup.tgz -C /data .

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 42: استعادة حجم

إعادة البيانات المؤرشفة إلى حجم جديد.

docker volume create app-data-restored
docker run --rm -v app-data-restored:/data -v "$(pwd):/backup" alpine:3.20 \
  tar xzf /backup/app-data-backup.tgz -C /data

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 43: tmpfs للبيانات المؤقتة

حفظ الملفات المؤقتة في الذاكرة وحذفها عند الإيقاف.

docker run --rm --tmpfs /tmp:rw,size=64m alpine:3.20 sh -c "echo scratch > /tmp/x && cat /tmp/x"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 7: شبكات الحاويات

تمنح Docker الحاويات شبكتها الخاصة. تتيح الشبكات المعرّفة من المستخدم للحاويات أن تجد بعضها بالاسم، أما نشر المنافذ فيحدد الخدمات التي يمكن الوصول إليها من الخارج.

شبكة الجسر والشبكات المعرّفة من المستخدم

تمنح شبكة الجسر الافتراضية الحاويات عناوين IP فقط. أما شبكة الجسر المعرّفة من المستخدم فتوفر DNS أيضاً، فتصل حاوية إلى أخرى باسمها، مثل http://api أو redis://cache.

نشر المنافذ بأمان

تُنشر المنافذ افتراضياً على كل الواجهات. اربط المنفذ بـ 127.0.0.1 لتبقي قاعدة البيانات خاصة بحاسوبك. الأمر EXPOSE يوثّق المنفذ فقط ولا يفتحه.

عدة شبكات

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

مثال 44: عرض الشبكات وفحصها

معرفة الشبكات التي أنشأها Docker ونطاقات عناوينها.

docker network ls
docker network inspect bridge --format "{{range .IPAM.Config}}{{.Subnet}}{{end}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 45: إنشاء شبكة مخصصة

منح الحاويات المرتبطة شبكة خاصة بها.

docker network create app-net
docker run -d --name api --network app-net nginx:1.27
docker run --rm --network app-net alpine:3.20 wget -qO- http://api

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 46: العثور على الحاويات بالاسم

استخدام أسماء الحاويات كأسماء مضيفين داخل الشبكة.

docker run -d --name cache --network app-net redis:7-alpine
docker run --rm --network app-net redis:7-alpine redis-cli -h cache ping

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 47: ربط حاوية بشبكة ثانية

السماح لخدمة واحدة بالوصول إلى شبكتين منفصلتين.

docker network create back-net
docker network connect back-net api
docker inspect api --format "{{json .NetworkSettings.Networks}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 48: النشر على واجهة واحدة فقط

جعل قاعدة البيانات متاحة من هذا الجهاز فقط.

docker run -d -p 127.0.0.1:5432:5432 --name pg -e POSTGRES_PASSWORD=<strong-password> postgres:16

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 49: EXPOSE مقابل النشر

EXPOSE للتوثيق فقط، أما -p فيجعل المنفذ متاحاً من المضيف.

FROM node:20-alpine
# EXPOSE only documents the port; it does not open it to the host
EXPOSE 3000
CMD ["node", "server.js"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 50: استخدام شبكة المضيف

مشاركة مكدس شبكة المضيف في Linux لحالات خاصة.

docker run --rm --network host alpine:3.20 sh -c "ip addr show eth0 | head -3"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 8: أساسيات Docker Compose

يصف Compose تطبيقاً متعدد الحاويات في ملف YAML واحد. أمر واحد يشغّل كل خدمة وشبكة وحجم في المشروع ويوقفها ويفحصها.

ملف compose

يسرد المفتاح services كل حاوية. لكل خدمة قسم image أو build، إضافة إلى ports وenvironment وvolumes وnetworks. ينشئ Compose شبكة مشتركة للمشروع تلقائياً.

الأوامر

يشغّل docker compose up -d المشروع، ويعرض docker compose ps حالته، ويتبع docker compose logs -f المخرجات، ويوقف docker compose down الحاويات ويحذفها مع الشبكة الافتراضية.

ملفات التجاوز

يحتوي compose.yaml على الإعدادات المشتركة، ويُدمج compose.override.yaml تلقائياً. احتفظ بالتعديلات المحلية في ملف التجاوز حتى يبقى الملف الرئيسي متطابقاً في كل بيئة.

مثال 51: ملف Compose بسيط

وصف خدمة ويب في ملف YAML واحد.

services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 52: تشغيل مشروع Compose وإيقافه

تشغيل تطبيق متعدد الحاويات كاملاً أو إيقافه.

docker compose up -d
docker compose ps
docker compose down

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 53: بناء صورة داخل Compose

جعل Compose يبني الصورة من Dockerfile.

services:
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    ports:
      - "3000:3000"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 54: متغيرات البيئة في Compose

ضبط إعدادات كل خدمة داخل الملف.

services:
  api:
    build: ./api
    environment:
      APP_ENV: development
      LOG_LEVEL: info

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 55: انتظار خدمة أخرى

استخدام depends_on للتحكم في ترتيب البدء.

services:
  api:
    build: ./api
    depends_on:
      - db
  db:
    image: postgres:16

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 56: الأحجام المسماة في Compose

الاحتفاظ بملفات قاعدة البيانات عبر إعادة تشغيل المشروع.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: <strong-password>
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 57: السجلات وexec والأوامر لمرة واحدة

تشغيل الاختبارات أو الأدوات داخل المشروع.

docker compose logs -f api
docker compose exec db psql -U postgres
docker compose run --rm api npm test

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 58: ملفات override للتطوير

فصل إعدادات الإنتاج عن التعديلات المحلية.

# compose.yaml holds the shared settings
services:
  api:
    build: ./api
# compose.override.yaml is merged automatically on "docker compose up"
services:
  api:
    volumes:
      - ./api:/app
    environment:
      DEBUG: "1"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 9: Compose لتطبيقات حقيقية

تحتوي التطبيقات الحقيقية على طبقة ويب وقاعدة بيانات وذاكرة مؤقتة وبروكسي. يربطها Compose معاً، وينتظر جاهزيتها، ويتيح توسيع الأجزاء التي تحتاج سعة أكبر.

الويب وقاعدة البيانات والذاكرة المؤقتة

امنح كل خدمة مهمة واحدة، واحفظ ملفات قاعدة البيانات في حجم مسمى، واصل إلى كل خدمة باسمها. مرّر سلسلة الاتصال عبر متغير بيئة بدلاً من كتابة اسم المضيف في الشيفرة.

ترتيب البدء والصحة

لا ينتظر depends_on إلا بدء الحاوية. أضف healthcheck إلى قاعدة البيانات واستخدم condition: service_healthy حتى يبدأ التطبيق بعد أن تقبل قاعدة البيانات الاتصالات.

التوسيع والعزل

يغيّر docker compose up --scale عدد نسخ الخدمة عديمة الحالة. افصل الشبكات بحيث يستطيع البروكسي العام الوصول إلى الواجهة البرمجية، لكن لا يستطيع الوصول إلى قاعدة البيانات مباشرة.

مثال 59: الملفات الشخصية للخدمات الاختيارية

تشغيل أدوات الإدارة عند الطلب فقط.

services:
  api:
    build: ./api
  pgadmin:
    image: dpage/pgadmin4
    profiles: ["tools"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 60: تشغيل profile

تفعيل خدمة اختيارية من سطر الأوامر.

docker compose up -d
docker compose --profile tools up -d

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 61: تطبيق ويب مع PostgreSQL

تطبيق كامل بقاعدة بيانات وبيانات دائمة.

services:
  web:
    build: ./web
    ports:
      - "8000:8000"
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: shop
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: <strong-password>
    volumes:
      - pg-data:/var/lib/postgresql/data
volumes:
  pg-data:

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 62: إضافة Redis كذاكرة مؤقتة

الاتصال بمخزن مفتاح-قيمة سريع باستخدام اسم الخدمة.

services:
  web:
    build: ./web
    environment:
      REDIS_URL: redis://cache:6379
  cache:
    image: redis:7-alpine
    command: ["redis-server", "--save", "", "--appendonly", "no"]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 63: Nginx كوكيل عكسي

توجيه حركة الويب إلى التطبيق عبر نقطة دخول واحدة.

services:
  proxy:
    image: nginx:1.27-alpine
    ports:
      - "80:80"
    volumes:
      - ./proxy/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app
  app:
    build: ./app

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 64: الانتظار حتى تصبح قاعدة البيانات سليمة

تشغيل التطبيق بعد أن تقبل قاعدة البيانات الاستعلامات.

services:
  api:
    build: ./api
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: <strong-password>
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 10: الإعدادات والأسرار

تتغير الإعدادات بين البيئات، ويجب ألا تنتهي الأسرار داخل صورة أو في Git. استخدم متغيرات البيئة وملفات env وملفات الأسرار، واقرأها بأمان في برنامجك.

متغيرات البيئة وملفات .env

يقرأ Compose ملف .env لاستبدال المتغيرات داخل ملف compose. أضف .env إلى .gitignore، وارفع ملف .env.example يحمل قيماً نائبة حتى يعرف المطورون الآخرون ما يجب ضبطه.

الأسرار كملفات

يُركَّب السر المبني على الملفات تحت /run/secrets داخل الحاوية. القراءة من ملف تُبقي القيمة خارج مخرجات docker inspect وقوائم العمليات، وهي أماكن قد تتسرب إليها متغيرات البيئة.

لا تُدمج الأسرار في الصور أبداً

تبقى القيمة المنسوخة إلى طبقة في سجل الصورة حتى لو حذفتها خطوة لاحقة. مرّر الأسرار وقت التشغيل، وافحص السجل بـ docker history، وغيّر أي سر دُفع إلى مكان ما في أي وقت.

مثال 65: توسيع نطاق خدمة

تشغيل عدة نسخ من عامل المعالجة.

docker compose up -d --scale worker=3
docker compose ps worker

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 66: شبكات الواجهة الأمامية والخلفية

إبقاء قاعدة البيانات بعيدة عن الشبكة العامة.

services:
  proxy:
    image: nginx:1.27-alpine
    networks: [front]
    ports: ["80:80"]
  api:
    build: ./api
    networks: [front, back]
  db:
    image: postgres:16
    networks: [back]
networks:
  front:
  back:

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 67: تحميل المتغيرات من .env

إبقاء القيم المحلية خارج ملف Compose.

cat > .env <<'EOF'
POSTGRES_PASSWORD=<strong-password>
APP_PORT=8000
EOF
docker compose config

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 68: استخدام env_file

تحميل مجموعة كاملة من الإعدادات من ملف.

services:
  api:
    build: ./api
    env_file:
      - ./config/api.env

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 69: ملف سر في Compose

تمرير كلمة المرور إلى الخدمة دون وضعها في الصورة.

services:
  api:
    build: ./api
    secrets:
      - db_password
secrets:
  db_password:
    file: ./secrets/db_password.txt

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 70: قراءة الإعدادات في Python

استخدام القيم الافتراضية وتحويل نصوص البيئة إلى أرقام.

import os

db_host = os.environ.get("DB_HOST", "localhost")
db_port = int(os.environ.get("DB_PORT", "5432"))
print(f"connect to {db_host}:{db_port}")

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 71: قراءة ملف السر في Python

تحميل كلمة المرور من مسار السر داخل الحاوية.

from pathlib import Path

secret_file = Path("/run/secrets/db_password")
password = secret_file.read_text(encoding="utf-8").strip()
print("password length:", len(password))

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 72: الإعدادات وقت التشغيل

تجاوز القيم في تشغيل واحد وفحص سجل الصورة.

docker run --rm -e LOG_LEVEL=debug -e DB_HOST=db.internal app:1.0
docker history --no-trunc app:1.0 | grep -i password || echo "no password in the layers"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 11: الموارد والصحة والسجلات

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

حدود الذاكرة والمعالج

يوقف الخيار --memory الحاوية التي تستهلك ذاكرة RAM أكثر من اللازم، ويحد الخيار --cpus حصتها من وقت المعالج. اضبط الحدود بناءً على الاستهلاك المقاس بـ docker stats بدلاً من التخمين.

سياسات إعادة التشغيل

يعيد unless-stopped تشغيل الحاوية بعد تعطل أو إعادة تشغيل المضيف، لكنه لا يعيدها إذا أوقفتها يدوياً. استخدمه للخدمات طويلة التشغيل، وأصلح سبب الأعطال المتكررة بدلاً من الاعتماد على إعادة التشغيل.

السجلات والصحة

تحتفظ Docker بمخرجات الحاوية في مشغّل السجلات. حدّد حجم json-file بالخيارين max-size وmax-file. تُظهر حالة الصحة starting أو healthy أو unhealthy، ويستطيع موازن الأحمال والمنسّق استخدامها.

مثال 73: حد الذاكرة

منع عملية خارجة عن السيطرة من استهلاك كل الذاكرة.

docker run -d --name api-mem --memory 256m --memory-swap 256m nginx:1.27-alpine
docker stats --no-stream api-mem

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 74: حد المعالج

إعطاء الحاوية نصف نواة معالج.

docker run -d --name api-cpu --cpus 0.5 nginx:1.27-alpine

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 75: سياسة إعادة التشغيل

إعادة تشغيل الخدمة بعد تعطل أو إعادة تشغيل الجهاز.

docker run -d --name worker --restart unless-stopped app:1.0
docker inspect worker --format "{{.HostConfig.RestartPolicy.Name}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 76: تحديد حجم ملفات السجل

منع ملفات السجل من ملء القرص.

docker run -d --name api-logs \
  --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 nginx:1.27-alpine

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 77: قراءة حالة الصحة

معرفة ما إذا كانت الحاوية سليمة أو تبدأ أو غير سليمة.

docker ps --format "table {{.Names}}\t{{.Status}}"
docker inspect --format "{{.State.Health.Status}}" api

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 78: تنظيف الكائنات غير المستخدمة

حذف الحاويات المتوقفة والصور المعلقة بأمان.

docker container prune -f
docker image prune -f
docker volume ls -qf dangling=true

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 12: أمان الحاويات

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

أقل الصلاحيات

شغّل الحاوية بمستخدم غير root، واسقط قدرات Linux التي لا تحتاجها بـ --cap-drop ALL، وأعد إضافة القدرات المحددة المطلوبة فقط. استخدم no-new-privileges لمنع العمليات من اكتساب صلاحيات إضافية.

أنظمة الملفات للقراءة فقط

يمنع الخيار --read-only الحاوية من تغيير ملفاتها. وفّر تركيبات tmpfs للمجلدات القليلة التي يجب أن تكون قابلة للكتابة، مثل /tmp.

الفحص والتحديث

افحص الصور بـ Docker Scout أو أداة أخرى، وأعد البناء بانتظام للحصول على صور أساس مُصحَّحة، وأوقف البناء عند العثور على ثغرات حرجة. لا تشغّل حاوية بالخيار --privileged إلا لسبب محدد.

مثال 79: إسقاط صلاحيات Linux

إزالة الصلاحيات التي لا يحتاجها التطبيق.

docker run -d --name locked --cap-drop ALL --cap-add NET_BIND_SERVICE -p 80:80 nginx:1.27-alpine

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 80: نظام ملفات جذري للقراءة فقط

منع الحاوية من الكتابة في ملفات النظام الخاصة بها.

docker run -d --name ro-app --read-only --tmpfs /tmp --tmpfs /var/run nginx:1.27-alpine

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 81: التشغيل بمعرّف مستخدم رقمي

اختيار معرّف المستخدم بدلاً من الاعتماد على الاسم.

docker run --rm --user 10001:10001 alpine:3.20 id

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 82: منع تصعيد الصلاحيات

منع العمليات من اكتساب صلاحيات إضافية.

docker run -d --name hardened --security-opt no-new-privileges:true nginx:1.27-alpine

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 83: فحص الصورة بحثاً عن الثغرات

البحث عن ثغرات CVE معروفة في الحزم داخل الصورة.

docker scout cves nginx:1.27-alpine
docker scout quickview nginx:1.27-alpine

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 84: إعدادات أمان في Compose

دمج خيارات التقوية في خدمة واحدة.

services:
  api:
    build: ./api
    user: "10001:10001"
    read_only: true
    cap_drop: [ALL]
    security_opt: ["no-new-privileges:true"]
    tmpfs: [/tmp]

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 13: تصحيح الأخطاء وحل المشكلات

عند فشل حاوية تكون الإجابات في سجلاتها وبيانات inspect وأحداثها وعملياتها. يجد روتين منظم معظم المشكلات خلال دقائق.

السجلات والأحداث وinspect

اقرأ docker logs أولاً. يعرض docker events ما فعلته Docker ومتى، ويعرض docker inspect رمز الخروج والمتغيرات والتركيبات والشبكة، فتستطيع مقارنة الحاوية العاملة بما تتوقعه.

فحص الموارد والعمليات

يعرض docker stats المعالج والذاكرة لكل حاوية، ويسرد docker top العمليات. غالباً ما يعني رمز الخروج 137 أن النواة أنهت العملية بسبب استهلاك ذاكرة مفرط، ويمكنك تأكيد ذلك من OOMKilled.

تصحيح الشبكة

شغّل صورة أدوات في مساحة الأسماء الشبكية نفسها باستخدام --network container:الاسم، ثم استخدم curl وdig وping من الداخل. هذا يختبر الرؤية الفعلية للحاوية للشبكة دون تغيير أي شيء.

مثال 85: متابعة أحداث Docker

مراقبة بدء الحاويات وإيقافها وفشلها في الوقت الفعلي.

docker events --filter type=container --since 10m

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 86: مراقبة استهلاك الموارد

عرض المعالج والذاكرة لكل حاوية.

docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 87: عرض العمليات داخل الحاوية

معرفة ما يعمل داخلها دون فتح صدفة.

docker top web

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 88: قراءة حقل واحد من inspect

الحصول على رمز الخروج ومعرفة ما إذا أنهى النواة العملية.

docker inspect web --format "{{.State.ExitCode}} {{.State.OOMKilled}}"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 89: نسخ الملفات من الحاوية وإليها

جلب ملف إعدادات أو وضع إصلاح داخل الحاوية.

docker cp web:/etc/nginx/nginx.conf ./nginx.conf
docker cp ./nginx.conf web:/etc/nginx/nginx.conf

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 90: التصحيح بصورة أدوات الشبكة

مشاركة شبكة الحاوية واختبارها بـ curl وdig.

docker run --rm -it --network container:web nicolaka/netshoot
# inside the tools shell: curl -v http://localhost:80

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 14: خطوط CI/CD للصور

يبني الخط الصورة ويختبرها ويفحصها ويدفعها في كل مرة تتغير فيها الشيفرة. تجعل الأتمتة كل صورة قابلة للتتبع إلى commit، وتبقي الإصدارات متطابقة من تشغيل إلى آخر.

البناء والدفع في CI

يسجّل الخط الدخول باستخدام بيانات اعتماد قصيرة العمر، ويبني الصورة، ويشغّل الاختبارات، ثم يدفعها بوسم يتضمن الـ commit. تعمل الخطوات نفسها في GitHub Actions وAzure Pipelines.

استراتيجية الوسم

ضع على كل صورة وسم commit في Git لتتبعها، ولا تنقل وسم الإصدار إلا عند اعتماد الإصدار. تجنّب الاعتماد على latest في الإنتاج لأنه يُخفي الشيفرة التي تعمل فعلاً.

نظافة العاملات

تتراكم الطبقات والأحجام على عُمّال CI. نظّف ذاكرة البناء المؤقتة وفق جدول زمني، وحدد حجماً أقصى لها، حتى لا يمتلئ قرص آلة البناء فيفشل بشكل غير متوقع.

مثال 91: البناء والدفع في GitHub Actions

أتمتة بناء الصورة مع كل دفعة إلى main.

name: build-image
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/example/app:${{ github.sha }}

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 92: بناء صورة في Azure Pipelines

استخدام مهمة Docker مع اتصال الخدمة.

trigger:
  - main
pool:
  vmImage: ubuntu-latest
steps:
  - task: Docker@2
    inputs:
      command: buildAndPush
      containerRegistry: my-acr-connection
      repository: sample-api
      tags: |
        $(Build.BuildId)

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 93: وسم الصورة بتزامن Git

تتبع كل صورة إلى الشيفرة المصدرية المحددة.

SHA=$(git rev-parse --short HEAD)
docker build -t app:$SHA .
docker tag app:$SHA app:latest
echo "built app:$SHA"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 94: تنظيف ذاكرة البناء على خادم CI

منع نفاد القرص على جهاز البناء.

docker builder prune --filter until=72h -f
docker image prune -a --filter until=168h -f

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

الموضوع 15: الإنتاج والتنسيق

يكفي مضيف واحد لخدمة صغيرة. عندما تحتاج إلى عدة أجهزة وإصلاح ذاتي وتحديثات متدرجة، يشغّل منسّق مثل Kubernetes أو Docker Swarm الحاويات عنك.

قائمة مراجعة الإنتاج

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

أساسيات Kubernetes

يُبقي Deployment عدداً من النسخ المتماثلة قيد التشغيل ويجري تحديثات متدرجة. يمنح Service النسخ عنواناً ثابتاً واحداً. تعمل صورة الحاوية نفسها دون تغيير في Compose وفي Kubernetes.

Swarm ومتى تستخدمه

يستخدم Docker Swarm ملفاً شبيهاً بـ Compose في صيغة stack، ويشغّل الخدمات عبر عدة أجهزة. ولـ Kubernetes منظومة أوسع وهو شائع في الخدمات السحابية المُدارة، فاختر الأداة التي يستطيع فريقك تشغيلها.

مثال 95: إعدادات Compose للإنتاج

تثبيت الإصدار وتحديد الذاكرة وإعادة التشغيل وفحص الصحة.

services:
  api:
    image: registry.example.com/team/api:1.4.2
    restart: unless-stopped
    env_file: ./config/api.env
    ports:
      - "127.0.0.1:8000:8000"
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 3s
      retries: 3

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 96: Deployment في Kubernetes

تشغيل ثلاث نسخ من الصورة نفسها في المجموعة.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: registry.example.com/team/api:1.4.2
          ports:
            - containerPort: 8000
          resources:
            limits:
              memory: 512Mi
              cpu: "500m"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 97: Service في Kubernetes

منح النسخ عنواناً ثابتاً داخل المجموعة.

apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 8000
  type: ClusterIP

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 98: نشر مكدس Docker Swarm

تشغيل الخدمات نفسها عبر عدة أجهزة.

docker swarm init
docker stack deploy -c stack.yml shop
docker service ls

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 99: فحص الحاويات بـ Docker SDK

قراءة حالات الحاويات من كود Python.

import docker

client = docker.from_env()
for c in client.containers.list():
    state = c.attrs["State"].get("Health", {}).get("Status", "no healthcheck")
    print(c.name, state)

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

مثال 100: نص قائمة مراجعة الإصدار

إيقاف الإصدار إذا كانت الصورة تعمل بصلاحية root أو بها ثغرات حرجة.

#!/usr/bin/env bash
set -euo pipefail
IMAGE="$1"
docker image inspect "$IMAGE" > /dev/null
USER_ID=$(docker run --rm --entrypoint id "$IMAGE" -u)
[ "$USER_ID" != "0" ] || { echo "image runs as root"; exit 1; }
docker scout cves --exit-code --only-severity critical "$IMAGE"
echo "release checks passed for $IMAGE"

*لم يُشغَّل هذا المثال هنا لأن Docker لم يكن مثبتاً على الحاسوب الذي أُعدّت به هذه المقالة.*

المختبر: بناء تطبيق ويب صغير وتشغيله ونشره

اجمع المقالة كلها في مشروع واحد: تطبيق ويب صغير، وDockerfile ببناء متعدد المراحل، وملف Compose فيه قاعدة بيانات وحجم، وفحوص الصحة، وفحص قبل الإصدار.

1. أنشئ مجلداً فيه تطبيق صغير وملف requirements.txt، ثم اكتب Dockerfile يشغّل التطبيق بمستخدم غير root ويتضمن HEALTHCHECK.

2. أضف ملف .dockerignore وابنِ الصورة بوسم إصدار، مثل docker build -t lab-web:1.0.0 .

3. اكتب compose.yaml فيه خدمة الويب وخدمة PostgreSQL تستخدم حجماً مسمى وhealthcheck، ثم شغّله بـ docker compose up -d.

4. تحقق من حالة الصحة بـ docker compose ps واقرأ السجلات بـ docker compose logs -f web.

5. شغّل docker scout cves على الصورة، وأصلح ما تستطيع بتحديث صورة الأساس، ثم أعد البناء.

6. شغّل قائمة المراجعة قبل الإصدار من المثال 100 على صورتك، وأوقف الإصدار إذا فشلت.

7. أوقف المشروع بـ docker compose down، ولا تحذف الحجم المسمى إلا عندما لا تحتاج بياناته.

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

بعد المختبر، اقرأ الوثائق الرسمية لـ Docker لبيئة التشغيل التي اخترتها، وتدرب في مشروع منفصل، وتعلّم منسّقاً واحداً بعمق عندما تحتاج خدمتك إلى أكثر من جهاز. احتفظ بأمثلة هذه المقالة في مستودع حتى تعيد تشغيلها بعد كل تحديث لـ Docker.

الخلاصة

تحوّل Docker التطبيق إلى صورة تعمل بالطريقة نفسها في كل مكان. تعلمت تشغيل الحاويات وإدارتها، وتثبيت الصور بالوسوم والـ digest، وكتابة Dockerfile فعال بالبناء متعدد المراحل، وحفظ البيانات في الأحجام، وربط الخدمات بالشبكات، ووصف التطبيق كاملاً بـ Compose، والتعامل بأمان مع الإعدادات والأسرار، وتحديد الموارد، وتقوية الحاويات، وتصحيح الأخطاء بشكل منهجي، وأتمتة البناء في CI/CD، والاستعداد لتنسيق الإنتاج.