
Go Àr för nÀrvarande en monopollanguage bland programmeringssprÄk som mÀnniskor vÀljer för att skriva operatorer för Kubernetes. Det finns objektiva skÀl till detta, som:
- Det finns ett kraftfullt ramverk för att utveckla operatorer i Go â .
- Applikationer som har "förÀndrat spelreglerna" som Docker och Kubernetes Àr skrivna i Go. Att skriva sin egen operator i Go innebÀr att prata med ekosystemet pÄ ett gemensamt sprÄk.
- Hög prestanda för applikationer i Go och enkla verktyg för att hantera concurrency "direkt ur lÄdan".
NB: Förresten har vi redan beskrivit hur man skriver sin egen operator i Go Men vad gör man om brist pĂ„ tid eller helt enkelt brist pĂ„ motivation hindrar dig frĂ„n att studera Go? Artikeln ger ett exempel pĂ„ hur man kan skriva en solid operator med ett av de mest populĂ€ra sprĂ„ken som nĂ€stan varje DevOps-ingenjör kĂ€nner till â
Möt: Kopiatorn â kopieringsoperatorn! Python.
För vĂ„rt exempel kommer vi att titta pĂ„ utvecklingen av en enkel operator som Ă€r avsedd för att kopiera ConfigMap antingen vid skapandet av ett nytt namespace eller vid Ă€ndringar i en av tvĂ„ entiteter: ConfigMap och Secret. Ur ett praktiskt perspektiv kan operatorn vara anvĂ€ndbar för massiv uppdatering av applikationskonfigurationer (genom att uppdatera ConfigMap) eller för att uppdatera kĂ€nsliga data â till exempel nycklar för att arbeta med Docker Registry (vid tillĂ€gg av ett Secret i namespace).
vad som bör finnas hos en bra operator
SĂ„, Interaktion med operatorn sker genom:
- (hĂ€danefter â CRD). Operatorn kan konfigureras. För detta kommer vi att anvĂ€nda kommandoradsflaggor och miljövariabler.
- Byggandet av Docker-container och Helm-diagram Àr utformat sÄ att anvÀndarna enkelt kan (bokstavligen med ett kommando) installera operatorn i sitt Kubernetes-kluster.
- CRD
För att operatorn ska veta vilka resurser och var den ska söka, behöver vi definiera en regel för den. Varje regel kommer att representeras som ett enda CRD-objekt. Vilka fÀlt bör detta CRD ha?
, som vi kommer att söka efter (ConfigMap eller Secret).
- ResurstyperListan över namespace
- , dÀr resurserna ska finnas.VÀljare
- , som vi kommer att söka efter resurser i namespace.Vi beskriver CRD:
ĐпОŃĐ”ĐŒ 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 Och vi ska genast skapa en enkel regel â för att söka i namnomrĂ„det med namnet default alla ConfigMap med etiketter av typen copyrator: "true":
apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
name: main-rule
labels:
module: copyrator
ruleType: configmap
selector:
copyrator: "true"
namespace: defaultKlart! Nu mÄste vi pÄ nÄgot sÀtt fÄ information om vÄr regel. Jag vill genast pÄpeka att vi inte kommer att skriva förfrÄgningar till API-servern sjÀlva. Vi kommer att anvÀnda ett fÀrdigt Python-bibliotek :
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')}Som ett resultat av denna kod fÄr vi följande:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}Perfekt: vi lyckades fĂ„ regeln för operatören. Och det viktigaste â vi gjorde detta, sĂ„ att sĂ€ga, pĂ„ Kubernetes sĂ€tt.
Miljövariabler eller flaggor? Vi tar allt!
Vi gÄr vidare till huvudkonfigurationen för operatören. Det finns tvÄ grundlÀggande tillvÀgagÄngssÀtt för att konfigurera applikationer:
- anvÀnda kommandoradsparametrar;
- anvÀnda miljövariabler.
Kommandoradsparametrar gör det möjligt att lÀsa instÀllningar mer flexibelt, med stöd för och validering av datatyper. I Python-biblioteket finns en modul argparser, som vi kommer att anvÀnda. Detaljer och exempel pÄ dess möjligheter finns tillgÀngliga i .
SÄ hÀr ser ett exempel pÄ instÀllning av lÀsning av kommandoradsflaggor ut för vÄrt fall:
parser = ArgumentParser(
description='Copyrator - kopiera operatör.',
prog='copyrator'
)
parser.add_argument(
'--namespace',
type=str,
default=getenv('NAMESPACE', 'default'),
help='Operatörs NamnomrÄde'
)
parser.add_argument(
'--rule-name',
type=str,
default=getenv('RULE_NAME', 'main-rule'),
help='CRD Namn'
)
args = parser.parse_args()à andra sidan kan vi enkelt föra in tjÀnsteinformation om podden i containern med hjÀlp av miljövariabler i Kubernetes. Till exempel kan vi fÄ information om det namespace dÀr podden körs med följande konstruktion:
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace Operatorns arbetslogik
För att förstÄ hur vi delar upp metoderna för att arbeta med ConfigMap och Secret, anvÀnder vi speciella kartor. DÄ kan vi förstÄ vilka metoder vi behöver för att övervaka och skapa objekt:
LIST_TYPES_MAP = {
'configmap': 'list_namespaced_config_map',
'secret': 'list_namespaced_secret',
}
CREATE_TYPES_MAP = {
'configmap': 'create_namespaced_config_map',
'secret': 'create_namespaced_secret',
}DÀrefter mÄste vi fÄ hÀndelser frÄn API-servern. Vi implementerar detta pÄ följande sÀtt:
def handle(specs):
kubernetes.config.load_incluster_config()
v1 = kubernetes.client.CoreV1Api()
# HÀmta metoden för övervakning av objekt
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)Efter att ha mottagit hÀndelsen gÄr vi vidare till den huvudsakliga logiken för att bearbeta den:
# ĐąĐžĐżŃ ŃĐŸĐ±ŃŃĐžĐč, ĐœĐ° ĐșĐŸŃĐŸŃŃĐ” бŃĐŽĐ”ĐŒ ŃДагОŃĐŸĐČаŃŃ
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) Den huvudsakliga logiken Àr klar! Nu behöver vi packa allt detta i ett enda Python-paket. Vi upprÀttar filen setup.py, skriver in metadata om projektet:
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: Klienten kubernetes för Python har sin egen versionering. Mer om kompatibiliteten mellan klientversioner och Kubernetes-versioner kan du lÀsa i .
Just nu ser vÄrt projekt ut sÄ hÀr:
copyrator
âââ copyrator
â âââ cli.py # Logik för arbete med kommandoraden
â âââ constant.py # Konstanter som vi nĂ€mnde tidigare
â âââ load_crd.py # Logik för inlĂ€sning av CRD
â âââ operator.py # Huvudlogik för operatorn
âââ setup.py # Utformning av paketetDocker och Helm
Dockerfilen kommer att bli extremt enkel: vi tar basbilden python-alpine och installerar vÄrt paket. Dess optimering fÄr vÀnta tills senare:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]Distribution för operatören Àr ocksÄ mycket enkel:
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 }}-accSlutligen mÄste vi skapa en motsvarande roll för operatören med nödvÀndiga rÀttigheter:
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 }}Resultat
PÄ sÄ sÀtt, utan rÀdsla eller fördömelse och utan att studera Go, har vi lyckats skapa vÄr egen operatör för Kubernetes i Python. SjÀlvklart finns det mycket mer att utveckla: i framtiden kan den hantera flera regler, arbeta i flera trÄdar, och sjÀlvstÀndigt övervaka Àndringar i sina CRD...
För att du ska kunna lÀra kÀnna koden bÀttre har vi lagt den i . Om du Àr intresserad av exempel pÄ mer seriösa operatörer som har implementerats med Python kan du titta pÄ tvÄ operatörer för distribution av mongodb ( och ).
P.S. Och om du inte orkar förstĂ„ dig pĂ„ Kubernetes-hĂ€ndelser eller föredrar att anvĂ€nda Bash â vĂ„ra kollegor har förberett en fĂ€rdig lösning i form av (vi gjorde det i april).
P.P.S.
LÀs Àven i vÄr blogg:
- «»;
- «»;
- «»;
- «»;
- «».
KĂ€lla: habr.com
