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” .
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.

tanıtım (~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).

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.

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 . Sistem yöneticilerinin tanıdık yöntemleri kullanarak kendi bildirimlerini oluşturmalarına olanak tanır.
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).

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

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 , 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:

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:

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:

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.

Tüm bu bilgilere dayanarak temel bir algoritma geliştirilebilir. Tüm ad alanları üzerinde yinelenir ve:
- eğer
hasLabelhususlartruegeç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 replaceveyacreate;
- küresel sırrı yerel olanla karşılaştırır:
- eğer
hasLabelhususlarfalsegeç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.
- yerel Sır mevcutsa, bunu kullanarak silin
- Secret'in verilen ad alanında olmadığından emin olur:

sitemizden indirebilirsiniz .
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:

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ı:

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.

Ö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.

Ö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.
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.

İ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.

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. . Sorularınız için bizimle iletişime geçmekten çekinmeyin: bunları özel bir toplantıda tartışabilirsiniz. (Rusça) veya (İ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 . Bunlar arasında vurgulamaya değer , 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.

Videolar ve slaytlar
Performanstan video (~23 dakika):
Raporun sunumu:
PS
Blogumuzda da okuyun:
- «";
- «";
- «";
- «.
Kaynak: habr.com
