Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Tahun ini, konferensi utama Kubernetes Eropa - KubeCon + CloudNativeCon Europe 2020 - diadakan secara virtual. Namun, perubahan format tersebut tidak menghalangi kami untuk menyampaikan laporan yang telah lama direncanakan, “Go? Pesta! Temui operator Shell” yang didedikasikan untuk proyek Sumber Terbuka kami operator shell.

Artikel ini, terinspirasi oleh pembicaraan tersebut, menyajikan pendekatan untuk menyederhanakan proses pembuatan operator untuk Kubernetes dan menunjukkan bagaimana Anda dapat membuatnya sendiri dengan sedikit usaha menggunakan operator shell.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Memperkenalkan video laporan tersebut (~23 menit dalam bahasa Inggris, terasa lebih informatif daripada artikelnya) dan kutipan utamanya dalam bentuk teks. Pergi!

Di Flant kami terus mengoptimalkan dan mengotomatiskan segalanya. Hari ini kita akan membicarakan konsep menarik lainnya. Bertemu: skrip shell cloud-native!

Namun, mari kita mulai dengan konteks di mana semua ini terjadi: Kubernetes.

API dan pengontrol Kubernetes

API di Kubernetes dapat direpresentasikan sebagai semacam server file dengan direktori untuk setiap jenis objek. Objek (sumber daya) di server ini diwakili oleh file YAML. Selain itu, server memiliki API dasar yang memungkinkan Anda melakukan tiga hal:

  • untuk menerima sumber daya menurut jenis dan namanya;
  • mengubah sumber daya (dalam hal ini, server hanya menyimpan objek yang "benar" - semua objek yang bentuknya salah atau ditujukan untuk direktori lain akan dibuang);
  • melacak untuk sumber daya (dalam hal ini, pengguna segera menerima versi terkini/yang diperbarui).

Jadi, Kubernetes bertindak sebagai semacam server file (untuk manifes YAML) dengan tiga metode dasar (ya, sebenarnya ada metode lain, tapi kami akan menghilangkannya untuk saat ini).

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Masalahnya adalah server hanya dapat menyimpan informasi. Untuk membuatnya berfungsi, Anda perlu pengawas - Konsep terpenting dan mendasar kedua di dunia Kubernetes.

Ada dua tipe utama pengontrol. Yang pertama mengambil informasi dari Kubernetes, memprosesnya berdasarkan logika bersarang, dan mengembalikannya ke K8. Tipe kedua mengambil informasi dari Kubernetes, namun, tidak seperti tipe pertama, tipe ini mengubah status beberapa sumber daya eksternal.

Mari kita lihat lebih dekat proses pembuatan Deployment di Kubernetes:

  • Pengontrol Penerapan (termasuk dalam kube-controller-manager) menerima informasi tentang Deployment dan membuat ReplicaSet.
  • ReplicaSet membuat dua replika (dua pod) berdasarkan informasi ini, namun pod tersebut belum dijadwalkan.
  • Penjadwal menjadwalkan pod dan menambahkan informasi node ke YAML-nya.
  • Kubelet membuat perubahan pada sumber daya eksternal (misalnya Docker).

Kemudian seluruh rangkaian ini diulangi dalam urutan terbalik: kubelet memeriksa container, menghitung status pod, dan mengirimkannya kembali. Pengontrol ReplicaSet menerima status dan memperbarui status kumpulan replika. Hal yang sama terjadi dengan Deployment Controller dan pengguna akhirnya mendapatkan status yang diperbarui (saat ini).

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Operator shell

Ternyata Kubernetes didasarkan pada kerja sama berbagai pengontrol (operator Kubernetes juga merupakan pengontrol). Timbul pertanyaan, bagaimana cara membuat operator sendiri dengan sedikit usaha? Dan di sinilah yang kami kembangkan datang untuk menyelamatkan operator shell. Hal ini memungkinkan administrator sistem untuk membuat pernyataan mereka sendiri menggunakan metode yang sudah dikenal.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Contoh sederhana: menyalin rahasia

Mari kita lihat contoh sederhana.

Katakanlah kita memiliki cluster Kubernetes. Ini memiliki ruang nama default dengan beberapa Rahasia mysecret. Selain itu, ada namespace lain di cluster. Beberapa di antaranya memiliki label khusus yang ditempelkan padanya. Tujuan kami adalah menyalin Rahasia ke dalam ruang nama dengan label.

Tugas ini diperumit oleh fakta bahwa namespace baru mungkin muncul di cluster, dan beberapa di antaranya mungkin memiliki label ini. Di sisi lain, ketika label dihapus, Rahasia juga harus dihapus. Selain itu, Rahasia itu sendiri juga dapat berubah: dalam hal ini, Rahasia baru harus disalin ke semua ruang nama yang diberi label. Jika Rahasia terhapus secara tidak sengaja di namespace mana pun, operator kami harus segera memulihkannya.

Sekarang tugas telah dirumuskan, sekarang saatnya untuk mulai mengimplementasikannya menggunakan operator shell. Namun pertama-tama, ada baiknya menjelaskan beberapa patah kata tentang operator shell itu sendiri.

Cara kerja operator shell

Seperti beban kerja lainnya di Kubernetes, operator shell berjalan di podnya sendiri. Di pod ini di direktori /hooks file yang dapat dieksekusi disimpan. Ini bisa berupa skrip di Bash, Python, Ruby, dll. Kami menyebut file yang dapat dieksekusi tersebut sebagai kait (kait).

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Operator shell berlangganan event Kubernetes dan menjalankan hook ini sebagai respons terhadap event yang kita perlukan.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Bagaimana operator shell mengetahui hook mana yang harus dijalankan dan kapan? Intinya setiap hook memiliki dua tahap. Selama startup, operator shell menjalankan semua hook dengan sebuah argumen --config Ini adalah tahap konfigurasi. Dan setelah itu, kait diluncurkan dengan cara biasa - sebagai respons terhadap peristiwa yang mengikatnya. Dalam kasus terakhir, hook menerima konteks pengikatan (konteks yang mengikat) - data dalam format JSON, yang akan kita bahas lebih detail di bawah.

Membuat operator di Bash

Sekarang kami siap untuk implementasi. Untuk melakukan ini, kita perlu menulis dua fungsi (omong-omong, kami sarankan perpustakaan shell_lib, yang sangat menyederhanakan penulisan kait di Bash):

  • yang pertama diperlukan untuk tahap konfigurasi - ini menampilkan konteks pengikatan;
  • yang kedua berisi logika utama hook.

#!/bin/bash

source /shell_lib.sh

function __config__() {
  cat << EOF
    configVersion: v1
    # BINDING CONFIGURATION
EOF
}

function __main__() {
  # THE LOGIC
}

hook::run "$@"

Langkah selanjutnya adalah memutuskan benda apa yang kita butuhkan. Dalam kasus kita, kita perlu melacak:

  • rahasia sumber untuk perubahan;
  • semua namespace di cluster, sehingga Anda tahu namespace mana yang diberi label;
  • rahasia target untuk memastikan bahwa semuanya sinkron dengan rahasia sumber.

Berlangganan ke sumber rahasia

Konfigurasi pengikatannya cukup sederhana. Kami menunjukkan bahwa kami tertarik pada Rahasia dengan namanya mysecret di ruang nama default:

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

function __config__() {
  cat << EOF
    configVersion: v1
    kubernetes:
    - name: src_secret
      apiVersion: v1
      kind: Secret
      nameSelector:
        matchNames:
        - mysecret
      namespace:
        nameSelector:
          matchNames: ["default"]
      group: main
EOF

Akibatnya, hook akan terpicu ketika rahasia sumber berubah (src_secret) dan menerima konteks pengikatan berikut:

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Seperti yang Anda lihat, ini berisi nama dan keseluruhan objek.

Melacak namespace

Sekarang Anda perlu berlangganan namespace. Untuk melakukan ini, kami menentukan konfigurasi pengikatan berikut:

- name: namespaces
  group: main
  apiVersion: v1
  kind: Namespace
  jqFilter: |
    {
      namespace: .metadata.name,
      hasLabel: (
       .metadata.labels // {} |  
         contains({"secret": "yes"})
      )
    }
  group: main
  keepFullObjectsInMemory: false

Seperti yang Anda lihat, bidang baru dengan nama telah muncul di konfigurasi jqFilter. Seperti namanya, jqFilter memfilter semua informasi yang tidak perlu dan membuat objek JSON baru dengan bidang yang kami minati. Sebuah hook dengan konfigurasi serupa akan menerima konteks pengikatan berikut:

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Ini berisi array filterResults untuk setiap namespace di cluster. Variabel Boolean hasLabel menunjukkan apakah label dilampirkan pada namespace tertentu. Pemilih keepFullObjectsInMemory: false menunjukkan bahwa tidak perlu menyimpan objek lengkap dalam memori.

Melacak rahasia target

Kami berlangganan semua Rahasia yang memiliki anotasi tertentu managed-secret: "yes" (ini adalah target kami dst_secrets):

- name: dst_secrets
  apiVersion: v1
  kind: Secret
  labelSelector:
    matchLabels:
      managed-secret: "yes"
  jqFilter: |
    {
      "namespace":
        .metadata.namespace,
      "resourceVersion":
        .metadata.annotations.resourceVersion
    }
  group: main
  keepFullObjectsInMemory: false

Dalam hal ini jqFilter menyaring semua informasi kecuali namespace dan parameter resourceVersion. Parameter terakhir diteruskan ke anotasi saat membuat rahasia: parameter ini memungkinkan Anda membandingkan versi rahasia dan selalu memperbaruinya.

Sebuah hook yang dikonfigurasi dengan cara ini, ketika dijalankan, akan menerima tiga konteks pengikatan yang dijelaskan di atas. Mereka dapat dianggap sebagai semacam gambaran (foto) gugus.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Berdasarkan semua informasi ini, algoritma dasar dapat dikembangkan. Itu mengulangi semua ruang nama dan:

  • jika hasLabel hal true untuk namespace saat ini:
    • membandingkan rahasia global dengan rahasia lokal:
      • jika keduanya sama, tidak menghasilkan apa-apa;
      • jika berbeda - dijalankan kubectl replace или create;
  • jika hasLabel hal false untuk namespace saat ini:
    • memastikan bahwa Rahasia tidak ada dalam namespace yang diberikan:
      • jika Rahasia lokal ada, hapus menggunakan kubectl delete;
      • jika Rahasia lokal tidak terdeteksi, ia tidak melakukan apa pun.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Implementasi algoritma di Bash Anda dapat mengunduh di kami repositori dengan contoh.

Begitulah cara kami membuat pengontrol Kubernetes sederhana menggunakan 35 baris konfigurasi YAML dan jumlah kode Bash yang hampir sama! Tugas operator shell adalah menghubungkan keduanya.

Namun, menyalin rahasia bukan satu-satunya bidang penerapan utilitas. Berikut beberapa contoh lagi untuk menunjukkan kemampuannya.

Contoh 1: Membuat perubahan pada ConfigMap

Mari kita lihat Deployment yang terdiri dari tiga pod. Pod menggunakan ConfigMap untuk menyimpan beberapa konfigurasi. Saat pod diluncurkan, ConfigMap berada dalam kondisi tertentu (sebut saja v.1). Oleh karena itu, semua pod menggunakan ConfigMap versi khusus ini.

Sekarang mari kita asumsikan bahwa ConfigMap telah berubah (v.2). Namun, pod-pod tersebut akan menggunakan ConfigMap versi sebelumnya (v.1):

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Bagaimana caranya agar mereka beralih ke ConfigMap baru (v.2)? Jawabannya sederhana: gunakan template. Mari tambahkan anotasi checksum ke bagian tersebut template Konfigurasi penerapan:

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Hasilnya, checksum ini akan didaftarkan di semua pod, dan akan sama dengan Deployment. Sekarang Anda hanya perlu memperbarui anotasi ketika ConfigMap berubah. Dan operator shell sangat berguna dalam kasus ini. Yang perlu Anda lakukan hanyalah memprogram sebuah hook yang akan berlangganan ConfigMap dan memperbarui checksum.

Jika pengguna membuat perubahan pada ConfigMap, operator shell akan menyadarinya dan menghitung ulang checksumnya. Setelah itu keajaiban Kubernetes akan berperan: orkestrator akan mematikan pod, membuat yang baru, menunggu hingga menjadi Ready, dan melanjutkan ke yang berikutnya. Hasilnya, Deployment akan melakukan sinkronisasi dan beralih ke ConfigMap versi baru.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Contoh 2: Bekerja dengan Definisi Sumber Daya Kustom

Seperti yang Anda ketahui, Kubernetes memungkinkan Anda membuat tipe objek khusus. Misalnya, Anda dapat membuat kebaikan MysqlDatabase. Katakanlah tipe ini memiliki dua parameter metadata: name и namespace.

apiVersion: example.com/v1alpha1
kind: MysqlDatabase
metadata:
  name: foo
  namespace: bar

Kami memiliki cluster Kubernetes dengan namespace berbeda tempat kami dapat membuat database MySQL. Dalam hal ini operator shell dapat digunakan untuk melacak sumber daya MysqlDatabase, menghubungkannya ke server MySQL dan menyinkronkan status cluster yang diinginkan dan diamati.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Contoh 3: Pemantauan Jaringan Cluster

Seperti yang Anda ketahui, menggunakan ping adalah cara paling sederhana untuk memantau suatu jaringan. Dalam contoh ini kami akan menunjukkan bagaimana menerapkan pemantauan tersebut menggunakan operator shell.

Pertama-tama, Anda harus berlangganan node. Operator shell memerlukan nama dan alamat IP setiap node. Dengan bantuan mereka, dia akan melakukan ping ke node ini.

configVersion: v1
kubernetes:
- name: nodes
  apiVersion: v1
  kind: Node
  jqFilter: |
    {
      name: .metadata.name,
      ip: (
       .status.addresses[] |  
        select(.type == "InternalIP") |
        .address
      )
    }
  group: main
  keepFullObjectsInMemory: false
  executeHookOnEvent: []
schedule:
- name: every_minute
  group: main
  crontab: "* * * * *"

Parameter executeHookOnEvent: [] mencegah hook berjalan sebagai respons terhadap peristiwa apa pun (yaitu, sebagai respons terhadap perubahan, penambahan, penghapusan node). Namun, dia akan berlari (dan perbarui daftar node) Terjadwal - setiap menit, seperti yang ditentukan oleh lapangan schedule.

Sekarang timbul pertanyaan, bagaimana tepatnya kita mengetahui masalah seperti kehilangan paket? Mari kita lihat kodenya:

function __main__() {
  for i in $(seq 0 "$(context::jq -r '(.snapshots.nodes | length) - 1')"); do
    node_name="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.name')"
    node_ip="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.ip')"
    packets_lost=0
    if ! ping -c 1 "$node_ip" -t 1 ; then
      packets_lost=1
    fi
    cat >> "$METRICS_PATH" <<END
      {
        "name": "node_packets_lost",
        "add": $packets_lost,
        "labels": {
          "node": "$node_name"
        }
      }
END
  done
}

Kami mengulangi daftar node, mendapatkan nama dan alamat IP mereka, melakukan ping dan mengirimkan hasilnya ke Prometheus. Operator Shell dapat mengekspor metrik ke Prometheus, menyimpannya ke file yang terletak sesuai dengan jalur yang ditentukan dalam variabel lingkungan $METRICS_PATH.

Disini begitu Anda dapat membuat operator untuk pemantauan jaringan sederhana dalam sebuah cluster.

Mekanisme antrian

Artikel ini tidak akan lengkap tanpa menjelaskan mekanisme penting lainnya yang dibangun di dalam operator shell. Bayangkan ia mengeksekusi semacam hook sebagai respons terhadap suatu peristiwa di cluster.

  • Apa yang terjadi jika, pada saat yang sama, terjadi sesuatu di cluster? satu lagi peristiwa?
  • Akankah operator shell menjalankan contoh hook yang lain?
  • Bagaimana jika, katakanlah, lima kejadian terjadi dalam cluster sekaligus?
  • Akankah operator shell memprosesnya secara paralel?
  • Bagaimana dengan sumber daya yang dikonsumsi seperti memori dan CPU?

Untungnya, operator shell memiliki mekanisme antrian bawaan. Semua peristiwa diantrekan dan diproses secara berurutan.

Mari kita ilustrasikan hal ini dengan contoh. Katakanlah kita mempunyai dua pengait. Acara pertama menuju ke hook pertama. Setelah pemrosesannya selesai, antrian bergerak maju. Tiga peristiwa berikutnya dialihkan ke kait kedua - mereka dikeluarkan dari antrian dan dimasukkan ke dalamnya dalam "bundel". Itu adalah hook menerima serangkaian acara — atau, lebih tepatnya, serangkaian konteks yang mengikat.

Juga ini peristiwa dapat digabungkan menjadi satu peristiwa besar. Parameter bertanggung jawab untuk ini group dalam konfigurasi pengikatan.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Anda dapat membuat sejumlah antrian/kait dan berbagai kombinasinya. Misalnya, satu antrian bisa bekerja dengan dua hook, atau sebaliknya.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Yang perlu Anda lakukan hanyalah mengonfigurasi bidang yang sesuai queue dalam konfigurasi pengikatan. Jika nama antrian tidak ditentukan, hook berjalan pada antrian default (default). Mekanisme antrian ini memungkinkan Anda untuk sepenuhnya menyelesaikan semua masalah manajemen sumber daya saat bekerja dengan hook.

Kesimpulan

Kami menjelaskan apa itu operator shell, menunjukkan bagaimana shell dapat digunakan untuk membuat operator Kubernetes dengan cepat dan mudah, dan memberikan beberapa contoh penggunaannya.

Informasi rinci tentang operator shell, serta tutorial singkat tentang cara menggunakannya, tersedia di bagian yang sesuai repositori di GitHub. Jangan ragu untuk menghubungi kami jika ada pertanyaan: Anda dapat mendiskusikannya secara khusus Grup Telegram (dalam bahasa Rusia) atau dalam forum ini (dalam Bahasa Inggris).

Dan jika Anda menyukainya, kami selalu senang melihat terbitan/PR/bintang baru di GitHub, di mana, omong-omong, Anda dapat menemukan terbitan lain proyek yang menarik. Di antara mereka perlu disoroti addon-operator, yang merupakan kakak dari operator shell. Utilitas ini menggunakan diagram Helm untuk menginstal add-on, dapat memberikan pembaruan dan memantau berbagai parameter/nilai diagram, mengontrol proses instalasi diagram, dan juga dapat memodifikasinya sebagai respons terhadap peristiwa di klaster.

Pergi? Pesta! Temui operator shell (review dan laporan video dari KubeCon EU'2020)

Video dan slide

Video dari pertunjukan (~23 menit):


Penyajian laporan:

PS

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komentar