بيئات تشغيل الحاويات ومعيار OCI
بيئات تشغيل الحاويات ومعيار OCI
حين تُنفّذ الأمر docker run nginx، ربما تتخيّل أن Docker هو "الشيء الذي يُشغّل الحاويات". كان هذا التصوّر دقيقاً عام 2015، لكنّ البنية التحتية لبيئات الإنتاج اليوم أكثر تعقيداً بكثير. عُقد Kubernetes لا تتحدث مع Docker إطلاقاً — بل تتحدث مع containerd، الذي يتحدث مع runc، الذي يُنفّذ مجموعة من استدعاءات Linux للنظام. فهم هذه الطبقات ليس ترفاً أكاديمياً؛ بل يحدد أيّ علامات التشغيل يمكنك ضبطها، وكيف تُطبَّق سياسة الأمان، وأيّ بيئة تشغيل تختار حين تحتاج إلى عزل أقوى من الافتراضي.
المشكلة التي أوجدت معيار OCI
بحلول عام 2015، أصبح Docker مرادفاً للحاويات، غير أن بنيته المتكاملة أوجدت هشاشةً في النظام البيئي. أرادت كلٌّ من Kubernetes وCoreos rkt وغيرها تشغيل الحاويات، لكن لم يكن ثمة معيار موحّد — فكان كل طرف يُعيد تطوير صيغ الصور وسلوك بيئة التشغيل بشكل مستقل. أُسِّست مبادرة الحاويات المفتوحة (OCI)، تحت مظلة مؤسسة Linux، لحلّ هذه الإشكالية. وتُحدّد مواصفتين رئيسيتين:
- مواصفة صورة OCI — صيغة صورة الحاوية: بيان (manifest)، وطبقات نظام الملفات (tar)، وملف JSON للإعدادات يصف نقطة الدخول والبيئة وغيرها.
- مواصفة بيئة تشغيل OCI — ملف
config.jsonيصف كل ما يلزم لتشغيل حاوية: مسار نظام الملفات الجذر، وسيطات العملية، ومساحات الأسماء، وcgroups، والصلاحيات، وملف seccomp، ونقاط التحميل، والخطافات.
أي أداة تُنتج صورة OCI يمكن تشغيلها بأي بيئة تشغيل متوافقة مع OCI، وأي بيئة متوافقة يمكن توصيلها بأي منسّق متوافق. Docker وBuildah وKaniko وBuildKit كلها تُنتج صور OCI. أما runc وcrun وrunsc الخاص بـgVisor وKata Containers، فجميعها تُطبّق مواصفة بيئة تشغيل OCI.
الطبقات الكاملة لبيئة التشغيل
تستخدم مجموعات Kubernetes الحديثة ثلاث طبقات لبيئة التشغيل، لكل طبقة مهمة محددة:
containerd: بيئة التشغيل العالية المستوى
containerd مشروع متخرّج من CNCF وهو بيئة التشغيل الافتراضية في جميع خدمات Kubernetes المُدارة الكبرى (EKS, GKE, AKS). يُدير دورة حياة الحاويات كاملةً: سحب الصور وتخزينها (عبر نظام اللقطات)، وإعداد أنظمة ملفات overlay، وضبط شبكات CNI، وإدارة حالة الحاويات — لكنه لا يُنفّذ عملية الحاوية بنفسه؛ هذه المهمة منوطة ببيئة التشغيل منخفضة المستوى.
يُتيح containerd واجهة gRPC متوافقة مع CRI تتحدث إليها kubelet. يمكنك أيضاً التخاطب معه مباشرةً عبر أداة ctr (مستوى منخفض) أو الأكثر ودية nerdctl (واجهة سطر أوامر متوافقة مع Docker مدعومة بـcontainerd).
k8s.io؛ أوامر ctr المستقلة تستخدم default. دائماً حدّد -n k8s.io عند تصحيح أخطاء حاويات Kubernetes بأداة ctr.
runc: بيئة التشغيل المنخفضة المستوى OCI
runc هو التطبيق المرجعي لمواصفة بيئة تشغيل OCI، استُخرج من libcontainer الأصلية في Docker. إنه ملف Go صغير (~8 ميغابايت) يؤدي مهمة واحدة تحديداً: مُعطىً دليل يحتوي على rootfs/ وconfig.json، ينشئ فضاءات أسماء Linux، ويضبط حدود cgroup، ويُطبّق فلاتر seccomp ومجموعات ربط الصلاحيات، ثم يُنفّذ exec() على العملية. ينتهي بعدها تاركاً التحكم للـshim — لا يبقى في الذاكرة.
يمكنك استدعاء runc مباشرةً لفهم عمله أو لتصحيح حاوية متعطّلة:
أين يقع Docker اليوم؟
جرى إعادة هيكلة Docker (أداة سطر الأوامر والعفريت) اعتباراً من عام 2017. يُعدّ dockerd اليوم في جوهره طبقة تجربة مستخدم فوق containerd: يتولى Docker API وعمليات البناء عبر BuildKit وواجهة سطر الأوامر المألوفة. حين تُنفّذ docker run، تسير الاستدعاءات هكذا: أداة docker ← dockerd ← containerd ← shim ← runc ← النواة. على عُقد Kubernetes، لا يكون dockerd في المسار إطلاقاً — إذ تتحدث kubelet مباشرةً مع containerd عبر CRI.
NotReady بعد ترقية Kubernetes، تحقق من أن مسار مقبس CRI في إعداد kubelet يطابق بيئة التشغيل المثبّتة (/run/containerd/containerd.sock لـcontainerd، أو /var/run/crio/crio.sock لـCRI-O).
بيئات تشغيل بديلة: حين لا يكفي runc
يشترك runc في نواة المضيف — كل حاوية على العُقدة تستخدم النواة ذاتها. بالنسبة لأعباء العمل متعددة المستأجرين حيث لا يمكن الثقة الكاملة بعبء العمل (مثل خدمة CI عامة أو منصة serverless)، يُشكّل هذا إشكاليةً في حدود الأمان. ثمة بديلان جاهزان للإنتاج يعالجان هذا:
- gVisor (runsc) — بيئة تشغيل OCI من Google تضع نواةً وسيطة في فضاء المستخدم (تُسمى "Sentry") بين عملية الحاوية ونواة المضيف. استدعاءات النظام من الحاوية تصطدم بـSentry التي تُعيد تطبيق مجموعة واسعة من Linux بلغة Go. هذا يُقلّل بشكل جذري سطح هجوم نواة المضيف. عُقد GKE Sandbox تستخدم
runsc. حمل الأداء حقيقي (~10-20% لأعباء المعالج، أعلى لأعباء العمل الكثيفة باستدعاءات النظام). - Kata Containers — يُشغّل كل حاوية (أو Pod) داخل جهاز افتراضي خفيف الوزن يستخدم QEMU أو Cloud Hypervisor. عزل كامل على مستوى الجهاز الافتراضي؛ عملية الحاوية لا تلمس نواة المضيف أبداً. مستخدمة في Azure Confidential Containers. زمن بدء التشغيل أعلى (~1 ثانية مقابل ~100 ميلي ثانية لـrunc).
كلاهما متوافق مع OCI، لذا يمكن استخدامهما بديلاً عن runc داخل containerd بتسجيل RuntimeClass في Kubernetes:
runsc في بيئة التدريج قبل النشر للإنتاج.
اختيار بيئة التشغيل: مصفوفة القرار
على نطاق الشركات الكبرى، يُحدَّد اختيار بيئة التشغيل انطلاقاً من نموذج التهديد ونوع عبء العمل:
- أعباء عمل داخلية موثوقة على عُقد مخصصة —
runc(الافتراضي). أقل حمل، أوسع توافق. - SaaS متعدد المستأجرين أو CI عام —
runsc(gVisor) للمهام كثيفة المعالج/الذاكرة؛ Kata لكل ما يستلزم عزلاً قوياً على مستوى الجهاز الافتراضي. - البيئات الخاضعة للتنظيم (FedRAMP, PCI-DSS) — Kata Containers أو أجهزة افتراضية للحوسبة السرية؛ غالباً ما يشترط المدققون دليلاً على العزل على مستوى الأجهزة.
- الحافة / إنترنت الأشياء ذات الموارد المحدودة —
crun(إعادة تطبيق runc بلغة C، أسرع بـ15× في الإقلاع، وبصمة ذاكرة أقل بـ50×).
RuntimeClass في مواصفة Pod. هذه هي الفائدة العملية الملموسة من جهود توحيد معيار OCI.