
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
