Kubernetes Operator Pythonis ilma raamistike ja SDKta
Go-l on praegu monopol programmeerimiskeelte osas, mida inimesed Kubernetese jaoks avaldusi kirjutavad. Sellel on objektiivsed põhjused, näiteks:
Go-s on operaatorite arendamiseks võimas raamistik - Operaator SDK.
Mängu muutvad rakendused, nagu Docker ja Kubernetes, on kirjutatud Go-s. Operaatori Go-sse kirjutamine tähendab ökosüsteemiga samas keeles rääkimist.
Go rakenduste kõrge jõudlus ja lihtsad tööriistad samaaegsusega töötamiseks.
NB: Muide, kuidas kirjutada oma avaldust sisse Go, we juba kirjeldatud ühes meie välisautorite tõlkes.
Aga mis siis, kui Go õppimist takistab ajapuudus või lihtsalt öeldes motivatsioon? Artiklis on näide selle kohta, kuidas saate kirjutada hea avalduse, kasutades ühte kõige populaarsemat keelt, mida peaaegu iga DevOpsi insener teab - Python.
Tutvuge: Koopiamasin - koopiaoperaator!
Näiteks kaaluge lihtsa avalduse väljatöötamist, mis on mõeldud ConfigMapi kopeerimiseks kas uue nimeruumi ilmumisel või siis, kui muutub üks kahest olemist: ConfigMap ja Secret. Praktilisest vaatenurgast võib operaator olla kasulik rakenduste konfiguratsioonide hulgivärskendamisel (ConfigMapi värskendamisel) või salaandmete värskendamisel – näiteks võtmed Dockeri registriga töötamiseks (nimeruumi Secret lisamisel).
Operaatorit saab konfigureerida. Selleks kasutame käsurea lippe ja keskkonnamuutujaid.
Dockeri konteineri ja Helmi diagrammi ehitus on loodud nii, et kasutajad saaksid hõlpsalt (sõna otseses mõttes ühe käsuga) installida operaatori oma Kubernetese klastrisse.
CRD
Selleks, et operaator teaks, milliseid ressursse otsida ja kust otsida, peame tema jaoks paika panema reegli. Iga reegel esitatakse ühe CRD-objektina. Millised väljad peaksid selles CRD-s olema?
Ressursi tüüp, mida me otsime (ConfigMap või Secret).
Nimeruumide loend, milles ressursid peaksid asuma.
Valmis! Nüüd peame oma reegli kohta kuidagi teavet hankima. Lubage mul kohe teha broneering, et me ise klastri API serverisse päringuid ei kirjuta. Selleks kasutame valmis Pythoni teeki kubernetes-klient:
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')}
Suurepärane: meil õnnestus saada operaatorile reegel. Ja mis kõige tähtsam, me tegime seda, mida nimetatakse Kubernetese viisiks.
Keskkonnamuutujad või lipud? Võtame kõik!
Liigume edasi operaatori põhikonfiguratsiooni juurde. Rakenduste konfigureerimiseks on kaks peamist lähenemisviisi:
kasutada käsurea valikuid;
kasutada keskkonnamuutujaid.
Käsurea valikud võimaldavad teil sätteid paindlikumalt lugeda koos andmetüübi toe ja valideerimisega. Pythoni standardteegis on moodul argparser, mida me kasutame. Selle võimaluste üksikasjad ja näited on saadaval aadressil ametlik dokumentatsioon.
Meie puhul näeks käsurea lugemise lippude seadistamise näide välja selline:
Teisest küljest saate Kubernetese keskkonnamuutujaid kasutades hõlpsasti konteineri sees oleva paketi teenuseteavet üle kanda. Näiteks saame teavet nimeruumi kohta, milles pod töötab, järgmise konstruktsiooniga:
Et mõista, kuidas eraldada meetodid ConfigMapi ja Secretiga töötamiseks, kasutame spetsiaalseid kaarte. Siis saame aru, milliseid meetodeid on vaja objekti jälgimiseks ja loomiseks:
Järgmiseks peate saama sündmusi API serverist. Rakendame seda järgmiselt:
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)
Pärast sündmuse saamist liigume edasi selle töötlemise põhiloogika juurde:
# Типы событий, на которые будем реагировать
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)
Põhiloogika on valmis! Nüüd peame selle kõik ühte Pythoni paketti pakkima. Valmistame faili ette setup.py, kirjuta sinna projekti metainfo:
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: Pythoni kubernetes kliendil on oma versioonid. Lisateavet kliendiversioonide ja Kubernetese versioonide ühilduvuse kohta leiate aadressilt ühilduvusmaatriksid.
Nüüd näeb meie projekt välja selline:
copyrator
├── copyrator
│ ├── cli.py # Логика работы с командной строкой
│ ├── constant.py # Константы, которые мы приводили выше
│ ├── load_crd.py # Логика загрузки CRD
│ └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакета
Docker ja Helm
Dockerfile saab olema uskumatult lihtne: võtke python-alpine põhipilt ja installige meie pakett. Lükkame selle optimeerimise edasi paremate aegadeni:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]
Nii saime ilma hirmu, etteheiteid ja Go-d õppimata luua Pythonis Kubernetese jaoks oma operaatori. Muidugi on sellel veel ruumi kasvada: tulevikus saab see töödelda mitut reeglit, töötada mitmes lõimes, iseseisvalt jälgida muutusi oma CRD-des...
Koodiga lähemalt tutvumiseks oleme selle sisestanud avalik hoidla. Kui soovite näiteid Pythoni abil rakendatud tõsisematest operaatoritest, võite pöörata tähelepanu kahele operaatorile mongodb (esimene и teine).
PS Ja kui olete liiga laisk Kubernetese sündmustega tegelemiseks või olete lihtsalt rohkem harjunud Bashi kasutama, on meie kolleegid koostanud valmislahenduse kujul kesta operaator (Me teatas see aprillis).