Хүрээ болон SDK-гүй Python дээрх Kubernetes оператор

Хүрээ болон SDK-гүй Python дээрх Kubernetes оператор

Go нь одоогоор Kubernetes-д зориулж мэдэгдэл бичихээр сонгосон програмчлалын хэл дээр монополь эрхтэй. Үүний объектив шалтгаанууд байдаг, тухайлбал:

  1. Go-д операторуудыг хөгжүүлэх хүчирхэг тогтолцоо бий. SDK оператор.
  2. Docker, Kubernetes гэх мэт тоглоомыг өөрчилдөг программуудыг Go дээр бичсэн. Go-д оператороо бичнэ гэдэг нь экосистемтэй нэг хэлээр ярьдаг гэсэн үг.
  3. Go програмуудын өндөр гүйцэтгэл, хайрцагнаас гадуур зэрэгцэн ажиллах энгийн хэрэгслүүд.

NB: Дашрамд хэлэхэд Go-д өөрийн мэдэгдлийг хэрхэн бичих вэ, бид аль хэдийн тайлбарласан гадаадын зохиолчдын бидний орчуулсан нэгэнд.

Гэхдээ цаг дутмаг, энгийнээр хэлбэл урам зоригоос болж та Go сурахад саад болж байвал яах вэ? Энэ нийтлэлд бараг бүх DevOps инженерүүдийн мэддэг хамгийн алдартай хэлнүүдийг ашиглан хэрхэн сайн мэдэгдэл бичих жишээг өгсөн болно. Python.

Уулзах: Хувилагч - хуулбарлагч!

Жишээ болгон, шинэ нэрийн орон зай гарч ирэх эсвэл ConfigMap ба Нууц гэсэн хоёр объектын аль нэг нь өөрчлөгдөх үед ConfigMap-ийг хуулах энгийн хэллэг боловсруулах талаар бодож үзээрэй. Практик талаас нь авч үзвэл оператор нь програмын тохиргоог бөөнөөр нь шинэчлэх (ConfigMap-ийг шинэчлэх замаар) эсвэл нууц өгөгдлийг шинэчлэхэд тустай байж болно - жишээлбэл, Docker Бүртгэлтэй ажиллах түлхүүрүүд (нэрийн зайд Нууцыг нэмэх үед).

Тэгэхээр сайн оператор ямар байх ёстой:

  1. Оператортой харилцах нь ашиглан хийгддэг Тусгай нөөцийн тодорхойлолтууд (цаашид CRD гэх).
  2. Операторыг тохируулах боломжтой. Үүнийг хийхийн тулд бид командын мөрийн туг болон орчны хувьсагчдыг ашиглана.
  3. Docker контейнер болон Helm диаграмыг бүтээх нь хэрэглэгчдэд хялбар (шууд утгаараа нэг тушаалаар) Кубернетес кластерт операторыг суулгах боломжтой байхаар бүтээгдсэн.

CRD

Оператор ямар нөөц хайх, хаанаас хайхаа мэдэхийн тулд бид түүнд дүрэм тогтоох хэрэгтэй. Дүрэм бүрийг нэг CRD объект хэлбэрээр харуулах болно. Энэ CRD ямар талбартай байх ёстой вэ?

  1. Нөөцийн төрөл, бид үүнийг хайх болно (ConfigMap эсвэл Нууц).
  2. Нэрийн орон зайн жагсаалт, аль нь нөөцийг байрлуулах ёстой.
  3. Сонгогч, үүгээр бид нэрийн талбараас нөөц хайх болно.

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

Мөн бид үүнийг шууд бүтээх болно энгийн дүрэм — нэрийн талбарт нэрээр хайх default гэх мэт шошготой бүх ConfigMap copyrator: "true":

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

Бэлэн! Одоо бид ямар нэгэн байдлаар дүрмийнхээ талаар мэдээлэл авах хэрэгтэй. Бид өөрсдөө кластерийн API серверт хүсэлт бичихгүй гэдгээ шууд захиалгаа өгье. Үүний тулд бид бэлэн Python номын санг ашиглах болно kubernetes-үйлчлүүлэгч:

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

Энэ кодыг ажиллуулсны үр дүнд бид дараахь зүйлийг авна.

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

Гайхалтай: бид операторын дүрмийг авч чадсан. Хамгийн гол нь бид Кубернетес арга гэж нэрлэгддэг зүйлийг хийсэн.

Орчны хувьсагч эсвэл туг? Бид бүгдийг нь авдаг!

Үндсэн операторын тохиргоо руу шилжье. Програмыг тохируулах хоёр үндсэн арга байдаг:

  1. тушаалын мөрийн сонголтыг ашиглах;
  2. орчны хувьсагчдыг ашиглах.

Тушаалын мөрийн сонголтууд нь өгөгдлийн төрлийн дэмжлэг, баталгаажуулалтын тусламжтайгаар тохиргоог илүү уян хатан унших боломжийг танд олгоно. Python-ийн стандарт номын сан нь модультай argparser, бид үүнийг ашиглах болно. Түүний чадварын талаархи дэлгэрэнгүй мэдээлэл, жишээг эндээс авах боломжтой албан ёсны баримт бичиг.

Манай тохиолдолд унших командын тугуудыг тохируулах жишээ ийм байна:

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

Нөгөөтэйгүүр, Kubernetes дахь орчны хувьсагчдыг ашигласнаар та савны доторх pod-ийн талаарх үйлчилгээний мэдээллийг хялбархан дамжуулах боломжтой. Жишээ нь, бид дараах бүтэцтэй pod ажиллаж байгаа нэрийн талбарын талаар мэдээлэл авах боломжтой.

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

Операторын логик

ConfigMap болон Secret-тэй ажиллах аргуудыг хэрхэн салгах талаар ойлгохын тулд бид тусгай газрын зураг ашиглах болно. Дараа нь бид объектыг хянах, бүтээхэд ямар аргууд хэрэгтэйг ойлгох болно:

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

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

Дараа нь та API серверээс үйл явдлуудыг хүлээн авах хэрэгтэй. Үүнийг дараах байдлаар хэрэгжүүлье.

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)

Үйл явдлыг хүлээн авсны дараа бид түүнийг боловсруулах үндсэн логик руу шилждэг.

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

Гол логик бэлэн боллоо! Одоо бид энэ бүгдийг нэг Python багц болгон багцлах хэрэгтэй. Бид файлыг бэлтгэдэг setup.py, төслийн талаархи мета мэдээллийг тэнд бичнэ үү:

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 клиент нь өөрийн хувилбартай. Үйлчлүүлэгчийн хувилбарууд болон Kubernetes хувилбаруудын нийцтэй байдлын талаарх дэлгэрэнгүй мэдээллийг эндээс авах боломжтой нийцтэй матрицууд.

Одоо бидний төсөл дараах байдалтай байна.

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

Докер ба Хелм

Dockerfile нь гайхалтай энгийн байх болно: үндсэн python-alpine зургийг аваад манай багцыг суулгаарай. Түүний оновчлолыг илүү сайн цаг хүртэл хойшлуулъя:

FROM python:3.7.3-alpine3.9

ADD . /app

RUN pip3 install /app

ENTRYPOINT ["copyrator"]

Операторыг байрлуулах нь бас маш энгийн:

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

Эцэст нь та шаардлагатай эрх бүхий операторын зохих үүргийг бий болгох хэрэгтэй.

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

Үр дүн

Ингэснээр бид айдас, зэмлэл, Go-г сурахгүйгээр Python дээр Kubernetes-д зориулж өөрийн операторыг бүтээх боломжтой болсон. Мэдээжийн хэрэг, энэ нь өсөх зайтай хэвээр байна: ирээдүйд энэ нь олон дүрмийг боловсруулах, олон хэлхээнд ажиллах, CRD-ийн өөрчлөлтийг бие даан хянах боломжтой болно ...

Кодыг илүү нарийвчлан үзэхийн тулд бид үүнийг орууллаа нийтийн агуулах. Хэрэв та Python ашиглан илүү ноцтой операторуудын жишээг авахыг хүсвэл mongodb (-г ашиглах хоёр оператор руу анхаарлаа хандуулаарай.первый и хоёрдугаарт).

Жич Хэрэв та Kubernetes-ийн үйл явдлуудтай харьцахаас залхуурсан эсвэл Bash-ийг ашиглахад илүү дассан бол манай хамт олон бэлэн шийдлийг хэлбэрээр бэлтгэсэн болно. бүрхүүлийн оператор (Бид зарласан XNUMX-р сард).

PPS

Мөн манай блог дээрээс уншина уу:

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх