Kubernetes Operator f'Python mingħajr oqfsa u SDK

Kubernetes Operator f'Python mingħajr oqfsa u SDK

Go bħalissa għandha monopolju fuq il-lingwi tal-ipprogrammar li n-nies jagħżlu li jiktbu dikjarazzjonijiet għal Kubernetes. Hemm raġunijiet oġġettivi għal dan, bħal:

  1. Hemm qafas b'saħħtu għall-iżvilupp ta' operaturi f'Go - Operatur SDK.
  2. Applikazzjonijiet li jbiddlu l-logħob bħal Docker u Kubernetes huma miktuba f'Go. Li tikteb l-operatur tiegħek f'Go tfisser li titkellem l-istess lingwa mal-ekosistema.
  3. Prestazzjoni għolja ta 'applikazzjonijiet Go u għodod sempliċi biex jaħdmu b'konkorrenza barra mill-kaxxa.

NB: Mill-mod, kif tikteb id-dikjarazzjoni tiegħek stess f'Go, aħna diġà deskritt f’waħda mit-traduzzjonijiet tagħna minn awturi barranin.

Imma x'jiġri jekk ma titħalliex titgħallem Mur minħabba nuqqas ta' ħin jew, sempliċiment, motivazzjoni? L-artikolu jipprovdi eżempju ta 'kif tista' tikteb dikjarazzjoni tajba billi tuża waħda mill-lingwi l-aktar popolari li kważi kull inġinier DevOps jaf - Python.

Iltaqa': Kopjatur - operatur tal-kopja!

Bħala eżempju, ikkunsidra li tiżviluppa dikjarazzjoni sempliċi mfassla biex tikkopja ConfigMap jew meta jidher spazju tal-isem ġdid jew meta tinbidel waħda minn żewġ entitajiet: ConfigMap u Secret. Mil-lat prattiku, l-operatur jista 'jkun utli għall-aġġornament bl-ingrossa tal-konfigurazzjonijiet tal-applikazzjoni (billi jaġġorna l-ConfigMap) jew għall-aġġornament ta' data sigrieta - pereżempju, ċwievet biex taħdem mar-Reġistru Docker (meta żżid Sigriet mal-ispazju tal-isem).

Allura, x'għandu jkollu operatur tajjeb:

  1. L-interazzjoni mal-operatur titwettaq bl-użu Definizzjonijiet tar-Riżorsi tad-Dwana (minn hawn 'il quddiem imsejħa CRD).
  2. L-operatur jista 'jiġi kkonfigurat. Biex tagħmel dan, se nużaw bnadar tal-linja tal-kmand u varjabbli ambjentali.
  3. Il-bini tal-kontenitur Docker u t-tabella Helm hija mfassla sabiex l-utenti jkunu jistgħu faċilment (litteralment bi kmand wieħed) jinstallaw l-operatur fil-cluster Kubernetes tagħhom.

CRD

Sabiex l-operatur ikun jaf liema riżorsi għandu jfittex u fejn ifittex, irridu nistabbilixxu regola għalih. Kull regola se tkun rappreżentata bħala oġġett CRD wieħed. X'oqsma għandu jkollha din is-CRD?

  1. Tip ta' riżorsa, li se nfittxu (ConfigMap jew Secret).
  2. Lista ta' namespaces, li fiha għandhom ikunu jinsabu r-riżorsi.
  3. Selettur, li biha ser infittxu riżorsi fl-ispazju tal-isem.

Ejja niddeskrivu s-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

U noħolquha minnufih regola sempliċi — biex tfittex fl-ispazju tal-isem bl-isem default kollha ConfigMap b'tikketti simili copyrator: "true":

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

Lest! Issa rridu nġibu b'xi mod informazzjoni dwar ir-regola tagħna. Ħa nagħmel riżerva mill-ewwel li aħna mhux se niktbu talbiet lill-cluster API Server aħna stess. Biex tagħmel dan, se nużaw librerija Python lesta 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')}

Bħala riżultat tat-tħaddim ta 'dan il-kodiċi, aħna jkollna dan li ġej:

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

Kbir: irnexxielna nġibu regola għall-operatur. U l-aktar importanti, għamilna dak li jissejjaħ il-mod Kubernetes.

Varjabbli ambjentali jew bnadar? Nieħdu kollox!

Ejja ngħaddu għall-konfigurazzjoni tal-operatur prinċipali. Hemm żewġ approċċi bażiċi għall-konfigurazzjoni tal-applikazzjonijiet:

  1. uża għażliet tal-linja tal-kmand;
  2. uża varjabbli ambjentali.

L-għażliet tal-linja tal-kmand jippermettulek taqra s-settings b'mod aktar flessibbli, b'appoġġ u validazzjoni tat-tip tad-dejta. Il-librerija standard ta' Python għandha modulu argparser, li se nużaw. Dettalji u eżempji tal-kapaċitajiet tagħha huma disponibbli fi dokumentazzjoni uffiċjali.

Għall-każ tagħna, dan huwa eżempju tat-twaqqif ta' bnadar tal-linja tal-kmand tal-qari:

   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()

Min-naħa l-oħra, billi tuża varjabbli ambjentali f'Kubernetes, tista 'faċilment tittrasferixxi informazzjoni dwar is-servizz dwar il-pod ġewwa l-kontenitur. Pereżempju, nistgħu niksbu informazzjoni dwar l-ispazju tal-isem li fih qed jaħdem il-pod bil-kostruzzjoni li ġejja:

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

Loġika tal-operatur

Biex nifhmu kif nisseparaw il-metodi biex naħdmu ma' ConfigMap u Secret, se nużaw mapep speċjali. Imbagħad inkunu nistgħu nifhmu liema metodi għandna bżonn biex intraċċaw u noħolqu l-oġġett:

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

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

Sussegwentement, għandek bżonn tirċievi avvenimenti mis-server API. Ejja nimplimentah kif ġej:

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)

Wara li nirċievu l-avveniment, ngħaddu għal-loġika ewlenija tal-ipproċessar tiegħu:

# Типы событий, на которые будем реагировать
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)

Il-loġika prinċipali hija lesta! Issa rridu nippakkjaw dan kollu f'pakkett wieħed Python. Aħna nippreparaw il-fajl setup.py, ikteb meta informazzjoni dwar il-proġett hemmhekk:

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: Il-klijent kubernetes għal Python għandu l-verżjoni tiegħu stess. Aktar informazzjoni dwar il-kompatibilità bejn il-verżjonijiet tal-klijenti u l-verżjonijiet ta’ Kubernetes tista’ tinstab fi matriċi ta' kompatibilità.

Issa l-proġett tagħna jidher bħal dan:

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

Docker u Helm

Id-Dockerfile se jkun oerhört sempliċi: ħu l-immaġni bażi python-alpine u installa l-pakkett tagħna. Ejja nipposponu l-ottimizzazzjoni tagħha għal żminijiet aħjar:

FROM python:3.7.3-alpine3.9

ADD . /app

RUN pip3 install /app

ENTRYPOINT ["copyrator"]

L-iskjerament għall-operatur huwa wkoll sempliċi ħafna:

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

Fl-aħħarnett, trid toħloq rwol xieraq għall-operatur bid-drittijiet meħtieġa:

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

Total

Dak hu kif, mingħajr biża ', tmaqdir, jew tagħlim Go, stajna nibnu l-operatur tagħna stess għal Kubernetes f'Python. Ovvjament, għad għandu spazju biex jikber: fil-futur se jkun jista 'jipproċessa regoli multipli, jaħdem f'kamini multipli, jimmonitorja b'mod indipendenti l-bidliet fis-CRDs tiegħu...

Biex nagħtuk ħarsa aktar mill-qrib lejn il-kodiċi, daħħalnieh repożitorju pubbliku. Jekk trid eżempji ta' operaturi aktar serji implimentati bl-użu ta' Python, tista' ddawwar l-attenzjoni tiegħek fuq żewġ operaturi għall-iskjerament ta' mongodb (первый и 2).

PS U jekk inti għażżien wisq biex tittratta l-avvenimenti ta 'Kubernetes jew inti sempliċiment aktar imdorri li tuża Bash, il-kollegi tagħna ħejjew soluzzjoni lesta fil-forma operatur tal-qoxra (Aħna ħabbar f’April).

PPS

Aqra wkoll fuq il-blog tagħna:

Sors: www.habr.com

Żid kumment