Kubernetes Operator ma Python me ka ʻole o nā papa hana a me SDK

Kubernetes Operator ma Python me ka ʻole o nā papa hana a me SDK

Loaʻa iā Go i kēia manawa kahi monopoli i nā ʻōlelo papahana e koho nā kānaka e kākau i nā ʻōlelo no Kubernetes. Aia nā kumu kūpono no kēia, e like me:

  1. Aia kahi papa hana ikaika no ka hoʻomohala ʻana i nā mea hana ma Go - SDK mea hana.
  2. Ua kākau ʻia nā noi hoʻololi pāʻani e like me Docker a me Kubernetes ma Go. ʻO ke kākau ʻana i kāu mea hoʻohana ma Go ʻo ia ka ʻōlelo ʻana i ka ʻōlelo like me ka kaiaola.
  3. ʻO ka hana kiʻekiʻe o nā noi Go a me nā mea hana maʻalahi no ka hana me ka concurrency ma waho o ka pahu.

NB: Ma ke ala, pehea e kākau ai i kāu ʻōlelo ponoʻī ma Go, mākou i wehewehe mua ʻia ma kekahi o kā mākou unuhi ʻia e nā mea kākau haole.

Akā, pehea inā ʻaʻole e aʻo ʻia ʻoe e hele ma muli o ka nele o ka manawa a i ʻole ka hoʻoikaika ʻana? Hāʻawi ka ʻatikala i kahi hiʻohiʻona pehea e hiki ai iā ʻoe ke kākau i kahi ʻōlelo maikaʻi me ka hoʻohana ʻana i kekahi o nā ʻōlelo kaulana loa i ʻike ʻia e kēlā me kēia ʻenekini DevOps - Python.

Hui: Copier - kope kope!

No ka laʻana, e noʻonoʻo e hoʻomohala i kahi ʻōlelo maʻalahi i hoʻolālā ʻia e kope i kahi ConfigMap i ka wā e ʻike ʻia ai kahi inoa inoa hou a i ʻole ke hoʻololi ʻia kekahi o nā mea ʻelua: ConfigMap a me Secret. Mai kahi hiʻohiʻona kūpono, hiki i ka mea hoʻohana ke hoʻohana no ka hoʻonui nui ʻana i nā hoʻonohonoho noi (ma ka hoʻonui ʻana i ka ConfigMap) a i ʻole no ka hoʻonui ʻana i ka ʻikepili huna - no ka laʻana, nā kī no ka hana ʻana me ka Docker Registry (i ka hoʻohui ʻana i ka Secret i ka namespace).

A pēlā, he aha ka mea e pono ai ka mea hoʻohana maikaʻi:

  1. Hoʻohana ʻia ka pilina me ka mea hoʻohana Nā wehewehe kumu waiwai maʻamau (i kapa ʻia ma hope aku ʻo CRD).
  2. Hiki ke hoʻonohonoho ʻia ka mea hoʻohana. No ka hana ʻana i kēia, e hoʻohana mākou i nā hae laina kauoha a me nā ʻano hoʻololi kaiapuni.
  3. Hoʻolālā ʻia ke kūkulu ʻana o ka pahu Docker a me ka pakuhi Helm i hiki i nā mea hoʻohana ke maʻalahi (me ka ʻōlelo hoʻokahi kauoha) e hoʻokomo i ka mea hoʻohana i kā lākou hui Kubernetes.

CRD

I mea e ʻike ai ka mea hoʻokele i nā kumuwaiwai e ʻimi ai a me kahi e nānā ai, pono mākou e kau i kahi lula nona. E hōʻike ʻia kēlā me kēia lula ma ke ʻano he mea CRD hoʻokahi. He aha nā kahua e pono ai kēia CRD?

  1. ʻAno waiwai, a mākou e ʻimi ai (ConfigMap a i ʻole Secret).
  2. Ka papa inoa o nā papa inoa, kahi e waiho ai na waiwai.
  3. Koho, ma laila mākou e ʻimi ai i nā kumuwaiwai ma ka inoa inoa.

E wehewehe kākou i ka 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

A e hana koke mākou lula maʻalahi — e huli i ka inoa me ka inoa default nā ConfigMap me nā lepili like copyrator: "true":

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

Mākaukau! I kēia manawa pono mākou e kiʻi i ka ʻike e pili ana i kā mākou lula. E ʻae mai iaʻu e hoʻopaʻa koke ʻaʻole mākou e kākau i nā noi i ka pūʻulu API Server iā mākou iho. No ka hana ʻana i kēia, e hoʻohana mākou i kahi waihona Python i mākaukau kubernetes-mea kūʻai:

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

Ma muli o ka holo ʻana i kēia code, loaʻa iā mākou kēia:

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

Nui: ua hiki iā mākou ke loaʻa kahi lula no ka mea hoʻohana. A ʻo ka mea nui, ua hana mākou i ka mea i kapa ʻia ʻo ke ala Kubernetes.

Nā loli kaiapuni a i ʻole nā ​​hae? Lawe mākou i nā mea a pau!

E neʻe kāua i ka hoʻonohonoho hoʻonohonoho nui. ʻElua mau ala kumu no ka hoʻonohonoho ʻana i nā noi:

  1. hoʻohana i nā koho laina kauoha;
  2. hoʻohana i nā mea hoʻololi kaiapuni.

ʻAe nā koho laina kauoha iā ʻoe e heluhelu i nā hoʻonohonoho ʻoi aku ka maʻalahi, me ke kākoʻo ʻano ʻikepili a me ka hōʻoia. Loaʻa i ka waihona maʻamau o Python kahi module argparser, a mākou e hoʻohana ai. Loaʻa nā kikoʻī a me nā hiʻohiʻona o kona hiki palapala kūhelu.

No kā mākou hihia, ʻo ia ke ʻano o ka hoʻonohonoho ʻana i nā hae laina kauoha e like me:

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

Ma ka ʻaoʻao ʻē aʻe, me ka hoʻohana ʻana i nā mea hoʻololi kaiapuni ma Kubernetes, hiki iā ʻoe ke hoʻololi maʻalahi i ka ʻike lawelawe e pili ana i ka pod i loko o ka ipu. No ka laʻana, hiki iā mākou ke loaʻa ka ʻike e pili ana i ka namespace kahi e holo ai ka pod me kēia hana:

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

Loko o ka mea hana

No ka hoʻomaopopo ʻana pehea e hoʻokaʻawale ai i nā ʻano hana no ka hana ʻana me ConfigMap a me Secret, e hoʻohana mākou i nā palapala ʻāina kūikawā. A laila hiki iā mākou ke hoʻomaopopo i nā ala e pono ai mākou e hahai a hana i ka mea:

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

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

A laila, pono ʻoe e loaʻa nā hanana mai ka server API. E hoʻokō kākou penei:

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)

Ma hope o ka loaʻa ʻana o ka hanana, neʻe mākou i ka loiloi nui o ka hoʻoili ʻana iā ia:

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

Ua mākaukau ka loina nui! I kēia manawa pono mākou e hoʻopili i kēia mau mea āpau i hoʻokahi pūʻulu Python. Hoʻomākaukau mākou i ka faila setup.py, e kākau i ka ʻike meta e pili ana i ka papahana ma laila:

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: Loaʻa i ka mea kūʻai kubernetes no Python kona mana ponoʻī. Hiki ke loaʻa ka ʻike hou aʻe e pili ana i ka launa pū ʻana ma waena o nā mana mea kūʻai aku a me nā mana Kubernetes nā matrices kūpono.

I kēia manawa ua like kā mākou papahana:

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

Docker a me Helm

E maʻalahi loa ka Dockerfile: e lawe i ke kiʻi python-alpine kumu a hoʻokomo i kā mākou pūʻolo. E hoʻopaneʻe kākou i kāna hoʻonui ʻana a hiki i nā manawa maikaʻi:

FROM python:3.7.3-alpine3.9

ADD . /app

RUN pip3 install /app

ENTRYPOINT ["copyrator"]

He maʻalahi loa ka hoʻonohonoho ʻana no ka mea hoʻohana:

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

ʻO ka hope, pono ʻoe e hana i kahi kuleana kūpono no ka mea hoʻohana me nā kuleana kūpono:

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

ʻO ka hopena

ʻO ia ke ʻano, me ka makaʻu ʻole, hōʻino, a i ʻole ke aʻo ʻana iā Go, hiki iā mākou ke kūkulu i kā mākou mea hoʻohana ponoʻī no Kubernetes ma Python. ʻOiaʻiʻo, loaʻa iā ia kahi lumi e ulu ai: i ka wā e hiki mai ana e hiki iā ia ke hoʻoponopono i nā lula he nui, hana i nā pae he nui, nānā kūʻokoʻa i nā loli i kāna CRDs...

No ka hāʻawi ʻana iā ʻoe i kahi nānā pono i ke code, ua hoʻokomo mākou i loko waihona lehulehu. Inā makemake ʻoe i nā hiʻohiʻona o nā mea hana koʻikoʻi i hoʻokō ʻia me ka hoʻohana ʻana iā Python, hiki iā ʻoe ke hoʻohuli i kou manaʻo i ʻelua mau mea hana no ka lawe ʻana i ka mongodb (первый и ʻo ka lua).

PS A inā palaualelo ʻoe i ka hana ʻana i nā hanana Kubernetes a i ʻole ʻoe i maʻa maʻalahi i ka hoʻohana ʻana iā Bash, ua hoʻomākaukau kā mākou mau hoa hana i kahi hoʻonā mākaukau i ke ʻano. mea hoʻohana pūpū (ʻO mākou kūkala ʻia ia i ʻApelila).

PPS

E heluhelu pū ma kā mākou blog:

Source: www.habr.com

Pākuʻi i ka manaʻo hoʻopuka