تحتكر Go حاليًا لغات البرمجة التي يختارها الأشخاص لكتابة بيانات لـ Kubernetes. وهناك أسباب موضوعية لذلك، منها:
يوجد إطار عمل قوي لتطوير المشغلين في Go - مشغل SDK.
تتم كتابة التطبيقات التي تغير قواعد اللعبة مثل Docker وKubernetes بلغة Go. كتابة المشغل الخاص بك في Go تعني التحدث بنفس اللغة مع النظام البيئي.
أداء عالٍ لتطبيقات Go وأدوات بسيطة للعمل مع التزامن خارج الصندوق.
NB: بالمناسبة، كيف تكتب بيانك الخاص في Go، نحن الموصوفة بالفعل في إحدى ترجماتنا لمؤلفين أجانب.
ولكن ماذا لو تم منعك من تعلم Go بسبب ضيق الوقت، أو ببساطة، الحافز؟ تقدم المقالة مثالاً لكيفية كتابة بيان جيد باستخدام إحدى اللغات الأكثر شيوعًا التي يعرفها كل مهندسي DevOps تقريبًا - بايثون.
لقاء: ناسخة - مشغل نسخ!
على سبيل المثال، فكر في تطوير عبارة بسيطة مصممة لنسخ ConfigMap إما عند ظهور مساحة اسم جديدة أو عند تغيير أحد الكيانين: ConfigMap وSecret. من وجهة نظر عملية، يمكن أن يكون المشغل مفيدًا للتحديث المجمع لتكوينات التطبيق (عن طريق تحديث ConfigMap) أو لتحديث البيانات السرية - على سبيل المثال، مفاتيح العمل مع Docker Registry (عند إضافة Secret إلى مساحة الاسم).
يمكن تكوين المشغل. للقيام بذلك، سوف نستخدم إشارات سطر الأوامر ومتغيرات البيئة.
تم تصميم بنية حاوية Docker ومخطط Helm بحيث يمكن للمستخدمين بسهولة (باستخدام أمر واحد حرفيًا) تثبيت المشغل في مجموعة Kubernetes الخاصة بهم.
CRD
لكي يعرف المشغل ما هي الموارد التي يبحث عنها وأين يبحث، نحتاج إلى وضع قاعدة له. سيتم تمثيل كل قاعدة ككائن CRD واحد. ما هي الحقول التي يجب أن تحتوي عليها CRD هذه؟
نوع الموردوالتي سنبحث عنها (ConfigMap أو Secret).
قائمة مساحات الأسماء، حيث يجب أن تكون الموارد موجودة.
منتقى، والذي من خلاله سنبحث عن الموارد في مساحة الاسم.
مستعد! والآن نحن بحاجة إلى الحصول بطريقة أو بأخرى على معلومات حول القاعدة. اسمحوا لي أن أقوم بالحجز على الفور بأننا لن نكتب طلبات إلى خادم API للمجموعة بأنفسنا. للقيام بذلك، سوف نستخدم مكتبة بايثون جاهزة kubernetes-client:
import kubernetes
from contextlib import suppress
CRD_GROUP = 'flant.com'
CRD_VERSION = 'v1'
CRD_PLURAL = 'copyrators'
def load_crd(namespace, name):
client = kubernetes.client.ApiClient()
custom_api = kubernetes.client.CustomObjectsApi(client)
with suppress(kubernetes.client.api_client.ApiException):
crd = custom_api.get_namespaced_custom_object(
CRD_GROUP,
CRD_VERSION,
namespace,
CRD_PLURAL,
name,
)
return {x: crd[x] for x in ('ruleType', 'selector', 'namespace')}
عظيم: تمكنا من الحصول على قاعدة للمشغل. والأهم من ذلك أننا فعلنا ما يسمى بطريقة Kubernetes.
متغيرات البيئة أم الأعلام؟ نحن نأخذ كل شيء!
دعنا ننتقل إلى تكوين المشغل الرئيسي. هناك طريقتان أساسيتان لتكوين التطبيقات:
استخدام خيارات سطر الأوامر؛
استخدام متغيرات البيئة.
تتيح لك خيارات سطر الأوامر قراءة الإعدادات بمرونة أكبر، مع دعم نوع البيانات والتحقق من صحتها. تحتوي مكتبة بايثون القياسية على وحدة نمطية argparser، والتي سوف نستخدمها. التفاصيل والأمثلة على قدراتها متوفرة في الوثائق الرسمية.
في حالتنا، هذا هو المثال الذي سيبدو عليه إعداد إشارات سطر أوامر القراءة:
من ناحية أخرى، باستخدام متغيرات البيئة في Kubernetes، يمكنك بسهولة نقل معلومات الخدمة حول الكبسولة داخل الحاوية. على سبيل المثال، يمكننا الحصول على معلومات حول مساحة الاسم التي تعمل فيها الكبسولة بالبنية التالية:
بعد ذلك، تحتاج إلى تلقي الأحداث من خادم API. دعونا ننفذها على النحو التالي:
def handle(specs):
kubernetes.config.load_incluster_config()
v1 = kubernetes.client.CoreV1Api()
# Получаем метод для слежения за объектами
method = getattr(v1, LIST_TYPES_MAP[specs['ruleType']])
func = partial(method, specs['namespace'])
w = kubernetes.watch.Watch()
for event in w.stream(func, _request_timeout=60):
handle_event(v1, specs, event)
بعد استقبال الحدث ننتقل إلى المنطق الرئيسي لمعالجته:
# Типы событий, на которые будем реагировать
ALLOWED_EVENT_TYPES = {'ADDED', 'UPDATED'}
def handle_event(v1, specs, event):
if event['type'] not in ALLOWED_EVENT_TYPES:
return
object_ = event['object']
labels = object_['metadata'].get('labels', {})
# Ищем совпадения по selector'у
for key, value in specs['selector'].items():
if labels.get(key) != value:
return
# Получаем активные namespace'ы
namespaces = map(
lambda x: x.metadata.name,
filter(
lambda x: x.status.phase == 'Active',
v1.list_namespace().items
)
)
for namespace in namespaces:
# Очищаем метаданные, устанавливаем namespace
object_['metadata'] = {
'labels': object_['metadata']['labels'],
'namespace': namespace,
'name': object_['metadata']['name'],
}
# Вызываем метод создания/обновления объекта
methodcaller(
CREATE_TYPES_MAP[specs['ruleType']],
namespace,
object_
)(v1)
المنطق الرئيسي جاهز! نحتاج الآن إلى تجميع كل هذا في حزمة Python واحدة. نقوم بإعداد الملف setup.py، اكتب معلومات تعريفية عن المشروع هناك:
from sys import version_info
from setuptools import find_packages, setup
if version_info[:2] < (3, 5):
raise RuntimeError(
'Unsupported python version %s.' % '.'.join(version_info)
)
_NAME = 'copyrator'
setup(
name=_NAME,
version='0.0.1',
packages=find_packages(),
classifiers=[
'Development Status :: 3 - Alpha',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
author='Flant',
author_email='[email protected]',
include_package_data=True,
install_requires=[
'kubernetes==9.0.0',
],
entry_points={
'console_scripts': [
'{0} = {0}.cli:main'.format(_NAME),
]
}
)
NB: عميل kubernetes لـ Python له إصداره الخاص. يمكن العثور على مزيد من المعلومات حول التوافق بين إصدارات العميل وإصدارات Kubernetes في مصفوفات التوافق.
الآن يبدو مشروعنا كما يلي:
copyrator
├── copyrator
│ ├── cli.py # Логика работы с командной строкой
│ ├── constant.py # Константы, которые мы приводили выше
│ ├── load_crd.py # Логика загрузки CRD
│ └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакета
عامل الميناء وهيلم
سيكون ملف Dockerfile بسيطًا للغاية: التقط صورة python-alpine الأساسية وقم بتثبيت الحزمة الخاصة بنا. دعونا نؤجل تحسينه حتى أوقات أفضل:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]
بهذه الطريقة، بدون خوف أو عتاب أو تعلم لغة Go، تمكنا من بناء مشغلنا الخاص لـ Kubernetes في Python. بالطبع، لا يزال لديه مجال للنمو: في المستقبل سيكون قادرًا على معالجة قواعد متعددة، والعمل في سلاسل رسائل متعددة، ومراقبة التغييرات في CRDs الخاصة به بشكل مستقل...
لإلقاء نظرة فاحصة على الكود، قمنا بوضعه المستودع العام. إذا كنت تريد أمثلة على عوامل أكثر جدية تم تنفيذها باستخدام بايثون، فيمكنك تحويل انتباهك إلى عاملين لنشر mongodb (الأول и ثان).
ملاحظة: وإذا كنت كسولًا جدًا في التعامل مع أحداث Kubernetes أو كنت ببساطة معتادًا على استخدام Bash، فقد قام زملاؤنا بإعداد حل جاهز في النموذج مشغل القشرة (نحن أعلن ذلك في أبريل).