"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > كيفية إضافة انتخاب القائد المدعوم من Kubernetes إلى تطبيقات Go الخاصة بك

كيفية إضافة انتخاب القائد المدعوم من Kubernetes إلى تطبيقات Go الخاصة بك

تم النشر بتاريخ 2024-07-30
تصفح:867

How to add Kubernetes-powered leader election to your Go apps

نُشرت في الأصل بواسطة المدونة

مكتبة Kubernetes القياسية مليئة بالجواهر، مخبأة بعيدًا في العديد من الحزم الفرعية المتنوعة التي تشكل جزءًا من النظام البيئي. أحد الأمثلة التي اكتشفتها مؤخرًا هو k8s.io/client-go/tools/leaderelection، والذي يمكن استخدامه لإضافة بروتوكول اختيار القائد إلى أي تطبيق يعمل داخل مجموعة Kubernetes. ستناقش هذه المقالة ماهية انتخاب القائد، وكيفية تنفيذها في حزمة Kubernetes هذه، وستقدم مثالاً لكيفية استخدام هذه المكتبة في تطبيقاتنا الخاصة.

انتخاب الزعيم

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

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

باستخدام هذا المفهوم، يمكننا إنشاء برامج عالية التوفر مع قائد واحد ونسخ متماثلة متعددة في وضع الاستعداد.

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

عقود الإيجار Kubernetes

تستخدم هذه المكتبة عقد Kubernetes Lease، أو القفل الموزع، الذي يمكن الحصول عليه من خلال إحدى العمليات. الإيجارات هي موارد Kubernetes الأصلية التي تحتفظ بها هوية واحدة، لمدة معينة، مع خيار التجديد. فيما يلي مثال للمواصفات من المستندات:

apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  labels:
    apiserver.kubernetes.io/identity: kube-apiserver
    kubernetes.io/hostname: master-1
  name: apiserver-07a5ea9b9b072c4a5f3d1c3702
  namespace: kube-system
spec:
  holderIdentity: apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05
  leaseDurationSeconds: 3600
  renewTime: "2023-07-04T21:58:48.065888Z"

يتم استخدام عقود الإيجار بواسطة النظام البيئي k8s بثلاث طرق:

  1. نبضات العقدة: تحتوي كل عقدة على مورد تأجير مناظر وتقوم بتحديث حقل وقت التجديد الخاص بها بشكل مستمر. إذا لم يتم تحديث وقت تجديد عقد الإيجار لفترة من الوقت، فستكون العقدة ملوثة باعتبارها غير متاحة ولن تتم جدولة المزيد من البودات لها.
  2. انتخاب القائد: في هذه الحالة، يتم استخدام عقد الإيجار للتنسيق بين عمليات متعددة من خلال مطالبة القائد بتحديث هوية حامل عقد الإيجار. النسخ المتماثلة الاحتياطية، ذات الهويات المختلفة، عالقة في انتظار انتهاء صلاحية عقد الإيجار. إذا انتهت صلاحية عقد الإيجار، ولم يتم تجديده من قبل القائد، فسيتم إجراء انتخابات جديدة تحاول فيها النسخ المتماثلة المتبقية الحصول على ملكية عقد الإيجار عن طريق تحديث هوية المالك الخاصة به. نظرًا لأن خادم Kubernetes API لا يسمح بتحديثات الكائنات القديمة، فلن تتمكن سوى عقدة احتياطية واحدة من تحديث عقد الإيجار بنجاح، وعند هذه النقطة ستستمر في التنفيذ كعقدة رائدة جديدة.
  3. هوية خادم واجهة برمجة التطبيقات: بدءًا من الإصدار 1.26، كميزة تجريبية، ستنشر كل نسخة طبق الأصل من kube-apserver هويتها عن طريق إنشاء عقد إيجار مخصص. نظرًا لأن هذه ميزة جديدة ونحيفة نسبيًا، فليس هناك الكثير مما يمكن استخلاصه من كائن Lease بخلاف عدد خوادم واجهة برمجة التطبيقات (API) قيد التشغيل. ولكن هذا يترك مجالًا لإضافة المزيد من البيانات الوصفية إلى عقود الإيجار هذه في إصدارات k8s المستقبلية.

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

برنامج المثال

في هذا المثال الرمزي، نستخدم حزمة Leaderelection للتعامل مع اختيار القائد وتفاصيل التلاعب في عقد الإيجار.

package main

import (
    "context"
    "fmt"
    "os"
    "time"

    "k8s.io/client-go/tools/leaderelection"
    rl "k8s.io/client-go/tools/leaderelection/resourcelock"
    ctrl "sigs.k8s.io/controller-runtime"
)

var (
    // lockName and lockNamespace need to be shared across all running instances
    lockName      = "my-lock"
    lockNamespace = "default"

    // identity is unique to the individual process. This will not work for anything,
    // outside of a toy example, since processes running in different containers or
    // computers can share the same pid.
    identity      = fmt.Sprintf("%d", os.Getpid())
)

func main() {
    // Get the active kubernetes context
    cfg, err := ctrl.GetConfig()
    if err != nil {
        panic(err.Error())
    }

    // Create a new lock. This will be used to create a Lease resource in the cluster.
    l, err := rl.NewFromKubeconfig(
        rl.LeasesResourceLock,
        lockNamespace,
        lockName,
        rl.ResourceLockConfig{
            Identity: identity,
        },
        cfg,
        time.Second*10,
    )
    if err != nil {
        panic(err)
    }

    // Create a new leader election configuration with a 15 second lease duration.
    // Visit https://pkg.go.dev/k8s.io/client-go/tools/leaderelection#LeaderElectionConfig
    // for more information on the LeaderElectionConfig struct fields
    el, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
        Lock:          l,
        LeaseDuration: time.Second * 15,
        RenewDeadline: time.Second * 10,
        RetryPeriod:   time.Second * 2,
        Name:          lockName,
        Callbacks: leaderelection.LeaderCallbacks{
            OnStartedLeading: func(ctx context.Context) { println("I am the leader!") },
            OnStoppedLeading: func() { println("I am not the leader anymore!") },
            OnNewLeader:      func(identity string) { fmt.Printf("the leader is %s\n", identity) },
        },
    })
    if err != nil {
        panic(err)
    }

    // Begin the leader election process. This will block.
    el.Run(context.Background())

}

الأمر الجميل في حزمة انتخاب القائد هو أنها توفر إطارًا قائمًا على رد الاتصال للتعامل مع انتخابات القائد. بهذه الطريقة، يمكنك التصرف وفقًا لتغييرات محددة في الولاية بطريقة تفصيلية وتحرير الموارد بشكل صحيح عند انتخاب قائد جديد. من خلال تشغيل عمليات الاسترجاعات هذه في goroutines منفصلة، ​​تستفيد الحزمة من دعم التزامن القوي لـ Go للاستفادة بكفاءة من موارد الجهاز.

اختبارها

لاختبار ذلك، دعونا ندير مجموعة اختبار باستخدام النوع.

$ kind create cluster

انسخ نموذج التعليمات البرمجية إلى main.go، وأنشئ وحدة نمطية جديدة (go mod init Leaderelectiontest) وقم بترتيبها (go mod tidy) لتثبيت تبعياتها. بمجرد تشغيل go run main.go، يجب أن تشاهد الإخراج مثل هذا:

$ go run main.go
I0716 11:43:50.337947     138 leaderelection.go:250] attempting to acquire leader lease default/my-lock...
I0716 11:43:50.351264     138 leaderelection.go:260] successfully acquired lease default/my-lock
the leader is 138
I am the leader!

ستكون هوية القائد الدقيقة مختلفة عما هو موجود في المثال (138)، نظرًا لأن هذا مجرد معرف PID للعملية التي كانت قيد التشغيل على جهاز الكمبيوتر الخاص بي في وقت كتابة هذا التقرير.

وهذا هو عقد الإيجار الذي تم إنشاؤه في مجموعة الاختبار:

$ kubectl describe lease/my-lock
Name:         my-lock
Namespace:    default
Labels:       
Annotations:  
API Version:  coordination.k8s.io/v1
Kind:         Lease
Metadata:
  Creation Timestamp:  2024-07-16T15:43:50Z
  Resource Version:    613
  UID:                 1d978362-69c5-43e9-af13-7b319dd452a6
Spec:
  Acquire Time:            2024-07-16T15:43:50.338049Z
  Holder Identity:         138
  Lease Duration Seconds:  15
  Lease Transitions:       0
  Renew Time:              2024-07-16T15:45:31.122956Z
Events:                    

انظر أن "هوية المالك" هي نفس معرف العملية، 138.

الآن، لنفتح محطة طرفية أخرى ونشغل نفس ملف main.go في عملية منفصلة:

$ go run main.go
I0716 11:48:34.489953     604 leaderelection.go:250] attempting to acquire leader lease default/my-lock...
the leader is 138

سوف تنتظر هذه العملية الثانية إلى الأبد، حتى لا تستجيب العملية الأولى. دعنا ننهي العملية الأولى وننتظر حوالي 15 ثانية. الآن بما أن العملية الأولى لا تقوم بتجديد مطالبتها بعقد الإيجار، فلن يتم تحديث الحقل .spec.renewTime بعد الآن. سيؤدي هذا في النهاية إلى أن تؤدي العملية الثانية إلى إطلاق انتخاب قائد جديد، نظرًا لأن وقت تجديد عقد الإيجار أقدم من مدته. ولأن هذه العملية هي العملية الوحيدة الجارية الآن، فسوف تنتخب نفسها كزعيم جديد.

the leader is 604
I0716 11:48:51.904732     604 leaderelection.go:260] successfully acquired lease default/my-lock
I am the leader!

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

لا توجد ضمانات لزعيم واحد

هذه الحزمة ليست مضمونة، من حيث أنها "لا تضمن أن يقوم عميل واحد فقط بدور القائد (المعروف أيضًا باسم المبارزة)". على سبيل المثال، إذا تم إيقاف القائد مؤقتًا وترك عقد الإيجار الخاص به ينتهي، فستحصل نسخة احتياطية أخرى على عقد الإيجار. وبعد ذلك، بمجرد أن يستأنف القائد الأصلي التنفيذ، فإنه سيعتقد أنه لا يزال هو القائد وسيواصل العمل جنبًا إلى جنب مع القائد المنتخب حديثًا. بهذه الطريقة، يمكن أن ينتهي بك الأمر مع وجود قائدين يعملان في وقت واحد.

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

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

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

خاتمة

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

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

ترقبوا المزيد من التعمق في مصدر k8!

بيان الافراج تم إعادة نشر هذه المقالة على: https://dev.to/sklarsa/how-to-add-kubernetes-powered-leader-election-to-your-go-apps-57jh?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ Study_golang @163.com حذف
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3