Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Bu yıl, ana Avrupa Kubernetes konferansı - KubeCon + CloudNativeCon Europe 2020 - sanaldı. Ancak formattaki bu değişiklik, uzun zamandır planladığımız “Git mi? Bash! Açık Kaynak projemize adanmış Shell operatörüyle tanışın” kabuk operatörü.

Konuşmadan ilham alan bu makale, Kubernetes için operatör oluşturma sürecini basitleştirmeye yönelik bir yaklaşım sunuyor ve bir kabuk operatörü kullanarak minimum çabayla kendi operatörünüzü nasıl oluşturabileceğinizi gösteriyor.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

tanıtım raporun videosu (~23 dakika İngilizce, makaleden belirgin şekilde daha bilgilendirici) ve metin biçimindeki ana alıntı. Gitmek!

Flant'ta her şeyi sürekli olarak optimize ediyor ve otomatikleştiriyoruz. Bugün yine heyecan verici bir konseptten bahsedeceğiz. Tanışmak: bulutta yerel kabuk komut dosyası oluşturma!

Ancak tüm bunların gerçekleştiği bağlamla başlayalım: Kubernetes.

Kubernetes API'si ve denetleyicileri

Kubernetes'teki API, her nesne türü için dizinlere sahip bir tür dosya sunucusu olarak temsil edilebilir. Bu sunucudaki nesneler (kaynaklar) YAML dosyalarıyla temsil edilir. Ayrıca sunucunun üç şeyi yapmanıza olanak tanıyan temel bir API'si vardır:

  • almak türüne ve adına göre kaynak;
  • değiştirmek kaynak (bu durumda, sunucu yalnızca "doğru" nesneleri saklar - yanlış oluşturulmuş veya diğer dizinler için tasarlananların tümü atılır);
  • izleyin kaynak için (bu durumda kullanıcı derhal mevcut/güncellenmiş sürümünü alır).

Böylece Kubernetes, üç temel yöntemle bir tür dosya sunucusu (YAML bildirimleri için) görevi görür (evet, aslında başka yöntemler de var, ancak şimdilik bunları atlayacağız).

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Sorun, sunucunun yalnızca bilgi depolayabilmesidir. Çalışmasını sağlamak için ihtiyacınız var kontrolör - Kubernetes dünyasındaki ikinci en önemli ve temel kavram.

İki ana kontrolör türü vardır. Birincisi Kubernetes'ten bilgi alıp iç içe mantığa göre işliyor ve K8'lere geri gönderiyor. İkincisi Kubernetes'ten bilgi alır ancak ilk türden farklı olarak bazı dış kaynakların durumunu değiştirir.

Kubernetes'te Dağıtım oluşturma sürecine daha yakından bakalım:

  • Dağıtım Denetleyicisi (dahildir) kube-controller-manager) Dağıtım hakkında bilgi alır ve bir ReplicaSet oluşturur.
  • ReplicaSet bu bilgilere dayanarak iki kopya (iki bölme) oluşturur, ancak bu bölmeler henüz planlanmamıştır.
  • Zamanlayıcı, bölmeleri planlar ve YAML'lerine düğüm bilgilerini ekler.
  • Kubelet'ler harici bir kaynakta değişiklik yapar (Docker diyelim).

Daha sonra tüm bu sıra ters sırayla tekrarlanır: kubelet kapları kontrol eder, bölmenin durumunu hesaplar ve onu geri gönderir. ReplicaSet denetleyicisi durumu alır ve çoğaltma kümesinin durumunu günceller. Aynı şey Dağıtım Denetleyicisi'nde de olur ve kullanıcı sonunda güncellenmiş (geçerli) durumu alır.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Kabuk operatörü

Kubernetes'in çeşitli denetleyicilerin ortak çalışmasına dayandığı ortaya çıktı (Kubernetes operatörleri aynı zamanda denetleyicilerdir). Şu soru ortaya çıkıyor: Minimum çabayla kendi operatörünüzü nasıl yaratabilirsiniz? Ve burada geliştirdiğimiz şey kurtarmaya geliyor kabuk operatörü. Sistem yöneticilerinin tanıdık yöntemleri kullanarak kendi bildirimlerini oluşturmalarına olanak tanır.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Basit örnek: sırların kopyalanması

Basit bir örneğe bakalım.

Diyelim ki bir Kubernetes kümemiz var. Bir ad alanı var default bazı sırlarla mysecret. Ayrıca kümede başka ad alanları da vardır. Bazılarının üzerine özel bir etiket iliştirilmiştir. Amacımız Secret'ı bir etiketle ad alanlarına kopyalamaktır.

Kümede yeni ad alanlarının görünebilmesi ve bazılarının bu etikete sahip olabilmesi nedeniyle görev karmaşıklaşmaktadır. Öte yandan etiket silindiğinde Secret’ın da silinmesi gerekir. Buna ek olarak Secret'ın kendisi de değişebilir: bu durumda yeni Secret'ın tüm ad alanlarına etiketlerle kopyalanması gerekir. Secret herhangi bir ad alanında kazara silinirse operatörümüz onu hemen geri yüklemelidir.

Artık görev formüle edildiğine göre, onu kabuk operatörü kullanarak uygulamaya başlamanın zamanı geldi. Ama önce kabuk operatörünün kendisi hakkında birkaç söz söylemekte fayda var.

Kabuk operatörü nasıl çalışır?

Kubernetes'teki diğer iş yükleri gibi kabuk operatörü de kendi bölmesinde çalışır. Dizindeki bu bölmede /hooks yürütülebilir dosyalar saklanır. Bunlar Bash, Python, Ruby vb. dillerindeki komut dosyaları olabilir. Bu tür yürütülebilir dosyalara kancalar diyoruz (kancalar).

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Kabuk operatörü Kubernetes etkinliklerine abone olur ve ihtiyacımız olan olaylara yanıt olarak bu kancaları çalıştırır.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Kabuk operatörü hangi kancanın ne zaman çalıştırılacağını nasıl biliyor? Mesele şu ki, her kancanın iki aşaması vardır. Başlangıç ​​sırasında, kabuk operatörü tüm kancaları bir argümanla çalıştırır. --config Bu yapılandırma aşamasıdır. Ve bundan sonra kancalar, bağlandıkları olaylara yanıt olarak normal şekilde başlatılır. İkinci durumda, kanca bağlama bağlamını alır (bağlayıcı bağlam) - aşağıda daha ayrıntılı olarak konuşacağımız JSON formatındaki veriler.

Bash'te operatör oluşturma

Artık uygulamaya hazırız. Bunu yapmak için iki fonksiyon yazmamız gerekiyor (bu arada şunu öneriyoruz: kütüphane kabuk_lib'i, Bash'te kanca yazmayı büyük ölçüde basitleştiren):

  • ilki yapılandırma aşaması için gereklidir - bağlama bağlamını görüntüler;
  • ikincisi kancanın ana mantığını içerir.

#!/bin/bash

source /shell_lib.sh

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

function __main__() {
  # THE LOGIC
}

hook::run "$@"

Bir sonraki adım hangi nesnelere ihtiyacımız olduğuna karar vermektir. Bizim durumumuzda şunları izlememiz gerekiyor:

  • değişiklikler için kaynak sırrı;
  • kümedeki tüm ad alanları; böylece hangilerinin etiketlendiğini bilirsiniz;
  • Tümünün kaynak sırrı ile senkronize olduğundan emin olmak için sırları hedefleyin.

Gizli kaynağa abone olun

Bunun için bağlama konfigürasyonu oldukça basittir. Secret ile ilgilendiğimizi ismiyle belirtiyoruz. mysecret ad alanında default:

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

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

Sonuç olarak, kaynak sırrı değiştiğinde kanca tetiklenecektir (src_secret) ve aşağıdaki bağlayıcı bağlamı alın:

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Gördüğünüz gibi adı ve nesnenin tamamını içeriyor.

Ad alanlarının takibi

Artık ad alanlarına abone olmanız gerekiyor. Bunu yapmak için aşağıdaki bağlama yapılandırmasını belirtiyoruz:

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

Gördüğünüz gibi konfigürasyonda adında yeni bir alan belirdi. jqFilter.jqFilter. Adından da anlaşılacağı gibi, jqFilter tüm gereksiz bilgileri filtreler ve ilgimizi çeken alanlarla yeni bir JSON nesnesi oluşturur. Benzer konfigürasyona sahip bir kanca aşağıdaki bağlama bağlamını alacaktır:

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Bir dizi içerir filterResults kümedeki her ad alanı için. Boole değişkeni hasLabel belirli bir ad alanına bir etiketin eklenip eklenmediğini belirtir. Seçici keepFullObjectsInMemory: false Nesnelerin tamamını bellekte tutmaya gerek olmadığını belirtir.

Hedef sırlarını izleme

Belirtilen bir ek açıklamaya sahip tüm Sırlar'a aboneyiz managed-secret: "yes" (bunlar bizim hedefimiz 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

Bu durumda, jqFilter ad alanı ve parametre dışındaki tüm bilgileri filtreler resourceVersion. Son parametre, sır oluşturulurken ek açıklamaya aktarıldı: gizli dizilerin sürümlerini karşılaştırmanıza ve bunları güncel tutmanıza olanak tanır.

Bu şekilde yapılandırılan bir kanca, yürütüldüğünde yukarıda açıklanan üç bağlama bağlamını alacaktır. Bir tür anlık görüntü olarak düşünülebilirler (enstantane) küme.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Tüm bu bilgilere dayanarak temel bir algoritma geliştirilebilir. Tüm ad alanları üzerinde yinelenir ve:

  • eğer hasLabel hususlar true geçerli ad alanı için:
    • küresel sırrı yerel olanla karşılaştırır:
      • eğer aynılarsa hiçbir şey yapmaz;
      • eğer farklıysa - yürütülür kubectl replace veya create;
  • eğer hasLabel hususlar false geçerli ad alanı için:
    • Secret'in verilen ad alanında olmadığından emin olur:
      • yerel Sır mevcutsa, bunu kullanarak silin kubectl delete;
      • yerel Sır tespit edilmezse hiçbir şey yapmaz.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Algoritmanın Bash'te uygulanması sitemizden indirebilirsiniz örnekler içeren depolar.

35 satırlık YAML yapılandırması ve yaklaşık aynı miktarda Bash kodu kullanarak basit bir Kubernetes denetleyicisini bu şekilde oluşturabildik! Kabuk operatörünün görevi bunları birbirine bağlamaktır.

Ancak sırların kopyalanması, yardımcı programın tek uygulama alanı değildir. İşte onun neler yapabileceğini gösteren birkaç örnek daha.

Örnek 1: ConfigMap'te değişiklik yapma

Üç bölmeden oluşan bir Dağıtıma bakalım. Pod'lar bazı yapılandırmaları depolamak için ConfigMap'i kullanır. Pod'lar başlatıldığında ConfigMap belirli bir durumdaydı (buna v.1 diyelim). Buna göre tüm bölmeler ConfigMap'in bu özel sürümünü kullanır.

Şimdi ConfigMap'in değiştiğini varsayalım (v.2). Ancak bölmeler ConfigMap'in (v.1) önceki sürümünü kullanacaktır:

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Yeni ConfigMap'e (v.2) geçmelerini nasıl sağlayabilirim? Cevap basit: bir şablon kullanın. Bölüme bir sağlama toplamı açıklaması ekleyelim template Dağıtım yapılandırmaları:

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Sonuç olarak, bu sağlama toplamı tüm bölmelere kaydedilecek ve Dağıtımınkiyle aynı olacaktır. Artık ConfigMap değiştiğinde ek açıklamayı güncellemeniz yeterlidir. Ve kabuk operatörü bu durumda kullanışlı oluyor. Tek yapmanız gereken programlamak ConfigMap'e abone olacak ve sağlama toplamını güncelleyecek bir kanca.

Kullanıcı ConfigMap'te değişiklik yaparsa, kabuk operatörü bunları fark edecek ve sağlama toplamını yeniden hesaplayacaktır. Bundan sonra Kubernetes'in büyüsü devreye girecek: orkestratör kapsülü öldürecek, yeni bir tane yaratacak, onun olmasını bekleyecek Readyve bir sonrakine geçiyoruz. Sonuç olarak, Dağıtım senkronize edilecek ve ConfigMap'in yeni sürümüne geçecektir.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Örnek 2: Özel Kaynak Tanımlarıyla Çalışmak

Bildiğiniz gibi Kubernetes, özel nesne türleri oluşturmanıza olanak tanıyor. Örneğin, tür oluşturabilirsiniz MysqlDatabase. Diyelim ki bu türün iki meta veri parametresi var: name и namespace.

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

MySQL veritabanlarını oluşturabileceğimiz farklı ad alanlarına sahip bir Kubernetes kümemiz var. Bu durumda kaynakları izlemek için kabuk operatörü kullanılabilir MysqlDatabasebunları MySQL sunucusuna bağlayarak kümenin istenen ve gözlemlenen durumlarını senkronize eder.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Örnek 3: Küme Ağı İzleme

Bildiğiniz gibi ping kullanmak bir ağı izlemenin en basit yoludur. Bu örnekte, bu tür izlemenin kabuk operatörü kullanılarak nasıl uygulanacağını göstereceğiz.

Öncelikle düğümlere abone olmanız gerekecek. Kabuk operatörünün her düğümün adına ve IP adresine ihtiyacı vardır. Onların yardımıyla bu düğümlere ping atacak.

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: "* * * * *"

Parametre executeHookOnEvent: [] kancanın herhangi bir olaya (yani düğümlerin değiştirilmesine, eklenmesine, silinmesine yanıt olarak) çalışmasını engeller. Ancak o koşacak (ve düğüm listesini güncelleyin) tarifeli - sahanın öngördüğü şekilde her dakika schedule.

Şimdi şu soru ortaya çıkıyor: Paket kaybı gibi sorunları tam olarak nasıl biliyoruz? Kodlara bir göz atalım:

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
}

Düğümlerin listesini tekrarlıyoruz, adlarını ve IP adreslerini alıyoruz, onlara ping atıyoruz ve sonuçları Prometheus'a gönderiyoruz. Kabuk operatörü metrikleri Prometheus'a aktarabilirbunları ortam değişkeninde belirtilen yola göre konumlanan bir dosyaya kaydederek $METRICS_PATH.

Böyle bir kümede basit ağ izleme için bir operatör yapabilirsiniz.

Kuyruk mekanizması

Bu makale, kabuk operatörüne yerleşik başka bir önemli mekanizmayı tanımlamadan eksik kalacaktır. Kümedeki bir olaya yanıt olarak bir tür kanca çalıştırdığını düşünün.

  • Aynı anda kümede bir şeyler olursa ne olur? bir şey daha etkinlik?
  • Kabuk operatörü kancanın başka bir örneğini çalıştıracak mı?
  • Peki ya kümede aynı anda beş olay meydana gelirse?
  • Kabuk operatörü bunları paralel olarak işleyecek mi?
  • Bellek ve CPU gibi tüketilen kaynaklar ne olacak?

Neyse ki kabuk operatörü yerleşik bir sıraya alma mekanizmasına sahiptir. Tüm olaylar sıraya alınır ve sırayla işlenir.

Bunu örneklerle açıklayalım. Diyelim ki iki kancamız var. İlk olay ilk kancaya gider. İşlem tamamlandıktan sonra sıra ileri doğru hareket eder. Sonraki üç olay ikinci kancaya yönlendirilir - sıradan kaldırılır ve "toplu olarak" buraya girilir. Yani hook bir dizi olayı alır - veya daha doğrusu bir dizi bağlayıcı bağlam.

ayrıca bunlar olaylar tek bir büyük olayda birleştirilebilir. Parametre bundan sorumludur group bağlama konfigürasyonunda.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

İstediğiniz sayıda kuyruk/kanca ve bunların çeşitli kombinasyonlarını oluşturabilirsiniz. Örneğin, bir kuyruk iki kancayla çalışabilir veya tam tersi.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Tek yapmanız gereken alanı buna göre yapılandırmak queue bağlama konfigürasyonunda. Bir kuyruk adı belirtilmezse kanca varsayılan kuyrukta çalışır (default). Bu sıraya alma mekanizması, kancalarla çalışırken tüm kaynak yönetimi sorunlarını tamamen çözmenizi sağlar.

Sonuç

Kabuk operatörünün ne olduğunu açıkladık, hızlı ve zahmetsizce Kubernetes operatörleri oluşturmak için nasıl kullanılabileceğini gösterdik ve kullanımına ilişkin birkaç örnek verdik.

Kabuk operatörü hakkında ayrıntılı bilginin yanı sıra nasıl kullanılacağına ilişkin hızlı bir eğitim de ilgili bölümde mevcuttur. GitHub'daki depolar. Sorularınız için bizimle iletişime geçmekten çekinmeyin: bunları özel bir toplantıda tartışabilirsiniz. Telgraf grubu (Rusça) veya bu forum (İngilizce).

Ve eğer beğendiyseniz, GitHub'da yeni sayıları/PR'leri/yıldızları görmekten her zaman mutlu oluruz; bu arada, başkalarını da burada bulabilirsiniz ilginç projeler. Bunlar arasında vurgulamaya değer eklenti operatörü, kabuk operatörünün ağabeyi. Bu yardımcı program, eklentileri yüklemek için Helm grafiklerini kullanır, güncellemeler sunabilir ve çeşitli grafik parametrelerini/değerlerini izleyebilir, grafiklerin kurulum sürecini kontrol edebilir ve ayrıca kümedeki olaylara yanıt olarak bunları değiştirebilir.

Gitmek? Bash! Kabuk operatörüyle tanışın (KubeCon EU'2020'den inceleme ve video raporu)

Videolar ve slaytlar

Performanstan video (~23 dakika):

Oynat Video

Raporun sunumu:

PS

Blogumuzda da okuyun:

Kaynak: habr.com

DDoS korumalı siteler, VPS VDS sunucuları için güvenilir hosting satın alın 🔥 DDoS korumalı, güvenilir VPS ve VDS sunucu barındırma hizmeti satın alın | ProHoster