
Attualmente Go hà un monopoliu nantu à e lingue di prugrammazione chì a ghjente sceglie di scrive dichjarazioni per Kubernetes. Ci sò ragiuni oggettivi per questu, cum'è:
- Ci hè un quadru putente per u sviluppu di l'operatori in Go - .
- L'applicazioni chì cambianu u ghjocu cum'è Docker è Kubernetes sò scritte in Go. Scrivite u vostru operatore in Go significa parlà a stessa lingua cù l'ecosistema.
- Alte prestazioni di l'applicazioni Go è arnesi simplici per travaglià cù a simultaneità fora di a scatula.
NB: Per via, cumu scrive a vostra propria dichjarazione in Go, noi in una di e nostre traduzzioni da autori stranieri.
Ma chì s'ellu ùn hè impeditu di amparà Andà per mancanza di tempu o, simpliciamente, motivazione ? L'articulu furnisce un esempiu di cumu pudete scrive una bona dichjarazione utilizendu una di e lingue più populari chì quasi tutti l'ingegneri DevOps cunnosci - pitone.
Meet: Copiatore - operatore di copia!
Per esempiu, cunzidira à sviluppà una dichjarazione simplice pensata per copià un ConfigMap sia quandu un novu spaziu di nome appare o quandu una di duie entità cambia: ConfigMap è Secret. Da un puntu di vista praticu, l'operatore pò esse utile per l'aghjurnamentu di massa di e cunfigurazioni di l'applicazione (aghjurnendu u ConfigMap) o per l'aghjurnà di dati secreti - per esempiu, chjave per travaglià cù u Docker Registry (quandu aghjunghje Secret à u namespace).
Cusì, ciò chì un bon operatore deve avè:
- L'interazzione cù l'operatore hè realizatu cù l'usu (ci-après dénommé CRD).
- L'operatore pò esse cunfiguratu. Per fà questu, useremu bandieri di linea di cumanda è variabili di l'ambiente.
- A custruzione di u containeru Docker è u graficu Helm hè pensatu per chì l'utilizatori ponu facilmente (literalmente cun un cumandamentu) installà l'operatore in u so cluster Kubernetes.
CRD
Per chì l'operatore sappia quale risorse per circà è induve circà, avemu bisognu di stabilisce una regula per ellu. Ogni regula serà rapprisintata cum'è un oggettu CRD unicu. Chì campi deve avè stu CRD?
- Tipu di risorsa, chì avemu da circà (ConfigMap o Secret).
- Lista di spazii di nomi, in quale i risorse deve esse situatu.
- Selettore, da quale avemu da circà risorse in u namespace.
Descrivimu u 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 È avemu da creà subitu regula simplice - per circà in u namespace cù u nome default tutti ConfigMap cù etichette cum'è copyrator: "true":
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: defaultPronti ! Avà avemu bisognu di qualchì infurmazione nantu à a nostra regula. Lasciami fà una riservazione subitu chì ùn scriveremu micca richieste à u cluster API Server. Per fà questu, useremu una biblioteca Python pronta :
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')}In u risultatu di eseguisce stu codice, avemu i seguenti:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}Grande: avemu riesciutu à ottene una regula per l'operatore. È più impurtante, avemu fattu ciò chì hè chjamatu u modu Kubernetes.
Variabili d'ambiente o bandiere? Pigliemu tuttu !
Passemu à a cunfigurazione principale di l'operatore. Ci hè dui approcci basi per cunfigurà l'applicazioni:
- aduprà l'opzioni di linea di cumanda;
- aduprà variabili di ambiente.
L'opzioni di linea di cumanda permettenu di leghje i paràmetri in modu più flexible, cù supportu è validazione di tipu di dati. A biblioteca standard di Python hà un modulu argparser, chì avemu aduprà. I dettagli è esempi di e so capacità sò dispunibili in .
Per u nostru casu, questu hè ciò chì un esempiu di stallà i bandieri di a linea di cummanda di leghje:
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()Per d 'altra banda, utilizendu variabili di l'ambiente in Kubernetes, pudete facilmente trasfiriri l'infurmazioni di serviziu nantu à u pod in u containeru. Per esempiu, pudemu avè infurmazione nantu à u spaziu di nomi in quale u pod hè in esecuzione cù a seguente custruzzione:
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace Lògica di l'operatore
Per capisce cumu si separà i metudi per travaglià cù ConfigMap è Secret, avemu aduprà carte speciali. Allora pudemu capisce chì metudi avemu bisognu di seguità è creà l'ughjettu:
LIST_TYPES_MAP = {
'configmap': 'list_namespaced_config_map',
'secret': 'list_namespaced_secret',
}
CREATE_TYPES_MAP = {
'configmap': 'create_namespaced_config_map',
'secret': 'create_namespaced_secret',
}Dopu, avete bisognu di riceve avvenimenti da u servitore API. Implementemu cusì cusì:
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)Dopu avè ricivutu l'avvenimentu, andemu à a logica principale di trasfurmà:
# Типы событий, на которые будем реагировать
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) A logica principale hè pronta! Avà avemu bisognu di imballà tuttu questu in un pacchettu Python. Preparamu u schedariu setup.py, scrivite meta infurmazione nantu à u prugettu quì:
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: U cliente kubernetes per Python hà a so propria versione. Più infurmazione nantu à a cumpatibilità trà e versioni di u cliente è e versioni di Kubernetes pò esse truvata in .
Avà u nostru prughjettu s'assumiglia cusì:
copyrator
├── copyrator
│ ├── cli.py # Логика работы с командной строкой
│ ├── constant.py # Константы, которые мы приводили выше
│ ├── load_crd.py # Логика загрузки CRD
│ └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакетаDocker è Helm
U Dockerfile serà incredibbilmente simplice: pigliate l'imaghjini basati di python-alpine è installate u nostru pacchettu. Postponemu a so ottimisazione finu à tempi megliu:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]A implementazione per l'operatore hè ancu assai simplice:
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 }}-accInfine, avete bisognu di creà un rolu adattatu per l'operatore cù i diritti necessarii:
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 }}U risultatu
Hè cusì chì, senza paura, rimproveru, o amparà Go, pudemu custruisce u nostru propiu operatore per Kubernetes in Python. Di sicuru, hà sempre spaziu per cultivà: in u futuru puderà processà parechje regule, travaglià in parechje fili, monitorizà indipindente i cambiamenti in i so CRD ...
Per dà un ochju più vicinu à u codice, l'avemu messu in . Se vulete esempi di operatori più seri implementati cù Python, pudete turnà a vostra attenzione à dui operatori per implementà mongodb ( и ).
PS È s'è vo site troppu pigro per trattà cun l'avvenimenti di Kubernetes o sì solu più abituatu à aduprà Bash, i nostri culleghi anu preparatu una suluzione pronta in a forma (Noi in aprile).
PPS
Leghjite puru nant'à u nostru blog:
- «";
- «";
- «";
- «";
- «».
Source: www.habr.com
