Kubernetes Operator in Pythone sine tabulis et SDK

Kubernetes Operator in Pythone sine tabulis et SDK

Ire currently habet monopolium in programmatibus linguarum quae homines Kubernetes dictas scribere volunt. Rationes obiectivae huius rei sunt, ut:

  1. Est valida compages ad operatores promovendas in Go - Operator SDK.
  2. Applicationes lusus commutabiles sicut Docker et Kubernetes in Go scripta sunt. Operator tuus in Go scribens significat eandem linguam cum oecosystematis loqui.
  3. Excelsa observantia Ite applicationes et instrumenta simplicia ad operandum cum concurrentia ex archa.

NB: Obiter scribere sententiam tuam in Go, we? iam descriptus in uno e nostris translationibus exteris auctoribus.

Sed quid si a discendo prohiberis Ire intemperie temporis, vel, posito simpliciter, mo- mento? Articulus exemplum praebet quomodo scribere possis bonam enuntiationem utendo una e linguis popularibus quas fere omnes architectus DevOps scit - Python.

Opportunus: Scriba - exemplum operator!

Exempli gratia, considera explicans enuntiationem simplicem designatam ad exemplum ConfigMap vel cum novum spatium nominandi apparet vel cum unum e duobus rebus mutat: ConfigMap et Secret. Ex practica considerationis parte, auctor utilem esse potest ad molem adaequationis applicationis figurarum (adaequationem ConfigMap) vel ad adaequationem datam secretam - verbi gratia, claves ad operandum cum Subcriptio Docker (Secretum nomini spatii addens).

Sic, quid boni operantis habere:

  1. Commercium cum operator fit utens Consuetudo Resource Definitions (infra ut CRD).
  2. Auctor configurari potest. Ad hoc faciendum, vexilla linea et variabilium ambitu imperare utemur.
  3. Constructum Docker continentis et chart Helm designatum est ut utentes facile (litteram uno mandato) operatorem in botrum Kubernetes instituere possint.

CRD

Ut auctor sit scire, quae facultates quaerant et ubi quaeramus, necesse est ei regulam ponere. Quaelibet regula ut unum obiectum CRD repraesentabitur. Quid hoc CRD agri debent habere?

  1. Resource typequas exspectabimus (ConfigMap vel Secreta).
  2. Index spatiisin quo locantur opes.
  3. lego vestibulumper quam facultates in spatio nominali quaeramus.

CRD describemus:

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

Et nos statim creabimus simplex regula - quaerere in spatio nominali cum nomine default Omnes ConfigMap cum pittacia sicut copyrator: "true":

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

Paratus! Nunc opus est aliquo modo informationem de regula nostra accipere. Fac me statim reservationem facere quod petitiones botri API Servo ipsi non scribemus. Ad hoc utemur, bibliotheca Pythone parata kubernetes-client:

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

Ut ex hoc codice currens, sequentia dabimus:

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

Magna: auctoris regulam potuimus obtinere. Maximeque fecimus quod modo Kubernetes appellatur.

Environment variables vel vexilla? Omnia sumimus!

Ad principale operantis configurationem transeamus. Duae sunt praecipuae aditus ad applicationes conformandi:

  1. utere imperio lineae optiones;
  2. utuntur variabilibus environment.

Praecipe optiones rectae permittere te mollius legere unctiones, cum typum datarum auxilio et sanatio. Vexillum Pythonis bibliotheca moduli habet argparserqua utemur. Singula et exempla facultatum suarum in promptu sunt officialis documenta.

Pro nobis, hoc est quod exemplum constituendi legendi mandatum linea vexilla spectare placet;

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

Ex altera vero parte, variabilibus in Kubernetes ambitu adhibitis, informationes facile transferre potes de legumine intra continente. Exempli gratia, informationem de spatio nominali in quo vasculum currit cum hac constructione cognoscere possumus:

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

Operator logica

Ad intellegendum quomodo modos separandi operandi cum ConfigMap et Secreto, specialibus mappis utemur. Tunc comprehendi possumus quibus modis vestigemus ac crearemus obiectum;

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

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

Deinde res ab API servo accipere debes. Ad effectum adducamus hoc modo:

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)

Recepto eventu, ad summam logicam expediendam transgredimur:

# Типы событий, на которые будем реагировать
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)

Praecipua logica parata est! Nunc opus est omnia haec in unam Python sarcinam inicere. Praeparet tabella nobis setup.pyscribe meta informationem de re ibi;

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: Python kubernetes client suam versionem habet. Plura de compatibilitate inter clientes versiones et versiones Kubernetes inveniri possunt in convenientiam matrices.

Nunc consilium nostrum hoc spectat:

copyrator
├── copyrator
│   ├── cli.py # Логика работы с командной строкой
│   ├── constant.py # Константы, которые мы приводили выше
│   ├── load_crd.py # Логика загрузки CRD
│   └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакета

Docker et Helm

Dockerfile simplex erit incredibilis: basim python-alpinam imaginem sume et sarcinam nostram institue. Eius optimam differamus usque ad meliora tempora:

FROM python:3.7.3-alpine3.9

ADD . /app

RUN pip3 install /app

ENTRYPOINT ["copyrator"]

Auctoris instruere nimis simplex est;

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

Demum opus est creare munus congruum operanti cum iuribus necessariis;

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

exitum

Ita sine metu, opprobrio, aut discendo Ite, operatorem nostrum Kubernetes in Pythone construere potuimus. Utique, adhuc locum habet crescendi: in futuro multiplicare regulas poterit, in pluribus sequelis operari, independenter monitor mutationes in suis CRDs...

Ut propius inspiciamus in codicem, eum in repositio. Si exempla vis graviorum operatorium adhibitis Pythone adhibitis, animum advertere potes duobus operariis ad mongodb explicandum (первый и secundus).

PS Et si nimis piger es res cum Kubernetibus agere vel simpliciter solitus es uti Bash, collegae nostri in forma solutionem paratam paraverunt. testa-operator (Nos nuntiatum datum mense Aprili).

PPS

Lege etiam in nostro diario:

Source: www.habr.com

Add a comment