Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Tahun ini, persidangan Kubernetes Eropah utama - KubeCon + CloudNativeCon Europe 2020 - adalah maya. Walau bagaimanapun, perubahan dalam format tidak menghalang kami daripada menyampaikan laporan kami yang telah lama dirancang "Pergi? Bash! Temui Shell-operator” khusus untuk projek Sumber Terbuka kami pengendali shell.

Artikel ini, yang diilhamkan oleh ceramah, membentangkan pendekatan untuk memudahkan proses mencipta pengendali untuk Kubernetes dan menunjukkan cara anda boleh membuatnya sendiri dengan usaha yang minimum menggunakan pengendali shell.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Memperkenalkan video laporan tersebut (~23 minit dalam bahasa Inggeris, nyata lebih bermaklumat daripada artikel) dan ekstrak utama daripadanya dalam bentuk teks. Pergi!

Di Flant kami sentiasa mengoptimumkan dan mengautomasikan segala-galanya. Hari ini kita akan bercakap tentang satu lagi konsep yang menarik. Bertemu: skrip shell asli awan!

Walau bagaimanapun, mari kita mulakan dengan konteks di mana semua ini berlaku: Kubernetes.

API dan pengawal Kubernetes

API dalam Kubernetes boleh diwakili sebagai sejenis pelayan fail dengan direktori untuk setiap jenis objek. Objek (sumber) pada pelayan ini diwakili oleh fail YAML. Di samping itu, pelayan mempunyai API asas yang membolehkan anda melakukan tiga perkara:

  • untuk menerima sumber mengikut jenis dan namanya;
  • ubah sumber (dalam kes ini, pelayan hanya menyimpan objek "betul" - semua yang dibentuk dengan salah atau bertujuan untuk direktori lain dibuang);
  • trek untuk sumber (dalam kes ini, pengguna segera menerima versi semasa/dikemas kini).

Oleh itu, Kubernetes bertindak sebagai sejenis pelayan fail (untuk manifes YAML) dengan tiga kaedah asas (ya, sebenarnya ada yang lain, tetapi kami akan meninggalkannya buat masa ini).

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Masalahnya ialah pelayan hanya boleh menyimpan maklumat. Untuk menjadikannya berfungsi, anda perlukan pengawal - konsep kedua terpenting dan asas dalam dunia Kubernetes.

Terdapat dua jenis pengawal utama. Yang pertama mengambil maklumat daripada Kubernetes, memprosesnya mengikut logik bersarang, dan mengembalikannya kepada K8s. Yang kedua mengambil maklumat daripada Kubernetes, tetapi, tidak seperti jenis pertama, mengubah keadaan beberapa sumber luaran.

Mari kita lihat dengan lebih dekat proses mencipta Deployment dalam Kubernetes:

  • Pengawal Penerapan (termasuk dalam kube-controller-manager) menerima maklumat tentang Deployment dan mencipta ReplicaSet.
  • ReplicaSet mencipta dua replika (dua pod) berdasarkan maklumat ini, tetapi pod ini belum dijadualkan lagi.
  • Penjadual menjadualkan pod dan menambah maklumat nod pada YAML mereka.
  • Kubelets membuat perubahan pada sumber luaran (katakan Docker).

Kemudian keseluruhan jujukan ini diulang dalam susunan terbalik: kubelet menyemak bekas, mengira status pod dan menghantarnya kembali. Pengawal ReplicaSet menerima status dan mengemas kini keadaan set replika. Perkara yang sama berlaku dengan Pengawal Penerapan dan pengguna akhirnya mendapat status terkini (semasa).

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Shell-operator

Ternyata Kubernetes adalah berdasarkan kerja bersama pelbagai pengawal (pengendali Kubernetes juga pengawal). Persoalannya timbul, bagaimana untuk mencipta operator anda sendiri dengan usaha yang minimum? Dan di sini yang kami bangunkan datang untuk menyelamatkan pengendali shell. Ia membolehkan pentadbir sistem mencipta kenyataan mereka sendiri menggunakan kaedah biasa.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Contoh mudah: menyalin rahsia

Mari kita lihat contoh mudah.

Katakan kita mempunyai gugusan Kubernetes. Ia mempunyai ruang nama default dengan beberapa Rahsia mysecret. Di samping itu, terdapat ruang nama lain dalam kelompok. Sesetengah daripada mereka mempunyai label khusus yang dilampirkan padanya. Matlamat kami adalah untuk menyalin Rahsia ke dalam ruang nama dengan label.

Tugas ini rumit oleh fakta bahawa ruang nama baharu mungkin muncul dalam gugusan, dan sesetengah daripadanya mungkin mempunyai label ini. Sebaliknya, apabila label dipadamkan, Rahsia juga harus dipadamkan. Di samping itu, Rahsia itu sendiri juga boleh berubah: dalam kes ini, Rahsia baharu mesti disalin ke semua ruang nama dengan label. Jika Rahsia dipadamkan secara tidak sengaja dalam mana-mana ruang nama, pengendali kami harus memulihkannya dengan segera.

Sekarang tugas itu telah dirumuskan, tiba masanya untuk mula melaksanakannya menggunakan pengendali shell. Tetapi pertama-tama, ia patut mengatakan beberapa perkataan tentang pengendali shell itu sendiri.

Cara pengendali shell berfungsi

Seperti beban kerja lain dalam Kubernetes, pengendali shell berjalan dalam podnya sendiri. Dalam pod ini dalam direktori /hooks fail boleh laku disimpan. Ini boleh menjadi skrip dalam Bash, Python, Ruby, dll. Kami memanggil fail boleh laku seperti cangkuk (cangkuk).

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Pengendali Shell melanggan acara Kubernetes dan menjalankan cangkuk ini sebagai tindak balas kepada acara yang kami perlukan.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Bagaimanakah pengendali shell tahu cangkuk mana yang hendak dijalankan dan bila? Maksudnya ialah setiap cangkuk mempunyai dua peringkat. Semasa permulaan, pengendali shell menjalankan semua cangkuk dengan hujah --config Ini adalah peringkat konfigurasi. Dan selepas itu, cangkuk dilancarkan dengan cara biasa - sebagai tindak balas kepada peristiwa yang dilampirkan. Dalam kes kedua, cangkuk menerima konteks mengikat (konteks yang mengikat) - data dalam format JSON, yang akan kami bincangkan dengan lebih terperinci di bawah.

Membuat pengendali dalam Bash

Kini kami bersedia untuk pelaksanaan. Untuk melakukan ini, kita perlu menulis dua fungsi (dengan cara ini, kami mengesyorkan perpustakaan shell_lib, yang sangat memudahkan penulisan cangkuk dalam Bash):

  • yang pertama diperlukan untuk peringkat konfigurasi - ia memaparkan konteks yang mengikat;
  • yang kedua mengandungi logik utama cangkuk.

#!/bin/bash

source /shell_lib.sh

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

function __main__() {
  # THE LOGIC
}

hook::run "$@"

Langkah seterusnya ialah menentukan objek apa yang kita perlukan. Dalam kes kami, kami perlu menjejaki:

  • sumber rahsia untuk perubahan;
  • semua ruang nama dalam gugusan, supaya anda tahu mana yang mempunyai label yang dilampirkan padanya;
  • sasaran rahsia untuk memastikan bahawa mereka semua selari dengan rahsia sumber.

Langgan sumber rahsia

Konfigurasi mengikat untuknya agak mudah. Kami menunjukkan bahawa kami berminat dengan Rahsia dengan nama mysecret dalam ruang nama default:

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada 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, cangkuk akan dicetuskan apabila rahsia sumber berubah (src_secret) dan menerima konteks mengikat berikut:

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Seperti yang anda lihat, ia mengandungi nama dan keseluruhan objek.

Menjejaki ruang nama

Kini anda perlu melanggan ruang nama. 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, medan baharu telah muncul dalam konfigurasi dengan nama jqFilter. Seperti namanya, jqFilter menapis semua maklumat yang tidak diperlukan dan mencipta objek JSON baharu dengan medan yang menarik minat kami. Cangkuk dengan konfigurasi yang serupa akan menerima konteks mengikat berikut:

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Ia mengandungi tatasusunan filterResults untuk setiap ruang nama dalam kelompok. Pembolehubah Boolean hasLabel menunjukkan sama ada label dilampirkan pada ruang nama yang diberikan. Pemilih keepFullObjectsInMemory: false menunjukkan bahawa tidak ada keperluan untuk menyimpan objek lengkap dalam ingatan.

Menjejaki rahsia sasaran

Kami melanggan semua Rahsia yang mempunyai anotasi yang ditentukan managed-secret: "yes" (ini adalah sasaran 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 kes ini jqFilter menapis semua maklumat kecuali ruang nama dan parameter resourceVersion. Parameter terakhir telah diserahkan kepada anotasi semasa mencipta rahsia: ia membolehkan anda membandingkan versi rahsia dan memastikannya dikemas kini.

Cangkuk yang dikonfigurasikan dengan cara ini akan, apabila dilaksanakan, menerima tiga konteks mengikat yang diterangkan di atas. Mereka boleh dianggap sebagai sejenis syot kilat (gambar) kelompok.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Berdasarkan semua maklumat ini, algoritma asas boleh dibangunkan. Ia berulang ke atas semua ruang nama dan:

  • jika hasLabel perkara-perkara yang true untuk ruang nama semasa:
    • membandingkan rahsia global dengan rahsia tempatan:
      • jika mereka adalah sama, ia tidak melakukan apa-apa;
      • jika mereka berbeza - laksanakan kubectl replace atau create;
  • jika hasLabel perkara-perkara yang false untuk ruang nama semasa:
    • pastikan Rahsia tiada dalam ruang nama yang diberikan:
      • jika Rahsia setempat ada, padamkannya menggunakan kubectl delete;
      • jika Rahsia tempatan tidak dikesan, ia tidak melakukan apa-apa.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Pelaksanaan algoritma dalam Bash anda boleh memuat turun dalam kami repositori dengan contoh.

Begitulah cara kami dapat mencipta pengawal Kubernetes yang mudah menggunakan 35 baris konfigurasi YAML dan kira-kira jumlah kod Bash yang sama! Tugas pengendali shell adalah untuk menghubungkan mereka bersama-sama.

Walau bagaimanapun, penyalinan rahsia bukanlah satu-satunya bidang penggunaan utiliti. Berikut adalah beberapa lagi contoh untuk menunjukkan apa yang dia mampu.

Contoh 1: Membuat perubahan pada ConfigMap

Mari lihat Deployment yang terdiri daripada tiga pod. Pod menggunakan ConfigMap untuk menyimpan beberapa konfigurasi. Apabila pod dilancarkan, ConfigMap berada dalam keadaan tertentu (sebutkan ia v.1). Sehubungan itu, semua pod menggunakan versi ConfigMap khusus ini.

Sekarang mari kita anggap bahawa ConfigMap telah berubah (v.2). Walau bagaimanapun, pod akan menggunakan versi ConfigMap sebelumnya (v.1):

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Bagaimanakah saya boleh membuat mereka bertukar kepada ConfigMap baharu (v.2)? Jawapannya mudah: gunakan templat. Mari tambahkan anotasi semak pada bahagian tersebut template Konfigurasi penggunaan:

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Hasilnya, checksum ini akan didaftarkan dalam semua pod, dan ia akan sama seperti Deployment. Kini anda hanya perlu mengemas kini anotasi apabila ConfigMap berubah. Dan pengendali shell berguna dalam kes ini. Apa yang anda perlu lakukan ialah program cangkuk yang akan melanggan ConfigMap dan mengemas kini checksum.

Jika pengguna membuat perubahan pada ConfigMap, pengendali shell akan melihatnya dan mengira semula jumlah semak. Selepas itu keajaiban Kubernetes akan dimainkan: orkestra akan membunuh pod, mencipta yang baharu, tunggu ia menjadi Ready, dan beralih ke yang seterusnya. Akibatnya, Deployment akan menyegerakkan dan bertukar kepada versi baharu ConfigMap.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Contoh 2: Bekerja dengan Definisi Sumber Tersuai

Seperti yang anda ketahui, Kubernetes membenarkan anda membuat jenis objek tersuai. Sebagai contoh, anda boleh mencipta jenis MysqlDatabase. Katakan jenis ini mempunyai dua parameter metadata: name ΠΈ namespace.

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

Kami mempunyai gugusan Kubernetes dengan ruang nama yang berbeza di mana kami boleh mencipta pangkalan data MySQL. Dalam kes ini, pengendali shell boleh digunakan untuk menjejak sumber MysqlDatabase, menyambungkannya ke pelayan MySQL dan menyegerakkan keadaan kluster yang dikehendaki dan diperhatikan.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Contoh 3: Pemantauan Rangkaian Kluster

Seperti yang anda ketahui, menggunakan ping adalah cara paling mudah untuk memantau rangkaian. Dalam contoh ini kami akan menunjukkan cara melaksanakan pemantauan tersebut menggunakan pengendali shell.

Pertama sekali, anda perlu melanggan nod. Pengendali shell memerlukan nama dan alamat IP setiap nod. Dengan bantuan mereka, dia akan ping nod 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: [] menghalang cangkuk daripada berjalan sebagai tindak balas kepada sebarang peristiwa (iaitu, sebagai tindak balas kepada menukar, menambah, memadam nod). Namun, dia akan lari (dan kemas kini senarai nod) Dijadualkan - setiap minit, seperti yang ditetapkan oleh medan schedule.

Sekarang timbul persoalan, bagaimana sebenarnya kita tahu tentang masalah seperti kehilangan paket? Mari kita lihat kod:

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 senarai nod, mendapatkan nama dan alamat IP mereka, ping mereka dan menghantar hasilnya kepada Prometheus. Pengendali Shell boleh mengeksport metrik ke Prometheus, menyimpannya ke fail yang terletak mengikut laluan yang ditentukan dalam pembolehubah persekitaran $METRICS_PATH.

Di sini begitu anda boleh membuat operator untuk pemantauan rangkaian mudah dalam kelompok.

Mekanisme beratur

Artikel ini tidak lengkap tanpa menerangkan satu lagi mekanisme penting yang dibina ke dalam pengendali shell. Bayangkan bahawa ia melaksanakan sejenis cangkuk sebagai tindak balas kepada acara dalam kelompok.

  • Apakah yang berlaku jika, pada masa yang sama, sesuatu berlaku dalam kelompok? satu lagi peristiwa?
  • Adakah pengendali shell akan menjalankan satu lagi contoh cangkuk?
  • Bagaimana jika, katakan, lima peristiwa berlaku dalam kelompok sekaligus?
  • Adakah pengendali shell akan memprosesnya secara selari?
  • Bagaimana pula dengan sumber yang digunakan seperti memori dan CPU?

Nasib baik, shell-operator mempunyai mekanisme giliran terbina dalam. Semua acara beratur dan diproses secara berurutan.

Mari kita menggambarkan ini dengan contoh. Katakan kita mempunyai dua mata kail. Acara pertama pergi ke cangkuk pertama. Setelah pemprosesannya selesai, baris gilir bergerak ke hadapan. Tiga acara seterusnya dialihkan ke cangkuk kedua - ia dikeluarkan dari baris gilir dan dimasukkan ke dalamnya dalam "himpunan". Itu dia cangkuk menerima pelbagai acara β€” atau, lebih tepat lagi, pelbagai konteks yang mengikat.

Juga ini acara boleh digabungkan menjadi satu besar. Parameter bertanggungjawab untuk ini group dalam konfigurasi mengikat.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Anda boleh membuat sebarang bilangan baris gilir/cangkuk dan pelbagai kombinasinya. Sebagai contoh, satu barisan boleh berfungsi dengan dua cangkuk, atau sebaliknya.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Apa yang anda perlu lakukan ialah mengkonfigurasi medan dengan sewajarnya queue dalam konfigurasi mengikat. Jika nama baris gilir tidak dinyatakan, cangkuk berjalan pada baris gilir lalai (default). Mekanisme beratur ini membolehkan anda menyelesaikan sepenuhnya semua masalah pengurusan sumber apabila bekerja dengan cangkuk.

Kesimpulan

Kami menerangkan maksud pengendali shell, menunjukkan cara ia boleh digunakan untuk mencipta pengendali Kubernetes dengan cepat dan mudah, dan memberikan beberapa contoh penggunaannya.

Maklumat terperinci tentang pengendali shell, serta tutorial pantas tentang cara menggunakannya, tersedia dalam repositori di GitHub. Jangan teragak-agak untuk menghubungi kami dengan soalan: anda boleh membincangkannya secara istimewa Kumpulan telegram (dalam bahasa Rusia) atau dalam forum ini (dalam Bahasa Inggeris).

Dan jika anda menyukainya, kami sentiasa gembira melihat isu/PR/bintang baharu di GitHub, di mana, secara kebetulan, anda boleh mencari orang lain projek yang menarik. Antaranya patut ditonjolkan pengendali tambahan, yang merupakan abang kepada shell-operator. Utiliti ini menggunakan carta Helm untuk memasang alat tambah, boleh menyampaikan kemas kini dan memantau pelbagai parameter/nilai carta, mengawal proses pemasangan carta dan juga boleh mengubah suainya sebagai tindak balas kepada peristiwa dalam kelompok.

Pergi? Bash! Temui pengendali shell (semakan dan laporan video daripada KubeCon EU'2020)

Video dan slaid

Video daripada persembahan (~23 minit):


Pembentangan laporan:

PS

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komen