Kubernetes Operator katika Python bila mifumo na SDK

Kubernetes Operator katika Python bila mifumo na SDK

Go kwa sasa ina ukiritimba kwenye lugha za programu ambazo watu huchagua kuandika taarifa za Kubernetes. Kuna sababu za kusudi hili, kama vile:

  1. Kuna mfumo madhubuti wa kukuza waendeshaji katika Go - Opereta SDK.
  2. Programu za kubadilisha mchezo kama vile Docker na Kubernetes zimeandikwa katika Go. Kuandika opereta wako katika Go kunamaanisha kuzungumza lugha moja na mfumo ikolojia.
  3. Utendaji wa juu wa programu za Go na zana rahisi za kufanya kazi na sarafu nje ya boksi.

NB: Kwa njia, jinsi ya kuandika taarifa yako mwenyewe katika Go, sisi tayari imeelezwa katika mojawapo ya tafsiri zetu za waandishi wa kigeni.

Lakini vipi ikiwa umezuiwa kujifunza Nenda kwa kukosa muda au, kwa ufupi, motisha? Nakala hiyo inatoa mfano wa jinsi unavyoweza kuandika taarifa nzuri kwa kutumia moja ya lugha maarufu ambayo karibu kila mhandisi wa DevOps anajua - Chatu.

Kutana na: Copier - nakala operator!

Kama mfano, zingatia kutengeneza taarifa rahisi iliyoundwa kunakili ConfigMap ama wakati nafasi mpya ya majina inapotokea au wakati moja ya huluki mbili zinapobadilika: ConfigMap na Secret. Kwa mtazamo wa vitendo, mwendeshaji anaweza kuwa muhimu kwa uppdatering wa wingi wa usanidi wa programu (kwa kusasisha ConfigMap) au kwa kusasisha data ya siri - kwa mfano, funguo za kufanya kazi na Usajili wa Docker (wakati wa kuongeza Siri kwenye nafasi ya majina).

Hivyo, mwendeshaji mzuri anapaswa kuwa na nini:

  1. Mwingiliano na operator unafanywa kwa kutumia Ufafanuzi wa Rasilimali Maalum (hapa itajulikana kama CRD).
  2. Opereta inaweza kusanidiwa. Ili kufanya hivyo, tutatumia bendera za mstari wa amri na vigezo vya mazingira.
  3. Muundo wa kontena la Docker na chati ya Helm umeundwa ili watumiaji waweze kwa urahisi (halisi kwa amri moja) kusakinisha opereta kwenye nguzo yao ya Kubernetes.

CRD

Ili opereta ajue ni rasilimali gani ya kutafuta na wapi pa kuangalia, tunahitaji kuweka sheria kwa ajili yake. Kila kanuni itawakilishwa kama kitu kimoja cha CRD. Je, CRD hii inapaswa kuwa na nyanja gani?

  1. Aina ya rasilimali, ambayo tutatafuta (ConfigMap au Siri).
  2. Orodha ya nafasi za majina, ambayo rasilimali zinapaswa kuwepo.
  3. Uchaguzi, ambayo tutatafuta rasilimali katika nafasi ya majina.

Hebu tueleze 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

Na tutaiunda mara moja kanuni rahisi β€” kutafuta katika nafasi ya majina na jina default ConfigMap yote yenye lebo kama copyrator: "true":

apiVersion: flant.com/v1
kind: CopyratorRule
metadata:
  name: main-rule
  labels:
    module: copyrator
ruleType: configmap
selector:
  copyrator: "true"
namespace: default

Tayari! Sasa tunahitaji kwa namna fulani kupata habari kuhusu utawala wetu. Acha niweke nafasi mara moja kwamba hatutaandika maombi kwa Seva ya API ya nguzo sisi wenyewe. Ili kufanya hivyo, tutatumia maktaba ya Python tayari kubernetes-mteja:

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')}

Kama matokeo ya kuendesha nambari hii, tunapata yafuatayo:

{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}

Kubwa: tuliweza kupata sheria kwa operator. Na muhimu zaidi, tulifanya kile kinachoitwa njia ya Kubernetes.

Vigezo vya mazingira au bendera? Tunachukua kila kitu!

Hebu tuendelee kwenye usanidi mkuu wa operator. Kuna njia mbili za kimsingi za kusanidi programu:

  1. tumia chaguzi za mstari wa amri;
  2. tumia vigezo vya mazingira.

Chaguo za mstari wa amri hukuruhusu kusoma mipangilio kwa urahisi zaidi, kwa usaidizi wa aina ya data na uthibitishaji. Maktaba ya kawaida ya Python ina moduli argparser, ambayo tutatumia. Maelezo na mifano ya uwezo wake zinapatikana katika nyaraka rasmi.

Kwa upande wetu, hivi ndivyo mfano wa kusanidi bendera za mstari wa amri ungeonekana kama:

   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()

Kwa upande mwingine, kwa kutumia vigezo vya mazingira katika Kubernetes, unaweza kuhamisha kwa urahisi taarifa za huduma kuhusu ganda ndani ya kontena. Kwa mfano, tunaweza kupata habari kuhusu nafasi ya majina ambayo ganda linaendeshwa na ujenzi ufuatao:

env:
- name: NAMESPACE
  valueFrom:
     fieldRef:
         fieldPath: metadata.namespace 

Mantiki ya waendeshaji

Ili kuelewa jinsi ya kutenganisha mbinu za kufanya kazi na ConfigMap na Siri, tutatumia ramani maalum. Kisha tunaweza kuelewa ni njia gani tunahitaji kufuatilia na kuunda kitu:

LIST_TYPES_MAP = {
    'configmap': 'list_namespaced_config_map',
    'secret': 'list_namespaced_secret',
}

CREATE_TYPES_MAP = {
    'configmap': 'create_namespaced_config_map',
    'secret': 'create_namespaced_secret',
}

Ifuatayo, unahitaji kupokea matukio kutoka kwa seva ya API. Wacha tuitekeleze kama ifuatavyo:

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)

Baada ya kupokea tukio hilo, tunaendelea na mantiki kuu ya kusindika:

# Π’ΠΈΠΏΡ‹ событий, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±ΡƒΠ΄Π΅ΠΌ Ρ€Π΅Π°Π³ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ
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)

Mantiki kuu iko tayari! Sasa tunahitaji kusanikisha haya yote kwenye kifurushi kimoja cha Python. Tunatayarisha faili setup.py, andika habari ya meta kuhusu mradi hapo:

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='[email protected]',
    include_package_data=True,
    install_requires=[
        'kubernetes==9.0.0',
    ],
    entry_points={
        'console_scripts': [
            '{0} = {0}.cli:main'.format(_NAME),
        ]
    }
)

NB: Mteja wa kubernetes wa Python ana toleo lake mwenyewe. Maelezo zaidi kuhusu utangamano kati ya matoleo ya mteja na matoleo ya Kubernetes yanaweza kupatikana katika matrices ya utangamano.

Sasa mradi wetu unaonekana kama hii:

copyrator
β”œβ”€β”€ copyrator
β”‚   β”œβ”€β”€ cli.py # Π›ΠΎΠ³ΠΈΠΊΠ° Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строкой
β”‚   β”œβ”€β”€ constant.py # ΠšΠΎΠ½ΡΡ‚Π°Π½Ρ‚Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΠ»ΠΈ Π²Ρ‹ΡˆΠ΅
β”‚   β”œβ”€β”€ load_crd.py # Π›ΠΎΠ³ΠΈΠΊΠ° Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ CRD
β”‚   └── operator.py # Основная Π»ΠΎΠ³ΠΈΠΊΠ° Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°
└── setup.py # ΠžΡ„ΠΎΡ€ΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΏΠ°ΠΊΠ΅Ρ‚Π°

Docker na Helm

Dockerfile itakuwa rahisi sana: chukua picha ya msingi ya python-alpine na usakinishe kifurushi chetu. Wacha tuahirishe uboreshaji wake hadi nyakati bora:

FROM python:3.7.3-alpine3.9

ADD . /app

RUN pip3 install /app

ENTRYPOINT ["copyrator"]

Usambazaji kwa opereta pia ni rahisi sana:

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 }}-acc

Mwishowe, unahitaji kuunda jukumu linalofaa kwa mwendeshaji na haki zinazohitajika:

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 }}

Jumla ya

Hivyo ndivyo, bila woga, lawama, au kujifunza Go, tuliweza kuunda opereta wetu wenyewe kwa Kubernetes katika Python. Bila shaka, bado ina nafasi ya kukua: katika siku zijazo itakuwa na uwezo wa kuchakata sheria nyingi, kufanya kazi katika nyuzi nyingi, kufuatilia kwa kujitegemea mabadiliko katika CRD zake ...

Ili kukupa uangalizi wa karibu wa msimbo, tumeiweka hazina ya umma. Ikiwa unataka mifano ya waendeshaji wakubwa zaidi kutekelezwa kwa kutumia Python, unaweza kuelekeza mawazo yako kwa waendeshaji wawili kwa kupeleka mongodb (ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΠΈ pili).

PS Na ikiwa wewe ni mvivu sana kushughulikia hafla za Kubernetes au umezoea zaidi kutumia Bash, wenzetu wameandaa suluhisho lililotengenezwa tayari katika fomu. shell-operator (Sisi alitangaza mnamo Aprili).

PPS

Soma pia kwenye blogi yetu:

Chanzo: mapenzi.com

Kuongeza maoni