Kubernetes ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ Π½Π° Python Π±Π΅Π· Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ²Π΅ ΠΈ SDK

Kubernetes ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ Π½Π° Python Π±Π΅Π· Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ²Π΅ ΠΈ SDK

Go Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° Π΅ монополист срСд ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ½ΠΈΡ‚Π΅ Π΅Π·ΠΈΡ†ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ Ρ…ΠΎΡ€Π°Ρ‚Π° ΠΈΠ·Π±ΠΈΡ€Π°Ρ‚ Π·Π° написванС Π½Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ΠΈ Π·Π° Kubernetes. Има ΠΎΠ±Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΈ ΠΏΡ€ΠΈΡ‡ΠΈΠ½ΠΈ Π·Π° Ρ‚ΠΎΠ²Π°, ΠΊΠ°Ρ‚ΠΎ:

  1. Π‘ΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π° ΠΌΠΎΡ‰Π΅Π½ Ρ„Ρ€Π΅ΠΉΠΌΡƒΡŠΡ€ΠΊ Π·Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ΠΈ Π½Π° Go β€” Operator SDK.
  2. На Go са написани прилоТСния, ΠΊΠΎΠΈΡ‚ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ…Π° ΠΈΠ³Ρ€Π°Ρ‚Π°, ΠΊΠ°Ρ‚ΠΎ Docker ΠΈ Kubernetes. Π”Π° Π½Π°ΠΏΠΈΡˆΠ΅Ρ‚Π΅ свой ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ Π½Π° Go ΠΎΠ·Π½Π°Ρ‡Π°Π²Π° Π΄Π° Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚Π΅ с СкосистСмата Π½Π° Π΅Π΄ΠΈΠ½ ΠΈ ΡΡŠΡ‰ Π΅Π·ΠΈΠΊ.
  3. Високата производитСлност Π½Π° прилоТСнията Π½Π° Go ΠΈ лСснитС инструмСнти Π·Π° Ρ€Π°Π±ΠΎΡ‚Π° с concurrency "ΠΎΡ‚ кутията".

NB: ΠœΠ΅ΠΆΠ΄Ρƒ Π΄Ρ€ΡƒΠ³ΠΎΡ‚ΠΎ, ΠΊΠ°ΠΊ Π΄Π° Π½Π°ΠΏΠΈΡˆΠ΅Ρ‚Π΅ свой ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ Π½Π° Go, Π½ΠΈΠ΅ Π²Π΅Ρ‡Π΅ описахмС Π² Π΅Π΄ΠΈΠ½ ΠΎΡ‚ Π½Π°ΡˆΠΈΡ‚Π΅ ΠΏΡ€Π΅Π²ΠΎΠ΄ΠΈ Π½Π° чуТдСстранни Π°Π²Ρ‚ΠΎΡ€ΠΈ.

Но ΠΊΠ°ΠΊΠ²ΠΎ, Π°ΠΊΠΎ ΠΈΠ·ΡƒΡ‡Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Go Π²ΠΈ ΠΏΡ€Π΅Ρ‡ΠΈ ΠΎΡ‚ΡΡŠΡΡ‚Π²ΠΈΠ΅Ρ‚ΠΎ Π½Π° Π²Ρ€Π΅ΠΌΠ΅ ΠΈΠ»ΠΈ Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚Π°Ρ€Π½ΠΎ, мотивация? Π’ статията Π΅ Π΄Π°Π΄Π΅Π½ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ΅ Π΄Π° сС напишС качСствСн ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ Π΅Π΄ΠΈΠ½ ΠΎΡ‚ Π½Π°ΠΉ-популярнитС Π΅Π·ΠΈΡ†ΠΈ, ΠΊΠΎΠΉΡ‚ΠΎ ΠΏΠΎΡ‡Ρ‚ΠΈ всСки DevOps ΠΈΠ½ΠΆΠ΅Π½Π΅Ρ€ ΠΏΠΎΠ·Π½Π°Π²Π° β€” Python.

ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²ΡΠΌΠ΅ Π²ΠΈ: ΠšΠΎΠΏΠΈΡ€Π°Ρ‚ΠΎΡ€ β€” ΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π»Π΅Π½ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€!

Π—Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Ρ‰Π΅ Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°Ρ‚Π° Π½Π° прост ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€, ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½ Π·Π° ΠΊΠΎΠΏΠΈΡ€Π°Π½Π΅ Π½Π° ConfigMap, ΠΊΠ°ΠΊΡ‚ΠΎ ΠΏΡ€ΠΈ появата Π½Π° Π½ΠΎΠ² namespace, Ρ‚Π°ΠΊΠ° ΠΈ ΠΏΡ€ΠΈ промяна Π½Π° Π΅Π΄Π½Π° ΠΎΡ‚ Π΄Π²Π΅Ρ‚Π΅ ΡΡŠΡ‰Π½ΠΎΡΡ‚ΠΈ: ConfigMap ΠΈ Secret. ΠžΡ‚ практичСска Π³Π»Π΅Π΄Π½Π° Ρ‚ΠΎΡ‡ΠΊΠ° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ΡŠΡ‚ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС ΠΏΠΎΠ»Π΅Π·Π΅Π½ Π·Π° масово ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π½Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ Π½Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ (Ρ‡Ρ€Π΅Π· ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π½Π° ConfigMap) ΠΈΠ»ΠΈ Π·Π° ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π½Π° сСкрСтни Π΄Π°Π½Π½ΠΈ β€” Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΊΠ»ΡŽΡ‡ΠΎΠ²Π΅ Π·Π° Ρ€Π°Π±ΠΎΡ‚Π° с Docker Registry (ΠΏΡ€ΠΈ добавянС Π½Π° Secret Π² namespace).

И Ρ‚Π°ΠΊΠ°, ΠΊΠ°ΠΊΠ²ΠΎ трябва Π΄Π° ΠΏΡ€ΠΈΡ‚Π΅ΠΆΠ°Π²Π° Π΅Π΄ΠΈΠ½ Π΄ΠΎΠ±ΡŠΡ€ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€:

  1. ВзаимодСйствиСто с ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° сС ΠΎΡΡŠΡ‰Π΅ΡΡ‚Π²ΡΠ²Π° Ρ‡Ρ€Π΅Π· Custom Resource Definitions (ΠΏΠΎ-Π΄ΠΎΠ»Ρƒ β€” CRD).
  2. ΠžΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ΡŠΡ‚ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС настроСн. Π—Π° Ρ†Π΅Π»Ρ‚Π° Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Ρ„Π»Π°Π³ΠΎΠ²Π΅ Π½Π° командния Ρ€Π΅Π΄ ΠΈ ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° срСдата.
  3. Π‘ΡŠΠ·Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Docker ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ ΠΈ Helm Π³Ρ€Π°Ρ„ΠΈΠΊ Π΅ ΠΎΡ€Π³Π°Π½ΠΈΠ·ΠΈΡ€Π°Π½ΠΎ Ρ‚Π°ΠΊΠ°, Ρ‡Π΅ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΠΈΡ‚Π΅ Π΄Π° ΠΌΠΎΠ³Π°Ρ‚ лСсно (Π±ΡƒΠΊΠ²Π°Π»Π½ΠΎ с Π΅Π΄Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°) Π΄Π° инсталират ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° Π² своя Kubernetes ΠΊΠ»ΡŠΡΡ‚Π΅Ρ€.

CRD

Π—Π° Π΄Π° Π·Π½Π°Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ΡŠΡ‚ ΠΊΠΎΠΈ рСсурси ΠΈ къдС Π΄Π° Ρ‚ΡŠΡ€ΡΠΈ, трябва Π΄Π° Π·Π°Π΄Π°Π΄Π΅ΠΌ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ Π·Π° Π½Π΅Π³ΠΎ. Всяко ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ Ρ‰Π΅ бъдС прСдставСно ΠΏΠΎΠ΄ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° Π½Π° Π΅Π΄ΠΈΠ½ ΠΎΠ±Π΅ΠΊΡ‚ CRD. Какви ΠΏΠΎΠ»Π΅Ρ‚Π° трябва Π΄Π° ΠΈΠΌΠ° Ρ‚ΠΎΠ·ΠΈ CRD?

  1. Π’ΠΈΠΏ рСсурс, ΠΊΠΎΠΉΡ‚ΠΎ Ρ‰Π΅ Ρ‚ΡŠΡ€ΡΠΈΠΌ (ConfigMap ΠΈΠ»ΠΈ Secret).
  2. Бписък Π½Π° namespace’итС, Π² ΠΊΠΎΠΈΡ‚ΠΎ трябва Π΄Π° сС Π½Π°ΠΌΠΈΡ€Π°Ρ‚ рСсурситС.
  3. Π‘Π΅Π»Π΅ΠΊΡ‚ΠΎΡ€, ΠΏΠΎ ΠΊΠΎΠΉΡ‚ΠΎ Ρ‰Π΅ Ρ‚ΡŠΡ€ΡΠΈΠΌ рСсурси Π² namespace.

НСка опишСм 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

И Π²Π΅Π΄Π½Π°Π³Π° Ρ‰Π΅ създадСм простило ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ β€” Π·Π° Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅ Π² namespace с ΠΈΠΌΠ΅ 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 Server Π½Π° кластСра. Π©Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Π³ΠΎΡ‚ΠΎΠ²Π° Python Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° 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')}

Π’ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ Π½Π° Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚Π° Π½Π° Ρ‚ΠΎΠ·ΠΈ ΠΊΠΎΠ΄ Ρ‰Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ слСдното:

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

ΠžΡ‚Π»ΠΈΡ‡Π½ΠΎ: успяхмС Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎΡ‚ΠΎ Π·Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°. И Π½Π°ΠΉ-Π²Π°ΠΆΠ½ΠΎΡ‚ΠΎ β€” Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ Π³ΠΎ ΠΏΠΎ Π½Π°Ρ‡ΠΈΠ½Π°, ΠΏΠΎ ΠΊΠΎΠΉΡ‚ΠΎ сС ΠΏΡ€Π°Π²ΠΈ Π² Kubernetes.

ΠŸΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° срСдата ΠΈΠ»ΠΈ Ρ„Π»Π°Π³ΠΎΠ²Π΅? Π’Π·ΠΈΠΌΠ°ΠΌΠ΅ всичко!

ΠŸΡ€Π΅ΠΌΠΈΠ½Π°Π²Π°ΠΌΠ΅ към основната конфигурация Π½Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°. Има Π΄Π²Π° основни ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° Π·Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°Π½Π΅ Π½Π° прилоТСния:

  1. Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ Π½Π° командния Ρ€Π΅Π΄;
  2. Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° срСдата.

ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΡ‚Π΅ Π½Π° командния Ρ€Π΅Π΄ позволяват Π΄Π° сС Ρ‡Π΅Ρ‚Π°Ρ‚ настройки ΠΏΠΎ-гъвкаво, с ΠΏΠΎΠ΄Π΄Ρ€ΡŠΠΆΠΊΠ° ΠΈ Π²Π°Π»ΠΈΠ΄ΠΈΡ€Π°Π½Π΅ Π½Π° Ρ‚ΠΈΠΏΠΎΠ²Π΅ Π΄Π°Π½Π½ΠΈ. Π’ стандартната Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° Π½Π° Python ΠΈΠΌΠ° ΠΌΠΎΠ΄ΡƒΠ» argparser, ΠΊΠΎΠΉΡ‚ΠΎ Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅. ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎΡΡ‚ΠΈ ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΈ Π·Π° Π½Π΅Π³ΠΎΠ²ΠΈΡ‚Π΅ Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΠΈ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Π½Π°ΠΌΠ΅Ρ€ΠΈΡ‚Π΅ Π² ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»Π½Π°Ρ‚Π° докумСнтация.

Π•Ρ‚ΠΎ ΠΊΠ°ΠΊ Π·Π° нашия случай Ρ‰Π΅ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π·Π° настройка Π·Π° Ρ‡Π΅Ρ‚Π΅Π½Π΅ Π½Π° Ρ„Π»Π°Π³ΠΎΠ²Π΅Ρ‚Π΅ Π½Π° командния Ρ€Π΅Π΄:

   parser = ArgumentParser(
        description='Copyrator - copy ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€.',
        prog='copyrator'
    )
    parser.add_argument(
        '--namespace',
        type=str,
        default=getenv('NAMESPACE', 'default'),
        help='Namespace Π½Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°'
    )
    parser.add_argument(
        '--rule-name',
        type=str,
        default=getenv('RULE_NAME', 'main-rule'),
        help='ИмС на CRD'
    )
    args = parser.parse_args()

ΠžΡ‚ Π΄Ρ€ΡƒΠ³Π° страна, с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ Π½Π° срСдата Π² Kubernetes, ΠΌΠΎΠΆΠ΅ΠΌ лСсно Π΄Π° прСнСсСм слуТСбна информация Π·Π° pod Π²ΡŠΡ‚Ρ€Π΅ Π² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π°. НапримСр, информацията Π·Π° namespace, Π² ΠΊΠΎΠΉΡ‚ΠΎ Π΅ стартиран 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(
        'ΠΠ΅ΠΏΠΎΠ΄Π΄ΡŠΡ€ΠΆΠ°Π½Π° вСрсия Π½Π° python %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: ΠšΠ»ΠΈΠ΅Π½Ρ‚ΡŠΡ‚ kubernetes Π·Π° Python ΠΈΠΌΠ° собствСно вСрсиониранС. ΠŸΠΎΠ²Π΅Ρ‡Π΅ информация Π·Π° ΡΡŠΠ²ΠΌΠ΅ΡΡ‚ΠΈΠΌΠΎΡΡ‚Ρ‚Π° Π½Π° вСрсиитС Π½Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΈ вСрсиитС Π½Π° Kubernetes ΠΌΠΎΠΆΠ΅ Π΄Π° сС Π½Π°ΠΌΠ΅Ρ€ΠΈ Π² ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Π°Ρ‚Π° Π·Π° ΡΡŠΠ²ΠΌΠ΅ΡΡ‚ΠΈΠΌΠΎΡΡ‚.

Π‘Π΅Π³Π° Π½Π°ΡˆΠΈΡΡ‚ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° Ρ‚Π°ΠΊΠ°:

copyrator
β”œβ”€β”€ copyrator
β”‚   β”œβ”€β”€ cli.py # Π›ΠΎΠ³ΠΈΠΊΠ° Π·Π° Ρ€Π°Π±ΠΎΡ‚Π° с командния Ρ€Π΅Π΄
β”‚   β”œβ”€β”€ constant.py # ΠšΠΎΠ½ΡΡ‚Π°Π½Ρ‚ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ спомСнахмС ΠΏΠΎ-Π³ΠΎΡ€Π΅
β”‚   β”œβ”€β”€ load_crd.py # Π›ΠΎΠ³ΠΈΠΊΠ° Π·Π° Π·Π°Ρ€Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° CRD
β”‚   └── operator.py # Основна Π»ΠΎΠ³ΠΈΠΊΠ° Π½Π° Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚Π° Π½Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°
└── setup.py # ΠžΡ„ΠΎΡ€ΠΌΡΠ½Π΅ Π½Π° ΠΏΠ°ΠΊΠ΅Ρ‚Π°

Docker ΠΈ Helm

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, Π½ΠΈΠ΅ успяхмС Π΄Π° създадСм свой собствСн ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ Π·Π° Kubernetes Π½Π° Python. Π Π°Π·Π±ΠΈΡ€Π° сС, Ρ‚ΠΎΠΉ всС ΠΎΡ‰Π΅ ΠΈΠΌΠ° ΠΊΠ°ΠΊΠ²ΠΎ Π΄Π° Ρ€Π°Π·Π²ΠΈΠ²Π°: Π² Π±ΡŠΠ΄Π΅Ρ‰Π΅ Ρ‰Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π²Π° няколко ΠΏΡ€Π°Π²ΠΈΠ»Π°, Π΄Π° Ρ€Π°Π±ΠΎΡ‚ΠΈ Π² ΠΏΠ°Ρ€Π°Π»Π΅Π», Π΄Π° наблюдава ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚Π΅ Π² своитС CRD...

Π—Π° Π΄Π° сС Π·Π°ΠΏΠΎΠ·Π½Π°Π΅Ρ‚Π΅ ΠΏΠΎ-Π΄ΠΎΠ±Ρ€Π΅ с ΠΊΠΎΠ΄Π°, Π½ΠΈΠ΅ смС Π³ΠΎ ΡΡŠΠ±Ρ€Π°Π»ΠΈ Π² ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ. Ако искатС ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΈ Π½Π° ΠΏΠΎ-сСриозни ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ΠΈ, Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°Π½ΠΈ с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° Python, ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΎΠ±ΡŠΡ€Π½Π΅Ρ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° Π΄Π²Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° Π·Π° Ρ€Π°Π·Π³Ρ€ΡŠΡ‰Π°Π½Π΅ Π½Π° mongodb (ΠΏΡŠΡ€Π²ΠΈ ΠΈ Π²Ρ‚ΠΎΡ€Π° част Π½Π° Ρ†ΠΈΠΊΡŠΠ»Π°). Накратко Ρ‰Π΅ повторя:).

P.S. А Π°ΠΊΠΎ Π²ΠΈ ΠΌΡŠΡ€Π·ΠΈ Π΄Π° сС Π·Π°ΠΏΠΎΠ·Π½Π°Π²Π°Ρ‚Π΅ с ΡΡŠΠ±ΠΈΡ‚ΠΈΡΡ‚Π° Π½Π° Kubernetes ΠΈΠ»ΠΈ просто Π²ΠΈ Π΅ ΠΏΠΎ-Π½ΠΎΡ€ΠΌΠ°Π»Π½ΠΎ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ Bash β€” Π½Π°ΡˆΠΈΡ‚Π΅ ΠΊΠΎΠ»Π΅Π³ΠΈ ΠΏΡ€ΠΈΠ³ΠΎΡ‚Π²ΠΈΡ…Π° Π³ΠΎΡ‚ΠΎΠ²ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° Π½Π° shell-operator (Π½ΠΈΠ΅ обявиха Π³ΠΎ ΠΏΡ€Π΅Π· Π°ΠΏΡ€ΠΈΠ»).

P.P.S.

ΠŸΡ€ΠΎΡ‡Π΅Ρ‚Π΅Ρ‚Π΅ ΡΡŠΡ‰ΠΎ Π² нашия Π±Π»ΠΎΠ³:

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ πŸ”₯ ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ | ProHoster