
Go aktualisht ka një monopol në gjuhët e programimit që njerëzit zgjedhin të shkruajnë deklarata për Kubernetes. Ka arsye objektive për këtë, si p.sh.
- Ekziston një kornizë e fuqishme për zhvillimin e operatorëve në Go - .
- Aplikacionet që ndryshojnë lojën si Docker dhe Kubernetes janë shkruar në Go. Të shkruash operatorin tënd në Go do të thotë të flasësh të njëjtën gjuhë me ekosistemin.
- Performancë e lartë e aplikacioneve Go dhe mjete të thjeshta për të punuar me konkurencë jashtë kutisë.
NB: Nga rruga, si të shkruani deklaratën tuaj në Go, ne në një nga përkthimet tona nga autorë të huaj.
Por, çka nëse ju pengon të mësoni Go nga mungesa e kohës ose, thënë thjesht, motivimi? Artikulli ofron një shembull se si mund të shkruani një deklaratë të mirë duke përdorur një nga gjuhët më të njohura që di pothuajse çdo inxhinier DevOps - Piton.
Takoni: Kopjues - operator kopjimi!
Si shembull, merrni parasysh zhvillimin e një deklarate të thjeshtë të krijuar për të kopjuar një ConfigMap ose kur shfaqet një hapësirë e re emri ose kur ndryshon një nga dy entitetet: ConfigMap dhe Secret. Nga pikëpamja praktike, operatori mund të jetë i dobishëm për përditësimin masiv të konfigurimeve të aplikacionit (duke përditësuar ConfigMap) ose për përditësimin e të dhënave sekrete - për shembull, çelësat për të punuar me Regjistrin Docker (kur shtoni Secret në hapësirën e emrave).
Pra, çfarë duhet të ketë një operator i mirë:
- Ndërveprimi me operatorin kryhet duke përdorur (më tej referuar si CRD).
- Operatori mund të konfigurohet. Për ta bërë këtë, ne do të përdorim flamujt e linjës së komandës dhe variablat e mjedisit.
- Ndërtimi i grafikut të kontejnerit Docker dhe Helm është projektuar në mënyrë që përdoruesit të mund të instalojnë lehtësisht (fjalë për fjalë me një komandë) operatorin në grupin e tyre Kubernetes.
CRD
Në mënyrë që operatori të dijë se çfarë burimesh të kërkojë dhe ku të kërkojë, ne duhet të vendosim një rregull për të. Çdo rregull do të përfaqësohet si një objekt i vetëm CRD. Çfarë fushash duhet të ketë kjo CRD?
- Lloji i burimit, të cilin do ta kërkojmë (ConfigMap ose Secret).
- Lista e hapësirave të emrave, në të cilën duhet të vendosen burimet.
- Përzgjedhës, me anë të së cilës do të kërkojmë burime në hapësirën e emrave.
Le 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 ne do ta krijojmë atë menjëherë rregull i thjeshtë — për të kërkuar në hapësirën e emrave me emrin default të gjitha ConfigMap me etiketa si copyrator: "true":
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: defaultGati! Tani duhet të marrim disi informacione për rregullin tonë. Më lejoni të bëj një rezervim menjëherë se ne nuk do t'i shkruajmë kërkesat vetë serverit API të grupit. Për ta bërë këtë, ne do të përdorim një bibliotekë të gatshme 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 ekzekutimit të këtij kodi, marrim sa vijon:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}E shkëlqyeshme: arritëm të merrnim një rregull për operatorin. Dhe më e rëndësishmja, ne bëmë atë që quhet mënyra Kubernetes.
Variabla mjedisore apo flamuj? Ne marrim gjithçka!
Le të kalojmë te konfigurimi i operatorit kryesor. Ekzistojnë dy qasje themelore për konfigurimin e aplikacioneve:
- përdorni opsionet e linjës së komandës;
- përdorni variablat e mjedisit.
Opsionet e linjës së komandës ju lejojnë të lexoni cilësimet në mënyrë më fleksibël, me mbështetjen dhe vërtetimin e llojit të të dhënave. Biblioteka standarde e Python ka një modul argparser, të cilin do ta përdorim. Detajet dhe shembujt e aftësive të tij janë në dispozicion në .
Për rastin tonë, kështu do të dukej një shembull i konfigurimit të leximit të flamujve të linjës së komandës:
parser = ArgumentParser(
description='Copyrator - copy operator.',
prog='copyrator'
)
parser.add_argument(
'--namespace',
type=str,
default=getenv('NAMESPACE', 'default'),
help='Operator Namespace'
)
parser.add_argument(
'--rule-name',
type=str,
default=getenv('RULE_NAME', 'main-rule'),
help='CRD Name'
)
args = parser.parse_args()Nga ana tjetër, duke përdorur variablat e mjedisit në Kubernetes, mund të transferoni lehtësisht informacionin e shërbimit për podin brenda kontejnerit. Për shembull, ne mund të marrim informacion në lidhje me hapësirën e emrave në të cilën pod po funksionon me ndërtimin e mëposhtëm:
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace Logjika e operatorit
Për të kuptuar se si të veçojmë metodat për të punuar me ConfigMap dhe Secret, ne do të përdorim harta speciale. Atëherë mund të kuptojmë se cilat metoda na duhen për të gjurmuar dhe krijuar objektin:
LIST_TYPES_MAP = {
'configmap': 'list_namespaced_config_map',
'secret': 'list_namespaced_secret',
}
CREATE_TYPES_MAP = {
'configmap': 'create_namespaced_config_map',
'secret': 'create_namespaced_secret',
}Tjetra, ju duhet të merrni ngjarje nga serveri API. Le ta zbatojmë atë si më poshtë:
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)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 t'i paketojmë të gjitha këto në një paketë Python. Ne përgatisim dosjen setup.py, shkruani meta informacione rreth projektit atje:
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='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 versionin e tij. Më shumë informacion rreth përputhshmërisë midis versioneve të klientit dhe versioneve të Kubernetes mund të gjenden në .
Tani projekti ynë duket si ky:
copyrator
├── copyrator
│ ├── cli.py # Логика работы с командной строкой
│ ├── constant.py # Константы, которые мы приводили выше
│ ├── load_crd.py # Логика загрузки CRD
│ └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакетаDocker dhe Helm
Dockerfile do të jetë tepër e thjeshtë: merrni imazhin bazë python-alpin dhe instaloni paketën tonë. Le të shtyjmë optimizimin e tij deri në kohë më të mira:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]Vendosja për operatorin ë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 }}-accSë fundi, ju duhet të krijoni një rol të përshtatshëm 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 }}Total
Kështu, pa frikë, qortim ose duke mësuar Go, ne mundëm të ndërtonim operatorin tonë për Kubernetes në Python. Natyrisht, ajo ka ende hapësirë për t'u rritur: në të ardhmen do të jetë në gjendje të përpunojë rregulla të shumta, të punojë në fije të shumta, të monitorojë në mënyrë të pavarur ndryshimet në CRD-të e saj...
Për t'ju dhënë një vështrim më të afërt të kodit, ne e kemi vendosur atë . Nëse dëshironi shembuj të operatorëve më seriozë të zbatuar duke përdorur Python, mund ta ktheni vëmendjen tuaj te dy operatorë për vendosjen e mongodb ( и ).
PS Dhe nëse jeni shumë dembel për t'u marrë me ngjarjet e Kubernetes ose thjesht jeni mësuar të përdorni Bash, kolegët tanë kanë përgatitur një zgjidhje të gatshme në formën (Ne atë në prill).
PPS
Lexoni edhe në blogun tonë:
- «"?
- «"?
- «"?
- «"?
- «'.
Burimi: www.habr.com
