# Kubernetes Nedir?

genel bir başlangıç yapalım

Kubernetes, hem beyan temelli (imperative) yapılandırmayı, hem de otomasyonu (yaml or json ) (declarative) yapılandırmayı kolaylaştıran, container iş yüklerini ve hizmetlerini yönetmek için oluşturulmuş taşınabilir ve genişletilebilir (scalability) açık kaynaklı (open-source) bir platformdur. \
Kısaca : "Container Orchestration Manager" diyebiliriz.&#x20;

* Container iş yüklerini yönetmek için tercih edilen en önemli container orchestration platformudur.&#x20;
* İsteyen herkes tarafından ücretsiz bir şekilde kurulur. Tamamen moduler yapıda tasarlanmıştır.&#x20;
* istenirse bu modüler yapı genişletilebilir. Her kubernetes komponenti ayrı bir major modül olarak ayrılmıştır.&#x20;
* Kubernetes'den istediklerimizi declarative olarak belirtmemiz yeterlidir. Şunu yap, sonra şunu yap olarak değil de, Şunu istiyorum gibi.&#x20;
* Kubernetes genel olarak ondan istediklerimizi belirtmemizi sağlıyor.
* Google tarafından [CNCF](https://www.cncf.io/) organizasyonu altında büyümüş ve geliştirlmiştir.&#x20;
* Resmi olarak hiç bir firma tarafından ücretli desteği yoktur. Sadece google araması ve comunity ile alanıabilir. ( Kendini gelişren ve bu konuda özel destek veren firmalar var mutlaka iyi seçim yapmalısınız bu konuda.

Bir örnekleme yapalım sizler için.


# Bileşenler

Kubernetes ortamını oluşturan bir çok servis mevcuttur.

<figure><img src="/files/wt99OP8iFFgYt4ZlcOcN" alt=""><figcaption></figcaption></figure>

Kube-ApiServer : Kubernetes control plane kısmının, en önemli bileşeni ve giriş noktasıdır. Tüm diğer komponent ve node bileşenlerin direkt iletişim kurduğu tek komponent.

* Kube-ApiServer : Kubernetes control plane kısmının, en önemli bileşeni ve giriş noktasıdır. Tüm diğer komponent ve node bileşenlerin direkt iletişim kurduğu tek komponent.
* Tüm iletişim api üzerinden gerçekleşir. Kullanıcılar kubectl komut satırı client 'ı veya rest api çağrıları arayıcılığıyla api server ile bağlantı kurabilirler.&#x20;
* Biz kubernetes ile bağlantı kurmak için, api server 'a erişir, authentication işlemlerini tamamlar ve istekleri iletiriz.&#x20;
* Tüm komponentler iletişimi api server arayıcılığıyla gerçekleştirir.

ETCd : Tüm cluster bilgisi, metadata bilgileri ve kubernetes de oluşturulan tüm objelerin bilgilerinin tutulduğu, key-value veri deposudur.

* Kubernetes çalışması için, gerekli tüm veriler burada tutulur.&#x20;
* API hariç, diğer komponentler etcd ile direkt haberleşemezler.&#x20;
* ETCd ile iletişim kurmaları gerektiğinde, kube-apiserver arayıcılığıyla yaparlar.

Scheduler : Yeni oluşturulan, veya bir node ataması yapılmamış pod'ları izler ve üzerinde çalışacakları bir node seçer. (Pod: Container çervesini saran ince bir zar olarak düşünebiliriz) Pod 'un kaynak isteğini, özel gereksinimi, çeşitli parametreleri göz önünde bulundurur. Ve kaynak seçme algoritması sayesinde, pod için en uygun node hangisi olduğuna karar verir. Yada hangi node üzerinde zorunlu çalışması gerekiyor ise ona göre atama yapar.

Controller Manager : Mantıksal olarak, her controller ayrı bir süreçtir. Ancak karmaşıklığı azaltmak için hepsi tek bir binary olarak derlenmiştir. Ve tek bir proccess olarak çalışır. Bu controller'den bazıları şunlardır,

* Node controller&#x20;
* Job controller&#x20;
* Service account-Token controller&#x20;
* Endpoints controller

Controller manager bileşeni, istenilen durum ile mevcut durumu gözlemlerler. API arayıcılığıyla Etcd 'de saklanan, cluster durumunu izler ve eğer mevcut durum ile istenilen durum arasında fark varsa, bu durumu oluşturan kaynakları gerektiği gibi oluşturur, günceller veya silerek bu durumu eşitler.

Control Plane dediğimiz bu kısmı master node dediğimiz sistemler üzerinde çalıştırırız. Tüm bu komponentler tek bir Linux yüklü sisteme kurulup, oradan erişilebildiği gibi, yüksek erişilebilirlik sağlaması adına birden fazla sisteme kurulabilir.

Worker Node : Container runtime, container'dan sorumlu olan kısımdır. Kubernetes bir kaç container runtime destekler, docker-containerd-CRI-O. Worker node cluster 'a dahildir. Varsayılan olarak container engine containerd 'dir.

Kubelet : Cluster 'da çalışan her node bir agent'dir. Pod içerisinde tanımlanan container'ların, çalıştırılmasını sağlar. Kubelet çeşitli mekanizmalar arayıclığıyla sağlanan bir dizi pod tanımı alır ve bu pod tanımda belirtilen container 'ların, çalışır durumda ve sağlıklı olmasını sağlar.

* Her worker node üzerinde bulunur.&#x20;
* API server arayıcılığıyla etcd 'yi kontrol eder ve scheduler tarafından bulunduğu node üzerinde çalışması gereken pod belirtildiyse, kubelet bu pod'u bulunduğu node üzerinde yaratır.&#x20;
* ContainerD 'ye haber gönderir ve belirtilen özelliklerde container'ın, bulunduğu node üzerinde çalıştırılmasını sağlar.

Kube-Proxy : Node'lar üzerinde ağ kurallarını yönetir. Ve bu ağ kuralları cluster 'ın içindeki veya dışındaki ağ oturumları tarafından pod'larımızla ağ iletişimine izin verir.


# Kubectl ve Versiyon

* Kubernetes semantik olarak versiyonlanır. x(major).y(minor).z(patch).&#x20;
* Kubernetes sene de 3 minör versiyon yayınlar. Her minör versiyon yayınladığı tarihten itibaren 12 ay boyunca desteklenir. Kısacası 12 ay boyunca, her ay patch versiyonları yayınlanarak, hatalar ve güvenlik açıkları giderilir.&#x20;
* Kubernetes yönetimi, 3 farklı şekilde yapılır. Rest API,GUI araçları,Kubectl.&#x20;
* Demo ve Test ortamlarınız için Minikube ve docker desktop 'a kubernetes cluster kurabiliriz.&#x20;
* kubeadm ve kubespray gibi araçlarla kendi sanal sunucularımıza kubernetes cluster kurabiliriz.
* Amazon AWS ,Google GCP, Microsoft Azure gibi cloud servis sağlayıcılarının hazır kubernetes araçları ile kubernetes cluster kurabiliriz.&#x20;

Kubectl

* Kubectl aracı, bağlanacağı kubernetes cluster bilgilerine config dosyası arayıcılığıyla erişir.&#x20;
* Config dosyası içerisinde, kubernetes cluster bağlantı bilgilerini ve oraya bağlanırken kullanmak istediğimiz kullanıcıları belirtiriz.&#x20;
* Daha sonra bu bağlantı bilgileri ve kullanıcıları ve ek olarak namespace bilgilerini de, oluşturularak context'ler yaratırız.&#x20;
* Kubectl varsayılan olarak $home/.kube/ altındaki config isimli dosyaya bakar. Ama bunu KUBECONFIG environment variable değerini değiştirerek güncelleyebiliriz.&#x20;
* Kubectl config get-context ile mevcut çalışılan cluster bilgisini öğrenebiliriz.&#x20;
* Kubectl config use-context {context name} ile, geçmek istediğimiz context 'i yazarak, farklı bir cluster 'a geçiş yapabiliriz.

{% hint style="info" %}
[Kubectl Komutları Referans Listesi](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands)
{% endhint %}

kubectl ile ilgili bir kaç örnek paylaşalım açıklamları ile,

```
kubectl cluster-info
# cluster hakkında bilgi verir.

kubectl --help
# komutlar ve kullanımları ile alakalı bilgi almak için kullanırız.

kubectl get pods -n kube-system
# komutun çalışmasını istediğimiz cluster namespace bilgisi belirtebiliriz.

kubectl get pods -A -o wide
# Komutların sonuna -0 wide yazarsak, daha detaylı bir çıktı alabiliriz.
# wide yerine, yaml ve json yazabiliriz.

kubectl explain pod
# Pod objesi hakkında bilgi alabiliriz. 
```

{% embed url="<https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands>" %}


# Pod

* Pod'lar kubernetes de oluşturabileceğimiz ve yönetebileceğimiz en küçük birimlerdir.&#x20;
* Pod'lar bir veya daha birden fazla container barındırabilir. Ama çoğu durumda pod tek container barındırır.&#x20;
* Her pod'un eşsiz bir ID'si bulunur.&#x20;
* Her pod eşsiz bir IP adresine sahiptir.&#x20;
* Aynı pod içerisindeki containerlar, aynı node üstünde çalıştırılır ve bu containerlar birbirleriyle localhost üzerinden haberleşebilirler.

<pre><code># Pod oluşturma
kubectl run firstpod --image nginx --restart never

# Pod hakkında bilgi almak için
kubectl describe pods firstpod

# Pod loglarına bakmak için, dilersek -f parametresi ile logları canlı izleyebiliriz.
kubectl logs firstpod 

# Pod içerisinde komut çalıştırmak
kubectl exec firstpod -- ls

# Pod içerisine bağlanmak
kubectl exec -it firstpod -- /bin/sh

<a data-footnote-ref href="#user-content-fn-1"># Pod silmek</a>
kubectl delete pods firstpod
</code></pre>

{% hint style="info" %}
Bir image registry belirtmezsek, kubernetes default olarak docker image registry kullanır. Örneğin; Local olarak nexus repo kullanılabilir.&#x20;
{% endhint %}

* Oluşturacağımız pod 'ları yml, yaml, json dosyalarında belirtip, pod 'dan istediğimiz özellikleri ve gereksinimlerimizi yaml dosyasında belirtip, o dosya aracılığı ile pod oluştururabiliriz.

Bir YML veya YAML içerisinde bulunması zorunlu olan parametreler, aşağıdaki bigidir.

* api version
* kind
* metadata

Kind : oluşturmak istediğimiz objenin ne objesi olduğunu tanımladığımız yerdir. \[pod,service,deployment, vb] Örneğin, pod objesinin API versiyonunu öğrenmek için, "kubectl explain pods" yazıp, versiyon bilgisine ulaşabiliriz.&#x20;

Metadata : obje ile ilgili uniq bilgileri tanımladığımız alan. Birden çok alt başlık olabilir, name,label vb.&#x20;

Spec : oluşturmak istediğimiz objenin özelliklerini belirtiriz. Örneğin, container,service objelerinin özellikleri vb. Her obje tipine göre burada tanımlayacağımız bilgiler değişir.

```
kubectl apply -f pods1.yml
# Komutu ile yml dosyasını kubernetes 'e bildirerek bu yml içerisinde belirttiklerimizi
oluşturmasını isteyebiliriz.

kubectl edit pods firstpod
# Komutu ile pod ayarlarını güncelleyebiliriz.
```

Pod' ların durum bilgisi ve açıklamaları;

* Pod yaratılırken pod'umuz <mark style="background-color:orange;">"pending"</mark> kısmında kalıyorsa, kube scheduler servisi pod'umuz için uygun bir node bulamamış olabilir.&#x20;
* Cluster 'da bulunan tüm node'larda "kubelet" adında bir servis çalışır. Görevi bulunduğu node 'a atanmış görevleri tespit edip, yeni yaratılmış görev,pod vb varsa, bunu tespit edip hemen işlemlere başlar.&#x20;
* İlk olarak pod tanımında girilmiş container'lara bakar, bu container'ların oluşturulacağı imajları sisteme indirmeye başlar. Eğer bir şekilde bu imajları indiremezse <mark style="background-color:orange;">"err image pull"</mark> ardından, <mark style="background-color:orange;">"image pull backoff"</mark> aşamasına geçer.&#x20;
* Eğer böyle bir hata görürseniz, pod 'un imajı repository'den çekemediği ve bunu tekrar tekrar denediği anlamına gelir.&#x20;
* Eğer imaj da sorun yoksa, kubelet servisi o node 'da bulunan container engine ile haberleşir ve ilgili container'ın oluşturulmasını sağlar. Container çalışmaya başladığında, pod <mark style="background-color:orange;">running</mark> duruma geçer. Bu nokta da pod oluşturulmuş olur.

Pod' ların Restart Policy bilgileri ve açıklamaları,

<mark style="background-color:orange;">Always :</mark> Hangi durum da container kapatılırsa kapatılsın. Kubelet bu container'ı yeniden başlatır.&#x20;

<mark style="background-color:orange;">Never :</mark> Hiç bir durum da yeniden başlatılmaz.&#x20;

<mark style="background-color:orange;">On-failure :</mark> Hata kaynaklı kapanırsa, yeniden başlatılır.

Pod 'un altında bulunan container'lar çalışmaya devam ettikçe pod running olarak devam eder. Pod işini bitirip kapandığında restart policy never durum da ise, pod yaşam döngüsü succeded olarak yaşam döngüsünü tamamlar.&#x20;

Eğer Restart policy never veya on-failure olarak ayarlıysa, container'lardan biri hata verip kapanırsa, bu sefer pod 'un status kısmı failed (error) olarak işaretlenir ve yaşam döngüsünü böyle tamamlar.&#x20;

Restart policy always olarak seçiliyse, container'lar hatadan dolayı da kapanırsa, normal bir şekilde de kapanırsa yeniden başlatılır. Dolayısıyla pod succeded veya failed durumuna geçmez. Bunun yerine pod 'un içerisinde bulunan container'lar tekrar başlatılır ve running state devam eder.

Kubernetes pod'un sürekli yeniden başlatma işini belirli bir sıklıkla yapıyorsa, bazı şeylerin ters gittiğine kanaat getirir ve pod'u "CrashLoopBackOFF" durumuna sokar.

<mark style="background-color:orange;">Crash Loop BackOff :</mark> Pod sürekli fail duruma düşüp veya farklı sebeplerden dolayı yeniden başlatılırsa, bu duruma geçer. Bu duruma geçen container aralıklarla kontrol edilir ve her geçen süre boyunca yeniden başlatma aralıkları uzar. Eğer container tekrar çökmezse running duruma geçer. Eğer halen çalışmayı bırakırsa 5 DK aralıklarla pod 'un içerisinde bulunan container yeniden başlatılmayı dener.

<mark style="color:red;">**SideCar Container (Çoklu container pod için bir örnek)**</mark>

```
apiVersion: v1
kind: Pod
metadata:
  name: mc1
spec:
  volumes:
  - name: html
    emptyDir: {}
  containers:
  - name: 1st
    image: nginx
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  - name: 2nd
    image: debian
    volumeMounts:
    - name: html
      mountPath: /html
    command: ["/bin/sh", "-c"]
    args:
      - while true; do
          date >> /html/index.html;
          sleep 1;
        done
```

Kubernetes ana uygulamaya (container) bağlı ve onunla network seviyesinde izolasyon olmadan, ve gerektiği durumda aynı depolama alt yapısını kullanabilecek uygulamaları (container)ları pod içerisinde 2.container olarak çalıştırma imkanı sağlıyor.

Birlikte scale edilmesi gereken, birbirleriyle network ve storage seviyesinde erişmesi gereken container'ları(uygulamalar) aynı pod içerisinde ayrı ayrı container'lar olarak çalıştırabiliyoruz.

Aynı pod içerisinde, tanımlanmış tüm container'lar aynı worker node üzerinde oluşturulur.

Container'lar, ayrı birer ünitedir. Fakat pod'un lifecyle içerisinde yönetilir. Yani pod oluşturulunca 2 container birden oluşturulur. Pod silinince, 2 container birden silinir.

Aynı pod içerisinde bulunan container'lar arasında network lifecyle bulunmaz. Yani aynı pod içerisinde çalışan A ve B container'ları birbirlerine localhost üzerinden ulaşabilir. Bunlar network bakımından sanki aynı sunucu üzerinde çalışan proccess'ler gibidir.

Tek bir volume yaratılarak, her 2 container'a mount edebiliriz. Böylelikle aynı dosyalar üzerinde çalışılabilir.

Sidecar container'lar aynı IP adresine sahip olurlar.

<mark style="color:red;">**Bir pod içine bağlanmak**</mark>&#x20;

```
# Multi Container pod kullanarak oluşturduğumuz container'lardan birinin shell'ine
bağlanmak
kubectl exec -it mc1 -c 2nd -- /bin/sh
Tek bir container olsaydı, -c ile container ismi vermemize gerek olmazdı.

# Pod içerisinde bulunan 1st container'ın loglarını inceliyoruz.
kubectl logs -f mc1 -c 1st

# Kubectl'i çalıştırdığımız sunucunun 8080'ine gelen istekleri, kubernetes
cluter içerisindeki mc1 isimli pod'un 80 portuna yönlendir diyoruz.
kubectl port-forward pod/mc1 8080:80 --address='0.0.0.0'

```

<mark style="color:red;">**Init Container (Multi Container)**</mark>

Aynı pod içerisinde birden fazla container tanımlamak için kullanılır. Asıl uygulama container başlatılmadan önce ilk olarak init container çalışır.

İnit container yapması gereken işleri tamamlar ve kapanır.

Uygulama container, init container kapatıldıktan sonra çalışmaya başlar. Inıt container işlemleri tamamlamadan uygulama container başlamaz. Ana uygulama başlamadan önce çalışıp, eksik paketler vb varsa init container'ı bu nedenle kullanabiliriz.

Uygulama çalışmadan önce, tamamlamamız gereken şeyler varsa, bunları tamamlamadan esas uygulamayı çalıştırmak mantıklı değilse kullanılır. Örneğin; uygulamamızın bağımlı olduğu, başka bir uygulama veya servis var. Eğer bu ayakta veya hazır değilken uygulamayı başlatırsak uygulamada sıkıntı çıkıyor. Bu durumda pod tanımına bir init container ekler ve bu init container'ın, bu servisi gözlemesi için ayar yaparız. Inıt container içerisinde, bir uygulama çalıştırılır ve bu servisten okey alana kadar bu init container çalışır. ve Okey aldıktan sonra da kapanacak şekilde ayar yapabiliriz. Bu sayede pod oluşturulduğu ilk zaman ilk olarak init container çalışır ve diğer servisi beklemeye başlar. Diğer servis hazır olduğu anda, uygulama kapanır ve init container da kapanır. init container kapatıldığı anda, esas uygulamamızın çalıştığı container ayağa kalkar. Böylece servis hazır olana kadar, uygulamamız beklemiş olur.

<figure><img src="/files/9lOTk7TZWoIIgaqdmrFK" alt=""><figcaption></figcaption></figure>

Misal, ana uygulamamızın ihtiyacı olan bazı config dosyalarının, son güncel halini ana uygulama başlamadan sisteme çekilmesi gerekiyor. Bu durumda, bu çekme işini de init container ile halleder ve esas uygulama başlamadan bunları sisteme indirebiliriz. vb. aşağıda örnek yml kod var

```
apiVersion: v1
kind: Pod
metadata:
  name: website
spec:
  initContainers:
    - name: clone-repo
      image: alpine/git
      command:
        - git
        - clone
        - --progress
        - https://github.com/peterj/simple-http-page.git
        - /usr/share/nginx/html
      volumeMounts:
        - name: web
          mountPath: '/usr/share/nginx/html'
  containers:
    - name: nginx
      image: nginx
      ports:
        - name: http
          containerPort: 80
      volumeMounts:
        - name: web
          mountPath: '/usr/share/nginx/html'
  volumes:
    - name: web
      emptyDir: {}

```

[^1]:


# Label ve Selector

Etiketler, yani tag'ler Kubernetes'de her türlü objeye atayabildiğimiz, anahtar-değer eşlenikleridir. Etiketler sayesinde, bizler oluşturduğumuz objelere atabildiğimiz ve bizlerin anlayacağı ve gruplama yaparken kullanabileceğimiz bilgiler eklemiş oluruz. Bu sayede kubernetes tarafından bizlere core özellik olarak sunulmayan objelere belirli bir aidiyet ataması gerçekleştirebiliriz. Obje oluştururken, atanabileceği gibi, obje oluşturulduktan sonra da etiketler atanabilir. Etiketler key-value şeklinde oluşturulur.

```
1w2.net/tier:frontend
prefix   Key   Value

Prefix kısmı opsiyoneldir.
```

Kubernetes 'de objeler arası bağlantı da etiketler sayesinde kurulur. Servisler ve deployment objeleri, hangi pod'ların kendilerine ait olduğunu ve hangi pod'lar ile ilişki kuracaklarını etiketler sayesinde belirler.

Örneğin, bir servis oluşturur ve bu servise şunu deriz; Git ve tier : frontend etiketli podları bul, ve trafiği bunlara yönlendir. vb.

Özetle, Label(etiketler) objeleri gruplama imkanı vermenin yanı sıra, Kubernetes 'de objeler arası bağ kurmak için kullandığı en temel mekanizmadır.

{% hint style="info" %}
Bir yml dosyasında, birden çok pod tanımı yapabiliriz. Aralarında (---) olmak şartıyla.
{% endhint %}

<mark style="color:red;background-color:red;">Metadata kısmında, etiket tanımı yaparız. ve istediğimiz kadar bu etiket sayısını arttırabiliriz.</mark>

```
kubectl get pods -L "app"
# app etiketine sahip podları getirir. --show-label parametresini eklersek, mevcut
etiketleride gösterir.

kubectl get pods -L "app:firstapp" --show-labels
# App etiketine sahip ve firstapp değerine sahip podları getirir.

# Daha fazla etikete göre aramak için tırnak arasına , işareti koyup, 2.istediğimiz
etiketide arayabiliriz.
"app:firstapp,tier:frontend" 

"app:firstapp,tier!=frontend"
# Tier etiketi frontend değeri olmayan,değeri bul ve o podları getir.

kubectl get pods -L "app in (firstapp)" --show-label
# App anahtarına firstapp değeri olan podları getir.

"app in (firstapp,secondapp)"
# APP değeri firstapp veya secondapp olan podları listele.

"app notin (firstapp)"
# App anahtarının değeri firstapp olmayan podları listele.

"app in (firstapp),tier notin(frontend)"
# app anahtarının değeri firstapp olacak, tier etiketinin değeri frontend olmayan
podları getir.


kubectl label pods pod9 app=thirdapp
# Pod9 poduna etiket ekledik.
# Etiket güncellemelerini yml dosyasını güncelleyerek yapabiliriz.

kubectl label pods pod9 app -
# Label'i sildik.

kubectl label --overwrite pods pod9 team=teams3
# etkiketi güncelledik.

kubectl label pods -all foo:bar
# Tüm namespace altında bulunan podlara etiket eklemek için.
```

YML dosyasına gireceğimiz node selector parametresi ile bu pod'un çalışmasını istediğimiz, nodeları belirtebiliyoruz. Bunu da label arayıcılığıyla yapıyoruz.

Örneğin;&#x20;

```
nodeselector:
  hddtype:ssd
```

Kube scheduler 'e şunu diyoruz, bu podu oluştururken, hddtype:ssd değerine sahip olan bir node bul ve bu podu orada çalıştır. Bu pod objesi ile node objesini eşleştirdik. Labeller bize objeler arası ilişki kurmaya yarar dediğimiz kısım budur.

```
kubectl label nodes worker-2 hddtype:ssd
# node 'a etiket atadık

kubectl delete -f podlabel.yml
# podlabel.yml dosyasından oluşturduğumuz tüm podları siler.
```


# Annotation

* Objelere metadata eklemek için kullanabildiğimiz opsiyon olan label'lerin yanı sıra kullanabileceğimiz bir parametredir.&#x20;
* Label 'da olduğu gibi key-value eşlenikleri şeklinde metadata ekleyebildiğimiz, bir opsiyondur.&#x20;
* Label'ler gruplama yaparken, ve objeler arası bağ kurmak istediğimiz durumlarda, kullanırız. Bu ilişki kurma durumundan kaynaklı label'ler hassas bilgi sınıfına girer. Yani label eklememiz, veya çıkarmamız kubernetes içerisinde bir şeyleri tetikleyebilir ve etkileyebilir. Dolayısıyla, her türlü bilgiyi label olarak metadata 'ya eklemek doğru değildir. Fakat bazı ek bilgileri, objelere eklememiz gerekebilir.

Misal, oluşturduğumuz objelere, o objenin oluşturma tarihini, kimin oluşturduğu bilgisini eklemek istersek, Bunu label ile yapmak doğru olmayacaktır. Çünkü bu bilgiyi, selector kullanarak seçme isteğimiz yok. Ayrıca bu bilgiyi herhangi bir ilişki kurmak içinde kullanmayacağız. Dolayısıyla, bunu label 'a eklemek yerine, annotation 'a eklemek daha doğru olacaktır.

Bunun dışında, annotation'lar kubernetes 'in ana komponenti olmayan fakat kubernetes ile bağlantısı olan yazılımlar tarafından, ihtiyaç duyulacak bilgilerin, yazıldığı yerdir.

Misal, firmamızın destek ekibinin çağrı kabul yazılımı mevcut, bu çağrı kabul yazılımının kubernetes'den bilgi çekecek hale getirdik ve şöyle bir sistem kurmak istiyoruz. Bir obje de sorun oluştuğunda, bu çağrı yazılımı, bunu tespit etsin ve objenin destek sorumlusuna mail göndersin. Bu durumda o mail adresi bilgisini objenin metadata kısmına annotation olarak ekleyebiliriz ve destek yazılımı bu bilgiyi çekebilir. Bu sayede, bu insanlara mail gönderebilir.

Annotation bu ve benzeri durumlar için kullanılır. Oluşturma kuralları label'lar ile hemen hemen aynıdır.

```
12w2.net/notification-email:admin@example.com
 prefix        Key                 Value
  
```

<mark style="color:red;">Objeye onu tanımlayan, fakat label olarak eklememizin sakıncalı olacağı bilgileri Annotation olarak ekleyebiliriz.</mark>

* Prefix zorunlu değildir.&#x20;
* Key alanı 63 karakter ve daha altı olmalıdır.
* Alfanumaratik bir karakter ile başlamalı veya bitmelidir.&#x20;
* İçerisinde ( . - \_ ) gibi karakterler içerebilir.&#x20;
* Value alanında, bu kurallar geçerli değildir. Alfanumaratik olmayan karakterlerde bulunabilir.

```
# kubectl kullanarak annotation oluşturmak ve silmek
kubectl annotate pods pod1 foo:bar

# silmek
kubectl annotate pods pod1 foo-
```

Annotation YML örnek;

```
apiVersion: v1
kind: Pod
metadata:
  name: annotationpod
  annotations:
    owner: "mycat"
    notification-email: "admin@example.com"
    releasedate: "01.01.2021"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  containers:
  - name: annotationcontainer
    image: nginx
    ports:
    - containerPort: 80
```


# Namespaces

Şöyle bir senaryo düşünelim, Misal, bi firmanın bilgi işlem sorumlusuyuz, ve firmada çalışan tüm ekiplerin ortak çalıştıkları, dosyalar barındırabilecekleri bir yapı tasarlıyoruz. 10 ayrı ekip var ve bu 10 ayrı ekibinde erişip, dosya barındırabileceği bir alt yapı kuracağız. Bu sistemi bir file server yapılandırarak kurabiliriz. Bir file server kurup, bunun altında tüm ekiplerin erişebileceği bir paylaşım alanı yaratıp, sonra tek tek kullanıcı bilgisayarlarının buraya erişmesini sağlarız. Böylece herkes dosyaları buraya koyup, buradan erişebilir. Sistemi böyle kurduğumuzda ilk etapta her şey düzgün çalışır. Hatta file server problem çıkarmadan, bu çözüm sorun çıkarmadan çalışır. Ama birden fazla ekibin kullanacağı yapıyı, bu yaklaşım ile kurarsak, zaman içerisinde şu tarz problemler meydana gelir;

Öncelikle tüm kullanıcılar dosyalarını buraya yazdıkları için, tek bir paylaşım altında yüzlerce dosya oluşur. Ve bir zaman sonra yönetilemez hal alabilir. Bir diğer sıkıntı da, şu olur diyelim ki ben a.txt adında bir dosya oluşturdum. Başka biri de a.txt adında dosya oluşturup aynı yere atamaz. Dosyaların insanlara görünürlüğünü değiştirmek için, tek tek dosya bazından izinler yazılması gerekir.

Bu tür problemleri gidermek için, bu paylaşım altında ekip ve projeler için ayrı birer klasörler yaratırsak, tüm bu problemleri ortadan kaldırmış oluruz. Örneğin insan kaynakları için ik klasörü yaratırız. Böylece her ekibe özel bir dizin bulunur. Her ekip, kendi dizinlerinde dosyalarını barındırabilir. Bunu daha da genişletip, proje bazlı da klasörler yaratabiliriz.

Örneğin, yeni bir ürün geliştirmesi yapıyoruz. Bu ürün ile ilgili, tüm dosyaların tutulduğu ürün adında bir dizin oluşturup, ve projeye özel dosyaları burada saklayabiliriz. Bu sayede güvenlik\&erişim gibi ayarları klasör bazında yapabiliriz. Misal, şu X klasörüne Y kullanıcısı erişebilsin ancak değiştiremesin vb. Görüldüğü gibi klasörler kullanılarak binlerce dosya içerisinde kaybolmuyoruz. Klasörler bize gruplama imkanı tanır. Her kullanıcı grubu kendi dizinlerinde dosyalarını tutabilir. Dosya bazında tek tek, erişim izni, güvenlik ayarı yapmamıza gerek kalmaz. Çünkü bu ayarları klasör bazında yaparız. Ve yapılan bu ayarlamalar tüm klasör altında ki tüm dosyalar için geçerli olacaktır. Klasör bazında kotalar uygulayabiliriz.

Kubernetes 'de namespace 'de tam olarak bu işi yapar.

* Namespace 'ler adlar için bir kapsam sağlar.&#x20;
* Kaynak adlarının bir namespace içerisinde benzersiz olması gerekir.&#x20;
* Namespace 'ler birbirlerinin içerisine yerleştirilemez ve her kubernetes kaynağı yalnızca bir namespace içerisinde olabilir.&#x20;
* Namespace 'ler cluster kaynaklarını, birden çok kullanıcı arasında bölmenin bir yoludur.&#x20;
* Kubernetes cluster'ı, örnekteki file server olarak düşünürsek, Namespace ise burada tanımladığımız klasörlerdir.&#x20;
* Kubernetes altında objeleri, sanal klasörler altına oluşturabilir. Sonrasında bu sanal klasörler bazdında erişim ve kota ayarlamalarını yapabiliriz.&#x20;
* Her kubernetes kurulumda varsayılan olarak 4 namespace oluşturulur.

<mark style="background-color:orange;">kube-system :</mark> Kubernetes tarafından oluşturulan objelerin tutulduğu namespace 'dir.&#x20;

<mark style="background-color:orange;">kube-public :</mark> Kimliği doğrulanmamış olanlarda dahil, tüm kullanıcılar tarafından erişilmesine, ihtiyaç duyulan objelerin oluşturulabileceği yerdir.&#x20;

<mark style="background-color:orange;">kube-nodel-lease :</mark> Node hard link işlemleri için gerekli özel bir namespace'dir.

Kısacası kube ile başlayan namespace'ler, kubernetes tarafından oluşturulur. Cluster'ın işleyişi ile alakalı objelerin tutulduğu yerdir. Bunun yanı sıra, default adında namespace daha oluşturulur. Bizler aksini belirtmediğimiz sürece, tüm objeler default olarak burada oluşur.

Birden fazla ekip tarafından yönetilen, birden fazla uygulamanın deploy edildiği kubernetes cluster 'ında, namespace bazında objeleri ayırmak sağlıklı olacaktır.

Namespace oluşturulurken, kubectl ve yaml dosyalarıdan yararlanabiliriz.

```
# Mevcut namespace'de çalışır.
kubectl get pods

# namespacecustom namespace 'i içerisinde bulunan podları getirir.
# -n argümanıda kullanılır.
# kubectl get pods --namespace namespacecustom

# app adında namespace oluşturmak.
kubectl create namespaces app

Namespace içerisinde bulunan objeler ile iletişim kurarken objenin namespace'ni 
belirtmemiz gerekir.

# Farklı bir namespace'de bulunan containera bağlandık.
# kubectl exec --it namepod -n development -- /bin/bash

# Mevcut komut satırı namespace değiştirme. Bu komutdan sorna tüm komutlarımız
# development namespace içerisinde çalışacak.
kubectl config set-context --current --namespace:development

# namespace silmek
kubectl delete namespace development
```

Namespace YML için örnek;

```
apiVersion: v1
kind: Namespace
metadata:
  name: hr
```

```
kubectl apply -f create-namespace.yml
```


# Deployments

Kubernetes 'de container imajına çevirdiğimiz objeleri pod olarak deploy ederiz. En temel obje pod 'dur. Genelde Kubernetes üzerinde singleton (tekil) yönetilmeyen pod 'lar yaratmayız. Pod 'ları yöneten üst seviye objeler yaratırız, pod 'lar bu objeler tarafından, yaratılıp yönetilir.

Pod 'ları tek bir worker node üzerinde çalıştırmak, uygulama sağlığı açısından istenmeyen bir durumdur. Worker node 'a fiziksel olarak bir problem meydana geldiğinde, üzerinde bulunan pod 'lara erişim kaybolur. Ve uygulamamıza erişemeyiz.

Ayrı ayrı, yml dosyalarını kullanarak, farklı worker node üzerinde aynı imaj kullanarak pod oluşturduğumuz zaman ise, hem takip edilmesi güç, hem de manuel işlem yapmamız gerekecektir. Her worker node için ayrı bir yml dosyası oluşturup, hepsiyle ayrı ayrı ilgilenmemiz gerekecektir.

Deployment 'da istenilen durumu tanımlarız, ve deployment controller, mevcut durumu, istenilen durum ile karşılaştırıp, gerekli aksiyonları alır.

Deployment bir veya birden fazla pod 'u bizim belirlediğimiz desired(istenilen) state göre oluşturan, sonrasında bu desired state göre mevcut durum ile sürekli karşılaştırıp, gerektiği düzeltmeleri yapan obje tipidir.

Deployment obje oluşturmak için tanımı yapar, bu tanımın içinde oluşturmak istediğimiz, pod 'un hangi özelliklere sahip olacağını ve kaç adet olacağını belirtiriz. Deployment objesi oluştuğu zaman tanımladığımız özelliklerde ve adette pod oluşturulur.

Örneğin, Nginx imajından 3 tane pod oluşturan deployment yaratırız. Deployment bunu desired state olarak alır ve 3 pod oluşturur. Ardından deployment controller devreye girer ve sürekli olarak bu pod 'ları gözler. Bir pod 'da sorun olduğu zaman deployment controller duruma müdahale eder, sorunlu olan pod yerine, yeni bir pod oluşturur. Böylelikle desired state ve current state eşitlenir.

Deployment objeleri kurallar dahilinde pod güncellemeleri yapmamıza da imkan sağlar. Örneğin Desired state 'e eski imajımız yerine güncel imajımızı yazarsak, Deployment controller istediğimiz imaja göre pod 'ları oluşturacaktır. Bu işlemi kontrollü bir şekilde yapması için, ek parametreler belirtebiliriz. Örneğin, yeni imaj 'dan bir pod yarat, eski imaja sahip olan pod 'lardan birini sil ve sorun yoksa, kalan diğerlerini bu sırayla yap.

Böylelikle uygulamamızın yeni versiyonunu kesintisiz geçiş imkanı sağlıyoruz. Sorun olması halinde tek bir komut ile deployment ortamımızı bir komut ile bir önceki duruma alabiliriz.

```
#Imperative yöntemle Deployment oluşturma.
kubectl create deployment firstdeployment --image=nginx:latest --replicas=2

#Deployment listeleme.
kubectl get deployment

#Deployment silme.
kubectl delete deployment firstdeployment

#Deployment içerisindeki imajı güncelleme.
kubectl set image deployment/firstdeployment nginx=httpd:alpine

#Deployment replika sayısını değiştirme "Scaling"
kubectl scale deployment firstdeployment --replicas=5

#Deployment yapılan son değişikliğin geri alınması.
kubectl rollout undo deployment firstdeployment

#Deployment yapılan değişikliklerin kaydının tutulması için "--record" opsiyonu kullanılır.
kubectl set image deployment rolldeployment nginx=httpd:alpine --record=true 

#Deployment yapılan değişikliklerin listelenmesi.
kubectl rollout history deployment rolldeployment

#Deployment yapılan değişikliklerin izlenmesi.
kubectl rollout status deployment rolldeployment 

#Deployment üstünde yapılan değişikliklerin durdurulması.
kubectl rollout pause deployment rolldeployment

#Durdurulan rollout'un devam ettirilmesi.
kubectl rollout resume deployment rolldeployment
```

<mark style="color:red;">**YAML ile deployment oluşturmak;**</mark>

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: firstdeployment2
  labels:
    team: development
spec:
  replicas: 3
  selector:
    matchLabels:
      app: front-end
  template:
    metadata:
        labels:
          app: front-end
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
```

```
kubectl apply -f deploymentcreate.yml
```

* Template argümanı altında oluşturmak istediğimiz pod özelliklerini gireriz.

* Selector argümanı girerek şunu söylemiş oluyoruz;

* Deployment, senin yönetebileceğin pod 'lar, app:front-end etiketine sahip olacak, Bu etiketi görürsen anla ki, bu pod 'lar sana ait.

* Selector ve template altında olan label tanımları mutlaka her deployment obje oluştururken bulunmalıdır. Birden fazla deployment bulunan ortamlarda farklı farklı label kullanılması karışıklık yaşanmaması için önemlidir.

* Label 'ların objeye özel uniq olması çok önemlidir. Farklı farklı obje 'lerde aynı label tanımının olması karışıklığa yol açacaktır.


# ReplicaSet

Bir ReplicaSet 'in amacı, herhangi bir zaman da, çalışan kararlı bir replica pod setini sürdürmektir. Bu nedenle, belirli sayıda özdeş pod 'un, kullanabilirliğini garanti etmek için kullanılır.&#x20;

<figure><img src="/files/Sc8Wnm3rwuwPR2JZNLDf" alt=""><figcaption></figcaption></figure>

Kubernetes 'de belirtilen, template 'deki bilgilere göre X sayıda pod oluşturan, sonrasında mevcut durum ile, istenilen durum arasında fark oluşursa bunu çözen, bu gibi işleri halleden obje Deployment değildir.

ReplicaSet adında bir obje daha mevcuttur. Bu işlerin tamamını ReplicaSet objesi yapar. Biz bir Deployment objesi oluşturduğumuz zaman, bu deployment template altında belirlediğimiz özelliklerde, ReplicaSet objesi oluşturulur. Pod 'lar bu ReplicaSet objesi tarafınan oluşturulur.

Kubernetes ilk çıktığı zaman, replica controller adında bir obje tipi vardı (halen var, aktif olarak yoğun kullanılmasa da) Replication controller birden fazla sayıda aynı tip 'de Pod oluşturmak için kullanılıyordu. Fakat deploy ettiği pod 'lar ile ilgili değişiklik yapmak istediğimiz zaman bazı sıkıntılar çıkarıyordu. Bu sıkıntıları çözmek için, şöyle bir yola gidildi, Replica Controller 'ın sağladığı özellikler Deployment ve ReplicaSet adında 2 objeye ayrıldı.

ReplicaSet objesinin temel görevi, belirlediğimiz özelliklere göre, belirlediğimiz sayıda pod oluşturmak ve bunu desired state 'de kalmasını sağlamak.

<figure><img src="/files/ZeppGAASouBSbHaSzpT1" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/eDSQ8umKxzPPef8geTv2" alt=""><figcaption></figcaption></figure>

Deployment objesi, ReplicaSet 'in bir üst seviye objesi olarak dizayn edilmiştir. Ve pod tanımında bir güncelleme yaparsak, Bu güncellemenin, belirlediğimiz kurallara bağlı kalarak ve sırayla uygulanmasını sağlamak gibi düzenli, kesintisiz geçiş yapma imkanı sağlar.

Özetle biz bir deployment objesi oluşturduğumuz zaman, Bu deployment objesi, kendi yönettiği bir ReplicaSet objesi oluşturur ve bu ReplicaSet objesi de pod 'ları yaratır ve yönetir.

Biz deployment objesi tanımında bir değişiklik yaparsak, misal imaj güncellemesi gibi.. Bu yeni tanımla yeni bir ReplicaSet objesi daha yaratılır. İlk yaratılan ReplicaSet objesi yavaş, yavaş kendi oluşturduğu pod 'ları silmeye başlar. Yeni ReplicaSet 'de, yeni pod 'ları oluşturmaya başlar.

Silme, oluşturma vb işlemlerin hangi sırayla olacağını ve ne şekilde olacağını biz belirleyebiliriz. Bu bize bir kesinti olmadan, uygulama güncelleme ve yeni versiyon geçiş yapma imkanı sağlar.

Deployment objesi içerisinde tanımladığımız pod 'ları, ReplicaSet yaratır ve yönetir.

* Deployment objesi, pod güncellemeri, imaj güncellemelerini kolayca yapılmasına imkan sağlar. Bu nedenle biz direkt ReplicaSet oluşturmayız, onun yerine daha çok özellik sağlayan Deployment objesinden faydalanırız.&#x20;
* ReplicaSet objesini manuel olarak kendimiz oluşturursak, bu tarz güncellemeri (imaj vb) manuel olarak yapmamız gerekir. Bu yüzden Deployment objesini oluştururuz, Deployment objesi ReplicaSet objesini oluşturur, ReplicaSet 'de pod 'ları yaratır.&#x20;
* Bir değişiklik yaptığımız zaman, deployment objesi tüm otomasyonu bizim adımıza sağlar.&#x20;
* Fakat dilersek biz ReplicaSet objesini kendimiz de oluşturabiliriz. ReplicaSet ve Deployment objesi oluştururken kullandığımız YAML dosyası içeriği bire bir aynıdır. kind kısmında sadece obje isimleri değişiktir.

Tekrarlamak gerekirse, ReplicaSet bize rollout, undo, vb özellikleri sunmadığı için, Deployment objesini kullanmak bir hayli mantıklı bir seçenektir. Deployment objesi oluşturduğumuz zaman zaten kendisi ReplicaSet 'i otomatik olarak oluşturacaktır.&#x20;

ReplicaSet objesi oluştururken kullandığımız dosyada misal bir imaj güncellemesi/değişikliği yaparsak ve tekrardan yaml dosyasını apply edersek, bir değişiklik olmayacaktır. ReplicaSet bu tarz değişiklikleri mevcut pod 'lara uygulamaz. Ancak yeni bir pod yaratılacaksa o zaman bu pod 'ları güncel imaj 'dan yaratır. Bu nedenle direkt ReplicaSet yaratmıyor Deployment objesi kullanıyoruz.

Örnek bir ReplicaSet oluşturmak için kullanılan YML içeriği;&#x20;

```
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: web
  labels:
	env: dev
	role: web
spec:
  replicas: 4
  selector:
	matchLabels:
  	  role: web
  template:
	metadata:
  	  labels:
    	    role: web
	spec:
  	  containers:
  	  - name: nginx
    	    image: nginx

```


# Rollout & Rollback

Deployment yaml dosyalarımızda spec altında, strategy argümanı ile bizler, bu Deployment 'ı güncellediğimiz zaman, rollout işlemlerinin nasıl yapılabileceğini belirleriz. Kullanabileceğimiz 2 tip rollout tipi mevcuttur.

<mark style="background-color:orange;">1 - Recreate :</mark> Deployment 'da bir değişiklik yaptığımızda, öncelikle tüm mevcut pod 'ları siler ve ardından yeni podları oluşturur. Genellikle, uygulamanın, yeni versiyonu ve eski versiyonunun kısa bir süre için bile olsa, bir arada çalışmaması için kullanılır.

<mark style="background-color:orange;">2 - RollingUpdate :</mark> Bu seçenek DEFAULT olarak gelir. Yaml dosyamızda herhangi bir değişiklik yaptığımız zaman, gidip tüm podları silip yenisi oluşturmak yerine, bu işi aşamalı olarak yapar. Bu aşamaların nasıl olacağını belirlediğimiz 2 opsiyonumuz mevcut.

<mark style="background-color:orange;">2a - MaxUnavailable :</mark> Deployment 'da değişiklik yapıldığında, en fazla burada belirttiğim miktarda pod silinir. Misal 10 pod'lu ortamlarlarda, bir güncelleme yaptığımızı varsayalım. Bir güncelleme yaptığımızda bu güncellemeye başladığı anda, en fazla 2 tanesini siler. sonra yeni podları oluşturur. Ardından 2 eski pod daha siler. Yeni 2 pod daha oluşturur. gibi. Döngü bu şekilde devam eder. Pod sayımız 8 'in altına düşmez.

<mark style="background-color:orange;">2b - MaxSurge :</mark> Güncelleme sırasında toplam pod sayısının en fazla kaç olacağını belirler. Misal bizim desired state 'imiz 10 pod ama, bu geçiş sırasında 12 pod 'a kadar çıkabilir. Deployment 'ı güncellediğimiz de, kubernetes yeni bir replicaset oluşturacak, yeni tanımda 2 pod ayağa kalkacak, dolayısıyla eski 10 pod + 2 yeni pod toplamda 12 pod olacak. Sonrasında eski pod'lardan 2 tane silinecek ve ardından yeni replicaset 2 pod daha yaratarak, döngü devam edecek. Toplam pod sayısı 12 'yi geçmeyecek ve 8 'in altına düşmeyecek. Bir değer girmezsek MaxSurge ve Maxunavailable %25 olarak çalışır.

<mark style="color:red;">**Deployment objesini düzenlerken**</mark>**&#x20;**<mark style="color:red;background-color:orange;">**"kubectl edit"**</mark>**&#x20;**<mark style="color:red;">**komutunu da kullanabiliriz.**</mark>

<mark style="background-color:orange;">--record :</mark> kullandığımız komutların ardına --record  parametresi eklersek, tüm yapılan işlemleri bir history 'de tutar.

```
#Komutunu kullanarak deployment 'da yaşanan tüm değişiklikleri görebiliriz.
kubectl rollout history deployment rolldeployment

#Liste de bulunan 2. değişikliğin detaylarını göster diyoruz.
kubectl rollout history deployment rolldeployment --revision=2

#Bir önceki değişikliğe dönmek için kullanırız.
kubectl rollout undo deployment rolldeployment

#Deployment 'da yapılan 1. değişikliğe döner. Bir öncekine dönmekle sınırlı değiliz.
kubectl rollout undo deployment rolldeployment --to-revision=1

#Bu sayede deployment üzerinde yaptığımız değişiklikler, kataloglama ve sonrasında geri dönme imkanına kavuşuruz. Bir deployment oluşturur oluşturmaz hangi aşamalardan geçtiğini görmek için,
kubectl rollout status rolldeployment -w

#Deployment ortamında bir güncelleme yapıp, bir sorun gözlemlediğimizde durdurmak istersek;
kubectl rollout pause deployment rolldeployment

#Eğer sorunu tespit edip, giderdikten sonra kaldığı yerden devam etmek istersek,
kubectl rollout resume deployment rolldeployment
```


# Networking

Kubernetes kurulumunda pod'lara IP dağıtılması için, bir IP adres aralığı, yada Kubernetes terminolojisinde bilinen adıyla --pod-network-cidr belirlenir. Kubernetes de her pod, bu CIDR bloğundan atacanacak eşsiz IP adresine sahip olur. Aynı cluster içerisindeki tüm pod'lar, Varsayılan olarak, birbirleriyle herhangi bir kısıtlama olmadan ve NAT olmadan haberleşir.

CNI projesi Linux container 'larda, Ağ arabirimlerini yapılandırmak için, Eklentiler yazılabilmesini sağlayan kuralları belirler. CNI yalnızca container 'ların Ağ bağlantısıyla ve container 'lar silindiğinde ayrılan kaynakların kaldırılmasıyla ilgilenir. Bu odak nedeniyle CNI geniş bir desteğe sahiptir. CNI çözümlerine uygun, yapacağımız işe göre uygun bir plugin seçip, Kubernetes cluster 'ımızda kullanabiliriz. Bu, Worker node 'lar üzerinde bulunan pod 'ların birbirleriyle haberleşmesi ve Cluster network alt yapısının düzgün çalışabilmesi için zorunluluktur.

Bu driver (plugin)ler ortama uygun seçilmelidir. Özetle, Kubernetes ağ altyapımızın düzgün çalışabilmesi, pod 'ların ve diğer komponentlerin birbirleriyle sağlıklı haberleşebilmesi için CNI standartlarına uygun bir plugin seçmeliyiz. Kubernetes burada bize bir plugin sunmaz, sadece standartları belirler. CNI plugin 'i cluster 'da bulunan node'lara yükleyerek, Kubernetes 'in, bu network yönetimini yüklediğimiz plugin'e devretmesini sağlayabiliyoruz.

Örneğin Calico kullandığımızı düşünelim. Bu noktadan itibaren, Pod 'ların, IP adreslerinin atanmasını, IPtables kurallarının oluşturulması ve düzenlenmesi, Node 'lar arası POD cidr bloğunun nat 'siz çalışması için gereken, overlay tanımlarını yaparak, Kubernetes ağ alt yapısının düzgün çalışması için uygulanan tüm görevler calico driver tarafından halledilir.

Kubernetes 'de bir pod oluşturduğumuz zaman, scheduler bu pod'un çalışacağı node 'u seçer. Ve kubelet burada devreye girerek pod 'u oluşturur. Pod için atanacak IP adresinin belirlenmesi, bunun pod 'a atanması, ve alt yapı da ayarlanması gereken tüm işlerin halledilmesi işi ise, seçtiğimiz plugin olan calico tarafından sağlanır. Böylece pod 'lar farklı node üzerinde olsalar bile, birbirleriyle haberleşebilirler. Kubernetes de pod'ların birbirleriyle haberleşmeleri ve dış dünya ile haberleşmeleri , yani igress trafiği bu pluginler sayesinde gerçekleşir.


# Service

* Bir dizi pod üzerinde çalışan uygulamayı, ağ hizmeti olarak göstermenin soyut bir yoludur.&#x20;
* Kubernetes ile, uygulamanızı tanıdık olmayan, bir hizmet bulma mekanizması kullanılacak şekilde, değiştirmeniz gerekmez. Kubernetes pod 'lara, kendi IP adreslerini ve bir dizi pod için, tek bir DNS adı verir. Ve bunlar aralarında yük dengeleyebilir.

4 Tip Service Mevcuttur.

<mark style="color:red;">**ClusterIP**</mark>

<figure><img src="/files/KXAa6hXLQFY9ZEulQ0oA" alt=""><figcaption><p>Cluster IP</p></figcaption></figure>

ClusterIP tipinde, service objesi yaratıp, bunu label selector arayıcılığıyla pod 'lar ile ilişkilendirebiliriz. Bu objeyi yarattığımız zaman, bu obje cluster içerisindeki tüm pod 'ların ve kaynakların çözebileceği uniq bir dns ismine sahip olur.

Bunun yanında, bizler her kubernetes kurulumunda --service-cluster-ip-range ile sanal IP bloğu belirleriz. Misal 10.100.0.0/16 Cluster IP service objesi, yarattığımız zaman, bu objeye, bu IP bloğundan bir sanal IP adresi, Cluster 'ın DNS mekanizmasın da kaydedilir.

Bu IP adresi, Virtual bir IP adresidir. herhangi bir interface 'e map edilmez. kube-proxy servisi tarafından tüm node 'lardaki IPTable 'lara eklenir ve buraya ulaşan trafik, servisin ilişkili olduğu pod 'lara round-robin algoritmasıyla yönlendirilir. Yani Cluster içerisinden, Bu IP adresine gönderilen paketler, bu service ile ilişkilendirilmiş pod 'lardan bir tanesine yönlendirilir.

Şöyle bir senaryo düşünelim, Frontend ve Backend deployment adlarında 2 farklı deployment'ımız var. Ancak frontend altındaki pod 'lar, Backend altındaki podları tanımıyor ve bilmiyor bu durumda;

Backend pod 'lar ile ilişki kuracak, backend isimli bir servis yaratıp, Frontend'lere de backend pod 'lara ulaşmak istediğinde, gitmek gereken adres, backend isimli servis(adres) dersem, şu olur. Cluster içerisindeki herhangi bir pod, backend ismini çözmek istediğinde, ona cevap olarak, bu backend servisinin IP adresi döner. Pod o IP adresine ulaşmak istediğinde de, trafik backend pod'lardan birine yönlendirilir.

{% hint style="info" %}
Farklı namespace 'lerden bu service erişmek istenirse, tam isminin yazılması gerekmektedir. Aynı namespace içerisinde bu gerekli değildir.
{% endhint %}

Dolayısıyla tek tek frontend ' pod 'lara bağlanıp, her seferinde yeni eklenen pod 'ların IP adreslerini belirtmemiz gerekmez. Sadece gitmek istediğim yer backend deriz, O trafiği arkada bulunan backend servisine gönderir. Gerisini cluster halleder. Yeni eklenen pod'lar otomatik olarak bu service altına eklenir. Çıkarılan/Silinen pod'lar otomatik olarak bu servisten çıkarılır.

ClusterIP kubernetes altında, basit de olsa service discovery ve Load Balancing hizmetleri sunan obje tipidir. Böylece cluster içerisinde Frontend-Backend bağlantı sorunlarını çözdük.

```
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 5000
      targetPort: 5000
```

<mark style="color:red;">**NodePort**</mark>

2.sorun, frontend pod'lara dışarıdan erişilmesi gerekiyor.

Bu sorunu, nodeport service objesi ile çözeriz. Aynı cluster IP service objesi gibi, nodeport 'da service obje tipidir.

<figure><img src="/files/MCIaRNB3pK6CkMYYrYVf" alt=""><figcaption><p>Node Port</p></figcaption></figure>

Biz bir nodeport service objesi yaratır. Bunu label selector sayesinde, ihtiyacımız olan pod 'lar ile ilişkilendirebiliriz.

Bu obje yaratıldığı zaman şu olur, 30000-32767 arasında bir port seçilir ve tüm worker node 'larda bu port bizim frontend deployment 'mıza proxy edilir. Ve dışarıdan bu servisi oluşturduğumuz anda bizim uygulamamız için tanımladığı port üzerinden erişebiliriz. Kısacası, worker node 'un, IP adresinin, bu portuna gelen paketler, ilişkilendirdiğimiz pod 'ların publish ettiğimiz portlarına yönlendirilir. Bizlerde worker node 'ların, önüne bir Reverse Proxy veya Load Balancer koyarak, dış dünyadan erişilmesini sağlayabiliriz. Buraya ulaşacak paketler, worker node'lara, worker node'lara ulaşan paketlerde pod 'lardan birine ulaşarak kullanıcıların uygulamamıza gelmesini sağlayabiliriz.

```
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
```

<mark style="color:red;">**Load Balancer**</mark>

Load Balancer tipi service objelerini, sadece cloud service sağlayıcılar kullandığımızda kullanabiliriz. Load balancer tipi bir service oluşturduğumuzda, cloud service sağlayıcı, dış dünyadan erişilebilecek bir public IP adresine sahip, bir LB oluşturur, sonrasında bu LB bizim service objesine ilişkilendirilir. Dolayısıyla bu public adrese ulaşan paketler içeriden pod 'lara yönlendirilir.

<figure><img src="/files/55NZ2GrnWg5mAtDCKV5h" alt=""><figcaption><p>Load Balancer</p></figcaption></figure>

```
apiVersion: v1
kind: Service
metadata:
  name: frontendlb
spec:
  type: LoadBalancer
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
```

NodePort ile uygulamızın önüne bir LB ve Proxy koymaktan bahsetmiştik. Cloud service sağlayıcıları bunu otomatik sağlıyor. Buna da Load Balancer Service obje diyoruz.

Biz bir servis oluşturduğumuz zaman, bu servis endpoint adında bir obje oluşturur. Ve bu endpoint objesi, bizim belirlediğimiz selector tarafından seçilen pod 'ların, IP adreslerini üstünde barındırır. Service trafiği nereye yönlendireceğini bu listeye göre düzenler Bu liste dinamiktir, pod sildiğimizde o pod bu listeden IP adresi silinir. Yeni pod oluşturduğumuz da o pod 'un IP adresi bu listeye eklenir.

[<mark style="color:blue;">Eğer BeraMetal bir otama kurulum yapıyorsanız aşağdaıki kurulum linkini kullanabilirsiniz.</mark>](https://mvallim.github.io/kubernetes-under-the-hood/documentation/kube-metallb.html)

<mark style="color:red;">**External Name**</mark>

ExternalName türü ile tanımlanan bir Kubernetes Service’i önceki türlerde olduğu gibi selector kullanmak yerine DNS adını kullanacaktır. Bu türde, önceki türlerde olan proxy ya da forward işlemleri kullanılmamaktadır. Yönlendirme işlemi DNS seviyesinde gerçekleşmektedir.

<figure><img src="/files/u2Z9rFwIQAPGnjYqaTXO" alt=""><figcaption><p>External Name</p></figcaption></figure>

```
#Service objelerinin listelenmesi
kubectl get service

#Bir deployment'in expose edilmesi "imperative olarak service objesinin oluşturulması"
kubectl expose deployment "deployment_ismi" --type="service_tipi" --name="servis_ismi"

#Ör: kubectl expose deployment backend --type=ClusterIP --name=backend


#Service objelerinin silinmesi
kubectl delete service "servis_ismi"

#Ör: kubectl delete service backend
```


# Liveness Probe & Readiness Probe

### Liveness Probe

Liveness Probe Bazı durumlarda, container içerisinde başlatılan uygulama process çalışıyor olsa da, aslında yapması gereken işi yapmıyor olabilir.

Örneğin, bir web sayfası yayınlayan podumuz var. İçerisinde httpd tabanlı bir container çalışıyor. Bu pod başlayınca, container içinde ki, httpd deamon ayağa kalkıyor ve web sitemizi sunmaya başlıyor. Fakat bir süre sonra bu servis takılmaya başlıyor. Bir hatadan dolayı sayfaları sunmaya devam edemiyor. Ama httpd uygulaması, yani servis ayakta. Servis kapanmadı yada çökmedi. Ama esas yapması gereken iş olan, web sitemizi sunma işini yapmıyor.

Bu ve benzeri durumları yaşadığımız zaman ortamda şöyle bir sıkıntı oluşuyor. Eğer uygulama çalışmıyor olsa, yani uygulama kapansa, kubelet bunu tespit ederek, containerı yeniden başlatarak sorunu çözüyor.

Fakat uygulama ayakta olmasına rağmen, yapması gereken işi yapmıyorsa, kubelet bunu, tespit edemiyor. Dolayısıyla containerı yeniden başlatarak, sorunu çözme mekanizması çalışmıyor. Bu problemi Liveness proble ile çözebiliriz.

* Kubelet bir containerın, ne zaman yeniden başlatılacağını bilmek için, Liveness probe kullanır.

Örneğin; Liveness Probe bir uygulamanın çalıştığı ancak ilerleme sağlayamadığı, bir kitlenmeyi yakalayabilir. Böyle bir durumda bir containerı yeniden başlatmak, hatalara rağmen uygulamayı, daha kullanılabilir hale getirmeye yardımcı olabilir.

Liveness probe ile biz container içerisinde, bir komut çalıştırarak, ya da http endpoint' e istek göndererek ya da bir porta tcp connection sağlayarak uygulamanın doğru çalışıp, çalışmadığını kontrol etme imkanına sahip oluyoruz.

Bu sorgulamaların sonucunda, beklediğimiz (status ok) sonucu alırsak, containerın sağlıklı olduğunu, Eğer cevap beklediğimiz gibi değilse (status fail) containerın problemli olduğunu tespit edebiliyoruz. Kubelet bu sorgu sonucuna göre aksiyon alabiliyor. Liveness probe tanımlamaları yaml içerisinde&#x20;

* LivenessProbe: parametresi sekmesi altında tanımlanır.

<figure><img src="/files/LzdXKk5n8u6zZiT0nicL" alt=""><figcaption><p>Liveness Probe &#x26; Readiness Probe</p></figcaption></figure>

Kullanabileceğimiz 3 Tip Probe mevcut; (durumu kontrol etme aracı vardır.)

<mark style="color:red;">**1 - httpGet**</mark>&#x20;

```
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3
```

Bu probe seçtiğimizde, şunu diyoruz. Localhost 'a "path" parametresi altında belirlediğimiz path'e, "Port" Parametresinde belirlediğimiz porta httpGet isteği gönder. Cevap olarak, 200-400 arasında bir cevap kodu dönerse, yani cevap alabilirse işler yolundadır. Aksi bir cevap dönerse, hata olduğunu anlayıp, containerı restart et. İsteğimize custom bir http headerı ekleyebiliriz.

<mark style="background-color:orange;">InitialDelaySeconds</mark> ve <mark style="background-color:orange;">PeriodSeconds</mark> parametreleri, şu işe yarıyor; Bir pod oluşturduğumuzda, container çalışır fakat container içerisindeki, uygulama hemen ayağa kalkmayabilir. Ön hazırlıkları yapması gerekebilir. Bir yerden dosya çekmesi gerekebilir. kısacası uygulama container çalıştıktan sonra, hemen hizmet vermeyebilir. Eğer Liveness probe hemen başlatırsak, daha henüz uygulama hazır olmadığı için hata alacaktır. Ve hemen container restart edilecektir. Dolayısıyla daha tam anlamıyla hizmet sunmadan fail edecek ve sürekli restart olacaktır.

Bunu önlemek adına, initialDelaySeconds parametresini kullanıyoruz. Ve liveness probe 'a şunu diyoruz. Container başladıktan sonra, burada belirttiğimiz süre sonuna kadar bekle ardından sağlık kontrolüne başla.

PeriodSeconds ile, bu sağlık kontrolünün kaç saniye aralıklarla yapılacağını belirtiyoruz. Yani container başladı, initialdelaySeconds süresi boyunca bekledi, ardından httpget ile liveness check yapmaya başladı. 1. sorgusunu yaptı cevap olumlu, 2. sorguyu göndermeden Periodseconds da belirlediğimiz süre boyunca bekledi, sonrasında tekrar sorgu yaptı. Bu döngü bu şekilde devam edecek. Sorgular arası kaçar saniye beklemesini istiyorsak, periodSeconds parametresi ile belirtiyoruz.

<mark style="color:red;">**2 - Exec ile pod içine girerek (Komut yürütme)**</mark>

```
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
```

HTTP uygulamalarımızı httpget ile kontrol edebiliyoruz. Ancak uygulamalarımız http uygulaması değilse, bir konsol uygulaması vb. ise. Bunu httpget ile sorgulamamız mümkün değil.

Bunun yerine, bir script yazar, o script içerisinde uygulamamıza uygu sorgulama neyse, misal bir dosyanın olup, olmadığını kontrol edebiliriz. Ya da bir başka process 'in çalışıp, çalışmadığını kontrol edebiliriz. Uygulamamıza uygun sağlık kontrolünü seçeriz.

Liveness Probe altında httpGet dışında kullanabileceğimiz probe olan EXEC bize bu imkanı verir. Exec ile shell 'de, komut yada uygulama çalıştırırız. Bu uygulama(komut) bize 0 kodu dönerse, yani olumlu bir cevap verirse, sağlıklı olduğunu anlar. Bunun dışında bir cevap dönerse, problemli olarak işaretler ve container 'ı restart eder. Yukarıda ki örnekte exec probe ile bir komut çalıştırıyoruz. cat komutu ile /tmp/healty dosyasını kontrol ediyoruz. Eğer, tmp dizininde böyle bir dosya varsa, bize bu komutun sonucu 0 dönecektir ve olumlu olacaktır. Böyle bir dosya yoksa, exit code 1 olacak. Yani burada bir dosyanın olup, olmadığını kontrol ederek, uygulamanın sağlıklı çalışıp, çalışmadığını kontrol ediyoruz.

<mark style="color:red;">**3 - TCPSocket**</mark>

```
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20
```

Bazen httpget isteği ve komut çalıştırmak, bir şeyin sağlıklı olup, olmadığını anlamamıza yardımcı olmaz.

Misal bir mySQL veritabanı container çalıştırıyoruz. mysql process 3306 portundan erişilebilir. Biz de TCPSocket ile bu porta bir istek gönderip, eğer cevap alırsak uygulamanın düzgün çalıştığını varsayabiliriz. Olumsuz cevap alırsak, porta erişemezsek container restart edilecek.

### Readiness Probe

Senaryomuz; 3 frontend podumuzu oluşturacak bir deploymentımız, bu podlara dış dünyadan erişilebilmesini sağlayacak Load Balancer tipi bir service mevcut. Biz tüm bunları yaml dosyası arayıcılığıyla oluşturduk ve apply ettik. Ve ortamımız çalışmaya başladı. Load Balancer IP adresi üzerinden kullanıcılar bu frontend podlarımıza erişip, web sitemize gelebiliyorlar.

Şimdi gelin deployment objesinde güncelleme yapalım. Diyelim ki, web sitemizin bulunduğu imajı güncelledik ve deploymentımızı yeniden apply ettik. rolling-update stratejisi ile, podlar sırayla devreden çıkarlıp, yeni podlar devreye alındı.

Burada olanları yakından inceleyelim; Deployment da yeni bir imaj atadığımız zaman kubernetes hemen bu güncel imaj ile pod oluşturarak, bu yeni podları Load Balancer servisine dahil edecek.

Yani oluşturulup, başlatıldığı anda dış dünyadan bu podlara erişilebilinir olacak. Ya bizim uygulamamız ilk başladığı anda, bir yerlere bağlanıp, bazı dosyaları çekiyor ve sonrasında web sitemizi sunmaya başlıyorsa, yada başka bir nedenden dolayı container başlatılır, başlatılmaz bu uygulama hizmet veremiyorsa, aradan belirli bir zaman geçip, bazı işlemler yapması gerekiyorsa, eğer durum bu ise, bu podları oluşturduğumuz anda, bu podlar LoadBalancer servisinden trafik almaya başlayacağı için problem çıkacaktır.

Yani pod ayakta içindeki uygulama çalışıyor ancak, web sitemizi sunmaya hazır değil. Dolayısıyla bu pod, LoadBalancer servisine eklenmek için hazır değil. Peki bu pod 'un Load Balancer servisinden trafik almaya hazır olduğunu nasıl anlayacağız? Burada da, yardımımıza Readiness probe yetişiyor.

Kubelet, bir containerın trafiği kabul etmeye hazır olduğunu bilmek için, readiness probeları kullanır. Bir pod, tüm containerlar hazır olduğunda, hazır kabul edilir. Readiness probe sayesinde, bir pod hazır olana kadar, service arkasına eklenmez.

Readiness probelar, aynen liveness probelar gibi oluşturulur. httpGet, Exec, TCPSocket olarak kurgulanabilir.

Biz bir readiness check tanımlarız. Eğer bu readiness check olumlu sonuç dönüyorsa, Pod Service altına eklenir ve trafik alır. Eğer readiness check fail ediyorsa, bu pod service 'den çıkarılır.

Örneğimize dönersek, yeni imajı tanımladık ve yeni podlar oluşturuldu. Podlar çalışmaya başladı. Ama bu noktada Load Balancer service altına hemen eklenmedi. Podlar içerisinde readiness check mekanizması çalışmaya başladı initialdelayseconds süresi kadar bekledi, sonra tanımladığımız probe ile kontrolünü yaptı. Olumlu cevap aldığı anda, bu pod service arkasına eklenir ve trafik almaya başlar.

Readiness Check periodSeconds ile belirlenen süre kadar, sürede bir bu kontrolünü yapmaya devam eder. Fail edene kadar service arkasında trafik alır. Eğer fail ederse, pod service endpoint listesinden çıkarılır.

Eski podları direkt kapatmak yerine, kullanıcılara hata göstermemek için. Bir pod terminate edileceği zaman. Bu pod' a yeni trafik gelmemesi için pod service arkasından çıkarılır. Service ile bağlantısı kesilir. Böylece trafik almaz. Fakat kubernetes hemen bu podu kapatmaz. Çünkü yeni trafik gelmese bile, bu pod halen eski istekleri işlemeye devam ediyor olabilir. Bu nedenle, bu işlemleri beklemesi gerekebilir.

Pod tanımlarında, <mark style="background-color:orange;">terminationGracePeriodSeconds</mark> şeklinde bir parametre bulunur. Ve varsayılan olarak değeri 30 saniyedir. Aksini belirtmediğimiz sürece bu değer kullanılır. Biz veya kubelet podu terminate istediğimiz zaman pod' un içerisinde çalışan master process ine hemen sigkill sinyali gönderip hemen öldürmez. Bunun yerine sigturn sinyali gönderir yani, yaptığın bir işlem varsa düzgün bir şekilde tamamla, bitince de kendini kapat sinyalini gönderir. Eğer process bu sinyali aldığında eğer ortasında olduğu bir iş varsa, onu yapmayı bırakmaz. O işlemi tamamlar ve bittikten sonra tüm bağlantıları düzgün bir şekilde kapatıp sonrasında da process de kapatılır.

terminationGracePeriodSeconds süresi, bu container' a düzgün şekilde kendisini kapatabilmesi için atadığımız bir süredir. Eğer process bu süre içerisinde düzgün bir şekilde kapatılırsa sıkıntı olmayacaktır. Fakat kapanmazsa da, bu süre sonunda sigkill sinyali gönderilip, zorla kapatılır. Dolayısıyla uygulamamızın ne kadar süre içerisinde kapanabildiğini bilmemiz o na göre de terminationGracePeriodSeconds süresini düzenlememiz gerekebilir.

{% embed url="<https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>" %}

Readiness Aşamaları;

&#x20;1 - Deployment dosyası içerisinde imajımızı güncelledik.&#x20;

2 - Yeni imajda, yeni bir pod oluşturuldu.&#x20;

3 - Yeni pod çalışmaya başladı.&#x20;

4 - Readiness check mekanizması çalışmaya başladı. initialdelayseconds süresi kadar bekledi ve ardından ilk kontrolünü yaptı.&#x20;

5 - Kontrol sonucu olumlu olduğu anda, yeni pod service altına eklendi.&#x20;

6 - Eski pod henüz kapatılmadı, eğer işlemesi gereken istekler varsa, işlemeye devam ediyor, ve yeni istekler gelmemesi için service altından çıkarıldı.&#x20;

7 - Eski podun kendini düzgün kapatabilmesi için, sigterm sinyali gönderildi.&#x20;

8 - Eski pod üzerindeki işlemleri tamamladıktan sonra, kendini kapattı.

Örnek Readiness Probe Yaml içeriği;

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  labels:
    team: development
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: blue
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /healthcheck
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: 80
          initialDelaySeconds: 20
          periodSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
```

Unutmadan ekleyelim Readiness ve Liveness Probe 'lar aynı anda kullanılabilir.


# Resource Limits

Containerlar biz aksini belirtmediğimiz sürece varsayılan olarak, üzerinde çalıştıkları hostun CPU ve Memory kaynaklarına sınırsız şekilde erişirler. Yani biz 8 GB memory sahip bir worker sistem üzerinde container çalıştırdığımız zaman, bu container içerisindeki uygulama 8 GB memory 'nin tamamını kullanabilir. Keza aynı şekilde, tüm CPU çekirdeklerini de kullanabilir.

Bu durum özellikle kubernetes gibi yüzlerce container çalıştırdığımız dağıtık yapılarda, bizim için sorun oluşturur. Eğer bir pod koştuğu hostun üzerindeki tüm kaynakları tüketiyorsa, bu diğer podların işlerini düzgün yapmayacağı anlamına gelir. Bu nedenle, bu podların içerisinde çalışan, containerların ne kadar cpu ve memory kullanabileceklerini kısıtlamamız gerekmektedir. Pod tanımlarımızda, kullanabileceğimiz request ve limit opsiyonları bize bu imkanı verir.

Cpu kaynağı, CPU birimleri olarak ölçülür. Kubernetes de bir CPU şuna eşdeğerdir;

* AWS vCPU&#x20;
* GCP Core&#x20;
* Azure vCore&#x20;
* Hyperthread Bare-Metal

Kesirli değerlere izin verilir. 0.5 CPU talep eden, bir container 1 CPU talep eden, bir container 'ın, yarısı kadar CPU alır. Mili alamında M son ekini kullanabiliriz.

Örneğin, 100m CPU, 100Milicpu ve 0.1 CPU aynıdır. 1M'den daha fazla ince hassasiyete izin verilmez. CPU her zaman, mutlak bir miktar olarak talep edilir. Asla göreceli olarak talep edilmez.

<figure><img src="/files/GvTIWn8d94nwsOJ9DqWW" alt=""><figcaption><p>Resource Limits</p></figcaption></figure>

Misal; \
1 CPU core kullanmak için,\
cpu:"1" = cpu="1000" = cpu:"1000m" \
1 CPU 'nun %10nu kullanması için,\
cpu:"0.1" = cpu:"100" = cpu:"100m"

Kubernetes de CPU kısıtlamaları şu şekilde ayarlanır;

Her worker node'un belirli CPU kaynakları mevcuttur. Bu kaynaklar cloud üstünde yada sanal olarak çalşan sistemlerde vCPU olarak, bare-metal sunucularda hyper-thread olarak adlandırılır.

Biz bir pod tanımına cpu:1 tanımı eklersek, O pod 'un çalıştığı node üstünde core' lardan, sadece 1 tanesini kullanacağı anlamına gelir. Başka bir yazım şekli daha vardır. Her core 1000ms yani 1000mili cpu'luk güç demektir. Biz eğer cpu:100m yaparsak, pod çalıştığı host üstünde 1 core'un 10'da 1' gücüne erişebilir anlamına gelecektir. Yani, cpu:1 ile cpu:1000m aynı şeydir. Keza 0.1 cpu ile cpu:100m de aynıdır.

Memory tanımı daha kolaydır;

Cluster da mevcut durumda kullanılabilir bellek varsa, bir container belirlenen bellek isteğini aşabilir. Ancak bir container bellek sınırından fazlasını kullanmasına izin verilmez. Bir container sınırından daha fazla bellek ayırırsa, container sonlandırma için aday olur. Container limitinin ötesinde, bellek tüketmeye devam ederse, container sonlandırılır. Sonlandırılmış container yeniden başlatılabiliyorsa, diğer herhangi bir runtime hatası türünde, olduğu gibi kubelet onu yeniden başlatır.

Containerın kullanabileceği maksimum memory 'i, byte cinsinden tanımlayabiliriz. Örneğin, memory:64m dersek, bu containerın, en fazla 64megabyte memory kullanmasına izin verdiğimiz anlamına gelir. Buradaki m: megabyte g:gigabyte k:kilobyte kısaltmasıdır. Bunların yerine memory hesaplamasında kullanılan 2nin katlarını kullanan gösterim şekli olan, kibibyte,mebibyte,gibibyte tanımlarıda desteklenir. CPU ve Memory kısıtlama tanımlarını, iki ayrı bölümde tanımlayabiliyoruz. Bunlar, request ve limit parametreleri.

```
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: requestlimit
  name: requestlimit
spec:
  containers:
  - name: requestlimit
    image: obnet/stress
    resources:
      requests:
        memory: "64M"
        cpu: "250m"
      limits:
        memory: "256M"
        cpu: "0.5"
```

Request altında tanımladığımız değerler şu anlama geliyor, Kubernetes bu pod'u oluşturmaya başladığı zaman, scheduler bu podun oluşturacağı node seçecek. Bu seçme aşamasında kubernetese şunu diyoruz, Bu podu en az 64 megabyte memory, 250m milicpu yani çeyrek cpu core'un erişilebilir olduğu bir node üzerinde oluştur. Kısacası request kısmı bu podun oluşturulabilmesi için, minimum ne kadarlık boş kaynağın olması gerektiğini belirtiyor. Scheduler node seçiminde bu parametreleri de göz önüne alacak ve en az bu kadarlık kaynağın boş olduğu bir node seçecek. Eğer boşta 64mbyte memory ve 250m cpu sahip (çeyrek cpu), bir node bulamazsa, bu pod oluşturulmayacak.

Limit kısmı ise, tahmin edebileceğiniz üzere, bu containerın en fazla kullanabileceği sistem kaynağını belirtiyor. Yukarıdaki örnekte şunu diyoruz;

Bu pod içerisinde oluşturulan container, en falza 256megabyte memory kullanabilsin, en çok da yarım cpu (0.5) core 'na erişebilsin. Gördüğünüz gibi cpu kısmını istersek "0.5" yada "1" gibi belirtebiliyor, yada "250m" gibi milicpu halinde yazabiliyoruz. Yani buradaki "0.5" yerine "500m" de yazsak aynı olurdu.

Bu podu oluşturduğumuz da şu olacak, öncelikle scheduler üstünde en az 64megabyte memory ve çeyrek cpu core kaynağının boşta olduğu bir node bulacak, ve bu pod o node üzerinde oluşturulacak. Pod oluşturulacak ve container çalışmaya başlayacak. Bu container ise, en fazla yarım cpu (0.5) core 'unun gücüne denk gelecek şekilde bir CPU kullanabilecek ve sistemden en fazla 256mbyte allocate edecek.

Bu Container eğer yarım core 'dan fazla CPU kaynağına erişmek isterse ne olacak?

Bu durum mümkün olmayacak, CPU da belirlediğimiz Limit neyse, bu container en fazla o kadar CPU kaynağına erişebilecek.

Memory kısmında belirlediğimiz değer, bu containerın maksimum kullanabileceği memory değeridir. Fakat container bu değere geldiği zaman, daha fazlasını kullanamaz diye bir durum söz konusu değildir. Memory allocation CPU gibi çalışmıyor. teknik olarak farklı şeyler.

Bu nedenle, bu container içerisindeki uygulama sistemden daha fazla ram talebinde bulunabiliyor. Sistem varsayılan olarak bunu engelleme şansına sahip değil. İşte bu nedenle, eğer container memory limitine geldiğinde daha fazla memory allocate edilmesi için bir mekanizma olmadığından, bu duruma geldiğinde OOMKilled (out off memory) kill durumuna geçerek restart ediliyor. Eğer uygulamamız burada belirttiğimiz, memory kısıtından daha çok memory talep ederse pod restart edilecek. Bu nedenle, bu kısıtları uygulamamızın davranışına göre belirlememizin davranışına göre belirlememiz gerekiyor.

<mark style="background-color:orange;">Kubernetes node'lar üstünde cpu ve memory kullanımının kontrol edilmesi.</mark>

```
kubectl top node 
# kubectl top node "node_ismi" ile tekil bakılabilir 
```

<mark style="background-color:orange;">Kubernetes pod'lar üstünde cpu ve memory kullanımının kontrol edilmesi.</mark>

```
kubectl top pod 
# kubectl top pod "pod_ismi" ile tekil bakılabilir 
```


# Environment Variables

Konuya bir örnekle başlayalım. Misal, biz bir web uygulaması geliştiriyoruz. Ve bu web uygulaması, bir veritabanı uygulamasına bağlanarak verileri burada saklıyor. Bu uygulamanın, bağlandığı veri tabanının, sunucu adresi, kullanıcı adı ve şifre bilgilerini de bu uygulamanın içerisine ekledik diyelim. Daha sonra, bu uygulamayı container imajı haline getirdik ve artık istediğimiz container platformunda bu uygulamayı çalıştırmaya hazırız.

Bu senaryo da 2 sıkıntımız bulunmaktadır.

1 - Biz veritabanı bağlantı bilgilerini, container imajı içerisine hardcoded olarak yazdık. Yani bir şekilde, container imajı istemediğimiz insanların eline geçerse, bu imajın içerisinde bulunan bu bilgiler expose olur. Bu durumu asla istemeyiz.

2 - Bu (web) imajdan, container oluşturmak istediğimiz zaman, bu container her defasında, aynı veritabanına, aynı kullanıcı adı ve şifre ile bağlanmaya çalışacak. Veritabanı adresimiz, test ortamlarında ayrı, prod ortamlarında ayrı olabilir. Biz bu kullanıcı adı ve şifre bilgilerini zaman içerisinde güncellemiş olabiliriz. Bu senaryo da ne yapacağız? Her ortam için, her seferinde, yeni bilgilerle imaj oluşturacağız, yada her seferinde, container oluşturulduktan sonra, container 'a bağlanıp, bu bilgileri güncelleyeceğiz. 2. seçeneği seçtiğimizde bu sefer de, şöyle bir sorunumuz olacak. Kubernetes bir sorun anında, containerı silip, yeniden oluşturabilir. Scale ediyoruz, geri alıyoruz vb. Her seferinde oluşturulan, tüm containerlara bağlanıp, bunu manuel mi düzelteceğiz.?

Gördüğünüz gibi, bu tarz ortama göre değişebilen, ve hassas bilgileri container imajı içerisine gömmek, oldukça fazla sorun çıkartır. Fakat bununda bir çözümü var.

Bu tarz, çalıştırdığımız ortama göre değişebilecek bilgileri container imajına hardcoded olarak eklemeyiz. Bunun yerine, uygulamalarımızda bu bilgileri, çalıştırıldıkları sistemden okuyacakları şekilde, bir değişken olarak tanımlarız. <mark style="background-color:orange;">Yani Environment variables 'dan okunması için tanım yaparız.</mark>

Yukarıdaki örnekte uygulamamızın bağlanacağı, veritabanının adres ve kullanıcı bilgilerini direkt uygulamaya gömmek yerine veritabanı adresini $database . Kullanıcı adı bilgisini $username . Parola bilgisini de, $password isimli değişkenlerin değerlerine bakacak ve oradan bu değerleri okuyacak şekilde ayarlamasını söyleriz.

Bu sayede, bu hassas bilgileri, bu imajlara eklememiş oluruz. Ne zaman bu imajdan, container oluşturmak istersek, o an hangi değerlerin atanması gerekiyorsa, bu değerlerin Container da Environment variable olarak tanımlarız. Böylece Environment variables nedir ve Container ile ne alakası var sorularının cevaplarını buulmuş olduk.

Şimdi bunları pod tanımına nasıl ekleyeceğimize bakalım.

```
apiVersion: v1
kind: Pod
metadata:
  name: envpod
  labels:
    app: frontend
spec:
  containers:
  - name: envpod
    image: nginx
    ports:
    - containerPort: 80
    env:
      - name: USER
        value: "cahit"
      - name: database
        value: "testdb.1w2.net"
```

<mark style="background-color:orange;">Pod tanımları içerisinde, Environment variable'lar, container tanımı altında, env parametresi ile tanımlanıyor. env parametresi altında list şeklinde, tüm Environment variable 'larımızı tanımlıyoruz</mark>.

Öncelikle tanımlamak istediğimiz Environment variable 'ın ismini giriyor ve ardından da, value opsiyonu ile buna atamak istediğimiz değeri giriyoruz.

Yukarıdaki örnek pod tanımında, "USER" isimli Environment variable tanımlayıp, ve buna da, "Cahit" değerini atadım. Bir altındaki Environment variable 'da, "database" isimli Environment variable tanımlayıp, buna da "testdb.1w2.net" değerini atadık.

Bu container oluşturulduğunda, bu 2 Environment variable bu containerda tanımlanacak, Aynen az önceki örnekte belirttiğim gibi bir web uygulaması oluşturdum. Web uygulaması içerisinde, Selamlama yapacağı kişinin kim olacağını, hardcoded olarak eklemek yerine, çalıştığı ortamdaki "USER" isimli Environment variable değeri ne ise, ondan bu değeri alıp, selamlayacak.

```
root@k8s-master:/home/cahit# curl 127.0.0.1:8080

<html>
<body style="background-color:gray;">
<center><h1 style="color:magenta;">
    <p>K8S Fundamentals</p>
    <p>Pod: envpod</p>
    <p>Hello cahit</p>
        </h1>
</center>
</body>
```

envpod isimli pod içerisinde bulunan Environment variable'ları listelemek,

```
kubectl exec envpod -- printenv
```

Bu bir web uygulaması podu, normal bu pod'a dış dünyadan erişmek için, ya loadbalancer yada nodeport şeklinde bir servis tanımlamamız gerekir. Fakat kubernetes bize, şu imkanıda sunuyor. Biz kubectl kullanarak, herhangi bir pod 'a, deployment'a, service. vb. Kendi bilgisayarımızdan tünel açarak ilgili objenin portuna trafiği yönlendirebiliyoruz. Testlerde hızlı şekilde, objelerimize bağlanmak için, kullanabildiğimiz bu özelliğe "port forward" diyoruz.

Syntax:

```
kubectl port-forward {obje tipi/obje adı} hostport:podport
```

Örnek:

Kubectl 'i çalıştırdığımız cihazın, 8080 portuna gelen tüm istekleri, envpod isimli podun 80 portuna gönder/yönlendir.

```
kubectl port-forward pod/podenv 8080:80
```

{% hint style="info" %}
Container tanımı altında girdiğimiz, *Environment variable'ları, Bu container içerisinde, farklı yerlerde dilediğimiz şekilde kullanabiliriz.*
{% endhint %}


# Ephemeral Volumes

Containerlar, stateless uygulamalar için oldukça uygundur. Bildiğimiz üzere containerlar, container imajı dediğimiz objelerden oluşturulur. Container içersinde çalışması istenilen uygulama ve bu uygulama bağlılıkları read-only bir paket haline getirilir ve bu pakete container imajı deriz. Bu paketten bir container yaratılır. Container yaratıldığı zaman, bu containera ait tüm veriler, bu containera atanan layerda tutulur. Yani container içerisinde oluşturulan her türlü dosya containera ait ayrı bir layer da durur ve "container ayakta olduğu sürece" bu veri erişilebilirdir.

Ancak container silinirse, bu veriler de silinir. Aynı imajdan yeni bir container yarattığımız zaman, bu container da, eski container da yazılan dosyalar bulunmayacaktır.

Bu durum stateless veya yazdığı verinin container kapatıldığında, silinmesinin sorun olmadığı uygulamalar için sıkıntı değildir.

Örneğin, gelen paketi işleyen, sonucu başka bir servise gönderen bir backend uygulaması üstünde bir veri barındırmadığı için ve herhangi bir state tutmadığı için sıkıntı çıkarmaz.

Örneğin, bir frontend uygulamamız, bir web sitemiz olduğunu düşünürsek, kullanıcılarımız buraya browser arayıcılığıyla bağlanıyorlar. Ve bu frontend uygulamamız da, başka bir servise bağlanıp, çeşitli resim dosyalarını çekip, bunları kullanıcılara gösteriyor. Uygulama bu resim dosyalarını, bir defa çektikten sonra, bunu container içerisinde, bir dizine saklıyor ki, bir daha bu resmi göstermesi gerekirse, diğer servise bağlanıp, tekrar çekmekle zaman kaybetmesin. Yani bir nevi bu görselleri kendi üzerinde cache ederek (hazırda bekleterek) çalışıyor.

Bu uygulamayı kubernetes üzerinde deploy etmek için, bir deployment objesi oluşturduk ve bu uygulama bir replika olacak şekilde deploy ettik. Podumuz oluşturuldu ve çalışmaya başladı. Buraya erişebilecek servis objesini de oluşturduk. Böylece bu uygulamaya kullanıcılar bağlanabilir duruma geldi.

Ardından da, backend uygulaması ve servisini de oluşturduk. Kullanıcıları web sitemizi kullanmaya başladılar. Diyelim ki çeşitli resimleri seçtiler ve bizim uygulamamızda, yapması gereken işlemlere başladı. Diğer servise bağlandı ve bu resimleri çekti, işledi ve bağlı kullanıcılara servis etti.

Ama aynı zamanda, bu resimlerin, container içinde cache isimli dizine de kaydetti. Bir başka kullanıcı daha aynı resmi istediği zaman, tekrar zahmet edip diğer uygulamaya bağlanmasına gerek kalmadan, resimleri cache dizininden, çekerek gösterebilir duruma geldi. Böylece hız kazanmış olduk.

Örneğin, pod tanımında, liveness probe da var. Bir problem çıktığını düşünelim ve liveness probe fail etti. Bu durumda kubelet hemen devreye girip, bu container'ı silip, yeniden oluşturdu. Yani "kubectl get pods" kısmında restart olarak gözüken şey aslında container'ın yeniden oluşturulmasıdır.

Container silindiği zaman içindeki veriler de siliniyor. Dolayısıyla, yeni container, içerisinde bu cache dizini boş olacaktır. Çok büyük bir sıkıntı değil. Sonuç olarak bunlar cache dosyaları. Tekrar diğer servise bağlanarak resimleri çekebiliriz.

Ama sırf yeni container oluştuğunda cache dosyalarının silinmese, ben bu pod içerisinde tüm containerların erişebileceği ve podun yaşam süresi boyunca ayakta kalabilecek, bir mekanizmaya sahip olsam ve dosyaları bu belirttiğim yere yazabilsem.

Böyle bir senaryom olsa, ben bu cache dizinini, buraya bağlarım ve içerisine yazılan tüm dosyalar buraya yazılır. Yeni bir container oluşturulduğunda da, bu dosyalara erişmeye devam edebiliriz. Böylece cache den mahrum kalmayız.

Pod silinirse bu dosyalar da silinebilir. Önemli olan, pod ayakta durduğu sürece erişebileceğim ve containerın yaşam süresinden bağımsız bir şekilde, dosya saklayabileceğim, bir yapıya sahip olalım. Böyle bir yapı, bu senaryo da sorunumuzu çözerdi.

Kubernetes de bu sorunu Ephemeral Volume ile çözüyoruz. Ephemeral volume sayesinde, bizler bu tarz verileri fiziksel olarak container dışında tutma imkanına sahip oluyoruz. Bu sayede container silindiği zaman, yeni oluşturulan containerında, bu verilere ulaşabilmesini sağlıyoruz. Kubernetes pod tanımlarında kullanabilmemiz adına 2 tip ephemeral volume mevcuttur.

### 1 - EmptyDir

```
apiVersion: v1
kind: Pod
metadata:
  name: emptydir
spec:
  containers:
  - name: frontend
    image: cyolacan/apps1:latest
    ports:
    - containerPort: 80
    livenessProbe:
      httpGet:
        path: /healthcheck
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5
    volumeMounts:
    - name: cache-vol
      mountPath: /cache
  - name: sidecar
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "sleep 3600"]
    volumeMounts:
    - name: cache-vol
      mountPath: /tmp/log
  volumes:
  - name: cache-vol
    emptyDir: {}
```

emptyDir volume ilk olarak bir pod, bir node 'a atandığında oluşturulur ve bu pod, o node 'da çalıştığı sürece var olur. Adından da anlaşılacağı gibi emptyDir başlangıçta boştur. Pod içindeki tüm containerlar emptydir volume 'deki aynı dosyaları okuyabilir ve yazabilir. Bu volume her kapsayıcıda aynı veya farklı path'lere mount edilebilir.

Bir pod herhangi bir nedenle silindiğinde, emptyDir içindeki veriler kalıcı olarak silinir. Biz bir emptyDir tipinde volume yaratmak için, Pod tanımında gerekli ayarları yaptığımız zaman, kubernetes bu podun oluşturulduğu node üzerinde boş bir dizin yaratır.

Daha sonra biz bu klasörü container içerisindeki, herhangi bir dizine mount edebiliriz. Bizim örneğimizde ki /cache klasörüne, bu containerın bu mount ettiğimiz pathe yazdığı her türlü dosya, fiziksel olarak node üstünde oluşturulmuş olan, boş dizine yazılır. Dolayısıyla container silinse bile, bu dosyalar silinmez. Yeni container oluşturulduğu zaman tekrar bu volume, container da, aynı pathe mount edilir. Bu sayede yeni oluşturulmuş container da, bu dosyalara erişmeye devam eder.

{% hint style="info" %} <mark style="background-color:orange;">Bu dosyalar, POD 'un yaşam süresi boyunca erişilebilir durumdadır. Yani POD silinirse bu volume de silinir. Pod içerisinde bulunan container silinip, yeniden oluşturulabilir. Bu durumda var olan dosyalar etkilenmez ancak POD silinirse bu dosyalar kalıcı olarak silinir.</mark>
{% endhint %}

İster biz, ister kubernetes bir sorun anında, POD u silip yeniden başka bir pod oluşturursa, volume de silinmiş olur. Bu nedenle bu volume'lere ephemeral volume yani geçici volumeler diyoruz.

Bu volumeler sadece, bir şekilde geçici olarak podun yaşam süresi boyunca, containerın, yaşam süresinden bağımsız olarak saklamamız gereken geçici dosyaları tutmak için kullanırız.

### 2 - Host Path

```
apiVersion: v1
kind: Pod
metadata:
  name: hostpath
spec:
  containers:
  - name: hostpathcontainer
    image: ubuntu:latest
    ports:
    - containerPort: 80
    livenessProbe:
      httpGet:
        path: /healthcheck
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5
    volumeMounts:
    - name: directory-vol
      mountPath: /dir1
    - name: dircreate-vol
      mountPath: /cache
    - name: file-vol
      mountPath: /cache/config.json       
  volumes:
  - name: directory-vol
    hostPath:
      path: /tmp
      type: Directory
  - name: dircreate-vol
    hostPath:
      path: /cache
      type: DirectoryOrCreate
  - name: file-vol
    hostPath:
      path: /cache/config.json
      type: FileOrCreate
```

Bir hostpath volume, worker node dosya sisteminden podumuza bir dosya veya dizini bağlayabilme imkanı verir. Bu çoğu podun ihtiyaç duyacağı bir şey değildir. Ancak bazı uygulamalar için, güçlü bir kaçış kapısı sunar.

Hostpath, temel de emptydir ile mantık olarak aynıdır. Yine biz volume yaratılması için, pod tanımlarımıza gerekli parametreleri gireriz. Fakat bu sefer rastgele boş bir klasör yaratmasını söylemek yerine, podun oluşturulacağı worker node üstündeki, spesifik bir dizini veya dosyayı belirtiriz.

Misal, worker node üstündeki /tmp dizinini volume olarak kullanılmasını istediğimizi belirtebiliriz. Daha sonra bu dizini veya dosyayı container içerisindeki herhangi bir dizine mount edebiliriz.

Aynı şekilde, containerdan yazılan dosyalar bu mount ettiğimiz dizine yazılır ve container silinse de dosyalar silinmez. Genellikle podların çalıştıkları worker node üzerinde spesifik bir dizin ve dosyalara erişmesi gerektiği durumlarda işimize yarar. Misal bir path de konumlandırılmış bir unix soketine containerın bağlanması gerekirse, bunu hostpath ile containera mount edebiliriz.

Volume oluşturmak için,

* Volume parametresi ile oluşturulmak istenilen volume specs altında tanımlamak gerekiyor. Name anahtarı ile isim verilmeli ve volume tipini girmeliyiz.
* Bu volume containerda belirlediğimiz, ihtiyacımız olan pathe mount etmeliyiz. Volumemount anahtarı kullanarak mount etmek istediğimiz volume ismini ve mountpath ile bu volume bağlamak istediğimiz pathi belirlemeliyiz.

Hostpath için MountPath tipleri,

* directory => Bağlanacak Dosya(Dizin) zaten hali hazırda ilgili worker nodelar üzerinde mevcutsa, ilgili dosyayı veya dizini direkt olarak containera bağla.&#x20;
* DirectoryOrCreate ve FileOrCreate => Bağlanacak Dosya veya Dizin worker nodelar üzerinde varlığı bilinmiyorsa, kullanılır. Yani ilgili dizin ve ya dosya mevcutsa bağla, mevcut değilse oluştur ve ardından containera bağla.

{% hint style="info" %} <mark style="background-color:orange;">Ephemeral volume bizim depolama sıkıntımızın tamamını çözmez. Bizim bazı durumlarda pod yaşam süresinden bağımsız şekilde,  uzun süreli saklamamız gereken dosyalar da olabiliyor ve bunları ephemeral volume ile çözmemiz mümkün değil. Örneğin DB yapısı olan container içinde içide çalışan database uygulamarı bu durumda olan uygulamalra statefull uygulamlar denir. CSI ( container storage interface ) driveryada plugin i,le yükleme yapılır ve varolan storage üzerinden bir LUN ataması yapılarak kullanılır. Geiş bir konudur bu yüzden bir sonraki alanda bahsedilecek.</mark>
{% endhint %}


# CSI - Plugin

PV ve PCV storage yapıları hakkında bilgi verilecek. Buradan devam edeceğiz.

Desteklenen software ve hardware Storage Listesi için aşağıdaki linki kullanabilirsiniz.

{% embed url="<https://kubernetes-csi.github.io/docs/drivers.html>" %}

<figure><img src="/files/Snna7Yp3JAWIdOYbtlrN" alt=""><figcaption></figcaption></figure>

Hem kubernetes ortmaları içinde hem IBM Redhat OpenShift oramları için desteklenen CSI ( Container Storage Interface ) plug-in driver için desteklenen stoprage yazılım ve donanım listesine ulaşabilirsiniz.


# Secrets

<figure><img src="/files/z98dQb5ITKBQtT57Ua7c" alt=""><figcaption><p>Secrets</p></figcaption></figure>

environment variables konusunda, hassas bilgileri imaja direkt eklemenin doğru olmayacağından bahsetmiştik. Bunları, environment variables olarak pod tanımları içerisine ekliyorduk. Fakat bu senaryoda da imaj içerisine hassas bilgileri eklemedik ancak pod tanımında kullanacağımız yaml dosyası içerisine eklemiş olduk. Bu yaml dosyasına erişebilen herkes, bu bilgilere de erişebiliyor. İlk sıkıntımız bu. 2. problemimiz ise, bizler bu tarz hassas bilgileri, uygulamanın kodundan ayırdık fakat, bu seferde pod tanımımıza ekledik. Bu verilerin değişmesi gerektiği zaman, misal bir şifrenin güncellenmesi gerektiğinde, bizim bu yeni şifre bilgisini pod tanımı üzerinde yani yaml dosyamızda güncellememiz gerekiyor. Bu da yaml dosyalarının generic olmasını engelliyor.

Ayrıca bu hassas bilgilerin yönetimi ile, deploy edilen uygulamanın yönetimi, ayrı ekiplerde olabilir. Bu 2 işi birleştirdiğimiz zaman, bu ayrımı sağlayamıyoruz. Devops ve Security gibi düşünebilirsiniz.

Bu sorunu çözmek için, en mantıklı yöntem bu hassas bilgileri, uygulama deploy ettiğim pod tanımlarından ayırmam ve ayrı yönetebilmemdir. Kubernetes de, secret objesi tam olarak bu işi yapıyor.

Kubernetes secret, parolalar, Oauth Token ve SSH anahtarı gibi hassas bilgileri, depolamamıza ve yönetmemize olanak sağlar. Gizli bilgileri bir secret içinde saklamak, onu bir pod tanımına veya container imajına direkt olarak eklemekten daha güvenli ve esnektir. Kubernetes secret, parolalar, Oauth Token ve SSH anahtarı gibi hassas bilgileri, depolamamıza ve yönetmemize olanak sağlar. Gizli bilgileri bir secret içinde saklamak, onu bir pod tanımına veya container imajına direkt olarak eklemekten daha güvenli ve esnektir. DEVSECOPS içinde gerekli adımdır.

Secretler pod ve Deployment gibi bir kubernetes objesidir. Hassas bilgileri secret objelerinde saklar ve sonra bunları çeşitli yöntemlerle pod'un içerisine ekleyebiliriz.

Secretler, aynen pod ve diğer objeler gibi yaratılıp, yönetilir. Yani hem yaml dosyasıyla, hem de kubectl arayıcılığıyla imperative olarak secret objesi oluşturabiliriz.

Örnek bir secret yaml içeriği;

```
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  db_server: db.1w2.net
  db_username: admin
  db_password: P@ssw0rd1!
```

ApiVersion:v1 ve kind kısmı secret olmalıdır. Metadata kısmında "name" parametresi ile bu secrete isim vermeliyiz.&#x20;

{% hint style="info" %}
Oluşturulan secret ve bu secreti atayacağımız pod aynı namespace içerisinde olmalıdır. Yani biz bu secreti "default" namespace altında oluşturuyorsak, bu secreti atayacağımız pod da, default namespace altında olmalıdır.
{% endhint %}

Secret yaml dosyamızda type kısmını opaque olarak girdik. Ve bunun gibi 8 tane daha secret type mevcuttur. (basic auth, TLS vb..)

Secret objeleri, kullanıcı adı vb, TLS sertifikaları ya da SSH anahtarı gibi hassas verileri saklamamıza yarar, Biz opaque tipinde bir secret oluşturursak, tüm bu belirttiğim veri tiplerini, hiçbir sıkıntı olmadan saklayabiliriz.

Secret oluşturduğumuz yaml dosyası içerisinde, ekleyeceğimiz hassas bilgileri "stringData" parametresi içerisine yazıyoruz.

{% hint style="info" %}
stringData yerine, sadece "Data" parametresi kullanılan yerler ve kişiler olabilir. aralarındaki fark, stringData olduğu gibi, plaintext olarak yazılır. "Data" ise, hassas bilginin base64 encode edilmiş hali olarak yazılır.
{% endhint %}

{% hint style="info" %}
"kubectl get secrets" komutunu çalıştırdığımızda "data" kısmında yazan sayı, bizim girdiğimiz hassas veri miktarıdır.
{% endhint %}

Kubectl kullanarak secret oluşturma;

```
kubectl create secret generic "secret_ismi" --from-literal="anahtar"="değer" --from-file="anahtar"="değerin_okunacagi_dosya" --from-file="değerin_okunacagi_dosya"

kubectl create secret generic mysecret --from-literal=db_server=db.example.com --from-file=db_server=server.txt --from-file=config.js

kubectl create secre generic mysecret2 --from-literal=db_server=db.example.com --from-literal=db_username=admin --from-literal=db_password=password1
```

\--from-literal yerine --from-file kullanarak, hassas bilgileri shellden yazmak yerine, dosyadan okumasını söyleyebiliriz.

Secretları Pod tanımlarına eklemek için 2 seçeneğimiz mevcuttur. Bu değerleri poda volume arayıcılığıyla ekleyebilir ya da Environment variable olarak aktarırız.

```
apiVersion: v1
kind: Pod
metadata:
  name: secretpodvolume
spec:
  containers:
  - name: secretcontainer
    image: ubuntu
    volumeMounts:
    - name: secret-vol
      mountPath: /secret
  volumes:
  - name: secret-vol
    secret:
      secretName: mysecret3
```

```
apiVersion: v1
kind: Pod
metadata:
  name: secretpodenv
spec:
  containers:
  - name: secretcontainer
    image: alpine
    env:
      - name: username
        valueFrom:
          secretKeyRef:
            name: mysecret3
            key: db_username
      - name: password
        valueFrom:
          secretKeyRef:
            name: mysecret3
            key: db_password
      - name: server
        valueFrom:
          secretKeyRef:
            name: mysecret3
            key: db_server
```

```
apiVersion: v1
kind: Pod
metadata:
  name: secretpodenvall
spec:
  containers:
  - name: secretcontainer
    image: ubuntu
    envFrom:
    - secretRef:
        name: mysecret3
```

Yukarıdaki yaml örneğinde secretlerin podlara tanımlanması ile ilgili 3 örnek mevcuttur.

1 - secretpodvolume pod tanımında volume kullanılarak hassas bilgiler pod a aktarılmıştır. Özetle, secret-vol isimli bir volume yaratıyoruz. İsim herhangi bir şey olabilir. Daha sonra bu volume emptyDir ve ya Hostpath olarak değil, bu sefer secret den oluşturuyoruz. Diyoruz ki, bu volume mysecret3 isimli secret den oluşturulsun. Sonrasında bu volume Containerda bir dizine mount ediyorum. Bu pod oluşturulduğu zaman, Kubernetes secret içerisinde bulunan tüm keyleri birer dosya olarak, bu mount ettiğimiz pathe koyacak. Bu dosyaların içeriğini de, bu keylere atadığımız değerler olacak. Dolayısıyla artık uygulamamıza, örneğin veri tabanına mı bağlanmak istiyorsun? Kullanıcı adını /secret/db\_username dosyasından bakarak öğrenebilirsin.

2 - secretpodenv pod tanımında Environment variable kullanarak, hassas bilgiler poda değişken olarak atanmıştır. Bu örnekte, bir username adında, env variable tanımlıyoruz ve bunun value kısmını, mysecret3 isimli secret içerisinde tanımladığım db\_username anahtarının değeri olarak alıyorum. Diğer değişkenleri de bu şekilde mysecret3 içerisinde tanımladığım keylerden okuyarak yeni bir değişkene atıyorum.

3 - secretpodenvall pod tanımında da Environment Variable kullanıyoruz ancak, bütün değerleri tek tek okuyup, yeni bir değişkene atamak yerine, git mysecret3 içeriğine bak ve mysecret3 içerisinde bulunan tüm değerleri env. variable olarak pod'a atamasını söylüyoruz.

Oluşturduğumuz secretlar, etcd de şifrelenmemiş olarak saklanmaktadır. Cloud servis sağlayıcılarında yani yönetilen kubernetes hizmetlerinde bu tür veriler şifrelenmiştir. Ancak kendi kurup, yönettiğimiz kubernetes clusterı için bu durum söz konusu değildir. Kendi oluşturduğumuz secretları şifrelenmiş bir şekilde etcd tutmak için, encrypted secret data and rest dökümanı takip edilerek, secret'ları şifreleyebilirsiniz.

{% embed url="<https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/>" %}
Encrypting Secret Data at Rest
{% endembed %}

Secret objelerinin silinmesi,

```
kubectl delete secret "secret_ismi"

Ör: kubectl delete secret my-secret
```

Secret objelerinin listelenmesi,

```
kubectl get secret
```


# ConfigMap

<figure><img src="/files/P4fH00GiK8dNOgKez5GF" alt=""><figcaption></figcaption></figure>

ConfigMap, gizli olmayan verileri key/value eşlenikleri şeklinde depolamak için kullanılan bir API nesnesidir. Podlar, ConfigMap'i environment variable, komut satırı argümanları veya yapılandırma dosyaları olarak kullanabilirler.

ConfigMap objeleri, secret objeleri ile birebir aynı işe yarar. Key/Value şeklinde veriler tutup, bunları podlara environment variable yada volume olarak aktarabiliriz. Kubelet ve yaml dosyalarıyla oluşturulur. Secret ile aynı şekilde oluşturulur.

ConfigMap de gizli olmayan, fakat yine de pod tanımından ayırmamız gereken konfigürasyon verileri tarzı bilgileri tutarız. Yani gizli olmasını istediğimiz veriyi, secret objesinde tutarız. Gizli olmasına gerek olmayan verileri "configmap" objesinde tutarız.

{% hint style="info" %}
ConfigMap 'de veriler, base64 encode edilmiş şekilde saklanmaz.
{% endhint %}

Imperative olarak ConfigMap objelerinin oluşturulması,

```
kubectl create configmap "configmap_ismi" --from-literal="anahtar"="değer" --from-file="anahtar"="değerin_okunacagi_dosya" --from-file="değerin_okunacagi_dosya"

kubectl create configmap myconfigmap--from-literal=db_server=db.example.com --from-file=db_server=server.txt --from-file=config.json
```

ConfigMap objelerinin listelenmesi,

```
kubectl get configmap
```

ConfigMap objelerinin silinmesi,

```
kubectl delete configmap "configmap_ismi"

kubectl delete configmap my-configmap
```

Örnek, ConfigMap yaml dosyası.

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  db_server: "db.1w2.net"
  database: "mydatabase"
  site.settings: |
    color=blue
    padding:25px
---
apiVersion: v1
kind: Pod
metadata:
  name: configmappod
spec:
  containers:
  - name: configmapcontainer
    image: ubuntu
    env:
      - name: DB_SERVER
        valueFrom:
          configMapKeyRef:
            name: myconfigmap
            key: db_server
      - name: DATABASE
        valueFrom:
          configMapKeyRef:
            name: myconfigmap
            key: database
    volumeMounts:
      - name: config-vol
        mountPath: "/config"
        readOnly: true
  volumes:
    - name: config-vol
      configMap:
        name: myconfigmap
```

<figure><img src="/files/oDT324fMHaOqsH4SgP9H" alt=""><figcaption></figcaption></figure>


# Node Affinity

<figure><img src="/files/144wJAZ5qfsUAbxeo554" alt=""><figcaption></figcaption></figure>

Node Affinity kavramsal olarak node selector'a benzer ve nodelara atanan etiketlere göre podumuzun hangi node üstünde schedule edilmeye uygun olduğunu kısıtlamamıza olanak tanır.

Podlarımızın, uygun worker nodelar üzerinde oluşturulması için, imkan sunan pod opsiyonlarımızdandır. Node Selector ile çok benzerdir. Aynı şekilde pod tanımına ekleriz. Podumuzun scheduler edileceği node üstünde pod tanımında belirttiğimiz label'in olmasını bekleriz.

Affinity tanımı, pod tanımı içerisinde "affinity" parametresi altında eklenmektedir. Bu parametre altında kullanabileceğimiz 2 seçenek mevcuttur.&#x20;

* requiredDuringSchedulingIgnoredDuringExecution
* preferredDuringSchedulingIgnoredDuringExecution

```
apiVersion: v1
kind: Pod
metadata:
  name: nodeaffinitypod1
spec:
  containers:
  - name: nodeaffinity1
    image: ubuntu
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: app
            operator: In #In, NotIn, Exists, DoesNotExist
            values:
            - blue
```

```
apiVersion: v1
kind: Pod
metadata:
  name: nodeaffinitypod2
spec:
  containers:
  - name: nodeaffinity2
    image: ubuntu
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: app
            operator: In
            values:
            - blue
      - weight: 2
        preference:
          matchExpressions:
          - key: app
            operator: In
            values:
            - red
```

```
apiVersion: v1
kind: Pod
metadata:
  name: nodeaffinitypod3
spec:
  containers:
  - name: nodeaffinity3
    image: alpine
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: app
            operator: Exists #In, NotIn, Exists, DoesNotExist1.
```

1. pod tanımında bulunan, <mark style="background-color:orange;">requiredDuringSchedulingIgnoredDuringExecution</mark> seçeneği, şu demektir. Bu podu oluştururken, mutlaka alta yaptığım eşleşmeye uygun node bul ve bu podu orada oluştur. Uygun bir node bulamazsan, pod oluşturulmasın ve "pending" status da, uygun node bulana kadar beklesin. \
   \
   Bu pod tanımını kubernetes API server'a gönderirsem, şu olacak. Kubernetes tüm worker nodelarda, app=blue şeklinde bir label arayacak. Eğer bu labelin olduğu uygun bir worker node görürse bu podu orada schedule edecek. Tanım required olduğu için, Eğer bu labela sahip bir worker node bulamazsa, schedule edilmeyecek. Pod "pending" de bekleyecek. \
   \
   Kısacası required mutlaka sağlanması gereken eşleşme varsa, onu belirttiğimiz parametredir.&#x20;

Burada operator olarak kullanabileceğimiz birkaç seçenek daha mevcuttur. Bu seçenekler aslında node selector'dan ayrışmasını sağlıyor.\
\
app' in blue tanımı yaptığımızda, app anahtarına blue değerinin atanma şartı oluşturuyoruz. Yani pod  app=blue labela sahip, worker node bulursa schedule edilecek.\
\
Fakat burada, "in" yerine "notin" deseydik, Bu sefer pod, üstünde app=blue labeli <mark style="background-color:orange;">tanımlanmamış</mark> bir worker node üstünde oluşturulacaktır.

operator kısmına, "<mark style="background-color:orange;">exists</mark>" tanımı girseydik, ve değeri silseydik. şu olacaktı; bu podu oluşturmak için, üstünde "app" anahtarı tanımlanmış herhangi bir worker node bul demek olacaktı. Yani value(değeri) kısmı önemli değil.  sadece "app" anahtarı olması yeterli.

<mark style="background-color:orange;">DoesNotExists</mark> seçeneği ise, tam tersidir.  Üstünde "app" abahtarı olmayan bir worker node bul ve orada çalıştır.

<mark style="background-color:orange;">requiredDuringSchedulingIgnoredDuringExecution</mark> seçeneğinde, şart sağlanmazsa pod oluşturulmaz.

2. Pod tanımında bulunan, <mark style="background-color:orange;">preferredDuringSchedulingIgnoredDuringExecution</mark> seçeneği şu manaya gelir; Buradaki tanıma bak, bu podu, bu tanıma uygun node üstünde oluştur.  Fakat bu tanıma uygun bir node bulamazsanda "pending" olarak bekletme, başka bir node bul ve bu podu orada oluştur. \
   \
   Preferred tanımında gördüğünüz üzere, "weight"  de girebiliyoruz.  Weight 1 ile 100 arasında bir değer verebiliyoruz.  Ve birden fazla tanım girildiyse, onların hangisinin daha önce değerlendirileceğine bakıyor. Örneğimizde 2 tanım girili durumda, 1. tanımdaki weight :1 seçili, 2.tanımda weight:2 seçili. 2, 1'den daha büyük olduğu için, daha önceliklidir. Biz bu podu oluşturduğumuzda şu olacak;\
   \
   Scheduler tüm nodelara bakacak ve, app=blue ve app=red labellarına sahip nodeları listeleyecek. Bunlardan app=red weight değeri daha yüksek olduğu için, Eğer bu etikete sahip node varsa, pod buada oluşturulacak. Eğer bu worker node üstünde yer yoksa, veya label yoksa bu seferde app=blue olan node seçilecek. Eğer bu node da yer yoksa veya bu labellere sahip hiçbir node bulamazsa, <mark style="background-color:orange;">uygun olan herhangi bir worker node üstünde, pod oluşturulacak.</mark>&#x20;

<mark style="background-color:green;">Uzun lafın kısası, required olarak girildiyse tanım, mutlaka bu şart yerine getirilmelidir. Fakat preferred olaran girildiyse, bu şartı karşılamayı dener fakat karşılayamazsa, yine de schedule eder.</mark>

<mark style="background-color:orange;">IgnoredDuringExecution</mark> kısmı şu demektir; Biz bir pod oluşturduk, pod buradaki tanıma göre bir worker node buldu ve orada schedule oldu. Ve çalışmaya başladı. Fakat aradan zaman geçti ve biz bu çalıştığı worker nodedan podun node seçiminde kullandığı labeli sildik. Eğer Affinity altında IgnoredDuringExecution seçeneği seçiliyse, pod bir kere schedule edildikten sonra, çalışmaya devam edecek anlamına gelir. Zaten hali hazırda kubernetes de bunun için farklı bir alternatif yok. Yani RequireDuringExecution veya PreferredDuringExecution tanımları yok. Belki ileride bu özellikleri de görebiliriz.

Özetle,

pod, app=blue labelini şart koşuyor. Bu labela sahip node yoksa veya label var ama pod için yer yoksa, pod oluşturulmaz. \
\ <mark style="background-color:orange;">requiredDuringSchedulingIgnoredDuringExecution</mark> seçeneğini çok fazla kullanmayı tercih etmeyiz ki, örneğin SSDli nodelarda pod çalıştırmak için nodelara disk=ssd labeli ekledik. Ancak bir zaman sonra, bu nodelar dolacaktır ve yeni pod schedule etmek için yer kalmayacaktır. Bu durumda podumuz oluşturulmayacaktır.  (En azından bir süre). Uygun node bulana kadar "pendig" status da bekleyecektir. Yer olmasa bile, podumuzun farklı bir node da(misal disk=ssd labelina sahip olmayan) oluşturulmasını isteyebiliriz ki servisimiz, uygulamamız çalışabilsin. Bu nedenle preferred tanımı daha kullanışlı gözüküyor.

* POD app=blue labeli olmasını şart koşuyor.
* POD app=blue veya app=red labellerinin olmasını istiyor ancak şart koşmuyor.
* POD app adında, bir anahtar(label) olacak ama değerinin ne olduğu ile ilgilenmiyor. Sadece "app" adında label olup, olmadığına dikkat ediyor.


# Pod Affinity

<figure><img src="/files/U1NETMyYvQ95N0wgcTII" alt=""><figcaption><p>Pod Affinity</p></figcaption></figure>

Bazı durumlarda, bizler oluşturduğumuz podun schedule edileceği node üstünde çalışan başka podların olmasını veya olmamasını isteyebiliriz.&#x20;

Örneğin, AWS üstünde koşan kubernetes clusterımız var. Bu kubernete clusterın 4 worker node var. Ve bu 4 worker node da, aynı region içerisinde bulunan 2 farklı availability zone üzerinde dağıtılmıştır.&#x20;

Yani worker node 1 ve worker node 2 availability zone 1 de, worker node 3 ve worker node 4 de availability zone 2 de barınmaktadır.

Farzedelim ki biz frontent , cache ve veritabanından oluşan bir uygulama deploy edeceğiz. İlk olarak veritabanımızı oluşturacak pod tanımını hazırladık ve ve kubernetes api gönderdik ve kubernetes scheduler işini yaptı ve podu çalıştıracağı uygun bir node buldu. Ve varsayalım ki bu node ise, worker node 1, sonrasında frontend uygulamasını deploy ettik ve onu da worker node 3 de schedule etti. Son olarak da, cache podumuzu deploy ettik ve bu pod da son olarak worker node 4 de çalışmaya başladı.

Bu senaryo da bir kaç sıkıntı var,

İlk olarak, frontend podumuz ve veritabanı podumuz ayrı ayrı availability zonelar üzerinde schedule edildiler. Tahmin edebileceğiniz üzere frontend podumuz sürekli olarak veritabanı ile görüşüyor. Sürekli ona veri yazıyor ve ondan veri okuyor. Hemen hemen tüm cloud servis sağlayıcılar 2 availability zone arasında veri transferine ücrete tabi tutulur. Yani biz 2 podu da aynı availability zone üzerinde bulunan worker nodelarda koştursaydık, aynı availability zone üzerinde durdukları için aralarındaki veri transferine para ödemeyecekken, şimdi ayrı availability zone da durdukları için para ödemek zorunda kalacağız. Bunun olmasını engellemek için, node affinity yazarak, podları aynı yere toplayabiliriz. Ama bu seferde her şeyi manuel olarak ayarlamak gerekecek. Fakat bunun yerine şöyle bir şey yapabilseydik, sanırım işleri otomatik olarak kubernetes hallederdi. Eğer ben pod tanımına, bak bu podu oluşturacağın node'u seçerken git veritabanı poduna bak ve o pod hangi  availability zone da oluşturulduysa, bu podu da orada oluştur gibi bir tanım ekleyebilseydim bu sorunu çözebilirdik.

Diğer bir istediğimiz bir durumda şu olabilirdi;

Misal frontend ve cache podları ayrı worker node üzerinde oluşturulmuş. Bunları da keşke aynı worker node üzerinde çalıştırabilseydim. Sonuçta cache ve frontend arasında ne kadar az latency olursa, uygulamam o kadar performanslı çalışır.

Bunun kolay yolu da şu olurdu, cache pod tanımında şunu diyebilseydik, git frontend pod'a bak, o hangi node üstünde oluşturulduysa, bu pod da orada oluşturulsun.&#x20;

Bize tüm bunları sağlayan, Kubernetes özelliği mevcut.

<mark style="background-color:green;">Pod Affinity podumuzun hangi worker node üzerinde oluşturulmaya uygun olduğunu nodelardaki etiketlere göre değil, hali hazırda node da çalışmakta olan podlardaki etiketlere göre sınırlamamıza olanak tanır.</mark>

Bizlerin bir pod tanımına ekleyerek, o podun oluşturulacağı node seçimini ve o node üzerinde çalışan veya çalışmayan podların varlığına göre yapabilme imkanı veren özelliğe Pod Affinity diyoruz. Nasıl ki, node affinity de bir podun oluşturulacağı node ve o node da bulunan veya bulunmayan labellara göre seçebiliyorsak, Pod Affinity de ise, bunu ilgili node üzerinde çalışan pod olup, olmadığına göre seçebiliyoruz.&#x20;

Bizler daha önceden worker nodelarımıza çeşitli labellar eklemeyi gördük ve ekledik. Fakat nodelar üzerinde önceden de tanımlanmış(default gelen) bir çok label mevcuttur.

Her kubernetes node da,

* <mark style="color:green;">**kubernetes.io/arch**</mark> <mark style="color:blue;">=> İlgili node x64 veya ARM tabanlı bir işlemciye sahip olduğunu belirtir. Değer olarak Amd64 veya ARM değerlerini alabilir.</mark>
* <mark style="color:green;">**kubernetes.io/hostname**</mark> <mark style="color:blue;">=> İlgili node'un, hostname değerini alır.</mark>
* <mark style="color:green;">**kubernetes.io/os**</mark> <mark style="color:blue;">=> İlgili node'un işletim sistemi bilgilerini alır (Linux,Windows)</mark>

Labelları bulunur.

<mark style="color:blue;">Bunun yanında, cloud servis sağlayıcılarının yönetilen Kubernetes hizmetindeki nodelarda ilgili node'un hangi region da bulunduğunu belirten (</mark> <mark style="color:blue;"></mark><mark style="color:blue;"><mark style="color:green;">topology.kubernetes.io/region<mark style="color:green;"></mark> <mark style="color:blue;">) ve hangi availability zone da bulunduğu belirten, (</mark> <mark style="color:blue;"></mark><mark style="color:blue;"><mark style="color:green;">topology.kubernetes.io/zone<mark style="color:green;"></mark> <mark style="color:blue;">) anahtarları ile belirtilir.</mark>&#x20;

Örnek bir Pod Affinity tanımı içeren YAML dosyası içeriği,

```
apiVersion: v1
kind: Pod
metadata:
  name: frontendpod
  labels:
    app: frontend
    deployment: test
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
```

```
apiVersion: v1
kind: Pod
metadata:
  name: cachepod
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - frontend
        topologyKey: kubernetes.io/hostname
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: color
              operator: In
              values:
              - blue
          topologyKey: kubernetes.io/hostname
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: deployment
              operator: In
              values:
              - prod
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: cachecontainer
    image: redis:6-alpine
```

2. Pod tanını inceleyelim, Pod affinity tanımlarıda aynen node affinity tanımları gibi requiredDuringScheduling ve preferredDuringScheduling olarak hard ve soft gereksinimlere sahip olabilirler. Node affinity tanımlara oldukça benzer. Tek bir fark vardır. Pod affinity tanımlarıonda topologyKey adında bir daha gireriz. İlk tanımda requiredDuringSecheduling seçeneği girili, yani mecbur kılınmış. Bu tanımda diyoruz ki, schedule edileceği worker node üzerinde app=frontend labelina sahip bir pod olmalı. Yani bu podu bu şartı sağlayan host üstünde schedule et. topologyKey kısmında bunu belirtiyoruz. Burada hostname anahatarını seçersek, aynı hostname yani aynı worker node üzerinde bu pod çalıştırılacak.  \
   \
   Örneğin , diyelim ki hostname yerine "zone" anahtarını seçtik. Bu seferde aynı worker node olmasına gerek yok olmayacaktı. Buraya topology.kubernetes.io/zone yazsaydık, bu podu app=frontend labelli bir podun çalıştığı availability zone daki herhangi bir worker node üzerinde çalıştır. Manasına gelecekti.

Görüldüğü gibi node affinity de çalışacağı worker nodedaki labellara göre seçiyorduk. Pod affinitty de ise, hangi podlarla birlikte çalışacağını seçiyoruz.

Pod Affinity'nin Node Affinity göre bir farkı da anti-affinity tanımlarının ayrı yapılmasıdır. node anti affinity kısmını not-in ile halledebiliyorduk. Fakat pod anti affinity kısmını özel olarak tanımlamamız gerekiyor. Bunu da podAntiAffinity anahtarı ile yapıyoruz.

Örnekte bulunan antiAffinity kısmını  inceleyecek olursak,\
Bu podu deployment=prod tanımına sahip olan bir pod ile, aynı zone da schedule etme diyoruz.

Bu YAML dosyasını deploy edersek şu olur;

* Öncelikle frontend pod adındaki pod schedule edilecek. Gördüğünüz gibi app=frontend labeli eklemişiz. Bu da bizim frontend podumuzu temsil ediyor.
* Sonrasında ise, 2.podumuz olan, cachepod isimli podumuz deploy ederken, kubernetes pod affinity kurallarına bakacak, İlk kural olan RequireDuring kısmından bahsedersek, burada şunu diyoruz, Bu pod mutlaka app=frontend labelina sahip bir pod ile aynı node üzerinde çalıştırılsın. Eğer bu şekilde birden fazla node varsa, git color=blue labelina sahip bir podun çalıştığı worker node varsa, onu seç. Ve mümkünse bu seçtiğin worker node buludunduğu zone içerisinde deployment=prod labelina sahip bir pod bulunmasın.


# Taint and Toleration

<figure><img src="/files/8FGeHZ8Qhlco4GRRw5uO" alt=""><figcaption><p>Taint and Toleration</p></figcaption></figure>

3 worker node 'dan oluşan bir cluster yapısı düşünelim. Worker nodelarımızdan birine color=blue diğer node da color=green labelları ekledik. 3. worker node da ise herhangi bir label eklemedik.&#x20;

Bu cluster üzerinde 2 uygulama deploy edeceğiz, blue uygulamamızı, blue olarak etiketlenen worker node da, green uygulamamızı da green olarak etiketlenen worker node da schedule edilmesini istiyoruz.

Bu nedenle gerekli pod affinity ve node affinity tanımlarını yaptık ve uygulamalarımızı deploy ettik. Blue uygulamamız node affinity tanımları gereği color=blue etiketli worker node da, green uygulamamız color=green şeklinde etiketlenen worker node da çalışmaya başladı. Yani istediğimiz gibi oldu.

Şimdi iste, aynı cluster da başka bir ekibin daha, bir uygulama deploy ettiğini düşünelim. Onlarda tanımlarını yaptılar ve kubernetes api' na gönderdiler. Podlar schedule edilmeye başlandı ve bazı podlar blue olarak etiketlenmiş worker node da, bazı podlar green olarak etiketlenmiş worker node da, bazı podlar da sonuncu worker node da çalışmaya başladı.

Bu istediğimiz bir durum ise, bunda bir sıkıntı yok elbette. Kubernetes sıradan en uygun nodeları seçip, yeni iş yüklerini buralarda çalıştırmaya başlayacak.

Fakat biz, color=blue olarak işaretlenmiş worker node üzerinde sadece blue uygulamasının çalışmasını istiyorsak veya green olarak etiketlenmiş node da sadece green uygulaması çalışsın istiyorsak bunu node affinity veya pod affinity ile sağlayamayız. Affinity tanımları bir podun nerede schedule edileceğini belirtir. Yani pod'a göre bir tanımdır.

Bizim ihtiyacımız misal, şu, şu worker nodelarda sadece şu tipte uygulamalar çalışsın ise, affinity bunu sağlamaz.&#x20;

Örneğin bizim 10 node (worker) dan oluşan bir clusterımız var, Bu 10 node dan, 3 tanesi en hızlı işlemcilere, en yüksek ram kapasitesine ve hızlı disklere sahip, diğer 7 node ise bunlara göre daha yavaş, bu durumda şunu isteyebiliriz. Sadece müşterilerimize dönük uygulamalarımız bu hızlı nodelar üzerinde çalışsın, test uygulamalarımız veya düşük öncelikli uygulamalarımız hiç bir zaman bu hızlı worker nodelarda schedule edilip, kaynakları boşuna tüketmesin. Yani uzun lafın kısası, sadece belirli tipte podlar bu hızlı nodelar üzerinde schedule edilebilsin.&#x20;

Bu tür tanımları taint ve toleration tanımlarıyla gerçekleştirebiliyoruz. Biz bir worker node'a anahtar veri eşlenikleri şeklinde bir taint ve bu taint ile birlikte <mark style="background-color:orange;">NoSchedule|PreferNoSchedule|NoExecute</mark> olmak üzere bir emir ekleriz. Bu aşamadan itibaren, bu node üstünde, sadece bu tainti, yani bozukluğu tolere edecek podlar çalışır.

Örneğin, ben bir worker node a "platform=production:NoSchedule şeklinde bir taint eklersem, bu tainti tolere edecek bir tanım eklenmemiş hiç bir pod, bu node üzerinde schedule edilemez.&#x20;

Aynı şekilde, "platform=production:PreferNoSchedule" şeklinde bir tanım eklersek,  bu seferde mümkünse, bunu tolere edemeyen hiç bir pod bu worker node üzerinde schedule edilemez. Ama ilgili pod için uygun başka bir node bulunamazsa da, Son seçenek olarak bu worker node üstünde pod çalıştırılır.&#x20;

<figure><img src="/files/DnzLb0Lgw7eZ7bHtHPEu" alt=""><figcaption></figcaption></figure>

NoExecution ise, dikkatli kullanılması gereken bir seçenektir. Ben bir worker node a "platform=production:NoExecution" şeklinde bir taint tanımı girersem, bu tainti tolere edemeyen hiç bir pod burada schedule edilemez ve mevcut durumda worker node üzerinde bu tainti tolere edemeyen pod ve podlar varsa, ilgili podlar bu worker node üzerinden silinerek, başka uygun worker nodelarda yeniden oluşturulur.

Node'lara taint ekleme.

```
kubectl taint node "node_ismi" "anahtar=değer:eylem"

Ör: kubectl taint node worker-1 platform=production:NoSchedule
```

Yukarıdaki komutla şunu diyoruz, eğer bir pod tanımında, "platform=production:NoSchedule" şeklinde bir toleration tanımı yoksa, o pod burada schedule edilemeyecek.

Node'lardan taint kaldırma.

```
kubectl taint node "node_ismi" "anahtar-"

Ör: kubectl taint node minikube platform-
```

Örnek bir Toleration kullanımı;

```
apiVersion: v1
kind: Pod
metadata:
  name: toleratedpod1
  labels:
    env: test
spec:
  containers:
  - name: toleratedcontainer1
    image: alpine
  tolerations:
  - key: "platform"
    operator: "Equal"
    value: "production"
    effect: "NoSchedule"
```

```
apiVersion: v1
kind: Pod
metadata:
  name: toleratedpod2
  labels:
    env: test
spec:
  containers:
  - name: toleratedcontainer2
    image: ubuntu
  tolerations:
  - key: "platform"
    operator: "Exists"
    effect: "NoSchedule"
```

1. pod tanımında gördüğünüz gibi, bu podun tolere edebileceği taint tanımı toleration kısmında aynı taint tanımı ekler gibi eklenmiş. Key kısmına anahtar, operator kısmına Equal yani eşittir. Value kısmınada değeri giriyoruz. Ardından da effect kısmına aynı effecti giriyoruz. Bu podun tainti tolere edebilmesi için, birebir aynı tolerations tanımının yapılması gerekiyor. Biz de bu pod tanımında bunu sağlamış oluyoruz.
2. pod tanımında ise, bu sefer key kısmında anahtar var, fakat value kısmı yok, Onun yerine operator kısmına "Exists" girilmiş.  "Effect" kısmı ise aynı, bu da şu demek, Eğer Platform ve NoSchedule olarak girilmiş bir taint tanımı varsa, bunu tolere et. Yani değeri önemli değil.

Tolerations kısımları bu şeklilde, neyi tolere edeceksek, onun aynısını tolerations kısmına giriyoruz. Taint ve tolerations node affinity'nin yaptığını yapmaz. Bir poda git şu taintin olduğu yerde schedule ol demez, Tolerations sadece şunu der; Eğer scheduler podu schedule etmek için seçtiği node üstünde, bu taint varsa onu tolere edebilirsin. Yani bu pod şurada oluşturulsun node affinity'nin işidir. Worker node üzerinde sadece şu podlar çalışabilsin taint ve tolerations'un işidir.


# DaemonSet

<figure><img src="/files/zyNjAZTQjFwrEEuEK54J" alt=""><figcaption></figcaption></figure>

Örneğin 20 worker node'dan oluşan bir kubernetes clusterımız mevcut,  bizim bu worker nodeların tamamında çalışması gereken bir uygulamamız var. Biz bu worker nodelarda oluşan logları merkezi bir log sunucusuna göndermek istiyoruz.

Her bir worker node da bir uygulama çalıştıracağız ve bu uygulama o worker node da oluşan logları toplayacak ve merkezi log sunucusuna gönderecek.

Tek, tek tüm worker loglara bağlanıp, bu uygulamayı buraya kurup ayarlayabiliriz. Ancak bu da fazlasıyla iş yükü çıkartacaktır. 2. seçenek olarak, bu uygulamayı bir container imajı haline getiririz, ardından gerekli ayarların olduğu 20 pod tanımı yaparız, her birine node affinity ekleyip ve bu podları ayrı ayrı worker nodelar üstünde çalışacak şekilde deploy ederiz. Ancak bu senaryoda da yeni worker nodelar eklediğinde tüm bu işlemleri tekrar etmemiz gerekecektir.

Burada bize çözüm için, DaemonSet yardımcı olmaktadır.

DaemonSet, tüm (veya bazı) node'ların bir podun bir kopyasını çalıştırmasını sağlar. Cluster'a yeni node eklendikçe yeni eklenen bu worker nodelara da bu podlar eklenir. Cluster'dan node kaldırıldığında/silindiğinde bu podlarda kaldırılır. Bir daemonset silindiğinde, oluşturduğu podlar da silinecektir.

DaemonSet, Deployment objesine benzer bir kubernetes objesidir. Bir daemonset objesi oluşturulduğumuz zaman DaemonSet sistemdeki tüm nodelar üstünde template altında belirttiğimiz pod tanımına göre bir pod oluşturulur.  Varsayılan olarak, her node üstünde bir pod oluşturulur. Fakat biz bunu değiştirerek sadece belirli tiplerdeki node'lar üstünde oluşturulmasını sağlayabiliriz.

DaemonSet, her node da çalışmasını istediğimiz Log toplama uygulamaları, Storage Provision uygulamaları gibi uygulamaları kolay bir şekilde deploy edilmesini sağlar. Bizlerin bu tarz uygulamaları ayağa kaldırmak için tek, tek tanım yapmamız yerine, Bir daemonset oluşturarak, uygulamaları deploy etmemiz bize şu avantajları sağlar;

* İş yükü hafifler ve iş çok basite indirgenir.&#x20;
* Her yeni eklenen node için tekrar, tekrar ayar yapmamıza gerek kalmayacak.&#x20;

Çünkü daemonset sistemde yeni bir node tespit ettiği zaman, o node içerisinde bir pod oluşturup, uygulamanın orada da çalışmasını otomatize eder.

DaemonSet tanımı Deployment Objesine çok benzer bunu yukarıda da belirtmiştik. DaemonSet oluştururken bir yaml dosyasında istediğimiz özellikleri gireriz. Burada Deployment 'dan önemli bir farkı Deployment objesinde bulunan rollout özelliklerinden faydalanamıyoruz.

```
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: logdaemonset
  labels:
    app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # this toleration is to have the daemonset runnable on master nodes
      # remove it if your masters can't run pods
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
```

Yukarıdaki daemonset template yaml dosyasını kubernetes API'na gönderdiğimiz zaman, daemonset objesi oluşturulacak ve daemonset template altında gördüğümüz özelliklerde birer podu nodelar üzerinde çalıştırmaya başlayacak.&#x20;

İlk olarak deployment objesindeki gibi, daemonset 'de label selector tanımı mevcuttur ve daemonset oluşturacağı podları bu selector'e göre seçer. Bu nedenle, aynı label tanımının spec kısmında da olması gerekmektedir.

Bizler kendi kurduğumuz clusterlarda yada cloud servis sağlayıcılarda master nodelarda pod çalıştırmayız.  Master nodelarda "node-role.kubernetes.io/master:NoSchedule" şeklinde bir taint eklidir. Dolayısıyla bunu tolere edecek bir tanım ekli değilse, pod master node üzerinde schedule edilmez.

Biz bir DaemonSet oluşturuyoruz, Eğer biz DaemonSet 'i sadece worker nodelarda pod oluşturmasını istiyorsak, o zaman bir şey eklememize gerek yok. Ama bizim DaemonSet'in, master nodelarda da pod oluşturmasına da ihtiyaç duyuyorsak, o zaman bu tolerations tanımını bu yaml dosyasına eklememiz gerekmektedir.

Yaml dosyasında da gördüğünüz üzere, bu daemonset [fluentD](https://www.fluentd.org/) isimli imajdan podlar oluşturacak. Bu uygulama kubernetes dünyasında iyi bilinen log toplama aracıdır.&#x20;

Yaml dosyamızdan DaemonSet objesi oluşturacağız, DaemonSet yukarıdaki tanımlara göre tek, tek podları oluşturacak. Tolerations tanımımız olduğu için, master node'larda dahil, sistemdeki tüm node'larda bu podlar oluşturulacak.&#x20;

<figure><img src="/files/dCcA8Xoq0yCbQJsVHvvm" alt=""><figcaption></figcaption></figure>

Fakat bazen biz, sadece bazı tip nodelarda pod oluşturan bir daemonset de oluşturmak isteyebiliriz. Örneğin, log uygulamamızın sadece production için oluşturulan node'larda çalışmasını, test podlarımızın koştuğu ve onlara ayrılmış node'larda çalışmamasını isteyebiliriz. Bu şekilde belirli node'larda kısıtlama yapmak istiyorsak, bu tanıma nodeselector veya node affinity yanında, taint ve tolerations tanımlarıda ekleyerek bunu sağlayabiliriz.

<mark style="background-color:orange;">Varsayılan olarak DaemonSet, her node üzerinde istediğimiz POD'u oluşturacaktır.</mark>


# PV/PVC

persistent volume and persistent volume claim

<figure><img src="/files/kXuIDnRi93AcfJQyxkey" alt=""><figcaption></figcaption></figure>

[Ephemeral volume](/ephemeral-volumes) bölümünde, podun yaşam süresi boyunca saklamak ve yeni container oluşturulduğunda kaybetmemek istediğimiz dosyaları tutmak için <mark style="background-color:orange;">"emptydir"</mark> ve <mark style="background-color:orange;">"hostpath"</mark> tipinde volumelerin nasıl oluşturulduğunu görmüştük.

Bu yazıda, podun yaşam süresinden bağımsız şekilde oluşturabileceğimiz volumeleri yani persistent volumeleri inceleyeceğiz.

Sorun nedir?

İlk olarak 3 node'lu bir cluster'mız olduğunu düşünelim. 3 node'lu cluster üzerinde mySQL bir veritabanı çalıştırmak istiyoruz. Bunun için tek replika oluşturacak deployment tasarladık. Deployment içerisinde de mysql container'ın veritabanı dosyalarını tutacağı "emptyDir" tipinde bir volume oluşturmak için bir tanım girdik. Bu volume'u container'a mount edecek tanımlarıda ekledikten sonra, Deployment objemizi oluşturduk. mySQL container'dan oluşan podumuz uygun bir worker node üzerinde oluşturuldu ve çalışmaya başladı. Bu nokta da eğer container'da bir sıkıntı olursa, kubelet yeni bir container oluşturacak ve biz volume tanımını da eklediğimiz için sıkıntı çıkmadan çalışmaya devam edecek.

Ancak şöyle bir senaryo düşünelim;

Bu podun çalıştığı worker node da sıkıntı çıktı ve worker node devre dışı kaldı. Podumuz deployment objesinin bir parçası olduğu için, Kubernetes bu durumu düzeltmek için çalışmaya başlayacak ve uygun olan worker node'lardan bir tanesi üzerinde yeni pod yaratılacak. Fakat bu durum ephemeral volume kısmında da belirttiğimiz uygulama için sıkıntı yaratmıyorken, bu sefer mySQL veritabanımız için problem oluşturuyor. Çünkü veritabanı üzerinde geçici, yeniden kolayca üretebileceğimiz dosyaları değil, uzun süre barındırmak zorunda olduğumuz verileri tutuyor.

Pod'a ne olursa olsun, bu verileri kaybetmememiz gerekiyor. Pod'u oluşturduk, mySQL container çalıştı ve verileri "emptyDir" tipindeki volume'e yazmaya başladı. Bu volume fiziksel olarak o container'ın çalıştığı worker node üzerinde duruyor. Artık o worker node'a erişim gittiği için, Dolayısıyla pod başka bir worker node üstünde yeniden oluşturulduğunda, bu dosyalara erişilemeyecek. Ve bu mySQL için fazlasıyla kötü bir senaryodur.

<mark style="background-color:green;">Bunun için tek bir çözüm var, bizim bu volume'leri cluster dışında duran, fakat tüm worker node'lar tarafından ulaşılabilen bir yerde oluşturabiliyor olmamız lazım.</mark>

Eğer bunu başarabilirsek, pod hangi node üzerine taşınırsa taşınsın, aynı volume tekrar bağlayarak veri devamlılığı sağlayabiliriz. Bu tarz cluster dışında tutulan çeşitli türde depolama ünitelerinin üzerinde, oluşturabildiğimiz ve pod yaşam süresinden bağımsız ve daha uzun süre saklamamız gereken verileri saklamamıza imkan veren volume'ler mevcut. Biz bunlara Persistent Volume diyoruz.

Persistent Volume nasıl oluşturuyoruz?

Bunun için öncelikle, bir kaç ayar yapmamız gerekiyor. İlk olarak volume oluşturmak istediğimiz depolama birimi ile cluster'mızın konuşabiliyor olması gerekiyor.&#x20;

Bunun içinde cluster üstünde, bu depolama biriminin volume driver'larının yüklenmesi gerekiyor. <mark style="background-color:green;">Kubernetes, NFS-ISCSI gibi unıversal protokollere ait driver'ların yanında, AzureDisk-AzureFile-AWS EBS-Google PD-CEPHFS gibi bir çok bulut servis sağlayıcının özel storage çözümlerine ait driver'ları bünyesinde barındırıyor.</mark>

Yani Kubernetes, varsayılan olarak bu tiplerdeki depolama çözümleri ile konuşabiliyor durumdadır. Fakat depolama çözümleri sadece bunlarla sınırlı değildir. Kubernetes bunların yanında farklı depolama çözümleri ile de konuşabilecek yetenekleri, [Container Storage Interface (CSI](/csi-plugin)[)](/csi-plugin) adlı bir arayüz ile sağlıyor.&#x20;

CSI, isteğe bağlı blok ve dosya depolama sistemlerini Kubernetes gibi Container Orchestration'lar üzerindeki Container iş yüklerine maruz bırakmak için bir standart olarak geliştirmiştir. CSI'ın benimsemesiyle kubernetes volume katmanı genişletilebilir hale geldi. [3. taraf depolama sağlayıcıları](https://kubernetes-csi.github.io/docs/drivers.html), CSI kullanarak çekirdek kubernetes koduna dokunmak zorunda kalmadan Kubernetes'de yeni depolama sistemlerini açığa çıkaran eklentiler yazabilme imkanına kavuştu.

CSI, Kubernetes'in storage altyapısının nasıl ayarlanması gerektiğini belirten bir standarttır. Depolama çözümü üretenler, bu standart'a uygun driver'lar yazarak Kubernetes'in kendi alt yapılarıyla da konuşabilmelerine imkan sağlayabiliyorlar.

Yani özetlersek, bağlanacağımız depolama birimi volume driver kısmında saydıklarımızdan ise, (NFS,Azure,EBS) vb. bunlarla ilgili driver'lar hali hazırda Kubernetes üzerinde mevcut. Ancak bunlardan biri değilse, misal NetAPP firmasının sağladığı depolama cihazını kullanıyorsam da, bu sefer o firmanın(NetAPP misal) bize sağlayacağı CSI driver'nın sisteme yüklememiz gerekecektir.

İlk adım bu şekilde. Öncelikle cluster ile depolama alt yapısını birbirine bağlıyoruz ve bu işi halledip, Kubernetes cluster ile depolama birimini konuşabilecek hale getirdikten sonra, bizler gidip bu depolama birimi üzerinde Kubernetes Cluster'mız altında kullanmak için, depolama birimleri yaratıyoruz. Sonrasında da bu depolama birimini karşılığını Kubernetes Cluster'mız altında, persistent volume isimli bir obje olarak oluşturuyoruz.

Örnekle, Kubernetes Cluster'mızın erişebildiği NFS tabanlı bir depolama çözümümüz olduğunu varsayalım. Kubernetes altında, NFS driver'ları bulunduğu için, ek bir driver yüklememize gerek kalmadan bu 2 ortam birbirleri ile görüşebilir durumda. Sırada ise, bu depolama ünitesi üzerinde, bizlerin erişip kullanabileceği, depolama birimleri oluşturmakta. Bizler gidip NFS cihazımıza bağlanıyor ve örneğin "tmp" adında bir paylaşım yaratıyoruz. Bu işi de hallettikten sonra, sıra geldi bunun Kubernetes tarafında karşılığını yaratmaya. İşte bu nokta da oluşturacağımız objenin adı "Persistent Volume"dir.

<figure><img src="/files/qE0ds2IJo0cPnKwn3ZiS" alt=""><figcaption></figcaption></figure>

Yukarıda gördüğünüz gibi apiVersion: 1 olan, tipi de Persistent Volume olacak şekilde bir obje yaratıyoruz. Spec kısmı bağlanacağımız depolama ünitesine bağlanırken, kullanılacak driver'a göre değişebilir.&#x20;

Burada driver'a göre değişmeyen 3 seçenek mevcut.

<mark style="background-color:orange;">Capacity :</mark> Bu parametre bize ne kadarlık bir volume yaratmak istediğimizi belirtiyor. Yukarıdaki örnekte 5GB boyutunda bir volume oluşturduğumuz belirtilmiş.

<mark style="background-color:orange;">AccessMode :</mark> Bu volume'ın aynı anda birden fazla, pod'a bağlandığı zaman, ne şekilde bir davranış sergileyeceğini seçiyoruz. Burada da seçenekler 3'e ayrılıyor.

* <mark style="background-color:red;">ReadWriteOnce :</mark>  Bu volume aynı anda sadece tek bir pod'a bağlanabilir ve bağlanan pod hem     yazabilir hem de okuyabilir.&#x20;
* <mark style="background-color:red;">ReadOnlyMany :</mark> Bu volume aynı anda birden fazla poda bağlanabilir. Fakat podlar sadece bu volume de daha önceden bu volume eklenmiş dosyalar mevcut ise onları okuyabilirler. Dosya yazamazlar ve oluşturamazlar.
* <mark style="background-color:red;">ReadWriteMany :</mark> Volume aynı anda birden fazla pod'a bağlanarak, hem yazabilir hem okuyabilir.

Burada kullandığımız seçenekler, depolama ünitemizin ve driver'ların yeteneklerine göre değişebilir.

Örneğin, Azure üzerinde AzureDisk kullanılırsa sadece "ReadWriteOnce" desteklenirken, AzureFile tipi bir storage ve driver kullanılırsa, bu sefer yukarıda bahsettiğim 3 seçeneğe de desteği mevcuttur.

<mark style="background-color:orange;">PersistentVolumeReclaimPolicy :</mark> Burada bizler pod tarafından işi bitip, kullanılmayı bıraktıktan sonra, bu volume'e nasıl davranacağını belirtiyoruz. Burada, <mark style="background-color:green;">"retain" - "recycle" - "delete"</mark> seçenekleri mevcuttur.

* <mark style="background-color:red;">Retain :</mark> seçeneği seçildiği durumda, bu persistent volume kullanıldıktan sonra olduğu gibi kalıyor ve bizler buradaki dosyaları manuel olarak kurtarıp, başka bir yere taşıma imkanına kavuşuyoruz.&#x20;
* <mark style="background-color:red;">Recycle :</mark> seçeneğini seçtiğimiz durumda, bu sefer volume ile işimiz bittiğinde volume silinmiyor, fakat içindeki tüm dosyalar siliniyor. Bizler tekrar boş bir volume elde ederek başka işlerimizde kullanabiliyoruz.
* <mark style="background-color:red;">Delete :</mark> seçeneğini seçtiğimiz durumda, volume ile işimiz bittiğinde volume tamamen siliniyor.

Yukarıdaki örneği ele alırsak, NFS tabanlı erişim sağlayan 172.17.0.2 IP adresi üstünden erişilebilen bir depolama ünitesi üstündeki "TMP" isimli paylaşımı kullanacak. 5GB boyutunda, sadece tek bir pod'a bağlanabilecek ve bu pod tarafından, hem dosya yazmak hem de okumak için kullanılabilen pod'un, işi bittiği zaman içerisindeki dosyaların silineceği app=mySQL labeline sahip bir persistent volume oluşturuyoruz.

Bizler bir volume'ü direkt olarak bir pod ile eşleştirme imkanına sahip değiliz. Biz bir persistent volume'ü bir pod'a bağlamak için öncelikle Persistent Volume Claim tipinde bir obje daha yaratmamız gerekmektedir.

Persistent volume claim, bizlerin sistemde bulunan persistent volume'ler arasında işimize uygun olan bir tanesini seçmemize yani bunu kullanmak adına talep etmemize imkan veren objelerdir.

<figure><img src="/files/2KOKoHn0UgIXyGGdhnCW" alt=""><figcaption></figcaption></figure>

Yukarıda gördüğünüz üzere, yine V1 api da bulunan, PersistentVolumeClaim tipinde bir obje yaratılıyor. Spec kısmı Persistent volume'e çok benziyor. Burada da, ne  kadarlık bir volume talep ettiğimizi, hangi mode ile bağlanmak istediğimizi belirtiyoruz. <mark style="background-color:red;">Son olarak "selector" kısmında, bu persistent volume claim ile persistent volume'ü label üstünden eşleştiriyoruz.</mark>

<mark style="background-color:orange;">Persistent volume claim :</mark> (volume talebi) app:mysql etiketine sahip, persistent volume tarafından sağlansın diyoruz.

Kubernetes neden depolama birimi oluşturma ve bunu talep etme işini 2 farklı obje üzerinden hallediyor?

Bunu şöyle düşünelim, bir developer olarak kullandığım kubernetes cluster'ı biz yönetmiyor olabiliriz. Bizim işimiz olmayabilir ve bu işe bakan ayrı bir ekip olabilir. Dolayısıyla oradaki işlemleri bizim bilmemiz mümkün olmayabilir.&#x20;

A firmasında NFS tabanlı bir depolama ünitesi kullanıyorken, B firmasında ISCSI tabanlı bir depolama ünitesi kullanılabilir. Bu ikisi için "Persistent volume" ayarları değişik olabilir. An önce persistent volume'den bahsederken de belirtmiştik. "Spec" altındaki ayarlar depolama ünitelerine göre değişebilir. Dolayısıyla bu volume oluşturma işini Developer ekibine verirsek, her cihaz için ayrı ayrı ayar yapmayı bilmesi gerekir. Bu da bir hayli zorlayıcı olacaktır.

Kubernetes bu sıkıntıyı volume oluşturma ve oluşturulan volume'ü talep etme işini ayırarak çözmüştür. Persistent volume'ler, kubernetes cluster'ı yöneten ve depolama ünitelerine de erişimi olan sistem yöneticileri veya cluster adminler tarafından oluşturulur.

Bu ekip, ortama uygun depolama ürünlerinde gerekli ayarlamaları yapar, kullanılabilecek volume'leri oluşturur, ardından developer ekibi, kendi podlarında kullanmak adına uygun özelliklerde "persistent volume claim" yaratır. Bu claim'i, ne çeşiit bir depolama çözümü ile karşıladığımız developer ekibinin bilmesine gerek olan bir bilgi değildir.

Developer ekibi talebi oluşturur, bu talep kimi yerlerde NFS depolama üniteleri ile, kimi yerlerde ISCSI ile çözüm sağlanır.&#x20;

Persistent Volume Claim oluşturulur, cluster da ki mevcut "persistent volume"lerden uygun olan bu talebi karşılar. Bu sayede depolama alt yapısının ayarlanması ve talep edilmesi 2 farklı ekip tarafından halledilebilir.

Ayrıca talep ve oluşturma adımları ayrıldığı için, tek bir talep yani "Persistent volume claim" tanımı bir çok cluster da kullanılabilir hale gelir ki, bu da uygulama taşınabilirliğini sağlar.

Elimizde artık persistent volume yani (pv) bir de persistent volume claim (pvc) mevcut. Gelelim bunu pod'a bağlamaya,

<figure><img src="/files/L6WF2MWOWVzsZDt7s5Ei" alt=""><figcaption></figcaption></figure>

Pod tanımında bizler kullanmak istediğimiz volume'ü talep eden, persistent volume claim ile bir bağ kurarız. Pod tanımlarında, "spec" parametresi altında yeni bir volume oluşturur ve bunun hedefini oluşturduğumuz "persistent volume claim" olarak belirleriz. Sonrasında da bunu pod altında gerekli path' e mount ederiz. Bu pod oluşturulduğu anda bu talep devreye girer ve bu talebi karşılayan Persistent volume" objesindeki tanıma göre, bu podun oluşturulacağı worker node üzerinde ayarlamalar yapılır.

Storage cihazına bağlanılır, arkada driver yapması gerekenleri yapar, sonucunda da bu pod, içerisindeki container'ın ilgili path' ine depolama cihazı üstünde duran, bu volume bağlanır.

Bu noktada itibaren, bu path' e yazılan dosyalar aslında bu volume'e yazılır. Eğer bu pod bulunduğu worker node üstünden bir şekilde silinir, başka bir worker node'a taşınırsa, bu sefer aynı işlemler, taşındığı worker node üzerinde gerçekleştirilir. Ve yeni pod, bu volume'e bağlanır. Bu sayede podun yaşam süresinden daha uzun süre boyunca veri saklayabileceğimiz, alt yapıya kavuşmuş oluruz.

```
apiVersion: v1
kind: PersistentVolume
metadata:
   name: mysqlpv
   labels:
     app: mysql
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /
    server: 10.255.255.10
```

{% hint style="info" %}
Persistent volume cluster'dan bağlanabileceğimiz, bir depolama ünitesindeki paylaşımlara veya diskleri kubernetes podlarına mount edebilmemize imkan sağlıyor.
{% endhint %}

pv.yaml : Yukarıdaki örnekte, mysqlpv adında bir persistent volume oluşturuyoruz. Burada bir de label atıyoruz. Label kısmı önemli, çünkü persistent volume claim bu volume'ü bu label'a göre seçecek. 5G alan talep ediyoruz. Volume tipi olarak ReadWriteOnce seçiyoruz. En altta NFS sunucu IP adresi ve paylaşımın path' ini belirtiyoruz. Buradaki ayarlar bağlanacağımız depolama ünitesine göre, kullanılacak driver'a göre değişir.

{% hint style="info" %}
Persistent volume talep eden, persistent volume claim oluşturmak gerekiyor. Böylelikle ilgili podlara bu volume'ü bağlayabiliriz.
{% endhint %}

```
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysqlclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
  storageClassName: ""
  selector:
    matchLabels:
      app: mysql
```

pvc.yaml : mysqlclaim adında bir persistent volume claim oluşturuyoruz. Burada belirttiğimiz access moduda persistent volume tanımı ile aynı olmak zorundadır. Storage kısmında talep edilen alan mevcuttur. "selector" kısmı ile "matchLabels" kullanılarak "app:mysql" labeline sahip persistent volume seçiyoruz. Hangi "Persistent Volume"ü talep ettiğimizi burada belirtiyoruz(Label-Selector).

```
apiVersion: v1
kind: Secret
metadata:
  name: mysqlsecret
type: Opaque
stringData:
  password: P@ssw0rd!
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysqldeployment
  labels:
    app: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql
          ports:
            - containerPort: 3306
          volumeMounts:
            - mountPath: "/var/lib/mysql"
              name: mysqlvolume
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysqlsecret
                  key: password
      volumes:
        - name: mysqlvolume
          persistentVolumeClaim:
            claimName: mysqlclaim
```

deploy.yaml : ( Pod tanımı ve volume' ü pod'a mount etmek ) "mysqlvolume" adında volume tanımlıyoruz. Bu volume'de "mysqlclaim" isimli "persistent volume claim"den sağlanmasını talep ediyoruz. Ardından, "volumeMounts" parametresi ile pod'un "/var/lib/mysql" pathine mount ediyoruz.


# Storage Class

<figure><img src="/files/0MxDARaAUXlx8HJ7Wipd" alt=""><figcaption></figcaption></figure>

Manuel olarak persistent volume ve bunu talep edecek podlarımıza bağlamakta kullandığımız persistent volume claim konularını inceledik. Fakat tahmin edeceğiniz üzere, persistent volume'lerin statik olarak yaratılması gerekliliği bizleri oldukça zorlayacak bir durumdur.&#x20;

Örnekleyelim;  10 ayrı ekip tarafından kullanılan büyük bir kubernetes cluster'mız olduğunu düşünelim. Onlarca farklı worker node, yüzlerce pod oluşturuluyor. Onlarca değişik uygulama, çeşitli ekipler tarafından yönetiliyor, siliniyor, güncelleniyor.&#x20;

Bizim böyle bir ortamda persistent volume ihtiyacımız olduğunda, her seferinde, önce storage cihazına gidip, paylaşım oluşturmamız, ardından onun karşılığı olan PV objesini oluşturmamız zahmetli olacaktır.

Bu zahmeti ortadan kaldırmak için Kubernetes biz PVC oluşturduğumuz anda depolama ünitesi ile haberleşerek, gerekli ayarları yaparak bize bir volume ve PV objesi oluşturan bir obje sunuyor. Bunun adı da <mark style="background-color:orange;">"Storage Class"</mark>dır.

Storage Class yöneticilerin, sundukları depolama "sınıflarını" tanımlamaları için bir yol sağlar. Farklı sınıflar, hizmet kalitesi düzeylerine veya yedekleme ilkelerine göre, cluster yöneticileri tarafından belirlenen isteğe bağlı ilkelere eşlenebilir. Kubernetes sınıflarının neyi temsil ettiği konusunda bilgi sahibi değildir. Bu kavram bazen de diğer depolama sistemlerinde <mark style="background-color:orange;">"profiller"</mark> olarak adlandırılır.

Örnek - Database için SSD yada Cold Data için Sata yada Data için External vb gibi. Bu kategorilerede istediğiniz gibi isim atayabilirsiniz; Gold1 - Gold2 - Gold3 vb .

Yani uzun lafın kısası, PV objesini manuel oluşturmak yerine, PVC objesine göre dinamik olarak oluşturma imkanı getirir. Bu özellikle Cloud Servis Sağlayıcılarının dinamik ortamlarında, her şeyi otomatize edebilirken, Sırf volume oluşturma işini manuel halletme saçmalığını ortadan kaldırır.

Storage Class objelerini bağlantımız olan depolama üniteleri üzerinde, ne çeşit bir volume yaratabileceğimizi belirleyen template'ler olarak düşünebiliriz.

Misal, üzerinde hem hızlı ssd, hem de yavaş mekanik disk olan storage ünitemiz olduğunu düşünelim. Bu durumda biz yavaş disklerin üstünde de otomatik volume yaratan, "yavas" isimli bir storage class ve hızlı diskler üstünde çalışan volume yaratan "hizli" isimli, storage class'lar oluşturabiliriz.

Developer ekipleri, uygulamalarında kullanmak için, volume talep etmek yerine PVC 'de uygulamalarının ihtiyaçlarına göre bu Storage Class'lardan birini seçerler ve istedikleri boyutta bir volume Storage Class objesi tarafından otomatik olarak oluşturulup, PVC'ye bind edilir.

<figure><img src="/files/NguJ7VRlE3BDRDslrHEc" alt=""><figcaption></figcaption></figure>

```
kubectl get sc 

# Sistemde bulunan storage class'ları listeler.
```

Yukarıdaki StorageClass özelliklerini inceleyelim,

<mark style="background-color:green;">Provisioner :</mark> Bizlere hangi tipte cihaz üstünde, volume oluşturmak istediğimizi belirtiyor.

<mark style="background-color:green;">Reclaim Policy :</mark> Bu disk kullanıldıktan sonra, işimiz bittikten sonra ne olacak.? Hatırlarsanız bu ayarı PV oluştururken giriyorduk. Şimdi Storage Class tanımlarında giriyoruz. Ek olarak burada <mark style="background-color:purple;">"recycle"</mark> özelliğini kullanamıyoruz. <mark style="background-color:orange;">"retain"</mark> ve <mark style="background-color:orange;">"delete"</mark> seçebiliyoruz.

<mark style="background-color:green;">volumebindingMode :</mark> <mark style="background-color:orange;">Immediate :</mark>  Bir PVC oluşturudulduğu anda, volume'un oluşturulup, hemen PVC'ye bind edilmesi gerektiğini söyler.

<mark style="background-color:green;">volumebindingMode :</mark> <mark style="background-color:orange;">WaitForfirstConsumer :</mark>  Pod oluşturulup, atanana kadar bu volume'un oluşturulmamasını söyler.

Bu 2 seçenek, altyapıda kullanılan storage özelliklerine göre değişiyor.

```
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standartdisk
parameters:
  cachingmode: ReadOnly
  kind: Managed
  storageaccounttype: StandartSSD_LRS
provisioner: kubernetes.io/azure-disk
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
```

Yukarıdaki örnekten yola çıkacak olursak, "standartdisk" isimli bir storageclass oluşturuyoruz. Geri kalan ayarlar kullandığımız depolama ünitesine göre değişiyor.&#x20;

AzureDisk üzerinde storage class oluşturmak istediğimiz için, provisioner olarak bunu seçiyoruz. Geri kalan ayarları AzureDisk oluşturacaksak AzureDisk dökümanından, farklı bir storage ünitesi için storage class oluşturacaksak, bunu ilgili cihazın/ürünün dökümanlarından bulup, burayı dökümana göre düzenleyebiliriz.&#x20;

Artık volume oluşturmak için PV oluşturmamıza gerek yok. PVC oluşturduğumuzda, ilgili diski hangi storage class'dan istiyorsak, otomatik/dinamik olarak bize sunuyor.

```
Version: v1
kind: PersistentVolumeClaim
metadata:
  name: mysqlclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
  storageClassName: "standartdisk"
```

Storage Class için PVC oluştururken, artık "selector" kullanarak, hangi PV olacağını ve kullanmak istediğimiz PV objesini seçmiyoruz. Bunun yerine yukarıda örnekte gördüğünüz üzere,  StorageClassName kısmında, kullanmak istediğimiz storage class'ı seçiyoruz(ismini giriyoruz). Bu örnekte yukarıda örneğini paylaştığım "standartdisk" storage class'ı kullanıyoruz.

Bu PVC şu manaya geliyor.

Git "standartdisk" storage class tanımındaki, özelliklere göre "5G" ve erişim modu "ReadWriteOnce" olan bir persistent volume oluştur.

PVC 'yi aynı mantık ile pod'a mount edebiliyoruz. PV-PVC bölümünde mount ettiğimiz gibi.

```
apiVersion: v1
kind: Secret
metadata:
  name: mysqlsecret
type: Opaque
stringData:
  password: P@ssw0rd!
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysqldeployment
  labels:
    app: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql
          ports:
            - containerPort: 3306
          volumeMounts:
            - mountPath: "/var/lib/mysql"
              name: mysqlvolume
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysqlsecret
                  key: password
      volumes:
        - name: mysqlvolume
          persistentVolumeClaim:
            claimName: mysqlclaim
```

Artık storageclass sayesinde, PV objesini önceden oluşturmamıza gerek kalmıyor. PV objesini nasıl oluşturacağımızı bilmemize gerek kalmıyor.

Yani biz, kullanacağımız disk ünitelerine göre storageclass tanımı yapıp, bunu PVC objesi ile çağırmamız yetiyor. PV objesi, PVC objesini tanımladıktan sonra otomatik olarak oluşacak.

{% hint style="info" %}
Çoğu Bulut servis sağlayıcının birden fazla özel storage class'ları bulunuyor. Eğer bir bulut servis sağlayıcıdan yönetilen Kubernetes hizmeti alıyorsanız, bunları kullanabiliyorsunuz.
{% endhint %}

Hangi storage class'ı kullanmak istiyorsak, bu kullanmak istediğimiz storage class'ı belirttiğimiz bir PVC objesi yaratıp, bu PVC objesini de bir pod'da ihtiyacımız olan pathe mount etmemiz yeterlidir.

PVC yaml dosyasında bulunan, "storageclassname" de belirttiğimiz storage class'a bakarak, bir PV oluşturulur. Yani storage class aslında kullanacağımız disk ünitesine göre değişen, PV objesini otomatik olarak oluşturulmasını sağlayan bir template'dir diyebiliriz.


# StatefulSet

<figure><img src="/files/Gwu5U1tOtEEWNtdZGaRj" alt=""><figcaption></figcaption></figure>

Kubernetes'in stateless iş yükleri için, uygun bir platform olduğundan, state tutan iş yükleri için ise bir çok problem ile karşılaştığımızdan önceki bölümlerde söz etmiştik.&#x20;

[Ephemeral ](/ephemeral-volumes)ve [Persistent volume](/pv-pvc) konularında ise, bu sorunları ortadan kaldırmak adına, kullanabileceğimiz objeleri gördük ve işin depolama kısmını çözdük. Ancak stateful uygulamalarda, tek sıkıntımız depolama değil. Bunu çözmüş olsak da, şu ana kadar işlediğimiz obje tipleri diğer sorunlarımızı çözmüyor.&#x20;

İşte bu durum ile başa çıkmamıza yardımcı olacak obje tipi olan, statefulSet objesinden bahsedeceğiz.

Sorun nedir?

İlk olarak kendimize sorun oluşturalım; 3 pod oluşturacak, bir deployment objesi oluşturduğumuzu düşünelim. Bu deployment objesi, bildiğiniz üzere bir replicaset oluşturuyor ve bu replicaset 'de, bizim belirlediğimiz tanıma göre random isimler verilen, birbirinin aynısı podlar oluşturuyor.

Bu replicaset altında oluşturulan podların, birbirinden farkı yok. Ve bu nedenle kubernetes bunlara eşit davranıyor. Biz bu deployment objesini, yukarı doğru scale ettiğimiz zaman, istediğimiz sayıda yeni pod ekleniyor ve aşağı doğru scale edersek de, bu sefer bu podlardan istediğimiz sayıda pod <mark style="background-color:red;">**rastgele**</mark> olarak siliniyor.&#x20;

Yani 5 podlu deployment objesini, 3 pod'a indirmek istersek, kubernetes ilk hangisi yaratılmış, hangisi diğerinden önce yaratılmış vb şeylere bakmıyor ve rastgele olarak 2 podu siliyor. Podlar üzerinde herhangi bir state tutulmadığı için bu durum sıkıntı yaratmıyor. &#x20;

Fakat şöyle bir senaryo da,

Örneğin, yeni nesil noSQL veritabanlarından birini kubernetes üzerinde deploy etmek istiyoruz. Bu veritabanları hemen, hemen hepsi şu şekilde çalışır. Bir adet master instance bulunur ve bunun üzerinden bir cluster oluşturulur. Ardından bu cluster'a yeni instance'lar ekleriz. Yazma işlemlerini master üzerinden yaparken, sorguları herhangi bir instance'a gönderebilirsiniz.

Tüm veri bu istancelar üstünde dağıtık şekilde durur.&#x20;

Böyle bir veritabanı alanı olan, apache cassandra 'yı 3 instance'dan oluşacak, bir cluster olacak şekilde şu ana kadar öğrendiğimiz obje tipleri ile kubernetes'e deploy ettiğimizi düşünün,&#x20;

Bunun için nasıl bir yol izleyeceğiz?

Cassandra master olacak instance'ı deploy edeceğiz, ardından buna bağlanarak yada başlangıç komutları ile cassandra uygulamasını master hale getirecek şekilde, bir cluster oluşturacağız. Sonrasında 2. instance oluşturacağız ve bu 2. instance bağlanıp, 1. instance'daki cassandra cluster'a  2. instance' ı dahil edeceğiz. Ardından 3. instance oluşturup, aynı işlemleri yapacağız. Böylelikle 3. instance ile bir cassandra cluster'mız olacak.

Kubernetes'de nasıl ayağa kaldıracağız?

Bu adımları takip ederek, Kubernetes de nasıl bu uygulamayı ayağa kaldıracağımıza bakalım,

İlk olarak singleton podlar şeklinde deploy etmeyi deneyelim, Master olacak instance oluşturacak pod tanımını yaptık içine PVC tanımı ekledik ki, cassandra verileri tutabilecek, bir  PV'ye sahip olabilsin. Pod ayağa kalktı, bunu master hale getirecek komutları, ya başlangıç komutu olarak yada sonrasında bağlanıp hallettik. Ardından 2. pod tanımı daha yaptık, bunu da, 2.instance olarak ayağa kaldırdık. Ardıdan 3.pod tanımını yapıp, ayağa kaldırdık. Ve böylelikle gerekli ayarları yaptıktan sonra, cluster ayağa kalktı.&#x20;

Ancak burada bir çok sorun var,&#x20;

* İşlemler manuel ve zahmetli.&#x20;
* Singleton podların fail durumunu kontrol eden bir mekanizma yok.
* Yeni bir pod eklemek veya çıkarmak istersem, tüm süreci yönetiyorum.

SingletonPod işimize yaramadı, Peki deployment kullansak?

3 adet cassandra podu oluşturacak, deployment objesi oluşturduk ve deploy ettik. Bu deployment objesi 3 adet birbirinin aynısı pod oluşturdu ve hepsine rastgele bir isim verdi.  Problem şu ki, hepsi aynı anda oluşturuldu. Ben ilk oluşanı master olarak ayarlamak istesem de, ayarlayamam. Çünkü hepsi, bu sorun dersek, bu işlemleri manuel olarak yapalım. Bağlanıp, birini master'a çevirelim, Diğerlerine de tek, tek bağlanıp cluster'a kattık. Aradan zaman geçti, worker nodelardan birinde sıkıntı oldu ve cassandra podlarından birine erişilemiyor.&#x20;

Deployment objemiz tarafından yönetildiği için, sıkıntı değil, hemen yeni bir pod oluşturulur. Ama bu yeni pod tamamen rastgele bir isim alacak ve sıfırdan oluşacak. Misal bu bahsettiğimiz pod Master instance olarak ayarladığımız Pod ise ne yapacağız? Cluster bu durumda çökmüş olacak.

Yada şunu düşünün, scale down veya up işlemi yaptığımızda, kubernetes buraya rastgele bir pod ekleyecek veya rastgele olarak içlerinden birini silecek. Bunların hepsi stateless uygulamalar için problem değil, fakat gördüğünüz üzere cluster uygulamaları vb stateful uygulamalardan oluşan clusterlar kurmak istersek, mevcut kubernetes objeleri sıkıntımızı çözmüyor.

<figure><img src="/files/M8UnTNNlvAWFIAujgTuP" alt=""><figcaption></figcaption></figure>

Tüm bu sıkıntıları çözmek adına, statefulset adında bir obje mevcuttur.

* Statefulset tarafından oluşturulan her pod, statefulset tanımında belirlediğimiz PVC'ye göre bir PV'ye sahip olur. Yani her podun kendine ait bir PV'si olur.
* Statefulset altında, podlar sırayla oluşturulup, sırayla silinir. Misal 3 podlu bir stateful oluşturduğumuz zaman, öncelikle pod0 oluşturulur. Ve bu pod ayağa kalkıp, readiness ve liveness checklerinden geçip, işlemlerini tamamlamadan bir sonraki pod oluşturulmaz. Ne zaman pod hazır ve running duruma geçer, o zaman bir sonraki pod oluşturulur.  Aynı şekilde 3.podda 2.pod tamamlanmadan oluşturulmaz. Tam tersi durumda da bu geçerlidir. Biz 3 podlu bir statefulseti 2 poda indirirseniz, kubernetes random bir pod seçip, onu silme yoluna gitmez.  En son yaratılan pod hangisi ise, ilk olarak o silinir.&#x20;
* Statefulset tarafından oluşturulan her pod'a statefulsetismi-0 ,1 ,2 ,3 şeklinde devam eden, sabit bir isim verilir. İsimler random seçilmez ve bu isimler aynı zamanda containerların hostname olarak da ayarlandığı için, her uygulama bu isimle ulaşabilir.&#x20;

Statefulset objesi, deployment objesine oldukça benzerdir. Yukarıda bahsettiğim özellikler haricinde aynıdır. Bu özellikler sayesinde, bizlerin stateful uygulamaları deploy etmemiz oldukça kolaylaşır.

Az önceki cassandra örneğini statefulset ile ayağa kaldıracak olursak,  3 pod oluşturacak bir statefulset objesi yaratıp, deploy ettik. Kubernetes hemen ilk podu ayağa kaldırdı. Biz bu poda başlangıç scripti eklemiştik. Bu script ortamda bizim belirlediğimiz isimde cluster var mı? yok mu? bunu kontrol ediyor. Ve yoksa bu isimle bir cluster oluşturuyor. Script çalıştı, ortamda bu isimle cluster olmadığı için, cassandra cluster'ı oluşturuldu. Liveness ve readiness probe tamamlandı ve artık hem cluster, hem de podumuz hazır.

Birinci pod sağlıklı şekilde çalışmaya başladığı anda Kubernetes 2.podu da oluşturmaya başladı, Bu podda da, aynı script var, ama bu sefer ortamda böyle bir cluster mevcut, dolayısıyla yeni bir cluster oluşturmak yerine, bu mevcut cluster'a dahil oldu. Liveness ve readiness probe tamamlandı ve bu pod hazır.&#x20;

Bu pod'da hazır olduğu için 3.pod da oluşturulmaya başlandı, onda da aynı işlemler uygulandı ve cluster ayağa kalktı.&#x20;

Şimdi bu podlardan, cassandra-1 podu silinirse, statefulset objesi, aynı podu, aynı isimle tekrar ayağa kaldırıp, aynı PV bu poda atanacak. ve sorun olmayacak.&#x20;

<mark style="background-color:blue;">Yada yeni pod eklersek, yeni identy olacak ve aynı şekilde cluster'a katılacak. Scale down olursa da, herhangi birini seçip, silmek yerine en son oluşturulan silinecek.</mark> Gördüğünüz gibi, cassandra cluster oluşturma problemini bu özellikleri sayesinde "statefulset" ile çözebildik.&#x20;

Örnek statefulset objesi yaml içeriği,

```
apiVersion: v1
kind: Service
metadata:
  labels:
    app: cassandra
  name: cassandra
spec:
  clusterIP: None
  ports:
  - port: 9042
  selector:
    app: cassandra
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: cassandra
  labels:
    app: cassandra
spec:
  serviceName: cassandra
  replicas: 3
  selector:
    matchLabels:
      app: cassandra
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      terminationGracePeriodSeconds: 1800
      containers:
      - name: cassandra
        image: gcr.io/google-samples/cassandra:v13
        imagePullPolicy: Always
        ports:
        - containerPort: 7000
          name: intra-node
        - containerPort: 7001
          name: tls-intra-node
        - containerPort: 7199
          name: jmx
        - containerPort: 9042
          name: cql
        resources:
          limits:
            cpu: "500m"
            memory: 1Gi
          requests:
            cpu: "500m"
            memory: 1Gi
        securityContext:
          capabilities:
            add:
              - IPC_LOCK
        lifecycle:
          preStop:
            exec:
              command: 
              - /bin/sh
              - -c
              - nodetool drain
        env:
          - name: MAX_HEAP_SIZE
            value: 512M
          - name: HEAP_NEWSIZE
            value: 100M
          - name: CASSANDRA_SEEDS
            value: "cassandra-0.cassandra.default.svc.cluster.local"
          - name: CASSANDRA_CLUSTER_NAME
            value: "K8Demo"
          - name: CASSANDRA_DC
            value: "DC1-K8Demo"
          - name: CASSANDRA_RACK
            value: "Rack1-K8Demo"
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -c
            - /ready-probe.sh
          initialDelaySeconds: 15
          timeoutSeconds: 5
        volumeMounts:
        - name: cassandra-data
          mountPath: /cassandra_data
  volumeClaimTemplates:
  - metadata:
      name: cassandra-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: standard
      resources:
        requests:
          storage: 1Gi
```

Replicas sayımızı belirtiyor ve label selector'a dikkat ediyoruz. Ardından template kısmında ise, oluşturulmasını istediğimiz pod tanımını giriyoruz. template içeriği kullandığımız image'a göre değişir.

Template kısmı haricinde, en altta "VolumeClaimTemplates" kısmı mevcut. Burada her bir pod için, bu özelliklerde bir PVC oluşturulmasını söylüyoruz.  Bu PVClerde standart isimli storage class'ı kullanarak, her bir pod için birer PV oluşturacak.&#x20;

Yani her podun, aynı PV'ye bağlanması yerine, her pod'a ayrı bir PV oluşturulacak. PVCleri VolumeClaimTemplates ile yaratıyoruz. PVCleri dahi kendimiz manuel olarak oluşturmuyoruz. statefulset objesini oluşturduğumuz zaman, PVC oluşturulacak, PVClerde gidecek PVleri oluşturup, podlara tek, tek bağlayacak.

En üst kısımda "service" tanımı mevcut, gördüğünüz gibi şu ana kadar oluşturduğumuz service tanımına oldukça benzer. Fakat tek fark mevcut, clusterIP=none olarak set edilmiş. Buna headless services diyoruz. Bunu oluşturduğumuz anda, ClusterIP tipinde, bir servis oluşturulacak, Fakat ana bir IP adresi atanmayacak. Bu bize şu konuda yardımcı oluyor; Ben ne zaman, bu servis ismine gitmek istersem, o bana bu servis altındaki podlardan bir tanesinin IP adresini dönecek. Bunun yanında her bir podda da, podismi.serviceismi şeklinde erişme imkanı sağlamış olacak.

<figure><img src="/files/JxrcKuLnwOnSjfXWhSWS" alt=""><figcaption></figcaption></figure>


# Job & Cronjob

Şu ana kadar gördüğümüz tüm kubernetes objelerinin tamamı müdahale edilip, kapatılmadığı sürece çalışmaya devam etmesi gereken uygulamalar ile ilgiliydi. Fakat bazı iş yükleri bu şekilde uzun süre çalışması gereken uygulamalar değildirler. Çalıştırılıp, işini yapıp, kapanması gereken bir çok uygulamalar mevcuttur.&#x20;

Eğer bu uygulamaları, diğer kubernetes objeleri (pod,deployment vb) ile oluşturursak, bunlar her kapandığında yeniden başlatılacak. Misal bir veritabanı uygulamamız var, biz buna bağlanıp "demo" tablosu adında tablo yaratan başka bir uygulama oluşturduk. Bu "demo" tablosunu yaratan uygulamayı gün içerisinde, ihtiyacamız olduğu anda çalıştırarak, bu tabloyu oluşturmak istiyoruz. Bu tablonun oluşup, verilerin set edilmesi yaklaşık 10 dakika sürdüğünü düşünelim. Biz bu uygulamayı container haline getirip, single pod olarak deploy etsek, uygulama çalışıp, işini halledip düzgün bir şekilde kapanırsa sıkıntı olmayacak. Fakat uygulama çakılırsa, single pod bunu tespit edip, yeniden başlamayacak. Bunun yanında, tek pod değilde aynı anda paralel olarak 3-5 pod birden çalıştırmak istesem, bu seferde single pod işimize yaramayacak. Mecburen Deployment objesi oluşturacağız, fakat bu seferde sıkıntı şu ki, benim bu uygulamamın, tek sefer çalışıp, işini düzgün yaparsa kapanması gerekiyor. Fakat deployment container kapandığında yeniden başlatacak. İşte tüm bu sıkıntıları gidermek adına kubernetes job objesi mevcut.

Bir job objesi, bir veya daha fazla pod oluşturur ve belirli bir sayıda, pod başarıyla sonlanana kadar pod yürütmeyi yeniden denemeye devam eder. Job belirtilen sayıda podun başarıyla tamamlanma durumunu izler, belirtilen sayıda podun başarıyla tamamlandığında job tamamlanır. bir job'un silinmesi oluşturduğu podları temizleyecektir.

Pod içersinde uygulama, başarıyla tamamlanıp, kapatılana kadar bekler. Eğer container fail ederse yeniden oluşturulur. job'un bir özelliğide, iş bittiğinde podları silmemesidir. Bu sayede podlar tarafından oluşturulan loglar kontrol edilebilir.

Job temelde 2 senaryo için kullanırız; ilk senaryo yukarıda da bahsettiğimiz senaryodur. Tek seferlik çalışıp, yapması gerekeni yaparak kapanan uygulamalarımızı job olarak deploy ederiz. Misal scriptler vb.

2\. senaryo ise, bir kuyruk veya işlenmesi gerken bir çok işimiz olduğunda, bunları eritmek adına, bu işler eriyene kadar çalışacak uygulamaları job şeklinde deploy ederiz.

Örnek job yaml dosyası içeriği;

```
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  parallelism: 2
  completions: 10
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34
        command: ["perl",  "-Mbignum=bpi", "-wle", "'print bpi(2000)'"]
      restartPolicy: Never
```

<mark style="background-color:orange;">apiVersion:</mark> batch/v1 olan, Kind:job olan ve ismi de, pi olan bir job oluşturmamıza yarayan yaml dosyası içeriği yukarıdaki gibidir.

Spec kısmında 4 seçenek mevcuttur; Bunlardan ilk ikisi parallelism ve completions buradaki gibi aksini belirtmediğimiz sürece varsayılan değeri "1"dir.&#x20;

completions bu job altında, kaç tane başarılı pod oluşturmasını istediğimizi belirttiğimiz kısımdır. Bu örnekte 10 pod çalıştırmak istediğimizi söylüyoruz.

parallelism ise, adından anlaşılacağı üzere, bu 10 pod oluşturma işini, aynı anda kaçar kaçar yapacağını belirttiğimiz kısımdır.

Biz bu job objesini oluşturduğumuz zaman, "template" parametresi altındaki ayarlara göre "2" adet pod oluşturulacak ve bu podlar çalışacak container oluşacak. Container içerisinde uygulama çalışacak. Ve uygulama işini yapıp kapanacak. Eğer işlem hata almadan, bu şekilde olduysa, Kubernetes hemen "2" pod daha oluşturacak ve aynı şekilde onlarda çalışacak ve kapanacak ki 10 pod bu şekilde tamamlanana kadar devam edecek.&#x20;

backOfflimits değerinin  5 olarak ayarlandığını görüyorsunuz. Bu değer, toplam kaç kere hata oluşursa, tüm işlemleri bırakarak, job'un fail etmesini istediğimizi belirttiğimiz kısımdır. Bu, şu demektir.  podları oluşturmaya başla, düzgün bir şekilde devam ederse sıkıntı yok. Ancak podlar fail ederse, 5 defa daha denemeye devam et, toplamda 5 fail alırsan da, artık job'a devam etme, çünkü belli ki bir şeyler yolunda gitmiyor ve job'u fail et.&#x20;

activeDeadlineSeconds değeri ise aynı mantık fakat, bu sefer fail sayısı yerine, saniye belirtiyoruz. Eğer bu görev 100 saniye içerisinde tamamlanamazsa, anlaki sıkıntı var ve job'u fail et diyoruz.

"template" altında belirttiğimiz değerler ise, bildiğimiz pod oluştururken kullandığımız argümanlardır.

Şu anda bu job ile, pi sayısının ilk "2000" rakamını listeleyen ve ardından da kapanan podlar yaratıyoruz. Bu örnekte toplam 10 pod oluşturulacak ve her pod pi sayısının ilk 2000 rakamını çıkaran bir uygulama çalıştıracak. Uygulama düzgün bir şekilde bu işlemi yaparsa, uygulama kapanacak, dolayısıyla pod da düzgün şekilde tamamlandı olarak işaretlenecek.

Son olarak da, restart policy parametresinin mantıken "never" veya "on failure" olarak set edilmesi gerekmektedir.

Job oluşturulduktan sonra, podlar template parametresi altındaki belirttiğimiz görevleri yapıp, tamamlandı olarak işaretleniyor. Podlar işini bitirince silinmez. Böylelikle podların oluşturduğu logları gözlemleyebiliriz.

### Cronjob

Bir cronjob objesi, bir crontab dosyasının bir satırı gibidir. Belirli bir zamanlamaya göre, cron talimatında yazılmış bir job'u periyodik olarak çalıştırır.

Bir job objesini manuel olarak başlatmak yerine, örneğin istediğim zamanlarda tekrarlayan, istediğim günlerde çalışacak şekilde schedule etmek istersek, cronjob kullanabiliriz. Cronjob objesi, Linux crontab 'ın Kubernetes halidir. Syntax olarak da aynıdır.&#x20;

Kubernetes Job'dan farklı olarak, schedule parametresi mevcuttur. Bu parametreye göre job'un ne zamanlar çalışmasını istediğimizi belirtebiliyoruz. Misal aşağıdaki örnekte, Her dakika da bir çalışacak ve bir pod oluşturacak.

```
apiVersion: batch/v1beta1 # not stable until kubernetes 1.21.
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
```


# Authentication

<figure><img src="/files/TrqT6JWeXqXSWVytpu7y" alt=""><figcaption></figcaption></figure>

Kubernetes kimlik doğrulama eklentileri arayıcılığıyla API isteklerinin kimliğini doğrulamak için, istemci sertifikaları, tarayıcı belirteçleri bir kimlik doğrulama proxy veya HTTP temel kimlik doğrulaması için kullanır.

API 'da bir kullanıcı objesi bulunmamasına rağmen, Cluster 'ın Certificate authority tarafından imzalanmış geçerli bir sertifika sunan herhangi bir kullanıcı (CA) kimliği doğrulanmış olarak kabul edilir. Bu yapılandırmada kubernetes sertifikanın konu kısmındaki ortak ad alanından, kullanıcı adını belirler. Misal, "/CN=bob"&#x20;

Oradan rol tabanlı erişim denetimi (RBAC) alt sistemi, kullanıcının bir kaynak üzerinde, belirli bir işlemi gerçekleştirme yetkisine sahip olup, olmadığını belirler.

Çoğu Enterprise uygulama 'da kullanıcı yönetiminin, uygulama içerisinden yönetildiğine şahit olmuşsunuzdur. Hemen hemen her platformun ayrı bir kullanıcı oluşturma ve yönetme bölümü bulunur. Sistem yönetimi ile uğraşan kişiler de, buradan yeni kullanıcılar oluşturabilir ve her yeni kullanıcı birer obje şeklinde oluşturulur.

Fakat bu iş, Kubernetes de böyle değildir. Kubernetes normal kullanıcı hesaplarının yaratıp, yönetebileceğimiz bir altyapı sunmaz. Yani Kubernetes üstünde gidip, ali,ayse,ahmet vb.. şeklinde kullanıcılar yaratamayız.

Pod gibi yada servis gibi oluşturabileceğimiz bir user objesi de bulunmaz. Kubernetes de kullanıcı oluşturma ve kimlik doğrulama işi, cluster dışında halledilecek şekilde tasarlanmıştır.&#x20;

Kubernetes X509 client certs-Static token file-OpenID connect tokens-webhook token- authentication proxy gibi altyapılardan birini yada birkaçını kullanarak, bu işin dışarıda halledilmesine imkan sağlar.&#x20;

Kubernetes kurulumunda, kube api server ayarlarında, bu altyapılardan hangilerini kullanacağımızı belirleriz ve bu kimlik doğrulama işini, bu altyapılara havale ederiz. Bunun yanında, her kubernetes cluster 'ında bir kök sertifika yetkilisi, yani Root Certificate authority altyapısı da bulunur.&#x20;

Bu Root Certificate authority tarafından imzalanan x509 client sertifika authentication için kullanılır. Kubernetes bu ve diğer altyapılar tarafından kimliği doğrulanmış kullanıcıların kubernetes ile görüşmesine imkan verir.

Fakat iş burada elbette bitmez, kimliğin doğrulanması bir kullanıcının cluster üstünde, her şeyi yapabilmesi-yapabileceği anlamına gelmez. Burada yetkilendirme yani, authorization kavramı devreye girer. İlerleyen yazılarda bahsedeceğiz.&#x20;

Az önce belirttiğim gibi, biz bu kimlik doğrulama altyapısının ne olacağını kubernetes kurulumunda belirlememiz gerekir. Fakat manuel bir kubernetes kurmak yerine, kubernetes dağıtımları yada Cloud servis sağlayıcıların sunduğu yönetilen kubernetes servisleri kullanıyorsanız, bu işlemler bizim adımıza otomatik olarak yapılır.

Örneğin, minikube varsayılan olarak, x509 client sertifikaları ile kimlik doğrulama yapacak şekilde ayarlanır. Biz minikube start dediğimiz zaman, admin yetkisine sahip bir kullanıcı olan minikube kullanıcısı için, sertifikalar hazırlanır ve kubectl config dosyamızın içerisine bu bilgiler girilir. Biz de kubectl command line ile kubernetes cluster 'mızda erişmek istediğimiz zaman, her seferinde bu sertifikalar ile kimliğimizi doğrulanmış olarak istek göndeririz.

Yada Azure üzerinde EKS kurduğumuz zaman, kurulumda seçenekleri belirleyerek, hem x509 client sertifikaları hem de, Azure active directory üzerinden authenticated olma imkanı sunuyor. Uzun lafın kısası, kubernetes kullanıcı oluşturma ve kullanıcı doğrulama işlemlerini, kendi üzerinde barındırmaz. Bunu dışarıda halledebileceğimiz seçenekler olarak sunar. Kendi üzerinde barındırmazdan kasıt şudur; biz gidip bir pod oluşturur gibi, bir servis objesi oluşturur gibi, veya herhangi bir obje oluşturur gibi kullanıcı oluşturamayız.

#### x509 Client sertifika oluşturma örneği;

Yeni bir firmada developer olarak işe başladık ve firmanın kubernetes clusterına bağlanmak istiyoruz. İlk olarak developer rolünü oynuyoruz. Ben ekibe yeni katılmış bir developerım ve Kubernetes cluster'a bağlanarak işlemler yapmak istiyorum.&#x20;

Kubernetes cluster'ı yöneten admine gittim ve bu isteğimi söyledim. O da bana, Kubernetes cluster'ın,  X509 Client sertifikları ile authenticated olacak şekilde çalıştığını söyledi. Ve eğer ben, bir private key ve CSR dosyası oluşturup, admine iletirsem, o Kubernetes Certificate authority kullanarak, bunu imzalayacak ve bana sertifika yaratabileceğini söyledi.

```
#Key ve CSR 'ı saklayacağımız dizini oluşturup, içerisine gidiyoruz.
mkdir auth-certs && cd auth-certs/

#KEY
openssl genrsa -out onrblt.key 2048
# Yukarıda oluşturduğumuz anahtarı kullanarak CSR oluşturmamız gerekiyor.
# CSR bizlerin Certificate authority(sertifika sağlayıcı)ya gönderip, bak bana bu özelliklerde bir dijital sertifika oluştur dememize imkan veren dosyalardır.
# Bu dosyayı oluşturup, ardından bunu kubernetes admine göndereceğiz, o da Kubernetes 'in Certificate authority ile bunu onaylayıp, imzalayarak, bizim sertifikamızı oluşturaracak.
# Ve bize bir sertifika gönderecek işte bizde bu sertifika ile cluster'a bağlanacağız.

#CSR
openssl req -new -key onrblt.key -out onrblt.csr -subj "/CN=onur@onurbolatoglu.com/O=DevTeam" 
# "-subj" opsiyonu ile bizler birnevi sertifika bilgilerini giriyoruz.
# Kubernetes de bu kısım önemli. Çünkü burada CN ile belirlediğimiz değer, kullanıcı adımız olacak. 
# "O" ile belirlediğimiz değer, bu kullanıcının hangi gruplara üye olacağını belirler.
```

-subj : Daha önce söylediğimiz üzere, kubernetes kendi içerisinde bir kullanıcı objesi tutmaz, sizin hangi kullanıcı olduğunuzu x509 sertifikalarında, bu alanlardan algılar. Bu nedenle bizler hangi kullanıcı adına sahip olmak istiyorsak ve bu kullanıcıyı eklemek istediğimiz gruplar varsa, bunları CSR oluşturma aşamasında " -subj " parametresinde belirtiyoruz.

Yukarıdaki komutlarla hem CSR hem de key dosyasını oluşturduk ve artık developer olarak işimiz bitti. Bu oluşturduğumuz CSR dosyasını kubernetes admine göndereceğiz ve sonraki işleri Kubernetes admin halledecek.

#### Kubernetes Admin kısmına geçersek;

Temel olarak, developer'ın ilettiği CSR 'ı kullanarak bir sertifika üreteceğiz. CSR'ı kopyaladığımız dizine geçiyoruz, Bu noktada bir kubernetes objesi yaratacağız. " CertificateSigningRequest " adında bir obje oluşturacağız. Bu obje ile, Developer'ın bize verdiği CSR'ı kubernetes'e göndereceğiz ve kubernetes de bize sertifika oluşturacak.

```
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: onrblt
spec:
  groups:
  - system:authenticated
  request: $(cat onrblt.csr | base64 | tr -d "\n")
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
EOF
```

```
kubectl get csr 
#Yukarıdaki komut ile oluşturduğumuz CSR'ı görüntüleyebiliriz.

kubectl certificate approve onrblt
#Yukarıdaki komut ile görüntülediğimiz CSR'ı kabul edelim ve sertifikayı oluşturalım.

kubectl get csr onrblt -o jsonpath='{.status.certificate}' | base64 -d >> onrblt.crt
#Yukarıdaki komut ile, oluşturduğumuz sertifikayı decode ederek "onrblt.crt" adındaki dosyaya yazdıralım ve developer arkadaşa bu dosyayı verebiliriz.
 
```

Biz bu sertifikayı kullanarak kubectl config'de context oluşturmalıyız.&#x20;

```
kubectl config set-credentials onur@onurbolatoglu.com --client-certificate=onrblt.crt --client-key=onrblt.key
#Yukarıdaki komut ile bir kubectl'de bir user ve sertifikalarını tanımlıyoruz.

kubectl config set-context onrblt-context --cluster=kubernetes --user=onur@onurbolatoglu.com
#Yukarıdaki komut ile bir kubectl context'i oluşturuyoruz. (Kullanıcı bilgilerini ve cluster bilgisini vererek)

kubectl config use-context onrblt-context
#Yukarıdaki komut ile mevcut context'i, az önceki oluşturduğumuz context ile değiştiriyoruz.
```

Böylelikle cluster'a yeni context (yeni kullanıcı) bilgileriyle bağlanabiliriz. Fakat bu bilgilerle uzak bir cluster'a bağlanacaksak,

```
kubectl config set-cluster kubernetes --server=https://10.90.0.170:6443  
#Yukarıdaki komut ile yeni bir cluster tanımlayıp, kubernetes ismini veriyoruz.

kubectl config set-cluster kubernetes --insecure-skip-tls-verify  
#Yukarıdaki komut ile olası TLS hatalarını görmezden gelmesini istiyoruz.

# Ardından onur@onurbolatoglu.com kullanıcısı ile, 10.90.0.170 üzerinde bulunan 
# kubernetes cluster'ımıza erişebiliriz. Yani artık çalıştırdığımız tüm komutlar 
# onur@onurbolatoglu.com kullanıcısı ile kubernetes takma isimli cluster'da
# çalıştırılmış olacaktır.
```

{% hint style="info" %}
Yeni kullanıcı ile cluster'a bağlandığımızda hiç bir işlem yapamayız. Bunun için yetkilendirme yapılması gerekir. (RBAC)
{% endhint %}

Şu ana kadar yaptığımız işlemler, bir kullanıcının kimlik doğrulaması yapılarak kubernetes 'e bağlanmasını sağlamak ile alakalıydı. Kimliği desteklenen ve ayarlanan yöntemlerle doğrulanan her kullanıcı kubernetes cluster'a erişebilir. Fakat her kullanıcı varsayılan olarak sıfır yetkiyle gelir. Listeleme dahil hiçbir işlem yapamaz.  Kullanıcı authenticated oldu fakat, authorization 'ı kısıtlı. Bir sonraki yazıda buna değineceğiz.

<figure><img src="/files/S4007RVIt54mtsNTvCIf" alt=""><figcaption></figcaption></figure>


# Role-based access control (RBAC)

<figure><img src="/files/hcsNuuddfomihJxXojd3" alt=""><figcaption></figcaption></figure>

Bir önceki bölümde, Kubernetes authentication altyapısının nasıl olduğunu ve x509 client sertifikaları oluşturup, kimlik doğrulamanın nasıl olacağını gördük. En son Developer'a sertifika oluşturup ilettikten sonra kubernetes ile konuşturmayı başarmıştık. Fakat Developer herhangi bir işlem yapamamıştı. Çünkü kimlik doğrulama, tek başına yeterli değil. Kubernetes de kullanıcılar varsayılan olarak sıfır yetki ile gelir. Yapabilecekleri işlemler için yetkilendirme gereklidir.

Authentication : Söylediğimiz kişinin doğruluğunun onaylanması ve doğrulanması işlemidir. Fakat kimliğin doğrulanmış olması her istediğimizi yapabileceğimiz anlamına gelmez. Örneğin kartlı giriş sistemlerinde, elimde kart varsa bunu sisteme okutarak orada çalışan birisi olduğumu yani kimliğimi doğrularım. Ama bu doğrulama tek başına bize her kapıyı açabilme imkanı vermez. Bunun yanında, hangi kapılardan geçebileceğimi, nerelere ulaşabileceğimiz de belirlenmiştir.  Misal çalıştığım bölümden geçebilirken, farklı bir bölüme giremeyebilirim. işte buna da yetkilendirme denir.

Authentication : Kimlik doğrulama.

Authorization : Yetki kontrolü.

Kubernetes yetkilendirme işlemlerini "RBAC" metoduna göre gerçekleştirir. RBAC yetki ve rollerin tanımlanması ve bu tanımlanmış yetki ve rollerin kimliğini doğrulanmış şekilde objelere atanması prensibine göre tanımlanır.

RBAC kuruluşunuzdaki bireysel kullanıcıların, rollerine dayalı olarak, bilgisayar veya ağ kaynaklarına erişimi düzenleme yöntemidir. RBAC yetkilendirmesi, yetkilendirme kararlarını, yönlendirmek için rbac.authorization.k8s.io API grubunu kullanır ve kubernetes API arayıcılığıyla ilkeleri dinamik olarak yapılandırmamıza imkan tanır.

RBAC mekanizması, role,rolebinding,clusterRole,ClusterRolebinding objeleri ile çalışır. Yapılabilecek işlemler, yani yetkiler role veya clusterRole objeleri olarak tanımlanır. Daha sonra RoleBinding ve ClusterRolebinding objeleri arayıcılığıyla bu roller servis hesapları, kullanıcılar yada gruplara bind edilir. Yani bağlanır.

Kullanıcılarda bağlı bulundukları rollerde belirlenen yetkilere kavuşurlar. Örneğin, ben default namespaces de pod objesi okuyup, listeleyebilecek yetkiler belirlediğim role yaratırım, daha sonra Rolebinding yaratarak bu rolü örneğin, bir önceki bölümde oluşturduğumuz <onur@onurbolatoglu.com> kullanıcına bind edebiliriz (bağlarız).

Bu noktadan itibaren bu kullanıcı, default isimli namespaces altındaki podları görüntüleyebilir. Eğer bind ettiğimiz tek role bu ise, kullanıcı sadece bir işlem(atadığımız işlemi) yapabilir. Örneğin başka bir namespaces de yer alan podları görüntüleyemez. Veya servis objesi oluşturamaz vb. Kullanıcılar sadece bind edildikleri rollerdeki tanımlanan yetkilere sahip olur.

Örnek role.yaml dosyası içeriği;

```
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"] # "services", "endpoints", "pods", "pods/log" etc.
  verbs: ["get", "watch", "list"] # "get", "list", "watch", "post", "put", "create", "update", "patch", "delete"
```

Role,Rolebinding,clusterRole,ClusterRolebinding şu ana kadar gördüğümüz diğer kubernetes objeleri gibi, birer kubernetes objesidir. Bu 4 obje de rbac.authorization.k8s.io/v1 API da bulunur. Tüm objeler gibi oluşturulurken, APIversion,Kind-Metadata kısımlarına sahiptirler.&#x20;

Tanımlar ise, "rules" parametresi altında yapılır. Rules altında bu role atanan yetkiler belirlenir. Bu örnekte tek bir rule oluşturulmuş ancak istersek birden fazla rule oluşturabiliriz. Her rule da 3 ana başlık belirleriz.

İlk olarak, bu kural hangi API grubundaki objeler ile ilgiliyse, bunu APIGroups kısmında belirtiriz. Burası boş bırakılırsa, core api grubundaki, objelerle ilgili olduğunu yani, V1 api'daki objeler ile ilgili yetkilendirme yaptığımızı belirtiriz.

2\. olarak belirlediğimiz argüman "Resources"dir. Bu tanım,  hangi kubernetes kaynakları ile ilgili olacağını belirtiriz. Bu örnekte biz, core API da bulunan, pods objesi ile ilgili bir kural yazdığımızı söylüyoruz. Buraya birden fazla obje eklenebilir. Bunun yanında bu objelerin sub objelerini de, örneğin pods objesinin sub objesi olan, logs objesi şeklinde sub obje de ekleyebiliriz.

Kuralda tanımladığımız 3. argüman "verbs" altında belirlediğimiz  yetkilerdir. Burada http request metotları şeklinde yapılabilecekler belirlenir.

Özetle, yukarıdaki yetkilendirmede Core API'da bulunan pod objesini okuyabilecek bir yetki tanımlıyoruz. Bu role de bu metadata altında namespace "default" olarak gözüküyor. Role ve ClusterRole arasında fark budur.

ClusterRole yaml dosyası içeriği;

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
```

Gördüğümüz gibi, role objesi ile neredeyse aynı. 2 farkı mevcut, Obje tipi clusterRole 2.farkı ise, namespace tanımı girilmemiş olmasıdır.&#x20;

Role objesi belirlediğimiz namespace için geçerli olan, namespace çapında yetki vermek için kullanılır. ClusterRole objesi ise, tüm cluster çapında geçerli olacak yetkilendirme için kullanılır.&#x20;

Yani, namespace bazında yetkilendirme istiyorsak, "role" objesini. Tüm cluster da geçerli olacak bir yetkilendirme istiyorsak, "ClusterRole" objesini kullanabiliriz.

Bir namespace bağlı olmayan objeler için, clusterRole objesini kullanmak mantıklı bir seçenek olacaktır.&#x20;

Yetkileri belirlediğimiz objeleri bunlar, "Role" ve "ClusterRole". Ama sadece yetki belirlemek yetmiyor. Bunları kullanıcılara bağlamamız (bind) gerekiyor. İşte burada devreye, RoleBinding ve ClusterRolebinding giriyor. Role objesini bir kullanıcıya bağlamak için Rolebinding objesini kullanabiliriz. ClusterRole objesini ise, bir kullanıcıya bağlamak için ClusterRoleBinding objesini kullanabiliriz.

Örnek roleBinding yaml dosyası içeriği;

```
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: onur@onurbolatoglu.com # "name" is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role #this must be Role or ClusterRole
  name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io
```

Rolebinding de bir kubernetes objesi ve role ile aynı api'da duruyor.&#x20;

Subjects kısmı ise, esas tanımı yaptığımız yer oluyor. subjects kısmında ilk olarak bu roleBinding'in kime bind edileceğini belirliyoruz. Bu örnekte, bir önceki yazımda oluşturduğum kullanıcı olan "<onur@onurbolatoglu.com>" kullanıcısına bağlamak istediğimizi belirtiyoruz.

Neyi bağlamak istediğimizi "roleRef" parametresi ile belirtiyoruz. Bu örnekte pod-reader isimli rolü bağlamak istediğimizi belirtiyoruz. &#x20;

Öncelikle role.yaml dosyamızı deploy edeceğiz ve role yaratılmış olacak. Ardından da bu RoleBinding yaml dosyası arayıcılığıyla Rolebinding objesi yaratacağız. Bu sayede, pod-reader isimli role <onur@onurbolatoglu.com> isimli kullanıcıyla bind edilecek. Bu noktadan itibaren, bu kullanıcı rolde belirlediğimiz yetkileri kullanabilecek.&#x20;

clusterRolebinding yaml dosyası içeriği;

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: DevTeam # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
```

Gördüğünüz üzere, roleBinding ile hemen hemen aynıdır. Sadece role yerine ClusterRole bağlıyoruz ve namespace tanımlamıyoruz. Bu örnekte, bu Cluster rolünü, kullanıcıya değil, gruba bind ediyoruz(bağlıyoruz).  Bu sayede bu ClusterRole objesinden bir kullanıcı değil, DevTeam grubuna bağlı olan tüm kullanıcılara yetki verecek.&#x20;

KubeAPI server ile görüşen bir çok komponent mevcuttur. Bu komponentleri API ile görüşebilmesi için Kubernetes tarafından default olarak gelen, ClusterRole ve ClusterRolebinding objeleri mevcuttur.

Bunların dışında admin,cluster-admin,edit,view adına 4 cluster role daha mevcuttur. Bunlar bizim işimizi kolaylaştırmak adına kubernetes tarafından oluşturulup, default olarak gelmektedir.

Eğer biz, bir kullanıcıyı cluster seviyesinde admin yetkileri ile yetkilendirmek istiyorsak, o kullanıcıya kubernetes'in bize sunduğu "cluster-admin" ClusterRole 'nü bind edebiliriz.

Sadece belirli bir namespace altında tüm yetkileri bir kullanıcıya vermek istersek, bu seferde "admin" ClusterRole 'nü bind edebiliriz. Bunun için ClusterRole objesini, ClusterRolebinding ile değilde, Rolebinding objesi ile kullanıp sadece namespace seviyesinde de atama yapabiliyoruz.

Edit: tüm kaynakları düzenleme yetkisi.\
View: tüm kaynakları düzenleme yetkisi.

Bu roller çok genel olduğu için, Kubernetes tarafından önceden tasarlanmıştır.

<figure><img src="/files/9MF5CTsCXpuxo65NoDl7" alt=""><figcaption></figcaption></figure>


# Service Account

<figure><img src="/files/KYVCkV3yGlZQsSTTmAxo" alt=""><figcaption></figcaption></figure>

Authentication kısmında, kullanıcılar olarak sertifikaları kullanarak, kubernetes API server'a authenticated olma yöntemlerini, Authorization kısmında ise, yetkilendirmeyi görmüştük. Fakat, kubernetes API server'a, sadece kullanıcıların erişmesi yeterli değil. Bir çok durumda, bizlerin pod içerisinde deploy ettiğimiz uygulamalarında Kube API server ile görüşerek, cluster üstünde işlemler yapması gerekebilir.

Kullanıcı hesapları insanlar içindir. Service Accounts podlarda çalışan processler tarafından kullanılmak üzere tasarlanmıştır. Örneğin, kubernetes üstünde uygulamalarınızı, deploy edecek bir uygulama yazdınız, ya da hali hazırda bu işlemleri yapan, bir yazılım kullanacaksınız. Bu uygulamayı bir pod olarak, kubernetes cluster'a deploy ettiniz.  Bu uygulamanın kubernetes API server ile görüşmesi ve ona komutlar ileterek deployment işlemlerini gerçekleştirmesi gerekiyor ve dolayısıyla bu pod içerisinde koşan uygulamanın öncelikle bir şekilde authentication adımını geçerek, kimliğinin doğrulanması sonrasında, bu işlemleri yapabilmesi adına gerekli authorization'a yani yetkiye sahip olması gerekiyor.

Bu ve benzer senaryolarda kullanılabilmesi adına, kubernetes service account isimli bir objeye sahip. [Daha  önceki yazıda bahsettiğimiz](/authentication), sertifika tabanlı authentication insanlar içindir. Bizler bu şekilde authenticate oluyoruz. Fakat uygulamaların authenticate olması için, uygun değildir. Uygulamalar için kullanabilecek tek seçenek, service account 'dur.

Örnek service account yaml dosyası;

```
apiVersion: v1
kind: ServiceAccount
metadata:
  name: testsa
  namespace: default
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: podread
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: testsarolebinding
  namespace: default
subjects:
- kind: ServiceAccount
  name: testsa
  apiGroup: ""
roleRef:
  kind: Role
  name: podread
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Pod
metadata:
  name: testpod
  namespace: default
spec:
  serviceAccountName: testsa
  containers:
  - name: testcontainer
    image: alpine:latest
    ports:
    - containerPort: 80
```

Service accountlar kubernetes objesi olarak yaratabildiğimiz, tek hesap türüdür. ekranın en üstünde gördüğümüz üzere, oldukça basit bir objedir.

Bir isim ve namespace tanımlar oluştururuz. Bu örnekte gördüğümüz üzere, "testsa" isimli bir service account oluşturuyoruz. Fakat tahmin edebileceğiniz üzere, ne türde hesap oluşturursak oluşturalım, bu hesap hiçbir yetkiye sahip olmaz. Bu nedenle hemen altta gördüğünüz üzere, Öncelikle "default" namespace da pod okuma yetkilerine sahip, bir role oluşturuyoruz. Ardından da, bu rolü "testsa" isimli bir service account'a bind edecek bir rolebinding oluşturuyoruz. Bu sayede, "testsa" isimli service account 'u default namespace'de podları listeleme,izleme ve özelliklerini görme hakkına sahip oluyor.

&#x20;En alttaki örnekte, pod tanımının spec kısmında, "serviceAccountName" isimli bir parametre mevcut,  bu parametre ile, pod'a hangi service account'u atanacağını belirtiyoruz. Eğer bu anahtara aksi belirtilmezse, default isimli service account atanacak.

Kubernetes her namespace için, bir adet default isimli service account oluşturur. Ve her pod 'a aksi belirtilmezse, bu service account bağlanır. Default service account'un hiç bir yetkisi yoktur. Ama dilersek bunlara role yada, clusterRole bind ederek, gerekli yetkiyi verebiliriz.

Bir service account oluşturduğumuz zaman, bu service account için bir secret oluşturulur. Bu secret da 3 bilgi bulunur; 1. service account'un oluşturulduğu namespaces'ın adı. 2. kubernetes API server ile iletişim kurarken, HTTPS/TLS bağlantısı kurmamız gerektiğinden, bu bağlantının hata vermemesi adına gerekli sertifika bilgisi, 3. kimlik doğrulamasında kullanabileceğimiz bir Json web token(jwt) bilgisi bulunur. Bu bilgiler, service account'un bağlı olduğu pod içerisinde, /var/run/secrets/kubernetes.io/serviceaccount/ dizininde bulunur.

&#x20;Authentication kısmında kubernetes'in bir çok authentication altyapısını desteklediğinden bahsetmiştik.

Sertifika tabanlı authentication yapabildiğimiz gibi, HTTP header bilgisine sahip json web tokenları kullanarak da authenticate olabiliriz. Her service account için, bu 3 bilginin bulunduğu secret yaratılır ve bu secret /var/run/secrets/kubernetes.io/serviceaccount/  klasörüne mount edilir. Böylece podun içerisindeki uygulamaya git buradaki değerleri oku ve kullan diyebiliriz.

Pod içerisinden nasıl kubernetes API erişeceğiz?\
Her kubernetes kurulumunda, kubernetes içerisinden API server'a erişebilmek için, "kubernetes" isimli bir service yaratılır.  "kubectl get svc" dediğimiz zaman "kubernetes" adında bir servis görürüz. Her podun içerisinden bu isme gittiğimiz zaman otomatik olarak kube api server'a erişebiliriz.

Service accountlar bizim pod içerisinde kullanabileceğimiz, JWT tokenlarını otomatik olarak oluşturup, podlara mount etme konusunda kullanabileceğimiz birer kubernetes objesidir.

<figure><img src="/files/pAjR8i8l0VQG4ssHzEqd" alt=""><figcaption></figcaption></figure>

Objeleri oluşturduktan sonra, service account'u bir pod'a bağlamış olduk ve Pod içerisinden API server 'a bağlanıp, aldığımız yetkilere göre sorgular çalıştırabiliriz. Misal pod içerisinde token'ı uzun uzun her sorguda yazmak yerine, bir değişken olarak verebiliriz.&#x20;

Örneğin;

```
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

ardından,

curl --insecure https://kubernetes/api/v1/namespaces/default/pods --header "Authorization:Bearer $TOKEN"

Sorgusunu çalıştırabilir ve podları listeleyebiliriz.

```

<figure><img src="/files/zWtvND3xlLHuvSpos7H7" alt=""><figcaption></figcaption></figure>

{% embed url="<https://iximiuz.com/en/posts/kubernetes-api-structure-and-terminology/>" %}


# Ingress

<figure><img src="/files/4R2L3benCqwIo3jGwTxU" alt=""><figcaption></figcaption></figure>

Service bölümünde, uygulamalarımızın dış dünyadan erişilebilmesi için, [node port ve Load balancer tipinde servislere sahip olduğumuzu](/networking) görmüş ve bunların detaylarını işlemiştik. Fakat bu yöntemlerin ikisi de OSI layer 'da yani IP layer' da çalıştıkları için bizlerin bazı sorunlarını çözmüyor.&#x20;

Bu bölümde öncelikle bu sorunları tek, tek tanımlayalım ardından da bu sorunların çözümü olan, Ingress controller ve ingress objelerini görelim. İlk olarak sorunu tanımlayalım;

Azure, Google, AWS gibi bir servis sağlayıcı üstünde yönetilen kubernetes hizmetlerinden, birini kullandığımızı varsayalım. Örneğin, Azure kubernetes service. Bu kubernetes cluster üstünde, bir uygulama deploy ettik ve uygulamayı da dış dünyadan erişilebilmesi için, Load balancer tipi bir servis ile expose ettik.

Load Balancer tipi bir service oluşturduğumuz zaman ne oluyordu?

Azure bizim için, bir load balancer yaratıp buna public bir IP atıyordu. Ve bu IP adresine gelen tüm istekleri bu servise yönlendiriyordu. Biz de bu IP adresi ile, bu servisin domaini DNS ile eşleştirerek kullanıcıların erişmesini sağlıyoruz.

Aynı kubernetes cluster da 2.bir uygulama deploy ettiğimizi ve aynı şekilde Load Balancer tipi bir servis ile dış dünyaya açtığımızı düşünün, aynı süreç işleyecek Azure bir Load Balancer daha yaratacak, bir tane daha public IP olacak. 3. bir servis yaratmak istersek, yine aynısı olacak. Bu süreç her yeni uygulama yayınlamak istediğimizde aynı şekilde devam edecek. Özetle 30 servisim varsa, 30 tane Load balancer olacak.&#x20;

Her bir load balancer için, ayrı ayrı para ödememiz gerekiyor. Bu ciddi problem. Her servis için ayrı bir servis oluşturursak, belirli bir zaman sonra işin içinden çıkamayız. Bu bizim ilk sorunumuz.

2\. sorunumuz ise, bizim micro servis mimarisinde bir uygulamamızın olduğunu düşünelim. Bu uygulama da şöyle çalışıyor; eğer kullanıcılar "[www.example.com](http://www.example.com)" şeklinde root path' e gelirlerse, "A" uygulamasına tarafından sayfa sunuluyor.&#x20;

Fakat kullanıcı "[www.example.com/about](http://www.example.com/about)" adresine gelirse, bu sayfa "B" uygulaması tarafından, sunuluyor.  Bu durumda, mevcut load balancer servisi ile ile bizim bu ortamı kurgulamamız imkansız çünkü, DNS'de [www.example.com/about](http://www.example.com/about) şeklinde path base olarak tanımlama yapamayız.  DNS 'de bu mümkün değil, bizim bir şekilde kullanıcı isteklerini bir yerde karşılayıp ve kullanıcının gitmek istediği URL anlayıp, buna göre arkada hangi servise gitmesi gerekiyorsa, ona yönlendirmem gerekiyor.

Uzun lafın kısası, bizim standart sadece IP ve port base yönlendirme yapan, L4 load balancer yerine, L7 de çalışan, application based load balancer'a ihtiyacımız var. Anlatığım bu 2 sorun da, kubernetes dünyasında ingress controller ve ingress objeleri ile çözüyoruz.

Ingress controller, L7 application load balancer kavramının kubernetes spesifikasyonlarına göre çalışan ve kubernetes de deploy ederek kullanabildiğimiz türüdür. <mark style="background-color:orange;">Nginx, Haproxy, Trafeik</mark> en bilinen ingress controller uygulamalarıdır.&#x20;

Özetle, hepsinin yaptığı iş aynıdır. Biz öncelikle bunlardan bir tanesini seçip ve yönergelerini takip ederek ingress controllerı kubernetes clusterımıza kuruyoruz. Bu kurulumu yaptıktan sonra, eğer cloud üstünde çalışıyorsak, bu ingress controller dış dünyaya bir load balancer servisi ile expose oluyor ve public bir IP adresine sahip oluyoruz. Bu noktadan itibaren, artık dış dünyadan kubernetes'e erişim sadece bu IP üstünden sağlanıyor. Sonrasında ise, bu ingress controller'ın, konfigürasyonunu yani, servislerimize dış dünyadan hangi URL üstünden, nasıl ulaşacak kısmını ise, Ingress objeleri dediğimiz objeler arayıcılığıyla belirliyoruz.

<figure><img src="/files/DEZatK1xaLPv5xCUHvEK" alt=""><figcaption></figcaption></figure>

Ingress objeleri bizim, ingress controller'ı kubernetes objeleri arayıcılığıyla ayarlamamızı sağlıyor. Genellikle HTTP olarak üzere, bir clusterdaki servislere harici erişimi yöneten bir API nesnesidir.

Yük dengeleme, SSL sonlandırması ve path base routing özelliklerini destekler.

L7 application load balancer 'mız olan ingress controller 'a bağlanarak, "[www.example.com](http://www.example.com)" A uygulamasına ve "[www.example.com/about](http://www.example.com/about)" gelen istekleri B uygulamasına,  [www.example2.com](http://www.example2.com) adresine gelen istekleri C uygulamasına yönlendir şeklinde o uygulamanın menülerine, özelliklerini kullanıp ayar yapmak yerine, tüm bu istekleri ingress objesi şeklinde oluşturuyoruz. Deploy ettiğimiz zaman, ingress controller bu objeyi okuyarak, gerekli düzenlemeyi otomatik olarak yapıyor.

Ingress controller bizlere, L7 application load balancer'ların sunduğu, SSL,Path based routing vb. bir çok özelliği kubernetes objeleri olarak deploy edebilmemize olanak sağlıyor.

Ingress için öncellikle, bir ıngress controller kurulması gerekiyor. Bir çok ıngress controller mevcut. Ama bunların genelde 2 tanesi öne çıkmış durumda,

1. Nginx
2. Trafeik

Biz nginx ile devam edeceğiz, Nginx'i kubernetes cluster'a nasıl kurabiliriz?

Her ingress controller kurulumu farklıdır. Parametreleri kurulduğu yere göre, aldığı opsiyonlara göre değişebilir. O nedenle bu gibi kurulumların detaylarını ingress controller 'ın kendi dökümantasyonundan öğrenebilirsiniz.

Minikube için Nginx ingress controller kurulumu;

{% embed url="<https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/#enable-the-ingress-controller>" %}

Uygulama;

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blueapp
  labels:
    app: blue
spec:
  replicas: 2
  selector:
    matchLabels:
      app: blue
  template:
    metadata:
      labels:
        app: blue
    spec:
      containers:
      - name: blueapp
        image: ozgurozturknet/k8s:blue
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /healthcheck
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
  name: bluesvc
spec:
  selector:
    app: blue
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: greenapp
  labels:
    app: green
spec:
  replicas: 2
  selector:
    matchLabels:
      app: green
  template:
    metadata:
      labels:
        app: green
    spec:
      containers:
      - name: greenapp
        image: ozgurozturknet/k8s:green
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /healthcheck
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
  name: greensvc
spec:
  selector:
    app: green
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todoapp
  labels:
    app: todo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: todo
  template:
    metadata:
      labels:
        app: todo
    spec:
      containers:
      - name: todoapp
        image: ozgurozturknet/samplewebapp:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: todosvc
spec:
  selector:
    app: todo
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
```

Bu yaml dosyası içerisinde, 3 uygulama deploy ediyoruz. Bu 3 uygulamanın da cluster içerisinden erişilebilir olması için clusterIP tipinde 3 ayrı service objesi oluşturup, uygulamalara atıyoruz.

Bizim bu uygulamaları L7 APP LB üstünden expose etmemiz gerekecek, bunun için gerekli ilk şey ingress controller'dır. Bu ingress controller 'ı minikube kullanıyorsanız yukarıdaki bağlantıya giderek kurulumunu yapabilirsiniz. İngress controller kurulumundan sonra, ingress objeleri oluşturup işlemleri gerçekleştirebiliriz.

```
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: appingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: k8sfundamentals.com
      http:
        paths:
          - path: /blue
            pathType: Prefix
            backend:
              service:
                name: bluesvc
                port:
                  number: 80
          - path: /green
            pathType: Prefix
            backend:
              service:
                name: greensvc
                port:
                  number: 80
```

Ingress de aynen pod, service gibi bir kubernetes objesidir. Networking.k8s.io/v1 api da bulunuyor. tipi de ingress 'dir.  Bu objenin spec kısmında, kurallar belirleyerek servisleri ingress üstünden dış dünyaya açıyoruz. Bu örnekte k8sfundamentals.com domainine ait bir servis yayınladığımızı söylüyor ve altında 2 path belirtiyoruz. Eğer bu domaine /blue şeklinde bir istek gelirse, bunun blusvc servisinden cevap vermesini, /green pathine istek gelirse, greensvc servisinden cevap vermesini sağlıyoruz.

Annotations kısmı ise, cluster'a deploy ettiğimiz ingress controller üstünde, herhangi bir ayar yapmak isterseniz bunu hemen, hemen tüm ingress controller için annotations arayıcılığıyla yapıyoruz. Misal bu servis dış dünyaya HTTPS olarak açmak isteseydik kullanmak istediğimiz sertifika bilgilerini annotations arayıcılığıyla girecektik. vb. kullanabileceğimiz annotationslara kullandığımız ingress controller dökümantasyonundan ulaşabiliriz.

```
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: todoingress
spec:
  rules:
    - host: todoapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: todosvc
                port:
                  number: 80
```

Bu ingress dosyasında da, todoapp.com adresine gelen bütün istekleri todosvc isimli servise gönder diyoruz.

Biz bu ingress objelerini deploy ettiğimiz zaman, Kubernetes tüm bu bilgileri bizim ingress controller'ımıza gönderecek ve orada tüm veriler birleştirilip ilgili ingress controller üzerinde gerekli ayarlamalar yapılacak.

Ek not, eğer sistemde birden fazla ingress controller varsa, '--ingress-class=" argümanı ile kullanmak istediğiniz ingress controller 'ın "ingress-class" değerini öğrenip, bunu ingress objesi dosyasında özellikle belirtip, istediğiniz ingress controller'ı kullanabilirsiniz. Kaynak ve örnek;

<figure><img src="/files/1zmV5boRbME79WxHcfeB" alt=""><figcaption></figcaption></figure>

```
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: todoingresstestv1
  annotations:
    kubernetes.io/ingress.class: "k8s.io/internal-nginx"
spec:
  rules:
    - host: todoappv1.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: todosvc
                port:
                  number: 80
```

Linkler :&#x20;

{% embed url="<https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/>" %}

{% embed url="<https://kubernetes.io/docs/concepts/services-networking/ingress/>" %}

{% embed url="<https://devopscube.com/kubernetes-ingress-tutorial/>" %}


# ImagePullPolicy & ImageSecret

Bildiğiniz üzere bizler sıklıkla dockerhub gibi public registryler ile çalıştık. İmajları buralardan indirip, kullandık. Gerçek hayatta sadece public registryler ile uğraşmıyoruz. Kendimize ait bir private registry kullanıyor olabiliriz.&#x20;

Private registry olduğu zamanda, tahmin edebileceğiniz üzere, bunlardan imaj çekebilmek için authentication olmamız gerekmektedir. Bu authentication işlemini yapmadan, kubernetes cluster bu private registrylerden image çekemeyecek. Dolayısıyla da, containerlarımızı çalıştıramayacağız.

```
apiVersion: v1
kind: Pod
metadata:
  name: imagesecrettest1
  labels:
    app: imagesecret
spec:
  containers:
  - name: imagesecretcontainer
    image: registry.gitlab.com/onurbolatoglu/images:nginx
    ports:
    - containerPort: 80
```

* Yukarıda basit bir pod tanımımız mevcut. Bir adet pod oluşturmak istiyoruz. Bu pod'a bir isim veriyoruz ve bir label atıyoruz.
* Spec kısmında, container'a bir isim veriyoruz. Ardından da, container'ın hangi imajdan oluşmasını istediğimizi söylüyoruz.  Ancak bu haliyle pod''u oluşturamayız. Çünkü bu pod tanımında, ilgili registry'e erişmek için kullandığımız bir authentication bilgisi yer almıyor.

Private registrylere bağlanıp, imaj çekebilmek için, registry urli, username ve password bilgisine ihtiyacımız bulunuyor. Bu bilgileri  edindikten sonra, bu bilgileri bir secret olarak kubernetes'e gönderiyoruz, kubernetes de bir secret oluşturuyoruz. Daha sonra bu secret'ı da kullanmak istediğimiz, pod tanımlarında özel bir parametre ile set ediyoruz.&#x20;

Bu sayede bizim oluşturduğumuz secret içerisinde, kullanıcı adı,parola,url bilgilerini kullanarak, kubernetes; private registry'e authentication olabiliyor ve imajı lokaline indirebiliyor.

Örnek secret oluşturma;

```
kubectl create secret docker-registry "secret_ismi" --docker-server="registry_url" --docker-username="kullanıcı_adı" --docker-password="şifre"

Misal,

kubectl create secret docker-registry regcred --docker-server=registry.gitlab.com --docker-username=onrblt --docker-password=glpatdasd

```

* docker-registry tipinde bir secret oluşturuyoruz.&#x20;
* Secret oluştururken, ilgili private registry'de yetkili olan, kullanıcı ve parolası yanı sıra, url bilgisi de gereklidir.

Private registry 'e erişmek için, oluşturduğumuz secret'ı aşağıdaki örnekteki gibi kullanabiliyoruz;

```
apiVersion: v1
kind: Pod
metadata:
  name: imagesecrettest2
  labels:
    app: imagesecret
spec:
  containers:
  - name: imagesecretcontainer
    image: registry.gitlab.com/onurbolatoglu/images:nginx
    imagePullPolicy: Always # IfNotPresent, Never
    # If the tag is latest, k8s defaults imagePullPolicy to Always. Otherwise k8s defaults imagePullPolicy to IfNotPresent
    ports:
    - containerPort: 80
  imagePullSecrets:
  - name: regcred
```

Hangi pod için bu registry kullanılacaksa, pod yaml dosyası içerisine "ImagePullSecrets" parametresi ile oluşturduğumuz secret'in ismini giriyoruz. Pod imajı indirmek için, "regcred" secret'ını kullanarak authentication olabilecek ve imajı kullanabilecek.

Diğer bir kısım ise, imagePullPolicy parametresidir;

Kubernetes nodelara imaj indirmek için, 3 farklı seçeneği kullanabiliriz.  Bu seçenekleri "imagePullPolicy" parametresi ile tanımlıyoruz.

imagePullPolicy parametresi eğer Always olarak seçili ise, her container oluşturulmaya çalışıldığında, her pod oluşturulmaya çalışıldığında, bu imaj tekrardan registry'den çekilecek. Node'da olup, olmaması önemli değil. Her pod oluşturulma esnasında, bu imaj ilgili registry'den indirilecek.&#x20;

Never seçili ise, bu sefer registry'e gidip, ilgili imajı çekmeye çalışmayacak. Kendi lokalindeki imajı kullanacak. Eğer ilgili imaj yoksa hata verecek. Hiçbir şekilde imajı gidip registry'den indirmeye çalışmaz, her zaman lokalindeki imajları kullanır.

IfNotPresent seçili ise, İlk önce lokale bakacak, kendi üzerinde bu imaj var mı, yok mu kontrol edecek. Yani pod'un schedule edileceği node, üzerinde ilgili imajı arayacak. Varsa kendi lokalindeki imajı kullanacak, eğer yoksa gidip registry'den ilgili imajı indirecek.

{% hint style="info" %}
Pod tanımlarında "latest" tanımlı bir imaj kullanıyorsanız, kubernetes varsayılan olarak "imagePullPolicy" parametresini, "Always" olarak set etmektedir. imagePullPolicy parametresini pod tanımı içerisinde eklemesek dahi, eğer kullandığımız imajın tag i "latest" ise, her zaman "Always" değeri kubernetes tarafından set edilir.

Misal olarak, "latest" tag i yerine, "v1" vb.. tagler kullanıyorsak, kubernetes, biz pod tanımına "imagePullPolicy" parametresini eklemesek bile, "IfNotPresent" değerini seçer.<https://kubernetes.io/docs/concepts/containers/images/>
{% endhint %}

{% embed url="<https://kubernetes.io/docs/concepts/containers/images/>" %}


# Static Pods

Bizler sürekli "kubectl" arayıcılığıyla, kubernetes api server'a komutlar vererek, pod oluşturma işlemlerini yaptık. Fakat kubernetes üstünde, bir tane daha pod oluşturma yöntemimiz daha var. Buna da "static pods" diyoruz.&#x20;

Bu özelliği bize sağlayan "kubelet" komponentidir. Kubelet kendi üzerinde, yani çalıştığı node üzerinde, bir klasör belirleyebiliyoruz. Bu klasörün içerisinde, biz yaml dosyaları şeklinde pod tanımımızı koyarsak, kubelet bu dosyayı ( podu ), bulunduğu node üzerinde ayağa kaldırır.

Yani direkt olarak, kubernetes api 'ya gidip, yeni bir pod talebi oluşturmak yerine, kubelet ile direkt haberleşip, yani kubelet 'in belirlediğimiz dizinine veya default dizinine oluşturulmasını istediğimiz pod tanımını yaptığımız "yaml" dosyasını koyarak kubelet'in podlar yaratmasını söyleyebiliyoruz. Bu duruma da Kubernetes ortamında "static pods" diyoruz.

Kubernetes cluster'ın, kendisi de static podlar ile ayağa kalkar ve cluster haline gelir. Kubelet master sunucu olacak node'un, /etc/kubernetes/manifest dizinine bakar ve kubernetes cluster için gerekli olan yaml dosyalarını okur ve oluşturur.

<figure><img src="/files/rYK25I2gHfZMaJIJ3xyr" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/1RA0IuVYbG0HK8bxhIGX" alt=""><figcaption></figcaption></figure>

Özetle, kubelet varsayılan olarak kendi üzerinde bulunduğu host 'da bir tane dizine bakar. Varsayılan olarak bu path "/etc/kubernetes/manifest/" dizinidir. Bu dizin içerisinde, yaml dosyası şeklinde pod tanımlarımızı koyarsak, kubelet yaml dosyasında tanımını yaptığımız podu bulunduğu node üzerinde ayağa kaldırır ve daha sonrasında bu podu, api server üzerinde "kubectl get pods" şeklinde komut çalıştırarak görüntüleyebiliriz. Bu static pod üzerinde değişiklik yapmak istediğimizde, ilgili node'un "manifest" dizinine gidip, yaml dosyası üzerinde istediğimiz değişikliği yapabiliriz. Bu podu api server üzerinden değiştiremeyiz. Ek olarak, Pod'u silmek istediğimizde, ilgili dosyayı dizinden sildiğimizde static pod silinir.

{% embed url="<https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/>" %}


# Network Policy

Bölüm öncesi notlar;

{% hint style="info" %}

> Kubernetes cluster altında, her podun uniq kendine ait bir IP adresi bulunmaktadır.

> Podlar birbirleri aralarında "nat" olmadan, haberleşebilirler.

> Podlar üzerinde barındığı "worker node'un" erişebildiği her hedefe erişebilir.
>
> Kubernetes cluster'ı altına koyduğumuz, bütün podlar varsayılan olarak, diğer bütün podlar ile haberleşebiliyorlar. IP olarak herhangi bir kısıtlama yok. Podları namespace olarak ayırabiliriz, fiziksel sunucu olarak ayırabiliriz(worker node olarak) fakat gene de varsayılan olarak podlar birbirlerine iletişim kurabilirler. Herhangi bir kısıtlama yok.
>
> Dış dünyadan da, ingress veya LB, NodePort gibi komponentler ile dış dünyadan erişim sağlanabiliyor.&#x20;
> {% endhint %}

Bazı durumlarda, biz bunları kısıtlamak isteyebiliriz. Misal, frontend podu, sadece backend podu ile haberleşebilsin. Frontend podu başka podlar ile konuşamasın veya başka podlardan frontend poduna istek gönderilemesin gibi ayarlamalar yapmamız gerekiyor.

<figure><img src="/files/tM05zrnQJygIJch2KMPy" alt=""><figcaption></figcaption></figure>

Network policy objesi, bu problemleri ortadan kaldırmak için, bize yardımcı olacak bir kubernetes objesidir. Yani podun başka bir pod ile konuşabilmesi veya başka birinin dışarıdan bu poda erişebilmesi gibi varsayılan durumu, biz "network policy'ler" ile değiştirebiliyoruz. Şöyle örneklendirebiliriz; Bu pod sadece "x" namespace'indeki "y" poduna erişebilsin. Bu pod sadece "z" IP aralığındaki IPlere erişebilsin. Bu poda sadece "abc=def" labelina sahip, podlar erişebilsin gibi kurallar ekleyebiliyoruz. Bu kuralları "network policy" objeleri sayesinde oluşturabiliyoruz.

Network policy olmadan, herhangi bir network policy yaratmadıysak, varsayılan olarak;\
\=> Her pod, diğer podlar ile konuşabilir.\
\=> Her pod, dış dünya ile konuşabilir. (Eğer üzerinde bulunduğu worker node konuşabiliyorsa)\
\=> Herkes dış dünyadan, ingress,Load Balancer,NodePort ile açtıysak, bu podlara dışarıdan erişebilirler. Varsayılan olarak kubernetes'in davranışı budur. Biz bu davranışı değiştirmek istersek yani kısıtlamak istersek, Network policy'lere başvuruyoruz. Varsayılan olarak bir kısıtlama söz konusu değil.

Örnek policy yaml dosyası;

```
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: networkpolicy-example
  namespace: ns-a
spec:
  podSelector:
    matchLabels:
      team: a
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 10.11.0.0/16
        except:
        - 10.11.1.0/24
    - namespaceSelector:
        matchLabels:
          team: b
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 80
  egress:
  - to:
    - ipBlock:
        cidr: 1.1.1.1/32
    ports:
    - protocol: TCP
      port: 80
```

Bu dosya da, bir network policy tanımlıyoruz ve network policy objemize bir isim veriyoruz. Spec kısmında ise, biz bu network policy objesi ile ilgili ayarları tanımlıyoruz.

Biz bir network policy yaratırız, daha sonrada bu network policy'nin hangi podlar üzerinde etkili olacağını "podSelector" ile seçeriz. Yani ilk olarak network policy'de tanımlamamız gereken, podSelector ayarıdır. Bu selector ayarı sayesinde biz bu oluşturduğumuz network policy'nin, hangi podlar üzerinde etkili olacağını belirtiriz. Hangi podlara bu network policy objesinin atanacağı "podSelector" anahtarı ile belirleriz.

Yukarıdaki örnekte, "ns-a" isimli namespace üzerinde, bir tane network policy oluşturuyoruz. Bu network policy de, bu namespace de bulunan "team=a" isimli label'a sahip olan, bütün podlara atamak istiyoruz. Dolayısıyla biz bu network policy'i, yarattığımız anda bu namespace içerisinde, bu labela(team=a) sahip olan bir pod var ise, bu podu bulacak ve bu network policy'i atayacak.

Network policy içerisinde, policy type belirlemeliyiz. 2 adet policy type mevcut. Bunlar;

1. Ingress, içeriye doğru gelen trafik anlamına gelir. Yani bizim yukarıda podSelector ile seçtiğimiz podlara doğru gelen trafik ile ilgili ayarlar Ingress de yapılır.
2. Egress, yukarıda podSelector ile seçtiğimiz podlardan dış dünyaya gidecek trafik ile ilgilidir.

2 tip policy seçebiliyoruz. ingress ve egress. Yukarıdaki örnekte iki policy type 'da seçtik. Ama sadece ingress veya sadece egress 'de seçebiliriz.

Ingress içeriye gelen trafik, egress dışarıya doğru giden trafik içindir.&#x20;

Daha sonra ingress tanımını yapıyoruz. Bunun için "ingress" şeklinde bir anahtar açıyoruz. Ardından bunun altında, "from" parametresini giriyoruz. Yani nereden biz bu ingress üzerinde izinler oluşturacağız. Bizim burada kullanabileceğimiz 3 parametre mevcut. Örnekte 3 ünüde kullandık fakat hepsini birlikte kullanmak zorunda değiliz.

```
    - ipBlock:
        cidr: 10.11.0.0/16
        except:
        - 10.11.1.0/24
```

İlk tanımda, bizim bu ingress trafiğine hangi IP blokları üzerinden izin vermek istediğimizi belirlediğimiz IPBlock tanımı. yukarıdaki örnekte biz şunu diyoruz. Oluşturduğumuz network policy'nin atandığı podlara bu IP adresi üzerinden(IP bloğu üzerinden) gelen ingress trafiğine port 80 üzerinden izin ver. Yani bu policy oluşturduğumuz zaman,  pod selector ile seçtiğimiz podlara, 10.11.0.0/16 subneti üzerinden 80 portu ile gelinirse, bu trafik bu podlara erişebilecek. Gördüğünüz gibi IP block (cidr) kısmında, IP tanımları yapabiliyoruz. "except" parametresi ile, bu bloğun tamamına izin ver, ama şuna izin verme gibi ayarlar yapmak istersek except ile bunu belirtebiliriz. Yani. 10.11.0.0/16 subnetine izin verirken, 10.11.1.0/24 subnetini dahil etmiyoruz. Böylelikle, 10.11.1.0/24 subnetine izin vermiyoruz. 10.11.1.0/24 haricinde tüm bloklar (10.11.0.0/16 içerisinde bulunan) 80 portu üzerinden, podSelector ile seçtiğimiz podlara erişebilecek. Yani bu seçeneğimizin adı "ipBlock"dur.

```
    - namespaceSelector:
        matchLabels:
          team: b
```

2. kullanacağımız tanım şekli, namespace üzerindeki labellar üzerinden tanımlayabildiğimiz "Namespace Selector"dür. Yani burada şunu diyoruz; bu oluşturduğumuz network policy'e, git podSelector ile seçtiğimiz podlara ata ve bu podlara "team=b" isimli labela sahip, namespace'deki bütün podlar 80 portu üzerinden erişebilsin.

Yani, "namespaceSelector" ile namespace üzerindeki labellara göre, namespaceleri seçiyoruz. Yani biz network policy'nin atanacağı podlara, namespace  b'den, namespace c'den, namespace d'den, herhangi bir podun erişmesini istiyorsak, tek tek oradaki podları belirtmek, oradaki podların IP adreslerini belirtmek yerine şunu diyebiliriz; Git team=b isimli labela sahip, namespaceleri bul, bu namespaceler içerisinde, bütün podlara bizim network policy'i atadığımız podların veya podun 80 portuna erişebilsin. Şeklinde belirtebiliyoruz. Bunu da 2.seçeneğimiz olan, namespaceSelector ile yapabiliyoruz.

```
- podSelector:
        matchLabels:
          app: frontend
```

3. seçeneğimiz pod selector, bu sefer de spesifik olarak pod seçebiliriz. Yani diyebiliriz ki; app:frontend labelına sahip, pod(lar) bu network policy'nin atandığı pod(lar)a 80 portu üzerinden erişebilsin diyebiliyoruz. Hem ingress, hem egress'de kullanabileceğimiz tanım şekilleri, bu şekilde 3 adettir. Yani biz podSelector ile pod seçeriz, namespaceSelector ile namespace seçeriz. Ya da, IPblock ile erişebilecek IP bloklarını seçeriz.

```
   ports:
    - protocol: TCP
      port: 80
```

Örnekte, ingress 'de "from" tanımı altında, nerelerden erişilebileceğini seçtik ve daha sonra ports kısmında hangi portların açık olacağını seçiyoruz. Dilersek ports kısmında, endport tanımı girersek port aralığı da verebiliyoruz.

Kısacası, ingress anahtarı ile gelen trafiği kısıtlamak için "from" anahtarı kısmında IPblock-namespaceSelector ve podSelector ile, gerekli select işlemlerini yaparak, kime izin vereceğimizi belirtiyoruz. Ardından port kısmında, hangi portlar üzerinden trafik geleceğini belirtiyoruz.

```
egress:
  - to:
    - ipBlock:
        cidr: 1.1.1.1/32
    ports:
    - protocol: TCP
      port: 80
```

Engress kısmında da, yine aynı mantık, yine yukarıda belirttiğim 3 seçeneği de kullanabiliriz. Fakat bu sefer podların dış dünyaya, yani dışa gönderdikleri trafiği kısıtlıyoruz.&#x20;

Bu örnekte, egress olarak network policy'nin atandığı podlardan sadece 1.1.1.1/32 IP adresine 80 portundan erişilebilsin, bunun dışında hiç bir yere erişim izni olmasın şeklinde bir policy oluşturmuş oluyoruz.

Network policy'ler bu şekilde oluşturuluyor. Bu örnekleri kendi ihtiyaçlarımıza göre belirtebiliriz.

Birden fazla policy, bir poda atanabilir. Bir poda yada podlara, birden fazla network policy atanmış ise, bu policylerde oluşturulmuş ingress ve egress kurallarının birleşimi şeklinde atanır. Yani her oluşturulan rule, onun üzerine eklenerek atanır.

```
egress:
  - to:
    - ipBlock:
        cidr: 1.1.1.1/32
    ports:
    - protocol: TCP
      port: 80
```

Network policy kısmında, belirlediğimiz kurallar izin vermek mantığındadır. Yani yukarıdaki örnekte diyoruz ki, 1.1.1.1'e izin ver. Bunun dışında buraya yazılmayan bütün egress trafiği kapatılır.

```
    - ipBlock:
        cidr: 10.11.0.0/16
        except:
        - 10.11.1.0/24
    - namespaceSelector:
        matchLabels:
          team: b
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 80
```

Aynı şekilde, ingress'de de diyoruz ki, bu IP bloğuna, bu namespace'e, bu poda izin ver. Bunun dışındaki bütün trafiği kapat. Varsayılan olarak network policy atanmamış pod veya podlar, her yerden ve her yere erişilebilir ve erişir. Pod'a network policy atandığı anda, sadece yazılan kurallarda yazılı source ve destination'lar geçerli olur.

{% embed url="<https://kubernetes.io/docs/concepts/services-networking/network-policies/>" %}
network policy için ek kaynak
{% endembed %}


# CRD & Operator

CRD ile bizler kubernetes'in default objeleri gibi kendimiz custom objeler yaratabiliyoruz. Yani aynen bir pod,deployment vb obje tipleri gibi kendimiz bir custom obje tipinin tanımını kubernetes'e gönderip, kendi oluşturduğumuz obje tipine göre objeler oluşturabiliyoruz.

```
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: crontabs.stable.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  group: stable.example.com
  # list of versions supported by this CustomResourceDefinition
  versions:
    - name: v1
      # Each version can be enabled/disabled by Served flag.
      served: true
      # One and only one version must be marked as the storage version.
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular name to be used as an alias on the CLI and for display
    singular: crontab
    # kind is normally the CamelCased singular type. Your resource manifests use this.
    kind: CronTab
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - ct
```

Yukarıdaki örnekteki yaml dosyasını biz kubernetes'e gönderirsek, crontabs.stable.example.com isminde bir obje tipi oluşturmuş oluyoruz.&#x20;

Operator ise, cluster admin'in veya developer'ın cluster'da yapacağı işlerin otomatize hale getirilmesinde yardımcı olmaktadır.&#x20;

Misal bir database oluşturmak için, gerekli tanımları yaml dosyasına girer ve bunu kubernetes'e göndeririz. Ardından gerekirse database uygulaması için gerekli tanımları vb girmemiz gerekebilir. Yani süreç burada manuel işliyor. Bizler bunun yerine bir operator oluşturup, CRD'leri kullanarak tüm bu manuel süreçleri otomatik hale getiriyoruz.&#x20;

Yani şunu diyebiliyoruz; Bir adet DB adında CRD oluşturuyoruz, bu DB CRD'inden DB objesi oluşturuyoruz, daha sonra yazdığımız operator ile ne zaman bir DB objesi yaratılırsa, kubernetes de git bir mysql deployment yarat şeklinde bir workflow kurabiliyoruz. Yani işin obje kısmını CRD ile halledip, otomatize etme kısmını da operator ile hallediyoruz. Kubernetes operator konusunda bize default bir SDK sağlamıyor. Fakat operator desteği sunan bir çok SDK mevcut. Aşağıdaki linkten bunları inceleyebilirsiniz.

{% embed url="<https://kubernetes.io/docs/concepts/extend-kubernetes/operator/>" %}

### Service Mesh

Kubernetes üzerinde bir çok servisi ayrı, ayrı podlar olarak çalıştırıyoruz. Bu kubernetes podları üzerinde çalışan servislerin birbirleri olan iletişiminin durumu, servislerin sorunlarının debug edilebilmesi gibi bir çok kolaylığı sağlayan uygulamalar service mesh uygulamalarıdır. Service mesh uygulamalarının servislerin durumu hakkında bilgi edinebilmesi ve proxy olarak görev alan kısım ise servislerimiz ile aynı pod içerisinde çalışan sidecar containerlardır. Proxy görevi gören sidecar containerlar asıl servisimize gelen tüm trafiği üzerinden geçirir.

{% embed url="<https://linkerd.io/2.12/getting-started/>" %}

{% embed url="<https://istio.io/>" %}


# Helm Nedir?

Helm bize kubernetes üzerinde paket yükleme imkanı sağlıyor. brew, chocolatey gibi bir paket yöneticisidir. helm bu paket yöneticilerinin kubernetes için kullanılan bir paket yöneticisidir. Biz uygulamalarımızı paketler haline getirmemizi veya hazır paketleri tek bir komut ile kubernetes'e yüklememizi sağlıyor.

```
helm version

# komutu ile sistemde yüklü olan helm'in sürümünü öğrenebiliyoruz.
```

Helm 'de bilmemiz gereken ilk kavram; chart 'dır. Chart bizim kubernetes üzerine kurabileceğimiz uygulamanın paketlenmiş haline denir. Helm de chartlar yaratırız veya yaratılmış olan chartları alırız.&#x20;

Herhangi bir Chart 'ı alıp, kubernetes üzerine yüklersek, buna da "Release" denilir. Yani Chart paketin kendi adıdır. Release ise, bunu biz kubernetes cluster'a yüklediğimiz zaman, buna verilen isimdir.

Diğer bilinmesi gereken kavram Repository'dir.  Helm 'i şöyle düşünün, helm bir client-side uygulamadır. Yani bizim kendi cihazlarımıza yükleyip, çalıştırdığımız bir uygulamadır. Bu uygulamaya biz repository eklemek zorundayız. Repository dediğimiz şey ise, helm chart 'larının bir arada tutulduğu çeşitli yerlerdir.

Misal biz bir repository oluşturuyoruz ve bu repository altına çeşitli helm chart 'lar ekleyebiliyoruz. Daha sonra, biz bu helm chart'ları kendi cihazlarımıza kurmak istiyorsak; öncelikle kurmak istediğimiz chart'ın bulunduğu repository'i kendi helm 'mize ekliyoruz. Yani helm'in chartları arayacağı listelere bu repository adresini ekliyoruz. Bu listelere repository ekledikten sonra, bu repository altında ilgili chartları kendi kubernetes cluster'mızda kurabiliyoruz. Kurduğumuz zaman da, bunlara release diyoruz.

Özetle, helm chartlarının bir arada tutulduğu repository'ler mevcut. Bu repository'leri helm'mize ekliyoruz. Onun üzerinden helm releaseler oluşturabiliyoruz.

Geçtiğimiz senelerde, artifacthub adında bir kubernetes projesi yapıldı. Artifacthub'da şu sorunu çözmeye yarıyor; Biz kubernetes üzerine bir çok şey kuruyoruz. Misal, helm chartlar, prometheus paketleri, kubectl pluginleri vb.. Bir çok artifact'i, bizim kubernetes üzerinde kullanmamız gerekebiliyor.  Fakat bu artifactler hep farklı farklı yerlerde duruyor. Misal helm repositoryleri her uygulama için farklı yerlerde, kubectl pluginleri kurmak istediğimizde zaman, zaman bunları çeşitli yerlerde github vs. aramak durumunda kalıyoruduk. Bu paketleri ortak arayabileceğimiz bir çözüm yoktu. ArtifactHub bu sorunu çözmek için, ortaya çıkmış bir projedir. Helm chart'ları dahil, artifacthub üzerinden kubernetes için paketleri arattırabiliyoruz.

Tıpkı dockerhub gibi, artifacthub ise helm chartlar ve diğer kubernetes artifactleri için kullanabiliyoruz.&#x20;

Helm üzerinden paketleri yani chartları arayabileceğimiz komutlar:

```
helm search hub wordpress 
# Artifacthub üzerinde ihtiyacımız olan chartı arattırabiliriz.

helm search repo wordpress
# Sistem de mevcut olan, repository listeleri üzerinde ihtiyacımız olan chartı arattırabiliriz.
```

Kuracağımız chart'ın bulunduğu repository adresini helm'mize ekliyoruz, helm böylelikle chart'ı nerede arayacağını anlıyor. Eğer repository mevcut değilse, chart bulunamaz ve chartı kuramayız.

Repository adresini helm'e ekledikten sonra;

```
helm show values bitnami/wordpress
# Komutu ile, bitnami/wordpress chartı içerisinde, hangi değerleri değiştirebileceğimizi ve kullanabileceğimizi görebiliyoruz.
```

```
echo '{mariadb.auth.database: user0db, mariadb.auth.username: user0}' > values.yaml
helm install -f values.yaml bitnami/wordpress --generate-name
```

Yukarıdaki örnekte şunu diyoruz, values.yaml isimli bir dosya oluştur. Mariadb'nin database ismini, mariadb'nin username ismini belirttiğim şekilde values.yaml dosyası içerisine ekle. Böylelikle 2.komut da görüleceği üzere, values.yaml dosyasını helm'e gösterek mariadb.auth.database ve mariadb.auth.username değerleri bizim values.yaml dosyasında belirttiğimiz şekilde baz alınarak release kurulacaktır.

Bizler bu şekilde, varsayılan değerleri kullanmak yerine kullanabileceğimiz değerleri "helm show values "chart ismi" komutu ile gözlemleyip, ardından bu değerler içerisinde değiştirmek istediğimiz kısımlar var ise, values.yaml isimli dosya oluşturup içerisine yazıyoruz.

Yukarıdaki örneği tekrarlamak gerekirse;

mariadb.auth.database  ve mariadb.auth.username  değerlerini kendimize göre özelleştirip, values.yaml dosyası içerisine ekliyoruz.  Ardından helm install -f values.yaml  ile özelleştirdiğimiz değişkenleri helm'e söylüyoruz ve release bu şekilde kuruluyor. -f values.yaml şeklinde belirtmezsek, default hali ile kurulur.

```
helm uninstall wordpress-example
# Komutu ile sisteme kurmuş olduğumuz wordpress-example release kaldırılır.
```

Bizler helm üzerinde upgrade işlemleri yapabiliyoruz. Misal biz bitanami'nin bize sağladığı wordpress chartını sisteme release olarak kurduk. Fakat zaman sonra bu release üzerinde bir takım değişiklikler yapma ihtiyacı duyduk.  Değiştirmek istediğimiz değerleri bildirdiğimiz bir dosya yaratıp, daha sonra, (Değiştirebileceğimiz değerleri show values ile görebildiğimizden yukarıda bahsettik)

```
helm upgrade -f yenienv.yaml wordpress-example bitnami/wordpress
```

Komutu ile, bizim değiştirdiğimiz/güncellediğimiz değerler ile belirttiğimiz release 'in upgrade edilmesini sağlayabiliyoruz. Böylelikle güncellemek istediğimiz release'i silip, tekrardan kurmak yerine veya release üzerinde kullanılan bir imajın yeni bir versiyonu çıktığı zaman o release'i silmek yerine, yeni istediğimiz değişkenleri/özellikleri bir dosya da belirtip, helm upgrade komutu ile yeni belirlediğimiz değişkenlerin bulunduğu dosyayı göstererek release'in bizim istediğimiz şekilde güncellenmesini sağlayabiliriz.&#x20;

Yapılan upgrade işlemi işimize yaramadıysa veya bazı şeyleri bozmasına neden olduysa, rollback ile bu değişiklikleri geri alabiliriz.

```
helm history wordpress-example
# Komutu ile wordpress-example isimli release'in revizyonlarını görebiliriz.

helm rollback wordpress-example 1
# Komutu ile wordpress-example release'in 1 numaralı revizyona döndürülmesini sağlayabiliriz.
```

Chart oluşturmak için;

{% embed url="<https://helm.sh/docs/topics/charts/>" %}

{% embed url="<https://phoenixnap.com/kb/create-helm-chart>" %}

Helm Docs;

{% embed url="<https://helm.sh/docs/>" %}

Artifacthub;

{% embed url="<https://artifacthub.io/>" %}


# Prometheus Stack - Monitoring

<figure><img src="/files/zibPlgG5qgeWOC4LPGRD" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/wAetSN5CmxuHobUVDuYl" alt=""><figcaption></figcaption></figure>

Production ortamlarda, kubernetes cluster durumunu ve benzeri şeyleri kontrol etmemiz ve olası sorunlarda alertler oluşturmamız önemli bir konudur. Monitoring konusunu aşağıdaki maddelere ayırabiliriz;

\=> Kubernetes Cluster'ın durumunu gözlemlememiz gerekiyor. Objelerin durumu, deploy edilen obje miktarını vb. ( Bunu promethues ile monitor edeceğiz )

\=> Objelerin mevcut durumu nedir? İstenilen şekilde çalışıyor mu? Podlar ne kadarlık bir kaynak tüketiyor gibi soruların cevaplarını öğrenmemiz gerekiyor. ( Bunu prometheus ile monitor edeceğiz )

\=> Worker, Manager nodeları izlememiz ve anlık kaynak kullanımlarını gözlemlememiz gerekiyor. ( Bunu prometheus ile monitor edeceğiz )

\=> Pod içerisinde çalışan uygulamalarımıza ait logları incelememiz gerekiyor. ( Prometheus ile yapabiliriz fakat, bunu daha efektif bir şekilde arama yapabileceğimiz EFK stack ile monitor edeceğiz, bir sonraki bölümde )

Prometheus 'a geçmeden, kubectl ile bilgi toplama;

```
kubectl get pods 
# çalışan podların listesini getirir.

kubectl get All -A 
# Sistemde bulunan tüm objelerin listesini getirir.

kubectl describe pods pod1 
# Pod1 hakkında detaylı bilgi alırız.

kubectl get events
# Cluster da oluşan tüm logları gösterir.

kubectl top node node1 
# Node1 isimli node hakkında kaynak tüketim miktarını getirir.

kubectl top pods pod1
# pod1 isimli podun kaynak tüketim miktarını getirir.

kubectl logs pod1
# pod1 isimli podun içerisinde çalışan uygulamanın loglarını getirir.
```

Yukarıdaki komutlar ile gerekli tüm bilgilere/loglara ulaşabiliriz fakat, production ortamlarda bu komutlar ile tek, tek toplamak uğraştırıcı ve zaman kaybı olacaktır ve yönetilemez hale gelecektir.

Misal çok fazla objenin bulunduğu Kubernetes cluster ortamlarında, bu şekilde kubectl ile manuel olarak log toplamaya çalışmak tavsiye etmeyeceğimiz bir davranış olacaktır.

Bunun yerine merkezi bir loglama sunucusu veya merkezi bir metric sunucusuna, kubernetes cluster'dan edindiğimiz loglar ve metricleri göndermemiz daha mantıklı bir çözüm olacaktır.  Böylelikle, edindiğimiz loglardan ve metric'leri görselleştirip, alertler oluşturabiliriz. Misal, node1 down olursa, mail gönder vb. gibi alertler oluşturabiliriz.

&#x20;Kısacası, Kubernetes üzerinde monitoring altyapısı sağlayabilecek bir servise ihtiyacımız var. Bu ihtiyacımızı Prometeus stack ile çözeceğiz. Prometheus stack ile uygulamala logları hariç, geriye kalan tüm metricleri alabileceğiz. Metricleri prometheus ile alıp, Grafana ile prometheus'u konuşturup, aldığımız metricleri Grafana ile görselleştirebiliriz.

Prometheus stack kurulumu;

```
kubectl create namespace monitoring
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kubeprostack --namespace monitoring prometheus-community/kube-prometheus-stack
kubectl --namespace monitoring get pods -l "release=kubeprostack"
```

Prometheus, Grafana, Alertmanager 'a dışarıdan erişmek için nodePort dosyası.

```
apiVersion: v1
kind: Service
metadata:
  name: prometheus-service
  namespace: monitoring
spec:
  selector:
    app.kubernetes.io/name: prometheus
    prometheus: kubeprostack-kube-promethe-prometheus
  type: NodePort
  ports:
  - nodePort: 32079
    port: 9090
    protocol: TCP
    targetPort: 9090
    
---
apiVersion: v1
kind: Service
metadata:
  name: prometheus-grafana-service
  namespace: monitoring
spec:
  selector:
    app.kubernetes.io/instance: kubeprostack
    app.kubernetes.io/name: grafana
  type: NodePort
  ports:
  - nodePort: 32078
    port: 80
    protocol: TCP
    targetPort: 3000
    
---
apiVersion: v1
kind: Service
metadata:
  name: prometheus-alertmanager-service
  namespace: monitoring
spec:
  selector:
    app.kubernetes.io/name: alertmanager
  type: NodePort
  ports:
  - nodePort: 32084
    port: 9093
    protocol: TCP
    targetPort: 9093
```

{% embed url="<https://github.com/aytitech/k8sfundamentals/blob/main/monitoring/kube-prometheus-stack.md>" %}

Prometheus ile grafana'yı konuşturmak 'dan bahsettik yukarıda, bunu nasıl yapıyoruz? Bunu biz değil, bizim yerimize bu stack'i yöneten kubernetes operator yapıyor.

Kubernetes üzerine prometeus'u kurduk ve kurduktan sonra bir çok ayarın yapılması gerekiyor. Misal, ruleların oluşturulması; gerekli tanımların yapılması vb. Özetle prometheus stack 'i kurup,yapılandırıp,yönetmemiz gerekiyor.  Kubernetes 'de operator dediğimiz bir yöntem mevcut; Bizler operatorler oluşturabiliyoruz ve kubernetes de operatorler oluşturduğumuz zaman, bizim admin olarak yapacağımız tüm manuel ve uzun sürecek işlemleri otomatik hale getirilmesini sağlıyoruz operator ile.&#x20;

Prometheus'u kurarken, yani helm chart'ı sisteme release olarak yükledikten sonra, operator prometheus 'un çalışması için gereken tüm ayarlamaları yapıyor. Tıpkı bir admin gibi, bu stack'in yönetiminden sorumlu oluyor.


# EFK Stack - Monitoring

Kubernetes üzerinde çalışan uygulamalarımızın loglarını toplamak için EFK stack kullanıyoruz.

* Elastic Search, logların toplandığı ve kaydedildiği yer.
* Kibana, Logların Elastic Search'den alınıp, grafik arayüz de görüntülememizi sağlar.
* FluentD veya logstash, logları uygulamalardan toplamamızı sağlar.

{% embed url="<https://github.com/aytitech/k8sfundamentals/tree/main/monitoring>" %}
efk için kaynbak github linki
{% endembed %}


