ما تحتاج معرفته عن Docker

ما تحتاج معرفته عن Docker

في المقال السابق، قدّمت شرحًا مفصلاً لمفهوم Pods، وهو الأهم في مشروع Kubernetes. في مقال اليوم، سأشارك المزيد من التفاصيل حول كائنات Pod.

Pods مقابل الآلات الافتراضية

حتى الآن، يجب أن تكون واضحًا تمامًا أن Pod هو أصغر وحدة تنسيق في مشروع Kubernetes، وليس الحاوية. ينعكس هذا التصميم في كائنات API، حيث تصبح الحاوية مجرد حقل عادي داخل سمات Pod. وبطبيعة الحال، يطرح السؤال: أي السمات تنتمي إلى كائن Pod، وأيها تنتمي إلى الحاوية؟

يلعب Pod دور “الآلة الافتراضية” في بيئات النشر التقليدية. هذا التصميم يهدف إلى جعل الانتقال من البيئات التقليدية (بيئات الآلات الافتراضية) إلى Kubernetes (بيئات الحاويات) أكثر سلاسة.

إذا اعتبرنا Pod هو “الآلة” في البيئة التقليدية، والحاوية هي “برنامج المستخدم” الذي يعمل على هذه “الآلة”، فإن العديد من جوانب تصميم كائن Pod تصبح أسهل في الفهم.

على سبيل المثال، السمات المتعلقة بالجدولة، الشبكات، التخزين، والأمان تكون أساسًا على مستوى Pod. السمة المشتركة لهذه السمات هي أنها تصف “الآلة” ككل، وليس “البرامج” التي تعمل داخلها.

على سبيل المثال، تكوين بطاقة الشبكة للـ “آلة” (أي تعريف شبكة Pod)، تكوين قرص الـ “آلة” (أي تعريف تخزين Pod)، وتكوين جدار الحماية للـ “آلة” (أي تعريف أمان Pod). ناهيك عن تحديد الخادم الذي ستعمل عليه هذه “الآلة” (أي جدولة Pod).

YAML في Kubernetes

في Kubernetes، نحدد موارد Pod من خلال ملفات تعريفية.

NodeSelector: هذا حقل يسمح للمستخدم بربط Pod بعقدة Node، كما هو موضح أدناه:

apiVersion: v1
kind: Pod
...
spec:
  nodeSelector:
    disktype: ssd

مثل هذا التكوين يعني أن هذا Pod يمكنه العمل فقط على عقدة تحمل وسم “disktype: ssd”؛ وإلا فسيفشل في الجدولة.

NodeName: بمجرد تعيين هذا الحقل لـ Pod، يعتبر مشروع Kubernetes أن Pod قد تمت جدولته، ونتيجة الجدولة هي اسم العقدة المعينة. لذلك، يتم تعيين هذا الحقل عادةً بواسطة المجدول، ولكن يمكن للمستخدمين أيضًا تعيينه “لخداع” المجدول، على الرغم من أن هذه الممارسة تُستخدم عادةً فقط أثناء الاختبار أو التصحيح.

HostAliases: يحدد محتوى ملف hosts الخاص بـ Pod (مثل /etc/hosts)، كما هو موضح أدناه:

apiVersion: v1
kind: Pod
...
spec:
  hostAliases:
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
...

تجدر الإشارة إلى أنه في مشروع Kubernetes، إذا كنت تريد تعيين محتوى ملف hosts، فيجب القيام بذلك من خلال هذه الطريقة. وإلا، إذا قمت بتعديل ملف hosts مباشرةً، فسيقوم kubelet تلقائيًا بالكتابة فوق المحتوى المعدل بعد حذف Pod وإعادة إنشائه.

بالإضافة إلى التكوينات المتعلقة بـ “الآلة” المذكورة أعلاه، قد تجد أيضًا أن أي سمات متعلقة بـ Linux Namespace للحاوية تكون أيضًا على مستوى Pod. هذا أيضًا سهل الفهم: تصميم Pod هو السماح للحاويات الموجودة داخله بمشاركة أكبر عدد ممكن من Linux Namespaces، مع الاحتفاظ فقط بقدرات العزل والتقييد الضرورية. بهذه الطريقة، يكون التأثير الذي يحاكيه Pod مشابهًا جدًا للعلاقة بين البرامج في آلة افتراضية.

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]

هذا ملف YAML لـ Pod من وثائق Kubernetes الرسمية. إنه بسيط جدًا في الواقع، فقط يعرّف حاوية باستخدام صورة nginx. ومع ذلك، في قسم containers في ملف YAML هذا، سترى أن الحاوية تحتوي على معلمات postStart و preStop.

ماذا يعني هذا؟

لنبدأ بـ postStart، والذي يشير إلى عملية يتم تنفيذها فور بدء تشغيل الحاوية. يجب أن يكون واضحًا أن العملية المعرفة بـ postStart تُنفَّذ بعد ENTRYPOINT لحاوية Docker، لكنها لا تضمن الترتيب بدقة. أي، عندما يبدأ postStart، قد لا يكون ENTRYPOINT قد انتهى بعد.

بالطبع، إذا انتهت مهلة تنفيذ postStart أو واجه خطأ، سيقوم Kubernetes بالإبلاغ عن رسالة خطأ تفيد بفشل بدء الحاوية في قسم Events الخاص بذلك Pod، مما يؤدي إلى وضع Pod في حالة فاشلة أيضًا.

وبالمثل، توقيت preStop يحدث قبل إنهاء الحاوية (على سبيل المثال، عندما تتلقى إشارة SIGKILL). من المهم توضيح أن تنفيذ عملية preStop متزامن. لذلك، فإنه يحجب عملية إنهاء الحاوية الحالية حتى تكتمل العملية المعرفة، وهذا يختلف عن postStart.

لذا، في هذا المثال، بعد بدء الحاوية بنجاح، تكتب “رسالة ترحيب” إلى /usr/share/message (أي العملية المعرفة بـ postStart). وقبل حذف هذه الحاوية، نستدعي أمر الخروج لـ nginx (أي العملية المعرفة بـ preStop)، وبذلك نحقق “إيقافًا رشيقًا” للحاوية.

بعد أن أصبحت على دراية بالحقول الرئيسية لـ Pod وقسم الحاوية الخاص به، سأشارك دورة حياة كائن Pod هذا في Kubernetes.

تظهر تغييرات دورة حياة Pod بشكل رئيسي في جزء Status من كائن API الخاص بـ Pod، وهو حقله الثالث المهم بعد Metadata و Spec. من بينها، pod.status.phase يمثل الحالة الحالية للـ Pod، مع الحالات المحتملة التالية:

  1. Pending. هذه الحالة تعني أن ملف YAML الخاص بـ Pod قد تم تقديمه إلى Kubernetes، وتم إنشاء كائن API وحفظه في Etcd. ومع ذلك، لا يمكن إنشاء بعض الحاويات في هذا Pod بسلاسة لسبب ما. على سبيل المثال، فشل الجدولة.

  2. Running. في هذه الحالة، تمت جدولة Pod بنجاح وربطه بعقدة محددة. جميع الحاويات التي يحتويها تم إنشاؤها بنجاح، وواحدة على الأقل قيد التشغيل حاليًا.

  3. Succeeded. هذه الحالة تعني أن جميع الحاويات في Pod قد اكتمل تشغيلها وخرجت. هذا الوضع شائع عند تشغيل المهام لمرة واحدة.

  4. Failed. في هذه الحالة، خرجت حاوية واحدة على الأقل في Pod بحالة غير طبيعية (رمز إرجاع غير صفري). ظهور هذه الحالة يعني أنك بحاجة إلى معرفة كيفية تصحيح أخطاء التطبيق في الحاوية، مثل التحقق من Events وسجلات Pod.

  5. Unknown. هذه حالة غير طبيعية، تشير إلى أن حالة Pod لا يمكن إبلاغها باستمرار بواسطة kubelet إلى kube-apiserver، ويرجع ذلك غالبًا إلى مشكلة اتصال بين master و Kubelet.

علاوة على ذلك، يمكن تفصيل حقل Status الخاص بكائن Pod إلى مجموعة من Conditions. تشمل قيم الحالة التفصيلية هذه: PodScheduled و Ready و Initialized و Unschedulable. تُستخدم بشكل رئيسي لوصف الأسباب المحددة للحالة الحالية.

على سبيل المثال، إذا كانت الحالة الحالية للـ Pod هي Pending، وكانت الحالة المقابلة هي Unschedulable، فهذا يعني وجود مشكلة في جدولته.

من بينها، الحالة الفرعية Ready تستحق اهتمامًا خاصًا: فهي تعني أن Pod لم يبدأ بشكل طبيعي فقط (في حالة Running)، ولكنه أيضًا جاهز لتقديم الخدمات خارجيًا. هناك فرق بين هاتين الحالتين (Running و Ready)، لذا قد ترغب في التفكير فيه بعناية.

رسائل الحالة هذه للـ Pod هي معيار مهم لتقييم حالة تشغيل التطبيق، خاصة عندما يدخل Pod حالة غير “Running”، يجب أن تكون قادرًا على الاستجابة بسرعة، والبدء في التتبع والتحديد بناءً على الحالة غير الطبيعية المعروضة، بدلاً من البحث المحموم في الوثائق.

ملخص

في مقال اليوم، شرحت بالتفصيل كائن API الخاص بـ Pod، وقدّمت طرق الاستخدام الأساسية لـ Pods، وحللت أوجه التشابه والاختلاف بين Pods والحاويات من حيث الحقول. آمل أن تساعد هذه الشروحات في فهم أفضل للحقول الأساسية في YAML الخاص بـ Pod ومعانيها الدقيقة.

في الواقع، كائن API الخاص بـ Pod هو المفهوم الأساسي في نظام Kubernetes بأكمله، ويستخدم أيضًا في وحدات التحكم (controllers) التي سأشرحها لاحقًا.