Pythonдогу Kubernetes оператору алкаксыз жана SDKсыз

Pythonдогу Kubernetes оператору алкаксыз жана SDKсыз

Go учурда адамдар Kubernetes үчүн билдирүү жазууну тандаган программалоо тилдеринде монополияга ээ. Мунун объективдүү себептери бар, мисалы:

  1. Go операторлорун өнүктүрүү үчүн күчтүү негиз бар - Оператор SDK.
  2. Docker жана Kubernetes сыяктуу оюнду өзгөртүүчү колдонмолор Go'до жазылган. Go'до операторуңузду жазуу экосистема менен бир тилде сүйлөөнү билдирет.
  3. Go тиркемелеринин жогорку өндүрүмдүүлүгү жана кутудан тышкары параллелдүүлүк менен иштөө үчүн жөнөкөй куралдар.

NB: Баса, Go өз арызын кантип жазуу керек, биз мурунтан эле сүрөттөлгөн чет элдик авторлордун котормолору-буздун биринде.

Бирок убакыттын жетишсиздигинен же жөн эле айтканда, мотивациядан улам барып үйрөнүүгө тоскоол болсочу? Макалада дээрлик ар бир DevOps инженери билген эң популярдуу тилдердин бирин колдонуп кантип жакшы билдирүү жазууга болорун мисал келтирет - Python.

Таанышуу: Көчүрүүчү - көчүрүү оператору!

Мисал катары, жаңы аттар мейкиндиги пайда болгондо же эки объекттин бири өзгөргөндө ConfigMapты көчүрүү үчүн арналган жөнөкөй билдирүүнү иштеп чыгууну карап көрөлү: ConfigMap жана Secret. Практикалык көз караштан алганда, оператор тиркемелердин конфигурацияларын жапырт жаңыртуу үчүн (ConfigMapты жаңыртуу менен) же жашыруун маалыматтарды жаңыртуу үчүн пайдалуу болушу мүмкүн - мисалы, Докер реестри менен иштөө үчүн ачкычтар (ат мейкиндигине Сырды кошууда).

Ошентип, жакшы оператор кандай болушу керек:

  1. Оператор менен өз ара аракеттенүү аркылуу ишке ашырылат Ыңгайлаштырылган ресурстун аныктамалары (мындан ары CRD деп аталат).
  2. Оператор конфигурацияланышы мүмкүн. Бул үчүн, биз буйрук сабынын желектерин жана чөйрө өзгөрмөлөрүн колдонобуз.
  3. Docker контейнеринин жана Helm диаграммасынын түзүлүшү колдонуучулар операторду Kubernetes кластерине оңой (түз мааниде бир буйрук менен) орното алышы үчүн иштелип чыккан.

CRD

Оператор кандай ресурстарды жана кайда издөө керектигин билиши үчүн, биз ага эреже коюшубуз керек. Ар бир эреже бир CRD объектиси катары көрсөтүлөт. Бул CRD кандай талааларга ээ болушу керек?

  1. Ресурстун түрү, биз издей турган (ConfigMap же Secret).
  2. Ат мейкиндиктеринин тизмеси, анда ресурстарды жайгаштыруу керек.
  3. Селектор, анын жардамы менен биз аттар мейкиндигинде ресурстарды издейбиз.

Келгиле, CRD сүрөттөп көрөлү:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: copyrator.flant.com
spec:
  group: flant.com
  versions:
  - name: v1
    served: true
    storage: true
  scope: Namespaced
  names:
    plural: copyrators
    singular: copyrator
    kind: CopyratorRule
    shortNames:
    - copyr
  validation:
    openAPIV3Schema:
      type: object
      properties:
        ruleType:
          type: string
        namespaces:
          type: array
          items:
            type: string
        selector:
          type: string

Жана биз аны дароо түзөбүз жөнөкөй эреже — аты менен аттар мейкиндигинде издөө default сыяктуу энбелгилери бар бардык ConfigMap copyrator: "true":

apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
  name: main-rule
  labels:
    module: copyrator
ruleType: configmap
selector:
  copyrator: "true"
namespace: default

Даяр! Эми кандайдыр бир жол менен биздин эреже тууралуу маалымат алышыбыз керек. Мага дароо ээлеп коюуга уруксат этиңиз, биз кластердик API серверине өзүбүздөн сурам жазбайбыз. Бул үчүн биз даяр Python китепканасын колдонобуз kubernetes-кардар:

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')}

Бул кодду иштетүүнүн натыйжасында биз төмөнкүлөрдү алабыз:

{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}

Мыкты: биз оператор үчүн эреже алууга жетиштик. Эң негизгиси, биз Kubernetes жолу деп аталган нерсени жасадык.

Айлана-чөйрөнүн өзгөрмөлөрү же желектерби? Биз баарын алабыз!

Келгиле, оператордун негизги конфигурациясына өтөбүз. Тиркемелерди конфигурациялоонун эки негизги ыкмасы бар:

  1. буйрук сабынын параметрлерин колдонуу;
  2. чөйрө өзгөрмөлөрүн колдонуу.

Буйрук сабынын параметрлери маалымат түрүн колдоо жана текшерүү менен орнотууларды ийкемдүү окууга мүмкүндүк берет. Python стандарттык китепканасында модулу бар argparser, биз колдонобуз. Анын мүмкүнчүлүктөрүнүн майда-чүйдөсүнө чейин жана мисалдар бар расмий документтер.

Биздин учурда, окуу буйрук сабынын желектерин орнотуунун мисалы ушундай болот:

   parser = ArgumentParser(
        description='Copyrator - copy operator.',
        prog='copyrator'
    )
    parser.add_argument(
        '--namespace',
        type=str,
        default=getenv('NAMESPACE', 'default'),
        help='Operator Namespace'
    )
    parser.add_argument(
        '--rule-name',
        type=str,
        default=getenv('RULE_NAME', 'main-rule'),
        help='CRD Name'
    )
    args = parser.parse_args()

Башка жагынан алганда, Kubernetes'теги чөйрө өзгөрмөлөрүн колдонуу менен, сиз контейнердин ичиндеги подкаст жөнүндө кызматтык маалыматты оңой өткөрүп бере аласыз. Мисалы, биз подкаст төмөнкү конструкция менен иштеп жаткан аттар мейкиндиги жөнүндө маалымат ала алабыз:

env:
- name: NAMESPACE
  valueFrom:
     fieldRef:
         fieldPath: metadata.namespace 

Оператор логикасы

ConfigMap жана Secret менен иштөө ыкмаларын кантип бөлүүнү түшүнүү үчүн биз атайын карталарды колдонобуз. Андан кийин объектти түзүү үчүн кандай ыкмалар керек экенин түшүнө алабыз:

LIST_TYPES_MAP = {
    'configmap': 'list_namespaced_config_map',
    'secret': 'list_namespaced_secret',
}

CREATE_TYPES_MAP = {
    'configmap': 'create_namespaced_config_map',
    'secret': 'create_namespaced_secret',
}

Андан кийин, сиз 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: Python үчүн kubernetes кардарынын өзүнүн версиясы бар. Кардар версиялары менен Kubernetes версияларынын ортосундагы шайкештик жөнүндө көбүрөөк маалымат бул жерден тапса болот шайкештик матрицалары.

Эми биздин долбоор мындай көрүнөт:

copyrator
├── copyrator
│   ├── cli.py # Логика работы с командной строкой
│   ├── constant.py # Константы, которые мы приводили выше
│   ├── load_crd.py # Логика загрузки CRD
│   └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакета

Докер жана Хелм

Dockerfile укмуштуудай жөнөкөй болот: базалык питон-альп сүрөтүн алып, биздин пакетти орнотуңуз. Келгиле, аны оптималдаштырууну жакшыраак убакытка жылдыралы:

FROM python:3.7.3-alpine3.9

ADD . /app

RUN pip3 install /app

ENTRYPOINT ["copyrator"]

Оператор үчүн жайгаштыруу да абдан жөнөкөй:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Chart.Name }}
spec:
  selector:
    matchLabels:
      name: {{ .Chart.Name }}
  template:
    metadata:
      labels:
        name: {{ .Chart.Name }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: privaterepo.yourcompany.com/copyrator:latest
        imagePullPolicy: Always
        args: ["--rule-type", "main-rule"]
        env:
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
      serviceAccountName: {{ .Chart.Name }}-acc

Акыр-аягы, сиз керектүү укуктарга ээ оператор үчүн ылайыктуу ролду түзүшүңүз керек:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Chart.Name }}-acc

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: {{ .Chart.Name }}
rules:
  - apiGroups: [""]
    resources: ["namespaces"]
    verbs: ["get", "watch", "list"]
  - apiGroups: [""]
    resources: ["secrets", "configmaps"]
    verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: {{ .Chart.Name }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: {{ .Chart.Name }}
subjects:
- kind: ServiceAccount
  name: {{ .Chart.Name }}

жыйынтык

Ошентип, коркпой, жемелебестен же Go үйрөнбөй туруп, биз Pythonдо Кубернетес үчүн өз операторубузду түзө алдык. Албетте, анын дагы деле өсө турган жери бар: келечекте ал бир нече эрежелерди иштетип, бир нече жипте иштей алат, CRDдеги өзгөрүүлөрдү өз алдынча көзөмөлдөй алат...

Кодду жакшыраак көрүү үчүн, биз аны киргиздик коомдук репозиторий. Эгер сиз Python аркылуу ишке ашырылган олуттуураак операторлордун мисалдарын кааласаңыз, анда mongodb (дипломатиясы) үчүн эки операторго көңүл бурсаңыз болот.первый и экинчи).

PS Эгерде сиз Kubernetes окуялары менен күрөшүүгө жалкоо болсоңуз же сиз жөн гана Bashты колдонууга көнүп калган болсоңуз, анда биздин кесиптештер формада даяр чечим даярдашкан. шел-оператор (Биз жарыялады апрелде).

PPS

Биздин блогдон дагы окуңуз:

Source: www.habr.com

Комментарий кошуу