
Go është aktualisht një monopol në mesin e gjuhëve të programimit që njerëzit zgjedhin për të shkruar operatorët për Kubernetes. Kësaj i japin arsyet objektive si:
- Ekziston një kornizë e fuqishme për zhvillimin e operatorëve në Go — .
- Në Go janë shkruar aplikacione që kanë 'transformuar lojën' si Docker dhe Kubernetes. Të shkruash operatorin tënd në Go do të thotë të flasësh me ekosistemën në një gjuhë.
- Performanca e lartë e aplikacioneve në Go dhe mjetet e thjeshta për punë me concurrency 'nga kutia'.
NB: Sidoqoftë, si të shkruash operatorin tënd në Go, ne në një nga përkthimet tona nga autorë të huaj.
Por çfarë nëse mungesa e kohës apo, thjesht, motivimi, po të pengon që të mësosh Go? Artikulli ilustron një shembull se si mund të shkruash një operator të mirë duke përdorur një nga gjuhët më të njohura, të cilën gati çdo inxhinier DevOps e njeh — Python.
Njoftoni: Koptirator — operatori i kopjimit!
Si shembull, le të shqyrtojmë zhvillimin e një operatori të thjeshtë, i destinuar për të kopjuar ConfigMap ose në momentin e shfaqjes së një namespace të ri, ose kur ndryshon një nga dy njësi: ConfigMap dhe Secret. Nga një këndvështrim praktik, operatori mund të jetë i dobishëm për azhurnimin masiv të konfigurimeve të aplikacionit (nëpërmjet azhurnimit të ConfigMap) ose për azhurnimin e të dhënave sekrete — për shembull, çelësave për përdorimin me Docker Registry (kur shtohet një Secret në namespace).
Pra, çfarë duhet të ketë një operator i mirë:
- Interaksioni me operatorin bëhet përmes (më pas — CRD).
- Operatori mund të konfigurohet. Për këtë do të përdorim flamujt e komandave dhe variablat e mjedisit.
- Ndërtimi i kontejnerit Docker dhe Helm-chartet janë përpunuar në atë mënyrë që përdoruesit të mund të instalojnë lehtësisht (praktikisht me një komandë) operatorin në klasterin e tyre Kubernetes.
CRD
Për të qenë në dijeni të burimeve dhe vendndodhjes së tyre, ne duhet t'i japim një rregull operatorit. Çdo rregull do të paraqitet si një objekt CRD. Çfarë fushash duhet të ketë ky CRD?
- Lloji i burimit, të cilin do të kërkojmë (ConfigMap ose Secret).
- Lista e namespaceve, në të cilat duhet të ndodhen burimet.
- Selector, sipas të cilit do të kërkojmë burimet në namespace.
Të përshkruajmë 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 Dhe menjëherë do të krijojmë një rregull të thjeshtë — për kërkimin në hapësirën emërtuese me emrin default të gjithë ConfigMap me etiketat e tipit copyrator: "true":
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: defaultKemi mbaruar! Tani duhet në një mënyrë të marrim informacion rreth rregullit tonë. Menjëherë theksoj se ne nuk do të shkruajmë vetë kërkesa për API Server-in e klustrit. Për këtë do të përdorim një bibliotekë të gatshme në Python :
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')}Si rezultat i këtij kodi do të marrim këtë:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}Shkëlqyeshëm: arritëm të marrim rregullin për operatorin. Dhe më e rëndësishmja — e bëmë këtë, siç thuhet, në stilin e Kubernetes.
Këto janë variablat e ambientit apo flamujt? Marrim gjithçka!
Tani kalojmë në konfigurimin kryesor të operatorit. Ka dy qasje bazë për konfigurimin e aplikacioneve:
- përdorimi i parametrave të linjës së komandës;
- përdorimi i variablave të ambientit.
Parametrat e linjës së komandës lejojnë leximin më të fleksibël të konfigurimeve, me mbështetje dhe validim të llojeve të të dhënave. Në bibliotekën standarde të Python-it ka një modul argparser, të cilin do ta shfrytëzojmë. Detajet dhe shembujt e mundësive të tij janë të disponueshme në .
Ja si do të duket shembulli i konfigurimit për leximin e flamujve të linjës së komandës për rastin tonë:
parser = ArgumentParser(
description='Copyrator - operatori i kopjimit.',
prog='copyrator'
)
parser.add_argument(
'--namespace',
type=str,
default=getenv('NAMESPACE', 'default'),
help='Hapësira emërtuese e operatorit'
)
parser.add_argument(
'--rule-name',
type=str,
default=getenv('RULE_NAME', 'main-rule'),
help='Emri i CRD'
)
args = parser.parse_args()Në anën tjetër, duke përdorur variablat e ambientit në Kubernetes, mund të transferojmë lehtësisht informacionin shërbimor mbi pod-in brenda kontejnerit. Për shembull, informacionin mbi namespace, në të cilin pod-i është nisur, mund ta marrim me ndihmën e kësaj strukture:
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace Logjika e funksionimit të operatorit
Për të kuptuar se si t’i ndajmë metodat për të punuar me ConfigMap dhe Secret, do të përdorim hartat speciale. Kështu do të mund të kuptojmë cilat metoda na duhen për ndjekjen dhe krijimin e objektit:
LIST_TYPES_MAP = {
'configmap': 'list_namespaced_config_map',
'secret': 'list_namespaced_secret',
}
CREATE_TYPES_MAP = {
'configmap': 'create_namespaced_config_map',
'secret': 'create_namespaced_secret',
}Më pas, duhet të marrim ngjarjet nga API server. Këtë do ta realizojmë si më poshtë:
def handle(specs):
kubernetes.config.load_incluster_config()
v1 = kubernetes.client.CoreV1Api()
# Marrim metodën për ndjekjen e objekteve
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)Pas marrjes së ngjarjes, kalojmë në logjikën kryesore të përpunimit të saj:
# Типы событий, на которые будем реагировать
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) Logjika kryesore është gati! Tani duhet ta paketojmë gjithçka në një paketë Python. E paraqisim skedarin setup.py, shkruajmë atje metainformacionin mbi projektin:
from sys import version_info
from setuptools import find_packages, setup
if version_info[:2] < (3, 5):
raise RuntimeError(
'Versioni i pa mbështetur i python %s.' % '.'.join(version_info)
)
_NAME = 'copyrator'
setup(
name=_NAME,
version='0.0.1',
packages=find_packages(),
classifiers=[
'Statusi i Zhvillimit :: 3 - Alpha',
'Gjuha e Programimit :: Python',
'Gjuha e Programimit :: Python :: 3',
'Gjuha e Programimit :: Python :: 3.5',
'Gjuha e Programimit :: Python :: 3.6',
'Gjuha e Programimit :: Python :: 3.7',
],
author='Flant',
author_email='maksim.nabokikh@flant.com',
include_package_data=True,
install_requires=[
'kubernetes==9.0.0',
],
entry_points={
'console_scripts': [
'{0} = {0}.cli:main'.format(_NAME),
]
}
)NB: Klienti kubernetes për Python ka versionimin e tij. Më shumë për komplementaritetin e versioneve të klientit dhe versioneve të Kubernetes mund të mësohet nga .
Tani projekti ynë duket kështu:
copyrator
├── copyrator
│ ├── cli.py # Logjika e punës me komandën e linjës
│ ├── constant.py # Konstanta, të cilat i kemi shqyrtuar më parë
│ ├── load_crd.py # Logjika e ngarkimit të CRD
│ └── operator.py # Logjika kryesore e funksionimit të operatorit
└── setup.py # Paraqitja e paketësDocker dhe Helm
Dockerfile do të jetë tejet i thjeshtë: do të marrim imazhin bazë python-alpine dhe do të instalojmë paketën tonë. Optimizimin e saj do ta shtyjmë për kohë më të mira:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]Për operatorin, vendosja është gjithashtu shumë e thjeshtë:
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 }}-accMë në fund, është e nevojshme të krijohet një rol përkatës për operatorin me të drejtat e nevojshme:
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 }}Përfundimi
Ja si, pa frikë, përbuzje dhe pa studiuar Go, arritëm të ndërtojmë operatorin tonë për Kubernetes në Python. Sigurisht, ai ka ende shumë për të zhvilluar: në të ardhmen, mund të përballojë disa rregulla, të punojë në shumë procese, të monitorojë vetë ndryshimet në CRD-të e tij...
Për t'u njohur më afër me kodin, e kemi vendosur në . Nëse dëshironi shembuj të operatorëve më seriozë të implementuar me Python, mund ta ktheni vëmendjen tuaj ndaj dy operatorëve për implementimin e mongodb ( dhe ).
P.S. Nëse ju duket e lodhshme të kuptoni ngjarjet e Kubernetes ose thjesht preferoni të përdorni Bash — kolegët tanë kanë përgatitur një zgjidhje të gatshme në formën e (ne e publikojmë nëpril).
P.P.S.
Lexoni gjithashtu në blogun tonë:
- «»;
- «»;
- «»;
- «»;
- «».
Burimi: habr.com
