حاويات Python الـ Distroless باستخدام uv: دليل

٤ مايو ٢٠٢٦

Distroless Python Containers with uv: 2026 Tutorial

ملخص

يستعرض هذا البرنامج التعليمي بناء حاوية Python جاهزة للإنتاج تشحن تطبيق FastAPI على بيئة تشغيل gcr.io/distroless/python3-debian12:nonroot من Google، مع حل التبعيات بواسطة uv 0.11.8 (تم إصداره في 27 أبريل 2026)1 في بناء Docker متعدد المراحل. ستنتهي بحاوية لا تحتوي على shell، ولا مدير حزم، وتعمل بمعرف المستخدم UID 65532، وتتم إعادة بنائها في ثوانٍ بفضل نقاط تثبيت ذاكرة التخزين المؤقت لـ BuildKit. الأمر برمته قابل للنسخ واللصق والتشغيل في حوالي خمس عشرة دقيقة على Docker Desktop أو أي مضيف Linux حديث.

ما ستتعلمه

تزيل حاوية Distroless Python مع uv سطح نظام التشغيل الذي تستغله معظم ثغرات CVE الإنتاجية: لا يوجد /bin/sh، ولا apt، ولا pip في وقت التشغيل، فقط الكود الخاص بك ومكتبة Python القياسية. في هذا البرنامج التعليمي، ستقوم بإنشاء هيكل خدمة FastAPI 0.136.12، وقفل تبعياتها باستخدام uv lock، وبناء صورة من مرحلتين تقوم بالترجمة المسبقة لـ Python bytecode من خلال نقاط تثبيت ذاكرة التخزين المؤقت لـ BuildKit، ونسخ البيئة الافتراضية الناتجة إلى مرحلة تشغيل Distroless، والتحقق من أن الصورة النهائية تعمل كمستخدم غير جذري (non-root) 65532 باستخدام Docker inspect. بعد ذلك، ستقوم بفحص الصورة باستخدام Trivy v0.70.03 لتأكيد خط أساس منخفض لثغرات CVE ومعرفة كيفية تصحيح أخطاء صورة Distroless عندما لا يكون هناك shell للدخول إليه.

المتطلبات الأساسية

قبل البدء، تأكد من أن بيئتك المحلية تطابق هذه الإصدارات. تفتقد محركات Docker القديمة بناء جملة تثبيت ذاكرة التخزين المؤقت (cache-mount) لـ BuildKit الذي يعتمد عليه هذا البرنامج التعليمي.

  • Docker Engine 23.0+ (Docker Desktop 4.19+ على macOS أو Windows) بحيث يكون BuildKit قيد التشغيل افتراضيًا ويكون بناء جملة --mount=type=cache متاحًا.
  • POSIX shell. تفترض الأمثلة أدناه استخدام bash/zsh على macOS أو Linux. على Windows، قم بتشغيلها داخل WSL2.
  • curl لمثبت uv.
  • حوالي 1 جيجابايت من مساحة القرص الحرة لذاكرة تخزين البناء المؤقتة والطبقات المتوسطة.
  • لا حاجة لتثبيت uv مسبقًا — الخطوة 1 تقوم بتثبيته.

نحن نلتزم بـ سلسلة Python 3.11 (أحدث إصدار هو 3.11.15، تم إصداره في 3 مارس 20264) لأن بيئة تشغيل gcr.io/distroless/python3-debian12 من Google تشحن Python 3.11 الخاص بـ Debian 12. مطابقة إصدار Python الفرعي للمُنشئ (builder) مع إصدار بيئة التشغيل هو ما يحافظ على عمل venv عند نسخها عبر المراحل — ستفشل venv الخاصة بـ Python 3.13 في البدء على بيئة تشغيل 3.11 لأن libpython3.13.so غير موجود في Distroless. إصدارات التصحيح (Patch versions) داخل 3.11.x مستقرة من حيث ABI، لذا فإن أي 3.11.x في المُنشئ سيكون جيدًا. أنت لا تحتاج إلى تثبيت Python على مستوى النظام لهذا البرنامج التعليمي، لأن uv سيتولى إدارة مفسرات Python نيابة عنك5.

الخطوة 1: تثبيت uv 0.11.8

تشحن شركة Astral أداة uv كملف ثنائي Rust ثابت واحد، لذا فإن التثبيت يتم بأمر واحد. حدد الإصدار صراحةً — استخدام latest محظور في البرامج التعليمية الخاصة بالإنتاج.

# macOS / Linux
curl -LsSf https://astral.sh/uv/0.11.8/install.sh | sh

# Verify
uv --version

المخرجات المتوقعة:

uv 0.11.8 (revision …)

إذا كان لديك بالفعل uv من إصدار سابق، فقم بتشغيل uv self update للانتقال إلى 0.11.8. كما أدى إصدار 0.11.7 في 15 أبريل 2026 إلى ترقية بناء CPython المرفق ليشمل ترقية أمنية لـ OpenSSL1، لذا يجب ترقية أي إصدار أقدم من 0.11.7 قبل المتابعة.

الخطوة 2: إنشاء هيكل مشروع FastAPI باستخدام uv

قم بإنشاء دليل مشروع جديد واترك uv يولد التخطيط.

mkdir distroless-fastapi-demo
cd distroless-fastapi-demo

uv init --python 3.11
uv add "fastapi==0.136.1" "uvicorn[standard]==0.46.0"

يقوم uv init بكتابة pyproject.toml، وتثبيت إصدار في .python-version، وملف hello.py. يقوم uv add بحل رسم بياني التبعيات مقابل Python الحالي ويكتب ملف قفل uv.lock. استبدل hello.py بتطبيق FastAPI بسيط:

# app.py
from fastapi import FastAPI

app = FastAPI(title="Distroless Demo", version="1.0.0")


@app.get("/healthz")
def healthz() -> dict[str, str]:
    return {"status": "ok"}


@app.get( "/")
def root() -> dict[str, str]:
    return {"message": "Hello from a Distroless Python container."}

قم بتشغيله محليًا للتأكد من أن كل شيء يعمل قبل وضعه في حاوية:

uv run uvicorn app:app --host 0.0.0.0 --port 8000

يجب أن يعيد curl http://localhost:8000/healthz القيمة {"status":"ok"}. أوقف الخادم باستخدام Ctrl+C.

الخطوة 3: إضافة ملف .dockerignore

يقوم Docker بنسخ سياق البناء الخاص بك إلى الـ daemon. يعد فقدان ملف .dockerignore هو السبب الأكبر وراء وصول الصور "الصغيرة" إلى مئات الميجابايت. أنشئ هذا الملف في جذر المشروع:

# .dockerignore
.git
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.pytest_cache
.mypy_cache
.ruff_cache
node_modules
.DS_Store
.env
.env.*
Dockerfile
.dockerignore
README.md

استبعاد .venv هو الأمر المهم — يقوم uv بإنشاء venv محلي لا تريد شحنه إلى daemon البناء.

الخطوة 4: كتابة Dockerfile متعدد المراحل

هذا هو جوهر البرنامج التعليمي. يتكون البناء من مرحلتين: مرحلة builder تعمل على python:3.11-slim-bookworm مع نسخ ملف uv 0.11.8 الثنائي من صورة Astral الرسمية، ومرحلة runtime تعتمد على gcr.io/distroless/python3-debian12:nonroot تنسخ فقط البيئة الافتراضية المبنية مسبقًا بالإضافة إلى كود التطبيق. تعمل كلتا المرحلتين على Debian 12 (bookworm)، لذا فإن إصدارات glibc و libstdc++ تتطابق تمامًا بين البناء ووقت التشغيل — وهو أمر مهم عندما تشحن أي من تبعيات Python الخاصة بك wheels مترجمة.

احفظ هذا كـ Dockerfile:

# syntax=Docker/dockerfile:1
# ---------- Stage 1: builder ----------
# Use the official Python 3.11 slim-bookworm image so the builder's
# Python series and glibc both match the Distroless python3-debian12
# runtime (both ship Debian 12's Python 3.11 and glibc 2.36).
# Astral's own ghcr.io/astral-sh/uv:*-python* images now default to
# trixie/Debian 13; mixing those with a debian12 runtime risks a
# glibc/libstdc++ mismatch at import time for any wheel that ships
# compiled C extensions.
FROM python:3.11-slim-bookworm AS builder

# Pull the uv 0.11.8 binaries straight from Astral's distroless image.
# This is Astral's recommended pattern for non-uv base images.
COPY --from=ghcr.io/astral-sh/uv:0.11.8 /uv /uvx /usr/local/bin/

# uv-specific environment for predictable Docker builds.
# UV_LINK_MODE=copy is required because the cache mount lives on a
# different filesystem than the target venv.
ENV UV_LINK_MODE=copy \
    UV_COMPILE_BYTECODE=1 \
    UV_PYTHON_DOWNLOADS=never \
    UV_PROJECT_ENVIRONMENT=/app/.venv

WORKDIR /app

# Copy lockfile + pyproject only. This layer is cached
# until either file changes, so source-only edits skip
# dependency resolution entirely.
COPY pyproject.toml uv.lock ./

# Install dependencies (no project code yet) into /app/.venv.
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-install-project --no-dev

# Now copy the app source and install the project itself.
COPY app.py ./
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-dev

# ---------- Stage 2: runtime ----------
FROM gcr.io/distroless/python3-debian12:nonroot

WORKDIR /app

# Copy the prebuilt venv and the application source from the builder.
# --chown=nonroot:nonroot sets ownership to UID:GID 65532:65532.
COPY --from=builder --chown=nonroot:nonroot /app/.venv /app/.venv
COPY --from=builder --chown=nonroot:nonroot /app/app.py /app/app.py

# Make the venv interpreter the default Python.
ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# Distroless ships with the "nonroot" account at UID:GID 65532.
# Use the numeric form so Kubernetes runAsNonRoot can verify it.
USER 65532:65532

EXPOSE 8000

# Distroless has no shell, so the command must be exec-form (a JSON array).
# Each element is passed directly to execve(2); there is no /bin/sh interpreting it.
CMD ["/app/.venv/bin/python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

تستحق بعض القواعد المضمنة في هذا الملف نظرة فاحصة. يستخدم المُنشئ python:3.11-slim-bookworm بدلاً من إحدى صور ghcr.io/astral-sh/uv:0.11.8-python... الجاهزة من Astral لأن Astral استبدلت صور ghcr القائمة على bookworm ببيئات trixie (Debian 13) الافتراضية في إصدار uv 0.9، ولا تزال Distroless python3-debian12 تعتمد على bookworm. يشحن Bookworm إصدار glibc 2.36؛ بينما يشحن trixie إصدار glibc 2.41 — تشغيل venv مبنية على trixie في بيئة تشغيل bookworm يمكن أن يفشل في وقت الاستيراد مع خطأ GLIBC_2.X not found إذا كانت أي wheel تشحن امتدادات C مرتبطة برموز glibc أحدث6. سحب ملف uv الثنائي مباشرة باستخدام COPY --from=ghcr.io/astral-sh/uv:0.11.8 هو مخرج Astral الموثق لهذه الحالة تحديدًا6. سطر UV_LINK_MODE=copy مطلوب عندما تعيش ذاكرة التخزين المؤقت لـ uv على نقطة تثبيت ذاكرة تخزين مؤقت لـ BuildKit لأن BuildKit يضع تلك الذاكرة على overlay منفصل، ويفشل وضع الارتباط الصلب (hardlink) الافتراضي لـ uv عبر أنظمة الملفات6. يقوم UV_COMPILE_BYTECODE=1 بترجمة كل .py مسبقًا إلى .pyc أثناء البناء حتى لا يدفع الطلب الأول لحاويتك تكلفة البداية الباردة6. بناء جملة --mount=type=cache خاص بـ BuildKit فقط ويقوم بالحفاظ على ذاكرة تخزين wheels المؤقتة لـ uv عبر عمليات البناء دون تضخيم أي طبقة نهائية للصورة7.

الخطوة 5: بناء الصورة باستخدام BuildKit

Docker build -t distroless-fastapi-demo:1.0.0 .

يقوم البناء الأول بحل رسم بياني التبعيات وتنزيل الـ wheels (أقل من ثلاثين ثانية على محطة عمل عادية). في البناء الثاني مع عدم وجود تغييرات في الكود المصدري، يتم تقديم طبقة التبعية بالكامل من ذاكرة تخزين BuildKit المؤقتة وينتهي البناء في ثوانٍ معدودة. مع تغيير ملف app.py فقط، يتم إعادة بناء الطبقتين الأخيرتين فقط.

لفحص تفاصيل الطبقات، استخدم Docker history:

Docker history distroless-fastapi-demo:1.0.0

يجب أن ترى عمليتي نسخ منفصلتين من مرحلة البناء (builder stage) ولا توجد طبقات تحمل فهارس apt أو gcc أو أدوات بناء — لا يوجد أي منها في مرحلة التشغيل (runtime stage) على الإطلاق.

الخطوة 6: التحقق — التشغيل، الفحص، والمعاينة

قم بتشغيل الحاوية واختبر نقاط النهاية (endpoints) للتأكد من أنها تخدم حركة المرور بالفعل.

Docker run -d --name distroless-demo -p 8000:8000 distroless-fastapi-demo:1.0.0

# انتظر ثانية واحدة لربط uvicorn، ثم افحص:
curl -sS http://localhost:8000/healthz
# {"status":"ok"}

curl -sS http://localhost:8000/
# {"message":"Hello from a Distroless Python container."}

الآن تأكد من أن مستخدم وقت التشغيل هو 65532، وليس root:

Docker inspect distroless-demo --format '{{.Config.User}}'
# 65532:65532

Docker exec distroless-demo /app/.venv/bin/python -c "import os; print(os.getuid(), os.getgid())"
# 65532 65532

تؤكد المخرجات الرقمية أن العملية تعمل كمستخدم nonroot المحدد داخل Distroless8. هذا هو بالضبط ما تتطلبه سياسة securityContext.runAsNonRoot: true في Kubernetes — ولأن المستخدم رقمي، يمكن لـ Kubernetes التحقق من حالة "غير الجذر" (non-root) دون الحاجة لجدول بحث UID8.

قم بإزالة الحاوية قبل المتابعة:

Docker rm -f distroless-demo

الخطوة 7: فحص الصورة بحثًا عن الثغرات الأمنية باستخدام Trivy

تقلل قاعدة Distroless من سطح الهجوم، لكنها لا تضمن عدم وجود ثغرات أمنية (CVEs) — تظل ملفات Python wheels و CPython نفسه يشحنان كودًا برمجياً. يعد Trivy من Aqua Security أحد أكثر الماسحات الضوئية مفتوحة المصدر اعتمادًا لصور الحاويات؛ قم بتثبيت الإصدار v0.70.0، وهو أول إصدار بعد حادثة سلسلة التوريد الخاصة بـ Aqua في مارس 20263. تم التأكد لاحقًا من اختراق الإصدارات 0.69.4 و 0.69.5 و 0.69.6 ويجب عدم استخدامها3.

# تثبيت Trivy v0.70.0 (Linux/macOS، brew tap يثبت الإصدار الآمن):
brew install aquasecurity/trivy/trivy

# أو سحب الصورة الرسمية مباشرة:
Docker pull aquasec/trivy:0.70.0

# فحص الصورة التجريبية:
Docker run --rm -v /var/run/Docker.sock:/var/run/Docker.sock \
    aquasec/trivy:0.70.0 image distroless-fastapi-demo:1.0.0

يقرأ Trivy البيانات التعريفية لحزم الصورة مقابل قواعد بيانات الثغرات الأمنية المستمدة من NVD، وتنبيهات GitHub، و Debian، و Red Hat OVAL، وغيرها، ثم يطبع جدول CVE مجمعًا حسب الخطورة. في بناء جديد مع الإصدارات المذكورة في هذا البرنامج التعليمي، عادةً ما تبلغ طبقة وقت تشغيل Distroless عن عدد قليل من ثغرات CVE عالية الخطورة أو لا تبلغ عن أي منها — معظم النتائج، إن وجدت، ستكون في تبعيات تطبيق Python الخاص بك، حيث يمكنك إصلاحها عن طريق تحديث الإصدارات في pyproject.toml وإعادة تشغيل uv lock.

الخطوة 8: تصحيح أخطاء صورة Distroless عند حدوث خلل

لا تحتوي Distroless على غلاف (shell). يفشل الأمر Docker exec -it container /bin/sh على الفور لأن /bin/sh غير موجود في صورة وقت التشغيل — يعيد وقت تشغيل OCI رمز خروج غير صفري (عادةً 126 أو 127 اعتمادًا على إصدار وقت التشغيل وخطوة البحث التي تفشل أولاً)9. هناك حلان عمليان.

استخدام وسم :debug. تشحن Google إصدارًا gcr.io/distroless/python3-debian12:debug-nonroot يضيف غلاف busybox مع الحفاظ على نفس تخطيط المكتبات. لبناء تصحيح أخطاء لمرة واحدة، استبدل سطر FROM في مرحلة وقت التشغيل بوسم debug وأعد البناء — سيبقى ملف Dockerfile الخاص بك دون تغيير بخلاف ذلك.

استخدام حاويات تصحيح الأخطاء المؤقتة (ephemeral debug containers). في Kubernetes 1.25+، يقوم الأمر kubectl debug -it my-pod --image=busybox --share-processes --copy-to=my-pod-debug بإرفاق sidecar من نوع busybox يشارك مساحات الأسماء (namespaces) الخاصة بحاوية Distroless الأصلية. ستحصل على غلاف يمكنه تنفيذ ls /proc/1/root/app لمعاينة نظام ملفات Distroless دون إعادة بناء الصورة.

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

الأخطاء الشائعة

failed to compute cache key أثناء البناء. أنت تستخدم إصدارًا من محرك Docker أقدم من 23.0 أو أن BuildKit معطل. قم بتشغيل DOCKER_BUILDKIT=1 Docker build . أو قم بترقية Docker Desktop إلى إصدار يشحن المحرك 23.0+.

uv sync --frozen يفشل مع رسالة lockfile out of date. تم إنشاء ملف uv.lock الخاص بك مقابل إصدار Python مختلف أو ملف pyproject.toml مختلف. قم بتشغيل uv lock محليًا (بدون --frozen) وقم بتسليم (commit) ملف القفل المعاد إنشاؤه، ثم أعد البناء.

تخرج الحاوية فورًا مع مخرجات قليلة أو معدومة من التطبيق. عندما لا يتمكن وقت تشغيل OCI من تنفيذ CMD الخاص بك، فإنك تحصل عادةً على رسالة على مستوى وقت التشغيل في Docker logs (مثل exec: "python": executable file not found in $PATH) ولكن لا يوجد تتبع أخطاء (traceback) من Python، لأن المترجم لم يبدأ أبدًا. السبب الجذري الأكثر شيوعًا هو استخدام صيغة shell لـ CMD (مثل CMD python -m uvicorn ...) — لا يوجد /bin/sh في Distroless لتفسيره. استخدم دائمًا صيغة exec (مثل CMD ["python", "-m", "uvicorn", ...]).

OSError: [Errno 30] Read-only file system. تحاول بعض مكتبات Python الكتابة في site-packages وقت الاستيراد. نظام ملفات Distroless قابل للكتابة في كل مكان افتراضيًا، ولكن خيار readOnlyRootFilesystem: true في Kubernetes سيؤدي إلى هذا الخطأ. قم بتركيب emptyDir في المسار المسبب للمشكلة (غالبًا /tmp أو ~/.cache) في مواصفات الـ pod الخاصة بك.

uv: command not found داخل حاوية وقت التشغيل. لا تتضمن Distroless أداة uv في وقت التشغيل — وهذا مقصود. تعيش uv فقط في مرحلة البناء. تستخدم مرحلة وقت التشغيل البيئة الافتراضية (venv) المبنية مسبقًا مباشرة عبر /app/.venv/bin/python.

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

الصورة التي بنيتها للتو هي خيار افتراضي قوي لأي خدمة ويب Python، ولكن هناك ثلاثة توسعات واضحة تستحق الاستكشاف لاحقًا. أولاً، استبدل وقت تشغيل python3-debian12 بصورة Python المستندة إلى Wolfi من Chainguard إذا كان فريقك يحتاج إلى إصدار Python أحدث مما يشحنه Debian 12 ودورة تصحيح ثغرات CVE بنظام الإصدارات المتدحرجة (rolling-release). ثانياً، إذا كانت خدمتك تحتاج إلى HTTP/2 (الذي لا ينهيه uvicorn حالياً بشكل أصلي)، فاستبدل uvicorn بـ hypercorn واتبع نفس شكل Dockerfile — ستقوم uv بمعالجة التبديل كتغيير في سطر واحد في pyproject.toml. ثالثاً، أضف خطوة SBOM حقيقية: مرر --format spdx-json إلى Trivy وانشر النتيجة بجانب الصورة حتى يتمكن المستهلكون اللاحقون من مراجعة التبعيات دون إعادة الفحص.

لمزيد من الخلفية العميقة حول جانب وقت التشغيل، يغطي منشورنا السابق حول بناء خلفيات برمجية للذكاء الاصطناعي فائقة السرعة باستخدام FastAPI أنماط الـ async، والبث (streaming)، وضبط uvicorn التي تتماشى بشكل طبيعي مع نشر Distroless. تمتد فلسفة تحصين الحاويات هنا أيضًا إلى أعباء العمل غير المتعلقة بـ Python — راجع دليل أفضل ممارسات أمان Kubernetes للتعرف على عناصر التحكم على مستوى العنقود (cluster) التي تقترن بصورة غير جذرية وبدون غلاف.

Footnotes

  1. ملاحظات إصدار uv 0.11.8، Astral، 27 أبريل 2026: https://GitHub.com/astral-sh/uv/releases. تضمن إصدار uv 0.11.7 (15 أبريل 2026) ترقية أمنية لـ OpenSSL في بناء CPython 20260414. 2

  2. إصدار FastAPI 0.136.1، 23 أبريل 2026: https://GitHub.com/fastapi/fastapi/releases. قام الإصدار بترقية Starlette إلى 1.0.0 وأضاف فحصًا صارمًا لـ Content-Type في طلبات JSON.

  3. مناقشة حادثة Trivy الأمنية رقم 10425 وإصدار v0.70.0، Aqua Security، بتاريخ 17 أبريل 2026: https://GitHub.com/aquasecurity/trivy/discussions/10425 و https://GitHub.com/aquasecurity/trivy/releases. تم تأكيد اختراق الإصدارات v0.69.4 و v0.69.5 و v0.69.6 وسحبها. 2 3

  4. إصدار Python 3.11.15، بتاريخ 3 مارس 2026 — آخر إصدار مجدول لإصلاح الأخطاء لنسخة 3.11 وفقاً لـ PEP 664: https://www.python.org/downloads/release/python-31115/ و https://peps.python.org/pep-0664/. أحدث إصدارات Python بشكل عام وقت الكتابة هي 3.13.13 و 3.14.4 (كلاهما في 7 أبريل 2026)، ولكن صورة Distroless python3-debian12 تأتي مع Python 3.11 الخاص بـ Debian 12، لذا يثبت هذا البرنامج التعليمي الإصدار عند 3.11.15 للحفاظ على توافق البيئة الافتراضية venv مع صورة وقت التشغيل.

  5. وثائق إدارة مفسر uv: https://docs.astral.sh/uv/concepts/python-versions/.

  6. دليل تكامل uv مع Docker، بما في ذلك UV_LINK_MODE، و UV_COMPILE_BYTECODE، وأنماط cache-mount: https://docs.astral.sh/uv/guides/integration/Docker/. 2 3 4

  7. مرجع Docker BuildKit cache-mount: https://docs.Docker.com/build/cache/optimize/.

  8. مستخدم Distroless غير الجذر (UID:GID 65532:65532) وتفاعل Kubernetes runAsNonRoot: https://GitHub.com/GoogleContainerTools/distroless/issues/443 و https://GitHub.com/GoogleContainerTools/distroless/blob/main/SUPPORT_POLICY.md. 2

  9. اتفاقيات أكواد الخروج في Linux: 126 ("تم العثور على الأمر ولكنه غير قابل للتنفيذ") مقابل 127 ("الأمر غير موجود"). موثق في دليل مرجع GNU Bash: https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html. تعيين أكواد الخروج في Docker: https://docs.Docker.com/reference/cli/Docker/container/run/#exit-status.


نشرة أسبوعية مجانية

ابقَ على مسار النيرد

بريد واحد أسبوعياً — دورات، مقالات معمّقة، أدوات، وتجارب ذكاء اصطناعي.

بدون إزعاج. إلغاء الاشتراك في أي وقت.