Memperkenalkan operator shell: membuat operator untuk Kubernetes menjadi lebih mudah

Sudah ada artikel di blog kami yang membahasnya kemampuan operator di Kubernetes dan bagaimana tulis sendiri operator sederhana. Kali ini kami ingin menyampaikan kepada Anda solusi Open Source kami, yang membawa pembuatan operator ke tingkat yang sangat mudah - lihat operator shell!

Kenapa?

Ide dari operator shell cukup sederhana: berlangganan event dari objek Kubernetes, dan ketika event ini diterima, luncurkan program eksternal, berikan informasi tentang event tersebut:

Memperkenalkan operator shell: membuat operator untuk Kubernetes menjadi lebih mudah

Kebutuhan akan hal ini muncul ketika, selama pengoperasian cluster, tugas-tugas kecil mulai muncul yang benar-benar ingin kami otomatisasi dengan cara yang benar. Semua tugas kecil ini diselesaikan dengan menggunakan skrip bash sederhana, meskipun, seperti yang Anda tahu, lebih baik menulis operator di Golang. Tentu saja, berinvestasi dalam pengembangan operator skala penuh untuk setiap tugas kecil seperti itu tidak akan efektif.

Operator dalam 15 menit

Mari kita lihat contoh apa yang dapat diotomatisasi dalam cluster Kubernetes dan bagaimana operator shell dapat membantu. Contohnya adalah sebagai berikut: mereplikasi rahasia untuk mengakses registri buruh pelabuhan.

Pod yang menggunakan image dari registri pribadi harus berisi tautan ke rahasia dengan data untuk mengakses registri di manifesnya. Rahasia ini harus dibuat di setiap namespace sebelum membuat pod. Hal ini dapat dilakukan secara manual, namun jika kita mengatur lingkungan dinamis, maka namespace untuk satu aplikasi akan menjadi banyak. Dan kalau yang aplikasinya juga tidak 2-3... jumlah rahasianya jadi banyak sekali. Dan satu hal lagi tentang rahasia: Saya ingin mengubah kunci untuk mengakses registri dari waktu ke waktu. Pada akhirnya, operasi manual sebagai solusi sepenuhnya tidak efektif — kita perlu mengotomatiskan pembuatan dan pembaruan rahasia.

Otomatisasi sederhana

Mari kita menulis skrip shell yang berjalan setiap N detik sekali dan memeriksa namespace untuk mengetahui keberadaan rahasia, dan jika tidak ada rahasia, maka rahasia itu dibuat. Keuntungan dari solusi ini adalah tampilannya seperti skrip shell di cron - pendekatan klasik dan dapat dimengerti oleh semua orang. Kelemahannya adalah dalam interval antara peluncurannya, namespace baru dapat dibuat dan untuk beberapa waktu tetap tanpa rahasia, yang akan menyebabkan kesalahan dalam peluncuran pod.

Otomatisasi dengan operator shell

Agar skrip kami berfungsi dengan benar, peluncuran cron klasik perlu diganti dengan peluncuran ketika namespace ditambahkan: dalam hal ini, Anda dapat membuat rahasia sebelum menggunakannya. Mari kita lihat bagaimana mengimplementasikannya menggunakan operator shell.

Pertama, mari kita lihat skripnya. Skrip dalam istilah operator shell disebut hooks. Setiap kait saat dijalankan dengan bendera --config memberi tahu operator shell tentang pengikatannya, mis. tentang acara apa yang harus diluncurkan. Dalam kasus kami, kami akan menggunakan onKubernetesEvent:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
  { "kind": "namespace",
    "event":["add"]
  }
]}
EOF
fi

Di sini dijelaskan bahwa kami tertarik untuk menambahkan acara (add) objek bertipe namespace.

Sekarang Anda perlu menambahkan kode yang akan dieksekusi ketika peristiwa tersebut terjadi:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
  # конфигурация
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
  "event":["add"]
}
]}
EOF
else
  # реакция:
  # узнать, какой namespace появился
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  # создать в нём нужный секрет
  kubectl create -n ${createdNamespace} -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
EOF
fi

Besar! Hasilnya adalah naskah yang kecil dan indah. Untuk “menghidupkannya kembali”, ada dua langkah yang tersisa: menyiapkan image dan meluncurkannya di cluster.

Mempersiapkan gambar dengan kail

Jika Anda melihat skripnya, Anda dapat melihat bahwa perintah-perintah tersebut digunakan kubectl и jq. Ini berarti bahwa gambar tersebut harus memiliki hal-hal berikut: hook kita, operator shell yang akan memantau kejadian dan menjalankan hook, dan perintah yang digunakan oleh hook (kubectl dan jq). Hub.docker.com sudah memiliki image siap pakai yang mengemas operator shell, kubectl, dan jq. Yang tersisa hanyalah menambahkan pengait sederhana Dockerfile:

$ cat Dockerfile
FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
ADD namespace-hook.sh /hooks

$ docker build -t registry.example.com/my-operator:v1 . 
$ docker push registry.example.com/my-operator:v1

Berjalan dalam sebuah cluster

Mari kita lihat hooknya lagi dan kali ini tuliskan tindakan apa dan dengan objek apa yang dilakukannya di cluster:

  1. berlangganan acara pembuatan namespace;
  2. membuat rahasia di ruang nama selain tempat peluncurannya.

Ternyata pod tempat gambar kita akan diluncurkan harus memiliki izin untuk melakukan tindakan ini. Ini dapat dilakukan dengan membuat ServiceAccount Anda sendiri. Izin tersebut harus dilakukan dalam bentuk ClusterRole dan ClusterRoleBinding, karena kami tertarik pada objek dari seluruh cluster.

Deskripsi akhir di YAML akan terlihat seperti ini:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: monitor-namespaces-acc

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: monitor-namespaces
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list", "create", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: monitor-namespaces
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: monitor-namespaces
subjects:
  - kind: ServiceAccount
    name: monitor-namespaces-acc
    namespace: example-monitor-namespaces

Anda dapat meluncurkan gambar yang telah dirakit sebagai Deployment sederhana:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1
      serviceAccountName: monitor-namespaces-acc

Untuk kenyamanan, namespace terpisah dibuat di mana operator shell akan diluncurkan dan manifes yang dibuat akan diterapkan:

$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml

Itu saja: operator shell akan mulai, berlangganan acara pembuatan namespace dan menjalankan hook bila diperlukan.

Memperkenalkan operator shell: membuat operator untuk Kubernetes menjadi lebih mudah

Dengan demikian, skrip shell sederhana berubah menjadi operator nyata untuk Kubernetes dan bekerja sebagai bagian dari cluster. Dan semua ini tanpa proses rumit dalam pengembangan operator di Golang:

Memperkenalkan operator shell: membuat operator untuk Kubernetes menjadi lebih mudah

Ada ilustrasi lain mengenai hal ini...Memperkenalkan operator shell: membuat operator untuk Kubernetes menjadi lebih mudah

Maknanya akan kami ungkapkan lebih detail dalam salah satu publikasi berikut.

penyaringan

Melacak objek memang bagus, tetapi sering kali ada kebutuhan untuk bereaksi mengubah beberapa properti objek, misalnya, untuk mengubah jumlah replika di Deployment atau mengubah label objek.

Ketika suatu peristiwa tiba, operator shell menerima manifes JSON dari objek tersebut. Kita dapat memilih properti yang kita minati di JSON ini dan menjalankan hook hanya ketika mereka berubah. Ada bidang untuk ini jqFilter, di mana Anda perlu menentukan ekspresi jq yang akan diterapkan ke manifes JSON.

Misalnya, untuk merespons perubahan label pada objek Deployment, Anda perlu memfilter bidang tersebut labels keluar lapangan metadata. Konfigurasinya akan seperti ini:

cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "deployment",
  "event":["update"],
  "jqFilter": ".metadata.labels"
}
]}
EOF

Ekspresi jqFilter ini mengubah manifes JSON Deployment yang panjang menjadi JSON pendek dengan label:

Memperkenalkan operator shell: membuat operator untuk Kubernetes menjadi lebih mudah

operator shell hanya akan menjalankan hook ketika JSON singkat ini berubah, dan perubahan pada properti lainnya akan diabaikan.

Konteks peluncuran kait

Konfigurasi hook memungkinkan Anda menentukan beberapa opsi untuk acara - misalnya, 2 opsi untuk acara dari Kubernetes dan 2 jadwal:

{"onKubernetesEvent":[
  {"name":"OnCreatePod",
  "kind": "pod",
  "event":["add"]
  },
  {"name":"OnModifiedNamespace",
  "kind": "namespace",
  "event":["update"],
  "jqFilter": ".metadata.labels"
  }
],
"schedule": [
{ "name":"every 10 min",
  "crontab":"* */10 * * * *"
}, {"name":"on Mondays at 12:10",
"crontab": "* 10 12 * * 1"
]}

Penyimpangan kecil: ya, dukungan operator shell menjalankan skrip gaya crontab. Detail lebih lanjut dapat ditemukan di dokumentasi.

Untuk membedakan mengapa hook diluncurkan, operator shell membuat file sementara dan meneruskan path ke file tersebut dalam variabel ke hook BINDING_CONTEXT_TYPE. File tersebut berisi deskripsi JSON tentang alasan menjalankan hook. Misalnya, setiap 10 menit hook akan berjalan dengan konten berikut:

[{ "binding": "every 10 min"}]

...dan pada hari Senin akan dimulai dengan ini:

[{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}]

Untuk onKubernetesEvent Akan ada lebih banyak pemicu JSON, karena itu berisi deskripsi objek:

[
 {
 "binding": "onCreatePod",
 "resourceEvent": "add",
 "resourceKind": "pod",
 "resourceName": "foo",
 "resourceNamespace": "bar"
 }
]

Isi dari field-field tersebut dapat dipahami dari namanya, dan lebih jelasnya dapat dibaca di dokumentasi. Contoh mendapatkan nama sumber daya dari suatu bidang resourceName menggunakan jq telah ditampilkan di hook yang mereplikasi rahasia:

jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

Anda bisa mendapatkan bidang lain dengan cara serupa.

Apa selanjutnya?

Di repositori proyek, di /contoh direktori, terdapat contoh hook yang siap dijalankan pada cluster. Saat menulis hook Anda sendiri, Anda bisa menggunakannya sebagai dasar.

Ada dukungan untuk mengumpulkan metrik menggunakan Prometheus - metrik yang tersedia dijelaskan di bagian ini METRIK.

Seperti yang Anda duga, operator shell ditulis dalam Go dan didistribusikan di bawah lisensi Open Source (Apache 2.0). Kami akan berterima kasih atas bantuan pembangunan apa pun proyek di GitHub: dan bintang, dan masalah, dan permintaan tarik.

Mengangkat tabir kerahasiaan, kami juga akan memberi tahu Anda bahwa operator shell adalah kecil bagian dari sistem kami yang dapat terus memperbarui add-on yang terpasang di cluster Kubernetes dan melakukan berbagai tindakan otomatis. Baca lebih lanjut tentang sistem ini diberitahu secara harfiah pada hari Senin di HighLoad++ 2019 di St. Petersburg - kami akan segera mempublikasikan video dan transkrip laporan ini.

Kami memiliki rencana untuk membuka sisa sistem ini: operator tambahan dan koleksi kait dan modul kami. Omong-omong, addon-operator sudah ada tersedia di github, namun dokumentasinya masih dalam proses. Pelepasan koleksi modul direncanakan pada musim panas.

Tetap disini!

PS

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komentar