ProHoster > Blog > uprava > Kubernetes Operator u Pythonu bez okvira i SDK-a
Kubernetes Operator u Pythonu bez okvira i SDK-a
Go trenutno ima monopol nad programskim jezicima koje ljudi biraju za pisanje izjava za Kubernetes. Za to postoje objektivni razlozi kao što su:
Postoji snažan okvir za razvoj operatora u Gou - Operator SDK.
Aplikacije koje mijenjaju igru poput Dockera i Kubernetesa napisane su u Gou. Pisanje vašeg operatera u Go znači govoriti istim jezikom s ekosustavom.
Visoke performanse Go aplikacija i jednostavni alati za rad s paralelnošću odmah po otvaranju.
NB: Usput, kako napisati vlastitu izjavu u Go, we već opisano u jednom od naših prijevoda stranih autora.
Ali što ako vas u učenju Goa sprječava nedostatak vremena ili, jednostavno rečeno, motivacije? Članak daje primjer kako možete napisati dobru izjavu koristeći jedan od najpopularnijih jezika koji gotovo svaki DevOps inženjer zna - Piton.
Upoznajte: Copier - kopir operater!
Kao primjer, razmislite o razvoju jednostavne izjave dizajnirane za kopiranje ConfigMap-a bilo kada se pojavi novi imenski prostor ili kada se promijeni jedan od dva entiteta: ConfigMap i Secret. S praktične točke gledišta, operator može biti koristan za skupno ažuriranje konfiguracija aplikacije (ažuriranjem ConfigMapa) ili za ažuriranje tajnih podataka - na primjer, ključeva za rad s Docker registrom (prilikom dodavanja Secreta u imenski prostor).
Operator se može konfigurirati. Da bismo to učinili, koristit ćemo oznake naredbenog retka i varijable okoline.
Izrada Docker spremnika i Helm grafikona dizajnirana je tako da korisnici mogu jednostavno (doslovno jednom naredbom) instalirati operatora u svoj Kubernetes klaster.
CRD
Kako bi operater znao koje resurse treba tražiti i gdje tražiti, moramo mu postaviti pravilo. Svako pravilo bit će predstavljeno kao jedan CRD objekt. Koja polja treba sadržavati ovaj CRD?
Vrsta izvora, koje ćemo tražiti (ConfigMap ili Secret).
Popis imenskih prostora, u kojem bi se resursi trebali nalaziti.
Selektor, pomoću kojeg ćemo tražiti resurse u imenskom prostoru.
Spreman! Sada moramo nekako doći do informacija o našem pravilu. Dopustite mi da odmah napomenem da sami nećemo pisati zahtjeve API poslužitelju klastera. Da bismo to učinili, koristit ćemo gotovu Python biblioteku kubernetes-klijent:
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')}
Kao rezultat pokretanja ovog koda dobivamo sljedeće:
Super: uspjeli smo dobiti pravilo za operatera. I što je najvažnije, napravili smo ono što se zove Kubernetes način.
Varijable okruženja ili zastavice? Uzimamo sve!
Prijeđimo na glavnu konfiguraciju operatera. Postoje dva osnovna pristupa konfiguriranju aplikacija:
koristiti opcije naredbenog retka;
koristiti varijable okoline.
Opcije naredbenog retka omogućuju vam fleksibilnije čitanje postavki, uz podršku i provjeru vrste podataka. Pythonova standardna biblioteka ima modul argparser, koji ćemo koristiti. Pojedinosti i primjeri njegovih mogućnosti dostupni su u službena dokumentacija.
Za naš slučaj, ovako bi izgledao primjer postavljanja zastavica čitanja naredbenog retka:
S druge strane, korištenjem varijabli okruženja u Kubernetesu možete lako prenijeti servisne informacije o podu unutar spremnika. Na primjer, možemo dobiti informacije o prostoru imena u kojem se pod izvodi sa sljedećom konstrukcijom:
Da bismo razumjeli kako odvojiti metode za rad s ConfigMap i Secret, koristit ćemo posebne karte. Tada možemo razumjeti koje su nam metode potrebne za praćenje i stvaranje objekta:
Zatim trebate primati događaje s API poslužitelja. Implementirajmo to na sljedeći način:
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)
Nakon primitka događaja, prelazimo na glavnu logiku njegove obrade:
# Типы событий, на которые будем реагировать
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)
Glavna logika je spremna! Sada sve ovo trebamo upakirati u jedan Python paket. Pripremamo datoteku setup.py, tamo napišite meta informacije o projektu:
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: Kubernetes klijent za Python ima vlastitu verziju. Više informacija o kompatibilnosti između verzija klijenta i verzija Kubernetesa možete pronaći u matrice kompatibilnosti.
Sada naš projekt izgleda ovako:
copyrator
├── copyrator
│ ├── cli.py # Логика работы с командной строкой
│ ├── constant.py # Константы, которые мы приводили выше
│ ├── load_crd.py # Логика загрузки CRD
│ └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакета
Docker i Helm
Dockerfile će biti nevjerojatno jednostavan: uzmite osnovnu python-alpine sliku i instalirajte naš paket. Odgodimo njegovu optimizaciju do boljih vremena:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]
Implementacija za operatera također je vrlo jednostavna:
Tako smo, bez straha, prijekora ili učenja Goa, uspjeli izgraditi vlastiti operator za Kubernetes u Pythonu. Naravno, ima još prostora za rast: u budućnosti će moći obrađivati više pravila, raditi u više niti, samostalno pratiti promjene u svojim CRD-ovima...
Da bismo vam pobliže pogledali kôd, stavili smo ga javno spremište. Ako želite primjere ozbiljnijih operatora implementiranih pomoću Pythona, možete obratiti pažnju na dva operatora za implementaciju mongodb-a (первый и drugi).
PS A ako ste previše lijeni baviti se Kubernetes događajima ili ste jednostavno više navikli koristiti Bash, naši kolege su pripremili gotovo rješenje u obliku ljuska-operator (Mi najavio to u travnju).