
Go on hetkel monopol kõigi programmeerimiskeelte seas, mida inimesed valivad Kubernetes'e operaatorite kirjutamiseks. Sellel on objektiivsed põhjused, nagu:
- Go jaoks on olemas kõige võimsam raamistik operaatorite arendamiseks — .
- Go-s on kirjutatud sellised 'mängu muutevad' rakendused nagu Docker ja Kubernetes. Oma operaatori kirjutamine Go-s tähendab rääkimist ökosüsteemiga ühes keeles.
- Go rakenduste kõrge jõudlus ja lihtsad tööriistad concurrency'ga töötamiseks 'karbist välja'.
NB: Muide, kuidas kirjutada oma operaatorit Go-s, oleme ühes meie tõlkes välismaistelt autoritelt.
Aga mis juhtub, kui Go õppimist takistab ajapuudus või lihtsalt motivatsiooni puudumine? Artiklis on näidatud, kuidas kirjutada korralik operaator, kasutades ühte populaarseimat keelt, mida tunneb praktiliselt iga DevOps-insener — Python.
Tutvustame: Kopiator — kopeerimisoperaator!
Võtame näiteks lihtsa operaatori, mis on mõeldud ConfigMap'i kopeerimiseks kas uue namespace'i ilmumisel või siis, kui üks kahest entiteedist: ConfigMap ja Secret muutub. Praktikas võib operaator olla kasulik rakenduse konfiguratsioonide massiliseks uuendamiseks (ConfigMap'i uuendamise kaudu) või salajaste andmete, näiteks Docker Registry võtmete uuendamiseks (kui Secret lisatakse namespace'i).
Nii et, mida peaks head operaatorit olema:
- Operaatoriga suhtlemine toimub (edasi — CRD).
- Operaatorit saab konfigureerida. Selleks kasutame käsurea lippu ja keskkonnamuutujaid.
- Docker-konteineri ja Helm-chart'i koostamine on tehtud nii, et kasutajad saaksid operaatori oma Kubernetes-i klastrisse hõlpsasti (literally ühe käsu) paigaldada.
CRD
Kuna operaator peab teadma, milliseid ressursse ja kust otsida, peame talle reegli määrama. Iga reegel esitatakse ühe CRD objektina. Millised väljad peaksid olema sellel CRD-l?
- Ressurssi tüüp, mida me otsime (ConfigMap või Secret).
- Namespace'ide nimekiri, milles peavad olema ressursid.
- Valija, mille põhjal otsime ressursse namespace's.
Kirjeldame CRD-d:
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 Ja loome kohe lihtsa reegli — otsimiseks namespace's nimega default kõik ConfigMap'id, millel on sildid kujul copyrator: "true":
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: defaultValmis! Nüüd peame kuidagi saama teavet meie reegli kohta. Ütleksin kohe, et me ei hakka iseseisvalt kirjutama päringuid API Server'ile klastris. Selleks kasutame valmis Python'i teeki :
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')}Selle koodi täitmise tulemusena saame järgmise:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}Suurepärane: saime operaatori reegli kätte. Ja mis kõige tähtsam - me viisime selle ellu, nagu öeldakse, Kubernetes'i moodi.
Keskkonnamuutujad või lipud? Võtame kõik!
Liigume edasi operaatori põhikonfiguratsiooni juurde. Rakenduste konfigureerimiseks on kaks põhiviisi:
- kasutada käsurea parameetreid;
- kasutada keskkonnamuutujaid.
Käsurea parameetrid võimaldavad lugeda seadeid paindlikumalt, toetades andmetüüpide valideerimist. Python'i standardraamatukogus on moodul argparser, mida me ka kasutame. Üksikasjad ja näited selle võimaluste kohta on saadaval .
Nii näeb välja meie juhtum, kuidas käsurea lippude lugemise seadistamine välja näeb:
parser = ArgumentParser(
description='Copyrator - kopeerimise operaator.',
prog='copyrator'
)
parser.add_argument(
'--namespace',
type=str,
default=getenv('NAMESPACE', 'default'),
help='Operaatori nimetusruum'
)
parser.add_argument(
'--rule-name',
type=str,
default=getenv('RULE_NAME', 'main-rule'),
help='CRD nimi'
)
args = parser.parse_args()Teisest küljest, Kubernetesis keskkonnamuutujate abil, on lihtne edastada teenuse teavet pod'i konteinerisse. Näiteks, et saada teavet nimetusruumi kohta, milles pod töötab, saame kasutada järgmist konstruktsiooni:
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace Operaatori tööloogika
Kuna mõistame, kuidas eraldada meetodeid ConfigMap'i ja Secret'i töötlemiseks, kasutame spetsiaalseid kaarte. Nii saame aru, milliseid meetodeid vajame objekti jälgimiseks ja loomiseks:
LIST_TYPES_MAP = {
'configmap': 'list_namespaced_config_map',
'secret': 'list_namespaced_secret',
}
CREATE_TYPES_MAP = {
'configmap': 'create_namespaced_config_map',
'secret': 'create_namespaced_secret',
}Seejärel on vajalik saada sündmusi API serverilt. Rakendame seda järgmiselt:
def handle(specs):
kubernetes.config.load_incluster_config()
v1 = kubernetes.client.CoreV1Api()
# Saame meetodi objekti jälgimiseks
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)Esmalt, pärast sündmuse saamist, läheme edasi selle põhiloogikale:
# Типы событий, на которые будем реагировать
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 tuleb kõik see pakkida ühte Python paketisse. Loome faili setup.py, kirjutame sinna projekti metainformatsiooni:
from sys import version_info
from setuptools import find_packages, setup
if version_info[:2] < (3, 5):
raise RuntimeError(
'Toetamata python versioon %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: Kubernetes klient Pythonis on oma versioonimise süsteem. Rohkem teavet kliendi ja Kubernetes versioonide ühilduvuse kohta leiate .
Praegu näeb meie projekt välja järgmine:
copyrator
├── copyrator
│ ├── cli.py # Käsureegli töötamise loogika
│ ├── constant.py # Ülaltoodud konstantsed väärtused
│ ├── load_crd.py # CRD laadimise loogika
│ └── operator.py # Operaatori põhiloogika
└── setup.py # Pakendi koostamineDocker ja Helm
Dockerfile saab olema äärmiselt lihtne: võtame aluseks pildi python-alpine ja installime meie paketi. Selle optimeerimise lükkame edasi paremate aegade jaoks:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]Operaatori juurutamine on samuti väga lihtne:
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 }}-accLõpuks on vajalik luua operaatori jaoks sobiv roll vajalike õigustega:
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 }}Kokkuvõte
Nii, kartmata, süüdistamata ja Go õpinguteta suutsime me Pythonit kasutades luua oma Kubernetesi operaatori. Loomulikult on selles veel arenguruumi: tulevikus suudab see töödelda mitmeid reegleid, töötada mitmes voos ja jälgida oma CRD-de muudatusi...
Selleks, et koodiga lähemalt tutvuda, oleme selle kokku pannud . Kui soovite näha tõsisemaid operaatoreid, mis on rakendatud Pythonis, võite pöörata tähelepanu kahele mongodb käitamise operaatorile ( ja ).
P.S. Ja kui teile ei meeldi Kubernetesi sündmuste uurimine või teile on mugavam kasutada Bash'i — meie kolleegid on valmistanud valmis lahenduse (me sellest aprillis).
P.P.S.
Lugege ka meie blogist:
- «»;
- «»;
- «»;
- «»;
- «».
Allikas: habr.com
