Trendyol’da Kubernetes’i Kendi İhtiyaçlarımıza göre Nasıl Extend Ettik ?

developer-guy
Trendyol Tech
Published in
14 min readApr 22, 2020

--

Öncelikle nasıl sorusunun cevabına geçmeden önce neden böyle bir ihtiyacımız oldu, alternatif yollar nelerdi ve neden bu yolu tercih ettik bunlarla başlayalım isterseniz. Bildiğiniz üzere Trendyol’un artan popülerliğinin ve piyasadaki en büyük e-ticaret platformu olma gayretinin getirdiği teknik yükleri karşılamak üzere sorunlarımıza ve ihtiyaçlarımıza cevap verebilecek yazılım pratiklerini, dillerini ve araçlarını oldukça en güncel halleriyle takip etmeye özen gösteriyoruz aslında Trendyol’u da kendisini bir teknoloji şirkete olarak lanse etmesinin ve kendisini bu derece dinamik bir şirket yapmasının ardındaki en büyük etken de bu.Ve bu noktada bahsettiğim bu teknik yükümüzü karşılamak için hangi pratikleri ne şekilde kullandığımızı, hangi yazılım dillerine önem verdiğimizi, hangi yazılım araçlarını ve kütüphaneleri nerede ve nasıl kullandığımızı sıklıkla Trendyol Tech Medium kanalımızdan ve ayrıca Trendyol Github hesabından da içeride kullandığımız ve open source edebileceğimizi düşündüğümüz araçları da paylaşmaya gayret ediyoruz.Mesela yukarıda bahsettiğimiz konularla ilgili örnekler vermem gerekirse kullandığımız yazılım pratiklerimiz ile ilgili bilgi veren Caner Patır ‘ ın yazısına , hangi dillere önem verdiğimiz ile ilgili örnek olması açısından Hüseyin Güner’in yazısına ve projelerimizde kullandığımız faydalı kütüphanelerin nasıl kullanılabileceği konusunda destek olabilecek Emre Savcı’nın yazısına göz atmanızı öneririm , yine yazının devam eden noktalarında paylaştığımız faydalı yazıların da linklerini paylaşıyor olacağım.Şimdi bu kadar Trendyolizm yaptıktan sonra konumuza dönelim. 😊

Aslında başlıca çözmeye çalıştığımız ve en çok önem verdiğimiz iki konu scalability ve availability konuları yani mümkün olduğunca az kaynak tüketimi ile oldukça fazla sayıda workload(iş yükü) ile trafiği karşılamaya çalışmak ve her duruma karşı -buradaki kastedilen durumlar beklenilenden fazla yükün sistemlerimize gelmesi,örnek olarak yapılan event dönemleri, beklenmeyen network ve sunucu taraflarında yaşanabilecek problemler-sistemlerimizin yine mümkün olduğunca ayakta kalabilmesini sağlamak.Bu konulara bir çözüm olarak Trendyol’un aktif/aktif çalışabilmesi için Multi DC/Cloud çalışmaları gerçekleştiriyoruz ve bununla beraber de organizasyonumuz içerisinde bazı standartlar ve iyileştirmeler sağlamayı hedefliyoruz.Bu sayede bildiğimiz ve deneyimlediğimiz tüm pratiklerimize , yazılım araçlarına vs. hep Multi DC/Cloud üzerinde nasıl çalıştırabiliriz diye yeni bir pencereden yaklaşıyoruz.Bahsettiğimiz bu Multi DC/Cloud projemizin paralel olarak ilerlettiğimiz bir çok alt başlığı var ve bunlarla ilgili örnek vermek gerekirse Software Config Management, Networking(Service Discovery,Cache,Load balancing) ve Logging önde gelen alt başlıklardan.

Tüm bu süreç boyunca organizasyonumuz içerisindeki tüm ekiplerlerle bilgi paylaşımı, fikir alışverişi yaparak ekiplerimizin de gelişimine katkı sağladığımız gibi diğer bir gayretimiz de bunu Trendyol’un iletişim kanallarını aktif şekilde kullanarak edindiğimiz tecrübelerimizi paylaşabilmek ve bu vesileyle de fayda sağlayabilmek.

Biz bu yazımızda Software Configuration Management kısmı ile ilgileneceğiz fakat ekibim içerisindeki diğer arkadaşlarım da paralel olarak ilerlettiğimiz bu alt başlıklarla ilgili slaytlar ve yazılar hazırlıyorlar mesela bu örnek olarak Networking tarafı ile ilgili gökhan karadaş ‘ ın İstio’yu anlattığı slaytına ve Oğuzhan Demir ‘ in Logging tarafında kullandığımız ES ile ilgili yazısına bakabilirsiniz ayrıca tüm bu konularla ilgili yazıların da bu süreç boyunca devam edeceğini bu yüzden Trendyol Tech Medium sayfasını takip etmenizi bu vesileyle bir kez daha öneririm.

In software engineering, software configuration management is the task of tracking and controlling changes in the software, part of the larger cross-disciplinary field of configuration management. SCM practices include revision control and the establishment of baselines. Wikipedia

Aslında bu noktaya kadar küçük bir detay dikkatinizi çekmiş olmalı, fark ettiyseniz konuyu Config Management olarak adlandırmadık çünkü Config Management aslında Infrastructure içerisindeki sunucularınıza, gerekli araçlarının kurulumlarını , yönetimlerini ve bakımını kolaylaştırmayı hedefleyen bir yaklaşım dolayısıyla bizim bu yazımızda ilgilendiğimiz konu Software Configuration Management yani uygulamalarımızın sahip olduğu konfigürasyonları nasıl yönetebileceğimiz.

İlk olarak işe uygulamalarımızın bu konudaki ihtiyaçlarını anlayarak başladık.Genellikle uygulamalarımız 3rd sistemler ile entegre olabilmek, kendi internal davranışlarını anlık değiştirebilmek, environment(dev, stage, prod) ihtiyaçlarını karşılayabilmek vs. gibi konularda bazı özel konfigürasyonlara ihtiyaç duyarlar.

Dolayısıyla buradan da anlıyoruz ki biz bu konfigürasyonları aslında 3 kategoriye ayırabiliriz.

  • Non-Sensitive Configurations → başkaları tarafından görüntülemesinin bir risk oluşturmadığı, saklanması için ek bir güvenlik gerektirmeyen konfigürasyonlar.
  • Sensitive Configurations → bizim için güvenli bir şekilde saklanması , erişimlerin denetlenebilir olması gereken ve başkaları tarafından görülmesinin riskli olduğu konfigürasyonlar
  • Dynamic Configurations → anlık olarak değişebilecek konfigürasyonlar yani uygulamaların runtime’ da davranışlarını değiştirebilmelerine yardımcı olmak amacıyla kullandıkları ayarlar.

Dolayısıyla bizim, uygulamalarımız için tüm bu konfigürasyon türlerini destekleyebilecek şekilde, mümkün olduğunca kolay adapte olunabilir, güvenli ve gözlemlenebilir/denetlenebilir bir şekilde bu süreci gerçekleştirebilmemiz gerekmekte.

Trendyol’da çoğu workloadlarımızı Kubernetes’e deploy edip orchestrate ediyoruz.Dolayısıyla bu süreç için sunduğumuz çözümümüzün merkezine Kubernetes’i koyarak başladık.

Kubernetes sektörde orchestration tooları arasında de-facto standardı olmuş durumda ve bu denli başarılı olmasının ardında bir çok etmen var bunlardan bazılarını belirtmek gerekirse:

- Service discovery and load balancing

- Storage orchestration

- Automated rollouts and rollbacks

- Self-healing

- Secret and configuration management

https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/#why-you-need-kubernetes-and-what-can-it-do

Görüldüğü üzere Kubernetes bir çok konuda bize native şekilde support sunuyor fakat Kubernetes’i çok daha özel yapan configurable and extensible şekilde dizayn edilmiş olması.Aslında yazının görselini de seçerken bu özelliğine atıfta bulunmak istedim.Yani Kubernetes içerisinde native olarak gelen neredeyse her bir componenti alternatifleri ile hatta kendi custom çözümlerimizle değiştirebilir veya bu componentlere ek işlevsellik kazandırabiliriz bu noktada bahsettiğim componentler için ne şekilde extensible noktalar tanımlandığını official dökümanından inceleyebilirsiniz.

NOT: Dökümanı okurken göreceğiniz Dynamic Admission Controllers bizim yazımızın başlığında belirttiğimiz Kubernetes’e kendi custom çözümlerimizi eklerken kullandığımız bir API Access Extension yani API Server’in davranışına ekstra yeni özellikler ekleyebilmemizi sağlayan bir yapı.

Admission Controllers Nedir?

Detayına geçmeden önce benim burada değinmek istediğim bir konu var, daha doğrusu ben anlarken ve parçaları yerine oturturken zorlandığım ama sizin bu konuda zorluk çekmenizi istemediğim detaylar aslında.Bu konuyu incelemeye başlacağınız ilk adres Kubernetes Admission Controllers. Fakat okumaya başladığınızda bir kaç kavram birbirine karışabiliyor.Şimdi Admission Controllers diye bir kavram var ki dökümantasyonunda Admission Plugins dendiğini de fark edeceksiniz fakat ilk başta dikkat etmemiz gereken bu ikisinin aynı şey olduğu yani Admission Plugins dendiğinde de Admission Controllers’ ın kastedildiğini düşünebiliriz.

Şimdi artık biraz detayına girelim.Kubernetes Admission Controllers için kendi official dökümantasyonundaki açıklaması şu şekilde:

An admission controller is a piece of code that intercepts requests to the Kubernetes API server prior to persistence of the object, but after the request is authenticated and authorized. The controllers consist of the list below, are compiled into the kube-apiserver binary, and may only be configured by the cluster administrator. In that list, there are two special controllers: MutatingAdmissionWebhook and ValidatingAdmissionWebhook.

Şimdi bu görsel üzerinden Admission Controllers’ ı açıklamak gerekirse:

  • Admission Controllers Kubernetes API Server’a gelen istekler auth/authz adımlarından geçtikten sonra -bunu belirtmek önemli çünkü Admission Controllers’ ın request objesi üzerinde validating&mutating işlemleri yapabilir dolayısıyla yetkisi olmayan kişilerin bu işlemi yapmasını istemeyiz- fakat request objesinin henüz final versiyonu Etcd’ye kaydedilmeden önce araya girip mutating&validating işlemleri yapabilen kod parçalarıdır.
  • Admission Controllers Kubernetes API Server binarysi içerisine derlenmişlerdir ve API Server başlatılırken --enable-admission-plugins flag’i ile defaul(varsayılan) olarak enable(etkin) olması istenilenler belirtilir.Bizim için default olarak enable edilen Admission Controller listesine buradan ulaşabilirsiniz.Şimdi basitçe Minikube clusterımız üzerinde enabled durumdaki Admission Controllers’ı nasıl görüntüleyebileceğimizi gösterelim.
Minikube cluster kurulumunu yaptıktan sonra , makineye ssh ile girip isterseniz htop isterseniz ps komutlarından çalışan processleri listelemek için faydalanabilirsiniz.Ben ps komutunu tercih edeceğim, öncelikle çalışan kube-apiserver process’inin PID değerini bulalım ve --enable-admission-plugins flag’ inin sahip olduğu değerlere dikkat edelim.$ minikube start --kubernetes-version 1.17.0 \
--vm-driver virtualbox \
--cpus 2 --memory 4090
$ minikube ssh
$ pgrep kube-apiserver
3608
$ ps -wwfp 3608
finding running kube-apiserver process
  • Admission Controllers ”validating”, “mutating” veya her ikisi gibi de davranabilir, buradaki mutate kavramını değiştirmek olarak düşünebilirsiniz. Mutating Controllers requesti hem mutate hem validate edebilirken Validating Controllers ise sadece validate edebilir.
  • Admission Controller işleminin 2 phase(adımı) vardır önce mutating phase işletilir ardından validating phase onu takip eder.
  • Mutating Admission Controllers sıralı(in sequence) şekilde çalışırken Validating Admission Controllers paralel şekilde çalışır.
  • Gelelim terimlerin biraz karıştığı noktaya.Bu default olarak enable edilen Admission Controllers listesinde 2 tanesinin özel yeri var bunlar: MutatingAdmissionWebhook ve ValidatingAdmissionWebhook

NOT: Bu Admission Controllers’ ın isimlerindeki “WebHook” suffixine takılmayın bu 2'si birer Admission Controller sadece “WebHook” diye suffixe sahipler, bahsettiğim bazı karmaşıklığa sebep olan durumlar bunlar işte 😊

  • MutatingAdmissionWebhook ve ValidatingAdmissionWebhook Admission Controllers kendi başlarına herhangi bir logic implemente etmezler bunun yerine ilgili eylemi clusterınızda çalışan bir servisin REST Endpoint(webhook)’dan elde ederler.Bu yaklaşım Admission Controller’ın logic(kodu)’ini Kubernetes API Server’dan ayrıştırmış olur ve kullanıcılara her ne zaman Kubernetes clusterında bir resource üzerinde CRUD işlemi gerçekleştiğinde kendi custom logic’lerini implemente etme imkanı verir.
  • Admission Webhooks yada Admission Controller Webhooks ise aslında birer HTTP callbackler’ dir yukarıda da belirttiğim gibi Admission Controllers tarafından gönderilen requesti alıp bu request ile işlem yaparlar.Kubernetes’in pre-defined Admission Controllers ile sağladığı davranışları extend edebilmemizi sağlayan yani kendi custom çözüm/işlevselliğimizi katabildiğimiz yapılardır.Dolayısıyla Admission Webhooks ile Admission Controllers’ın iki farklı kavram olduğuna dikkat edelim.

NOT: Yukarıdaki API Request’inin Lifecycle’a dikkatli bakacak olursak bu mutation işlemlerinden sonra Object Schema Validation adımı çalışıyor bu sayede mutation işlemleri sırasında request objesinin yapısının valid/invalid kontrolü gerçekleştiriliyor yani eğer request objesinin şemasını bozacak bir işlemimiz olduysa bu request object’in etcd kaydedilmesine izin verilmiyor.

Admission Controllers ile Neler Yapabilirim?

  • Clusterınızdaki güvenliğinizi artırabilirsiniz.Örneğin containerlarınızın sahip olduğu imagelarınızın sadece organizasyonunuz tarafından kabul edilen registry’lerden indirilmesini, containerlarının root veya privileged olarak çalıştırılmasının engellenmesini , eğer ki root veya priveleged modda çalıştırılmasına müsade ettiyseniz de container’ın root filesystem’nin her zaman readonly olarak bağlanmış olmasını , container imagelarının imagePullPolicy’nin Always olmasını bu sayede sadece registry’den image’ın indirilebilmesi uygun credentiallara sahip olan deploymentların deployunun yapılmasını sağlayabilirsiniz.
  • Clusterınız için best-practice yaklaşımlar uygulatabilirsiniz.Örneğin uygun label/annotationların ayarlanması, cpu/mem resource limitleri olmayan containerlara izin verilmemesini sağlayabilirsiniz.
  • Clusterınızdaki containerlarınızın konfigürasyonlarının validasyonunu yapabilirsiniz.Örneğin cpu/mem resource limitleri olmayan containerların default limit değerlerinin atanması, uygun olan label/annotation değerlerine izin verilmesi , container image’larının latest tagına sahip olmalarının engellenmesini sağlayabilirsiniz.
  • Clusterınızdaki containerlarınız için helper amaçlı containerlar(Sidecars) inject edebilirsiniz buna en iyi örneklerden birisi Service Mesh implementasyonlarından biri olan İstio’nun container networküne müdahale edebilmek için container’a inject ettiği istio-proxy.

Admission Controllers ve Webhooks hakkında yeterli bilgiyi verdikten sonra bahsettiğimiz tüm bu konfigürasyon türlerini destekleyebilmek için araştırma yaparken karşımıza Hashicorp un bu konu özelinde geliştirdiği Consul & Vault araçları çıktı.Bir süre bu araçlarla ilgili knowhow’ımızı geliştirdikten sonra gerçekten bizim için biçilmiş kaftanlar olduklarını anladık ve non-sensitive konfigürasyonlarımızın yönetimi için Consul’u , sensitive konfigürasyonlarımız yani secretlarımızın yönetimi için de Vault’u nu noktada tercih ettik.Eğer siz de bu konudaki knowhow’ınızı artırmak isterseniz Consul 101 ve Vault 101 adlı yazılarımızı okuyabilirsiniz.

Bir sonraki challange ise tüm bu süreçleri Admission Controllers ile nasıl birleştirebileceğimiz ve containerlarımıza nasıl bu logic’i ekleyebileceğimizdi.
Burada da karşımıza bu işi yapabilmemize imkan sağlayan yine Hashicorp’un consul-template toolu çıktı yani eğer bu tool olmasaydı bu işi kendimiz de yapabilirdik fakat bu tool bizi bu maliyetten kurtardı.

Şimdi implementasyon detaylarına girmeden kısaca consul-template’i biraz açıklayalım.Consul-Template projesi kendi dökümanında şu şekilde ifade edilmiş.

Template rendering, notifier, and supervisor for @hashicorp Consul and Vault data. https://www.hashicorp.com/

En basit haliyle şöyle ifade edebiliriz , consul-template aslında bir cli aracı ve kendine özgü bir Templating language’ e sahip ve bu aracı konfigüre etmek için kullanılan bir Configuration File Format’ına sahip ki bunu HCL language kullanarak yazıyoruz. Bu araç sayesinde templating language ‘i kullanarak yazmış olduğumuz template dosyalarımızdaki tanımların Consul&Vault üzerindeki gerçek değerleriyle değiştirilmesini ve bu template dosyalarına karşılık verdiğimiz dosyalara yazılmasını sağlıyoruz.Karmaşık gibi gelmiş olabilir ama aslında gerçekten basit hemen bunu bir demoyla gösterelim.

Konumuz ile ilgili olan bir iki template language örneği vermemiz gerekirse, basitçe consul-template’i kuralım:

$ brew install consul-template# Bu demomuz için kullanacağımız in.tpl'ı biraz açıklayalım.# Bizim için Consul K/V Store'undaki foo path'ine gider ve oradaki değeri bu tanım ile replace eder.
https://github.com/hashicorp/consul-template#key
foo: {{ key "foo" }}# "key" keywordü sayesinde Consul üzerinde arama yapacağını bildiği gibi "secret" keywordü ile de Vault üzerinde arama yapacağını bilir.Dolayısıyla Vault üzerindeki secret/passwords path'indeki "wifi" key'inin değerini replace eder.
https://github.com/hashicorp/consul-template#secret
{{ with secret "secret/passwords" }}
wifiPassword: {{ .Data.wifi }}
{{ end }}
$ consul agent -dev$ consul kv put foo bar$ vault server -dev $ export VAULT_ADDR="http://127.0.0.1:8200"$ export VAULT_TOKEN="s.cYT3j0sAV8RXSs3AFeibdyqu"$ vault kv put secret/passwords wifi='superse)ret'$ cat > in.tpl <<EOF
foo: {{ key "foo" }}
{{ with secret "secret/passwords" }}
wifiPassword: {{ .Data.data.wifi }}
{{ end }}
EOF
$ consul-template -once -consul-addr http://127.0.0.1:8500 -vault-addr $VAULT_ADDR -vault-token $VAULT_TOKEN -template in.tpl:out.txt -vault-renew-token=false$ cat out.txt

NOT: consul-template processini -once flag’i yardımıyla tek seferlik çalıştırıp sonlandırabildiğimiz gibi daemon olarak da bu flag’i vermeden çalıştırabiliriz.

Bu süreci implemente edebilmek için tüm araçlara sahibiz tek yapmamız gereken tüm bu parçaları bir araya getirmek.Bu konu ile ilgili bu süreçte beraber çalıştığımız Onur Yilmaz ‘ ın yazısına bir göz atmanızı öneririm . Ben yazımızın daha fazla uzamaması nedeniyle bu konuya özet şeklinde değineceğim.

Neler yaptığımızın teknik detaylarına fazla girmeden tüm bu parçaları nasıl birleştirdiğimizin kısa bir özetini yapalım.

  • İlk yapmamız gereken sidecar olarak consul-template’i inject edebilmemiz için bu consul-template’i dockerize edip bir image haline getirdik , bu neden önemliydi çünkü sidecar olarak çalıştırırken bazı konfigürasyon değerleri geçmemiz ve consul-template bu konfigürasyon değerlerine uygun çalıştırmamız gerekiyordu.Bu linkten image’ın detaylarına ulaşabilirsiniz , bu vesileyle bir konuya daha değinmek istiyorum , Trendyol olarak open source edebileceğimiz image’larımızı da Docker Hub hesabımızdan paylaşıyoruz , bakmanızı öneririm.
  • Daha sonraki adımda MutatingAdmissionWebhook backend’ini yazmaya başladık, bu sayede Kubernetes API Server’e gelen Pod create(yaratma) isteklerinde araya girecek , Pod spec’ine , uygulamaların ayağa kalkarken ihtiyaç duyabileceği secret ve config değerlerini önceden doldurabilmek için bir init container sokacak daha sonra bu değerlerin Consul&Vault üzerindeki değişimlerini dinleyip dosya üzerinde sync işlemlerini gerçekleştirebilmek için bir sidecar container ekleyebileceğiz.

Şimdi bazı teknik detaylara değinelim.İlk yapmamız gereken MutatingAdmissionWebhook ‘ umuzu Kubernetes API Server’a register edebilmek bunu, yaparken Kubernetes MutatingWebhookConfiguration adlı resource/object(ikisi tabir de kullanılıyor) yardımıyla yapıyoruz.

https://gist.github.com/developer-guy/07b9cadd2018a460f2c12501c374dc77

Öncelikle şundan bahsedelim, Kubernetes API Server tanımını yaptığımız webhook backendimiz ile MutatingWebhookConfiguration’ ın clientConfig altında belirttiğimiz bir service üzerinden devraldığı /mutate(burada bir kısıtlama yok istediğiniz path’e karar verebilirsiniz) endpoint’i üzerinden haberleşecek ve görüldüğü üzere herhangi bir port tanımı koymadık burada bir constraint var bu haberleşmenin istisnasız her zaman 443 HTTPS portu üzerinden olmasını bekliyor çünkü API Server.Dolayısıyla bu süreçte bir self-signed sertifika oluşturmamız ve bu süreçte oluşturduğumuz CA’nın(Certificate Authority) contentinin Base64 halini caBundle fieldına vermemiz gerekmekte.

Since a webhook must be served via HTTPS, we need proper certificates for the server. These certificates can be self-signed (rather: signed by a self-signed CA), but we need Kubernetes to instruct the respective CA certificate when talking to the webhook server. In addition, the common name (CN) of the certificate must match the server name used by the Kubernetes API server, which for internal services is <service-name>.<namespace>.svc.

The webhook configuration shown previously contains a placeholder ${CA_PEM_B64}. Before we can create this configuration, we need to replace this portion with the Base64-encoded PEM certificate of the CA.The openssl base64 -A command can be used for this purpose.

https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/

https://gist.github.com/developer-guy/e6788216e412366daff0d0082e891df3

NOT: Çok önemli bir detay olarak CSR oluştururken -subj ile verdiğimiz “/CN=sidecar-injector-service.platform.svc” bu değerin webhooks tanımı altındaki name field’i ile eşleşmesi gerekmekte çünkü bu sayede TLS’i verify edecek API Server.

Dedik ki bir service üzerinden haberleşecek hemen bu service tanımımıza göz atalım.

https://gist.github.com/developer-guy/ff63f5326b0aba63120a4b1099b78ee7

Tüm bunların detaylarına Trendyol Github hesabından ulaşabilirsiniz , teknik detayların önemli olan noktalarına değindiğim için devam ediyorum.

Peki her şeyi dizayn ettik fakat Kubernetes API Server bu endpointe gönderdiği istekte nasıl bir model kullanıyor ve nasıl bir response modeli bekliyor? Şimdi buna göz atalım.

Evet , Kubernetes API Server bize request olarak AdmissionReview modelini yolluyor ve yine response olarak AdmissionReview modelini bekliyor.Bu modelin detaylarına bakacak olursak:

https://www.slideshare.net/sttts/extending-kubernetes-admission-webhooks
https://www.slideshare.net/sttts/extending-kubernetes-admission-webhooks

Şimdi biz init ve sidecar container’ları Pod spec’ine ekleyebilmemiz için tek yapmamız gereken bunları JSONPatch şeklinde tanımlamamız çünkü Mutating Admission Webhooks mutation işlemlerini bu şekilde yapıyor ve bunu AdmissionResponse modeli altındaki Patch fieldına byte olarak vermemiz gerekmekte, kend kod örneğimizden bir kesit paylaşmak gerekirse.

https://gist.github.com/developer-guy/faf0700e3b259af4feae302167652c7c

Daha sonra bu patch operasyonlarını kullanarak yaptığımız mutation işlemlerinden basitçe Pod specine nasıl container eklediğimize bakalım.

https://gist.github.com/developer-guy/6e934cb11a315b63969299964389075d

Şimdi bu noktada aklınıza şu sorular gelmiş olabilir, tüm clusterdaki Pod isteklerini yakaladığımıza göre hangilerine bu injection işlemini uygulayacağımıza nasıl karar vereceğiz bir de Kubernetes için kritik olan namespaceler olabilir tüm bu işlemi bu namespaceler nasıl sınırlandırabiliriz.

Eğer bizim için kritik olduğunu düşündüğümüz bu namespacelerde Mutating Admission Webhook’u disable edebilmek için MutatingWebhookConfiguration’ın namespaceSelecctor field’ından faydalanıyoruz , paylaştığım örneğimizde var.

namespaceSelector: 
matchExpressions:
— key: sidecar/webhook
operator: NotIn
values:
— ignore
# Bu örneğimizde diyoruz ki eğer namespacemiz "sidecar/webhook" label'ına sahip ve bunun da değeri eğer ignore değilse dahil et.Dolayısıyla eğer kube-system namespace'imizi bu label ile işaretlersek "sidecar/webhook: ignore" şeklinde webhook tarafından requestleri intercept edilmez.

Diğer sorunun cevabı da yine benzer fakat bunun için kendi custom çözümüzü yapmamız gerekiyor.Genellikle bu konuda labellar veya annotationlar sıklıkla kullanılıyor , biz annotation tercih ettik , hem bu inject ettiğimiz containerlarımızın davranışlarını belirleyebilmek hem de injection ‘a karar verilebilmesi adına bazı standardlar getirdik örneğin :

"config.trendyol.com/inject" --> true/false değer kabul ediyoruz bu sayede inject işlemimin yapılmasının istenip/istenmediğine karar veriyoruz."config.trendyol.com/inject-file-dir" --> çıktı olarak vereceğimiz dosya path'ini belirliyoruz"config.trendyol.com/inject-file-config-<filename>" --> Consul üzerinden doldurulmasın istendiği <filename> ile belirtilen dosyayı oluşturuyoruz, bu çok olarak da tanımlanabilir yani business ve feature toggle configleri ayrı dosyaya yazılabilir."config.trendyol.com/inject-file-secret-<filename>" -->  Vault üzerinden doldurulmasın istendiği <filename> ile belirtilen dosyayı oluşturuyoruz, bu çok olarak da tanımlanabilir yani business ve feature toggle configleri ayrı dosyaya yazılabilir."config.trendyol.com/secret-root-path" --> Consul K/V Storedaki uygulama için belirlenen root routerın path bilgisini alıp bu path altındaki tüm configuration değelerini okuyup dosyayı otomatik dolduruyoruz."config.trendyol.com/config-root-path" --> Vault K/V Storedaki uygulama için belirlenen root routerın path bilgisini alıp bu path altındaki tüm secret değelerini okuyup dosyayı otomatik dolduruyoruz.apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
labels:
app: busybox
pod: busybox
annotations:
config.trendyol.com/inject: "true"
config.trendyol.com/inject-file-dir: "/var/busybox"
config.trendyol.com/config-output-filename: "config.txt"
config.trendyol.com/secret-root-path: "secret/busybox"
config.trendyol.com/config-root-path: "busybox/config"
Pod spec'lerimizin bu şekilde güncellenmesi gerekmektedir, ekstra olarak bu süreci manuellikten kurtarmak için injectorctl adında basit bir cli uygulaması yardımıyla bu süreci otomatikleştirmeyi hedefledik yine opensource olarak ulaşabilirsiniz fakat bunu içeride daha da kendi ihtiyaçlarımıza göre revize ettik annotationlar fark edeceksiniz ki eski kaldı.

siz de bu vesileyle kendi standartlarınızı koyabilirsiniz.

Best Practices

Idempotence

Admission Controllers arasında sıralama yapmak bazen zor olabilir , idompotent bir Mutating Admission Webhook kabul ettiği potansiyel olarak değiştirilmiş bir nesneyi başarıyla işleyebilmeli ve sonucu ilk uygulamanın ötesinde değiştirmeden birden çok kez uygulanabilmeli.Yani şöyle bir senaryo düşünün 2 adet Mutating Admission Webhook olduğunu varsayalım , ilki Pod’un containerlarının imagelarının imagePullPolicy’sini Always’e çekiyor , bir diğeri de bir sidecar container inject ediyor , eğer sidecar container inject edildikten sonra imagePullPolicy eklenmesi süreci işletilirse işlem başarılı gerçekleştirilmiş olur fakat diğer senaryoda eğer önce imagePullPolicy eklenip ardından sidecar inject edilirse inject edilen sidecarın imagePullPolicy değiştirilmemiş olur bu da idempotentliği bozan bir durum.İşte bu noktada MutatingWebhookConfiguration resource’sunun reinvocationPolicy fieldi yardıma koşuyor.

Intercepting all versions of an object

Bu kısımda da önerilen Admission Webhooks her zaman objectin/resource’un tüm versiyonlarına uygulanabilir olmalı bunu da .webhooks[].matchPolicy to Equivalent olarak ayarlayarak yapabiliriz ve bu objecti register ederken her zaman stable versiyonu seçtiğimizden de emin olmamız beklenir.Matching requests: matchPolicy

Avoiding operating on the kube-system namespace

Dediğimiz gibi cluster içerisindeki kritik bulduğumuz ve admission webhooksların bu namespace içerisindeki isteklerde araya girmesini istemediğimiz durumlar olabilir . Bunun için de namespaceSelector fieldından faydalanabiliriz ki faydalanıyoruz yukarıdaki mutatingwebhook.yaml da bunu görebilirsiniz.

Availability

Daha öncelerden de söylediğimiz gibi Admission Webhooks Kubernetes API Server’a gelen isteklerde araya girip işlem yapıyor dolayısıyla API Server’ın overall request latency’sine bir de webhook latency’leri ekliyoruz dolayısıyla bu işi daha efektif şekilde yönetebilmek için Timeouts lardan faydalanıyoruz.

Umarım bu makaleyi okuduktan sonra siz de kendi organizasyonunuza uygun şekilde bu Admission Controller konseptinden faydalanırsınız.

Bir sonraki yazımızda da bu sürecin e2e testini nasıl gerçekleştirdiğimizden ve Admission Webhook’umuzu nasıl debug edebileceğimizden bahsedeceğim, takipte kalın…

BONUS: Eğer bu kadar detayı implemente etmek ile uğraşmak istemezseniz de Openshift’in bu süreci kolaylaştırmak adına geliştirdiği şu projesine bir göz atmanızı öneririm.

Referanslar

# https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/
# https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/
# https://docs.giantswarm.io/guides/creating-your-own-admission-controller/
# https://www.youtube.com/watch?v=r_v07P8Go6w → Bunu mutlaka izleyin !
# https://banzaicloud.com/blog/k8s-admission-webhooks/ → Bu da gördüğüm en açıklayıcı yazılardan , mutlaka bunu okuyun.

--

--

developer-guy
Trendyol Tech

🇹🇷KCD Turkey Organizer🎖Best Sigstore Evangelist🐦SSCS Twitter Community Admin✍️@chainguard_dev Fan📦Container Addict📅Organizer at @cloudnativetr•@devopstr