Kubernetes Operator σε Python χωρίς πλαίσια και SDK
Η Go έχει επί του παρόντος το μονοπώλιο στις γλώσσες προγραμματισμού που επιλέγουν οι άνθρωποι να γράφουν δηλώσεις για το Kubernetes. Υπάρχουν αντικειμενικοί λόγοι για αυτό, όπως:
Υπάρχει ένα ισχυρό πλαίσιο για την ανάπτυξη χειριστών στο Go - SDK χειριστή.
Οι εφαρμογές που αλλάζουν παιχνίδια όπως το Docker και το Kubernetes είναι γραμμένες στο Go. Το να γράψετε τον χειριστή σας στο Go σημαίνει να μιλάτε την ίδια γλώσσα με το οικοσύστημα.
Υψηλή απόδοση των εφαρμογών Go και απλά εργαλεία για εργασία με ταυτόχρονη χρήση.
NB: Παρεμπιπτόντως, πώς να γράψετε τη δική σας δήλωση στο Go, εμείς έχει ήδη περιγραφεί σε μια από τις μεταφράσεις μας από ξένους συγγραφείς.
Τι γίνεται όμως αν σας εμποδίσει να μάθετε Go λόγω έλλειψης χρόνου ή, με απλά λόγια, κινήτρων; Το άρθρο παρέχει ένα παράδειγμα για το πώς μπορείτε να γράψετε μια καλή δήλωση χρησιμοποιώντας μια από τις πιο δημοφιλείς γλώσσες που σχεδόν κάθε μηχανικός DevOps γνωρίζει - Python.
Γνωρίστε: Αντιγραφέας - χειριστής αντιγραφής!
Για παράδειγμα, εξετάστε το ενδεχόμενο να αναπτύξετε μια απλή πρόταση που έχει σχεδιαστεί για την αντιγραφή ενός ConfigMap είτε όταν εμφανίζεται ένας νέος χώρος ονομάτων είτε όταν αλλάζει μία από τις δύο οντότητες: ConfigMap και Secret. Από πρακτική άποψη, ο χειριστής μπορεί να είναι χρήσιμος για μαζική ενημέρωση των διαμορφώσεων εφαρμογών (με ενημέρωση του ConfigMap) ή για ενημέρωση μυστικών δεδομένων - για παράδειγμα, κλειδιά για εργασία με το Μητρώο Docker (όταν προσθέτετε Secret στον χώρο ονομάτων).
Ετσι, τι πρέπει να έχει ένας καλός χειριστής:
Η αλληλεπίδραση με τον χειριστή πραγματοποιείται χρησιμοποιώντας Προσαρμοσμένοι ορισμοί πόρων (εφεξής καλούμενο CRD).
Ο χειριστής μπορεί να διαμορφωθεί. Για να γίνει αυτό, θα χρησιμοποιήσουμε σημαίες γραμμής εντολών και μεταβλητές περιβάλλοντος.
Η κατασκευή του γραφήματος Docker container και Helm έχει σχεδιαστεί έτσι ώστε οι χρήστες να μπορούν εύκολα (κυριολεκτικά με μία εντολή) να εγκαταστήσουν τον χειριστή στο σύμπλεγμα Kubernetes τους.
CRD
Για να γνωρίζει ο χειριστής ποιους πόρους να αναζητήσει και πού να ψάξει, πρέπει να του βάλουμε έναν κανόνα. Κάθε κανόνας θα αντιπροσωπεύεται ως ένα ενιαίο αντικείμενο CRD. Ποια πεδία πρέπει να έχει αυτό το CRD;
Τύπος πόρου, που θα αναζητήσουμε (ConfigMap ή Secret).
Κατάλογος χώρων ονομάτων, στην οποία θα πρέπει να βρίσκονται οι πόροι.
Επιλογέας, με το οποίο θα αναζητήσουμε πόρους στον χώρο ονομάτων.
Και θα το δημιουργήσουμε αμέσως απλός κανόνας — για αναζήτηση στο χώρο ονομάτων με το όνομα default όλα τα ConfigMap με ετικέτες όπως copyrator: "true":
Ετοιμος! Τώρα πρέπει με κάποιο τρόπο να λάβουμε πληροφορίες για τον κανόνα μας. Επιτρέψτε μου να κάνω μια κράτηση αμέσως ότι δεν θα γράφουμε αιτήματα στον διακομιστή 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')}
Ως αποτέλεσμα της εκτέλεσης αυτού του κώδικα, λαμβάνουμε τα εξής:
Υπέροχα: καταφέραμε να πάρουμε έναν κανόνα για τον χειριστή. Και το πιο σημαντικό, κάναμε αυτό που ονομάζεται τρόπος Kubernetes.
Μεταβλητές περιβάλλοντος ή σημαίες; Παίρνουμε τα πάντα!
Ας προχωρήσουμε στη διαμόρφωση του κύριου χειριστή. Υπάρχουν δύο βασικές προσεγγίσεις για τη διαμόρφωση των εφαρμογών:
χρήση επιλογών γραμμής εντολών.
χρήση μεταβλητών περιβάλλοντος.
Οι επιλογές γραμμής εντολών σάς επιτρέπουν να διαβάζετε τις ρυθμίσεις πιο ευέλικτα, με υποστήριξη τύπων δεδομένων και επικύρωση. Η τυπική βιβλιοθήκη της Python έχει μια ενότητα argparser, που θα χρησιμοποιήσουμε. Λεπτομέρειες και παραδείγματα των δυνατοτήτων του είναι διαθέσιμα στο επίσημη τεκμηρίωση.
Για την περίπτωσή μας, αυτό είναι το παράδειγμα ρύθμισης σημαιών της γραμμής εντολών ανάγνωσης:
Από την άλλη πλευρά, χρησιμοποιώντας μεταβλητές περιβάλλοντος στο Kubernetes, μπορείτε εύκολα να μεταφέρετε πληροφορίες υπηρεσίας σχετικά με το pod μέσα στο κοντέινερ. Για παράδειγμα, μπορούμε να λάβουμε πληροφορίες σχετικά με τον χώρο ονομάτων στον οποίο εκτελείται το pod με την ακόλουθη κατασκευή:
Για να κατανοήσουμε πώς να διαχωρίσουμε τις μεθόδους εργασίας με το ConfigMap και το 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: Ο πελάτης kubernetes για Python έχει τη δική του έκδοση. Περισσότερες πληροφορίες σχετικά με τη συμβατότητα μεταξύ των εκδόσεων πελάτη και των εκδόσεων Kubernetes μπορείτε να βρείτε στο πίνακες συμβατότητας.
Τώρα το έργο μας μοιάζει με αυτό:
copyrator
├── copyrator
│ ├── cli.py # Логика работы с командной строкой
│ ├── constant.py # Константы, которые мы приводили выше
│ ├── load_crd.py # Логика загрузки CRD
│ └── operator.py # Основная логика работы оператора
└── setup.py # Оформление пакета
Docker and Helm
Το Dockerfile θα είναι απίστευτα απλό: πάρτε τη βασική εικόνα python-alpine και εγκαταστήστε το πακέτο μας. Ας αναβάλουμε τη βελτιστοποίησή του για καλύτερες στιγμές:
FROM python:3.7.3-alpine3.9
ADD . /app
RUN pip3 install /app
ENTRYPOINT ["copyrator"]
Η ανάπτυξη για τον χειριστή είναι επίσης πολύ απλή:
Έτσι, χωρίς φόβο, μομφή ή μαθαίνοντας το Go, καταφέραμε να δημιουργήσουμε τον δικό μας χειριστή για το Kubernetes στην Python. Φυσικά, έχει ακόμα χώρο να αναπτυχθεί: στο μέλλον θα μπορεί να επεξεργάζεται πολλούς κανόνες, να δουλεύει σε πολλαπλά νήματα, να παρακολουθεί ανεξάρτητα τις αλλαγές στα CRD του...
Για να σας δώσουμε μια πιο προσεκτική ματιά στον κώδικα, τον βάλαμε δημόσιο αποθετήριο. Εάν θέλετε παραδείγματα πιο σοβαρών τελεστών που υλοποιούνται χρησιμοποιώντας Python, μπορείτε να στρέψετε την προσοχή σας σε δύο τελεστές για την ανάπτυξη του mongodb (πρώτα и δεύτερος).
Υ.Γ Και αν είστε πολύ τεμπέλης για να ασχοληθείτε με εκδηλώσεις του Kubernetes ή απλά είστε πιο συνηθισμένοι να χρησιμοποιείτε το Bash, οι συνάδελφοί μας έχουν ετοιμάσει μια έτοιμη λύση στη μορφή κέλυφος-χειριστής (Εμείς ανακοινώθηκε τον Απρίλιο).