
Go saat ini adalah perusahaan monopoli di antara bahasa pemrograman yang dipilih orang untuk menulis pernyataan untuk Kubernetes. Ada alasan obyektif seperti:
- Ada kerangka kerja yang kuat untuk mengembangkan operator di Go - Operator SDK .
- Go telah menulis aplikasi terbalik seperti Docker dan Kubernetes. Untuk menulis operator Anda sendiri di Go - berbicara bahasa yang sama dengan ekosistem.
- Aplikasi berkinerja tinggi di Go dan alat sederhana untuk bekerja dengan concurrency di luar kotak.
NB : Omong-omong, kami sudah menggambarkan cara menulis operator Anda sendiri di Go dalam salah satu terjemahan kami oleh penulis asing.Tetapi bagaimana jika belajar Go dicegah oleh kurangnya waktu atau, sepele, motivasi? Artikel ini memberikan contoh bagaimana Anda bisa menulis operator yang solid menggunakan salah satu bahasa paling populer yang hampir setiap insinyur DevOps tahu -
Python .
Bertemu: Copywriter - operator salin!
Sebagai contoh, pertimbangkan pengembangan operator sederhana yang dirancang untuk menyalin ConfigMap baik ketika ruang nama baru muncul, atau ketika salah satu dari dua entitas berubah: ConfigMap dan Secret. Dari sudut pandang aplikasi praktis, operator dapat berguna untuk memperbarui secara massal konfigurasi aplikasi (dengan memperbarui ConfigMap) atau untuk memperbarui data rahasia - misalnya, kunci untuk bekerja dengan Docker Registry (ketika menambahkan Rahasia ke namespace).
Jadi
apa yang harus dimiliki oleh operator yang baik :
- Interaksi dengan operator dilakukan menggunakan Definisi Sumber Daya Kustom (selanjutnya - CRD).
- Operator dapat disesuaikan. Untuk melakukan ini, kita akan menggunakan flag baris perintah dan variabel lingkungan.
- Perakitan wadah Docker dan grafik Helm sedang dikerjakan sehingga pengguna dapat dengan mudah (secara harfiah dengan satu perintah) menginstal operator di kluster Kubernet mereka.
CRD
Agar operator mengetahui sumber daya apa dan di mana mencarinya, kita perlu menetapkan aturan untuknya. Setiap aturan akan direpresentasikan sebagai objek CRD tunggal. Bidang apa yang harus dimiliki CRD ini?
- Jenis sumber daya yang kami cari (ConfigMap atau Secret).
- Daftar namespace tempat sumber daya harus ditemukan.
- Selector , dimana kita akan mencari sumber daya di namespace.
Kami menggambarkan 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
Dan segera buat
aturan sederhana - untuk mencari di namespace dengan nama
default
semua ConfigMap dengan label seperti
copyrator: "true"
:
apiVersion: flant.com/v1 kind: CopyratorRule metadata: name: main-rule labels: module: copyrator ruleType: configmap selector: copyrator: "true" namespace: default
Selesai! Sekarang Anda perlu entah bagaimana mendapatkan informasi tentang aturan kami. Saya harus melakukan reservasi segera bahwa kami tidak akan menulis permintaan ke server cluster API. Untuk melakukan ini, kita akan menggunakan pustaka Python
kubernetes-client yang sudah jadi :
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')}
Sebagai hasil dari kode ini, kita mendapatkan yang berikut:
{'ruleType': 'configmap', 'selector': {'copyrator': 'true'}, 'namespace': ['default']}
Luar biasa: kami berhasil mendapatkan aturan untuk operator. Dan yang paling penting, kami melakukan apa yang disebut cara Kubernetes.
Variabel atau bendera lingkungan? Kami mengambil semuanya!
Kami beralih ke konfigurasi utama operator. Ada dua pendekatan dasar untuk mengkonfigurasi aplikasi:
- Gunakan opsi baris perintah
- gunakan variabel lingkungan.
Opsi baris perintah memungkinkan Anda membaca pengaturan lebih fleksibel, dengan dukungan dan validasi tipe data. Pustaka standar Python memiliki modul
argparser
, yang akan kita gunakan. Detail dan contoh kapabilitasnya tersedia dalam
dokumentasi resmi .
Inilah contoh tampilan pengaturan tanda bendera perintah untuk kasus kami:
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()
Di sisi lain, menggunakan variabel lingkungan di Kubernetes, Anda dapat dengan mudah mentransfer informasi layanan tentang pod ke wadah. Misalnya, kita bisa mendapatkan informasi tentang namespace tempat pod berjalan dengan konstruksi berikut:
env: - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace
Logika Operator
Untuk memahami cara memisahkan metode untuk bekerja dengan ConfigMap dan Secret, kami akan menggunakan kartu khusus. Lalu kita bisa memahami metode apa yang perlu kita lacak dan buat objek:
LIST_TYPES_MAP = { 'configmap': 'list_namespaced_config_map', 'secret': 'list_namespaced_secret', } CREATE_TYPES_MAP = { 'configmap': 'create_namespaced_config_map', 'secret': 'create_namespaced_secret', }
Selanjutnya, Anda harus menerima acara dari server API. Kami menerapkannya sebagai berikut:
def handle(specs): kubernetes.config.load_incluster_config() v1 = kubernetes.client.CoreV1Api()
Setelah menerima acara, kami melanjutkan ke logika utama pemrosesan:
Logika dasar sudah siap! Sekarang Anda perlu mengemas semua ini menjadi satu paket Python. Kami menjalankan
setup.py
, menulis meta-informasi tentang proyek di sana:
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='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 : Klien kubernetes untuk Python memiliki versi sendiri. Anda dapat mempelajari lebih lanjut tentang kompatibilitas antara versi klien dan versi Kubernetes dari matriks kompatibilitas .Sekarang proyek kami terlihat seperti ini:
copyrator ├── copyrator │ ├── cli.py # │ ├── constant.py # , │ ├── load_crd.py # CRD │ └── operator.py # └── setup.py #
Docker dan Helm
Dockerfile akan menjadi sangat sederhana: ambil gambar dasar python-alpine dan instal paket kami. Kami akan menunda pengoptimalannya hingga waktu yang lebih baik:
FROM python:3.7.3-alpine3.9 ADD . /app RUN pip3 install /app ENTRYPOINT ["copyrator"]
Penyebaran untuk operator juga sangat sederhana:
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
Terakhir, Anda harus membuat peran yang sesuai untuk operator dengan hak yang diperlukan:
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 }}-acc
Ringkasan
Jadi, tanpa rasa takut, celaan dan belajar Go, kami dapat mengumpulkan operator kami sendiri untuk Kubernet dengan Python. Tentu saja, ia masih memiliki ruang untuk tumbuh: di masa depan, ia akan dapat memproses beberapa aturan, bekerja di beberapa utas, secara mandiri memantau perubahan dalam CRD-nya ...
Untuk melihat lebih dekat pada kode, kami memasukkannya ke dalam
repositori publik . Jika Anda ingin contoh operator yang lebih serius diimplementasikan menggunakan Python, Anda dapat mengalihkan perhatian Anda ke dua operator untuk menggunakan mongodb (yang
pertama dan
kedua ).
PS Dan jika Anda terlalu malas untuk berurusan dengan acara Kubernetes atau jika Anda lebih nyaman menggunakan Bash, kolega kami telah menyiapkan solusi siap pakai dalam bentuk
shell-operator (kami
mengumumkannya pada bulan April).
PPS
Baca juga di blog kami: