Python тіліндегі Kubernetes операторы фреймворксыз және SDK

Python тіліндегі Kubernetes операторы фреймворксыз және SDK

Go қазіргі уақытта адамдар Kubernetes үшін мәлімдеме жазуды таңдайтын бағдарламалау тілдеріне монополияға ие. Мұның объективті себептері бар, мысалы:

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

NB: Айтпақшы, Go бағдарламасында өз мәлімдемеңізді қалай жазуға болады, біз бұрыннан сипатталған шетелдік авторлардың аудармаларымыздың бірінде.

Бірақ егер сізге уақыттың жетіспеуі немесе қарапайым тілмен айтқанда мотивация оқуға кедергі болса ше? Мақалада әрбір DevOps инженері білетін ең танымал тілдердің бірін пайдаланып жақсы мәлімдеме жазудың мысалы келтірілген - Python.

Танысыңыз: Көшірме – көшіру операторы!

Мысал ретінде, жаңа аттар кеңістігі пайда болғанда немесе екі нысанның бірі өзгерген кезде ConfigMap файлын көшіруге арналған қарапайым мәлімдемені әзірлеуді қарастырыңыз: ConfigMap және Құпия. Практикалық тұрғыдан алғанда, оператор қолданба конфигурацияларын жаппай жаңарту үшін (ConfigMap жаңарту арқылы) немесе құпия деректерді жаңарту үшін пайдалы болуы мүмкін - мысалы, Docker тізілімімен жұмыс істеу кілттері (аттар кеңістігіне құпияны қосқанда).

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

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

Тамаша: біз операторға ережені ала алдық. Ең бастысы, біз Кубернетес жолы деп аталатын нәрсені жасадық.

Қоршаған ортаның айнымалылары немесе жалаушалары? Біз бәрін аламыз!

Негізгі оператор конфигурациясына көшейік. Қолданбаларды конфигурациялаудың екі негізгі тәсілі бар:

  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-да Kubernetes үшін өз операторымызды құра алдық. Әрине, оның әлі де өсетін орны бар: болашақта ол бірнеше ережелерді өңдей алады, бірнеше ағындарда жұмыс істей алады, CRD өзгерістерін тәуелсіз бақылай алады...

Сізге кодты жақынырақ қарау үшін біз оны енгіздік қоғамдық репозиторий. Егер сіз Python көмегімен іске асырылатын күрделі операторлардың мысалдарын алғыңыз келсе, mongodb қолдану үшін екі операторға назар аудара аласыз (первый и секунд).

PS Егер сіз Kubernetes оқиғаларымен айналысуға тым жалқау болсаңыз немесе сіз Bash-ті қолдануға үйреніп алсаңыз, біздің әріптестер пішінде дайын шешім дайындады. қабық операторы (Біз жарияланды сәуірде).

PPS

Біздің блогта да оқыңыз:

Ақпарат көзі: www.habr.com

пікір қалдыру