Kubernetes Operator in Python sonder raamwerke en SDK's
Go het tans 'n monopolie op die programmeertale wat mense kies om stellings vir Kubernetes te skryf. Daar is objektiewe redes hiervoor, soos:
Daar is 'n kragtige raamwerk vir die ontwikkeling van operateurs in Go - Operator SDK.
Spelveranderende toepassings soos Docker en Kubernetes word in Go geskryf. Om jou operateur in Go te skryf, beteken om dieselfde taal met die ekosisteem te praat.
Hoë werkverrigting van Go-toepassings en eenvoudige gereedskap om met gelyktydigheid uit die boks te werk.
NB: Terloops, hoe om jou eie stelling te skryf in Go, we reeds beskryf in een van ons vertalings deur buitelandse skrywers.
Maar wat as jy verhinder word om Go te leer deur gebrek aan tyd of, eenvoudig gestel, motivering? Die artikel verskaf 'n voorbeeld van hoe jy 'n goeie stelling kan skryf deur een van die gewildste tale te gebruik wat byna elke DevOps-ingenieur ken - Python.
Ontmoet: Kopieerder - kopieeroperateur!
As 'n voorbeeld, oorweeg dit om 'n eenvoudige stelling te ontwikkel wat ontwerp is om 'n ConfigMap te kopieer óf wanneer 'n nuwe naamruimte verskyn óf wanneer een van twee entiteite verander: ConfigMap en Secret. Uit 'n praktiese oogpunt kan die operateur nuttig wees vir grootmaat-opdatering van toepassingkonfigurasies (deur die ConfigMap op te dateer) of vir die opdatering van geheime data - byvoorbeeld sleutels om met die Docker Registry te werk (wanneer Geheim by die naamruimte gevoeg word).
Die operateur kan gekonfigureer word. Om dit te doen, sal ons opdragreëlvlae en omgewingsveranderlikes gebruik.
Die bou van die Docker-houer en Helm-kaart is so ontwerp dat gebruikers die operateur maklik (letterlik met een opdrag) in hul Kubernetes-groep kan installeer.
CRD
Ten einde die operateur te weet watter hulpbronne om te soek en waar om te soek, moet ons 'n reël vir hom stel. Elke reël sal as 'n enkele CRD-voorwerp voorgestel word. Watter velde moet hierdie CRD hê?
Hulpbron tipe, waarna ons sal soek (ConfigMap of Secret).
Lys van naamruimtes, waarin die hulpbronne geleë moet wees.
selector, waardeur ons na hulpbronne in die naamruimte sal soek.
Klaar! Nou moet ons op een of ander manier inligting oor ons reël kry. Laat ek dadelik 'n bespreking maak dat ons nie self versoeke na die cluster API Server sal skryf nie. Om dit te doen, sal ons 'n klaargemaakte Python-biblioteek gebruik kubernetes-kliënt:
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')}
As gevolg van die uitvoering van hierdie kode, kry ons die volgende:
Groot: ons het daarin geslaag om 'n reël vir die operateur te kry. En die belangrikste, ons het gedoen wat die Kubernetes-manier genoem word.
Omgewingsveranderlikes of vlae? Ons vat alles!
Kom ons gaan aan na die hoofoperateurkonfigurasie. Daar is twee basiese benaderings om toepassings op te stel:
gebruik opdragreëlopsies;
omgewingsveranderlikes te gebruik.
Opdragreëlopsies laat jou toe om instellings meer buigsaam te lees, met datatipe ondersteuning en validering. Python se standaard biblioteek het 'n module argparser, wat ons sal gebruik. Besonderhede en voorbeelde van sy vermoëns is beskikbaar in amptelike dokumentasie.
Vir ons geval, dit is hoe 'n voorbeeld van die opstel van leesopdragreëlvlae sou lyk:
Aan die ander kant, met behulp van omgewingsveranderlikes in Kubernetes, kan jy maklik diensinligting oor die peul in die houer oordra. Ons kan byvoorbeeld inligting kry oor die naamruimte waarin die peul loop met die volgende konstruksie:
Om te verstaan hoe om metodes te skei om met ConfigMap en Secret te werk, sal ons spesiale kaarte gebruik. Dan kan ons verstaan watter metodes ons nodig het om die voorwerp op te spoor en te skep:
Vervolgens moet u gebeure vanaf die API-bediener ontvang. Kom ons implementeer dit soos volg:
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)
Nadat ons die gebeurtenis ontvang het, gaan ons voort na die hooflogika om dit te verwerk:
# Типы событий, на которые будем реагировать
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)
Die hooflogika is gereed! Nou moet ons dit alles in een Python-pakket verpak. Ons berei die lêer voor setup.py, skryf meta-inligting oor die projek daar:
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: Die kubernetes-kliënt vir Python het sy eie weergawe. Meer inligting oor versoenbaarheid tussen kliëntweergawes en Kubernetes-weergawes kan gevind word in verenigbaarheidsmatrikse.
Nou lyk ons projek so:
copyrator
├── copyrator
│ ├── cli.py # Логика работы с командной строкой
│ ├── constant.py # Константы, которые мы приводили выше
│ ├── load_crd.py # Логика загрузки CRD
│ └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакета
Docker en Helm
Die Dockerfile sal ongelooflik eenvoudig wees: neem die basis python-alpine-beeld en installeer ons pakket. Kom ons stel die optimalisering daarvan uit tot beter tye:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]
Ontplooiing vir die operateur is ook baie eenvoudig:
Dit is hoe ons, sonder vrees, verwyt of Go leer, ons eie operateur vir Kubernetes in Python kon bou. Natuurlik het dit nog ruimte om te groei: in die toekoms sal dit veelvuldige reëls kan verwerk, in verskeie drade kan werk, veranderinge in sy CRD's onafhanklik kan monitor ...
Om jou die kode van nader te bekyk, het ons dit ingesit openbare bewaarplek. As jy voorbeelde wil hê van meer ernstige operateurs wat met Python geïmplementeer word, kan jy jou aandag vestig op twee operateurs vir die implementering van mongodb (eerste и 2).
NS En as jy te lui is om Kubernetes-gebeure te hanteer of jy is bloot meer gewoond daaraan om Bash te gebruik, het ons kollegas 'n klaargemaakte oplossing in die vorm voorberei dop-operateur (Ons aangekondig dit in April).