Amazon EKS Kubernetes Cluster Kurulumu ve Pratikleri
Gereksinimler
- Amazon Web Service hesabı
- aws cli
- eksctl cli
Önemli Notlar
- Bu yazıyı hazırlarken windows ortamında işlemleri yaptım. Windows yada Linux için aynı komutları kullanabilirsiniz.
- Amazon EKS servisi ücretlidir. Ay sonu sürpriz faturalar gelebilir.
- AIM User ve Public Key Oluşturma.
AWS yönetim konsolunda AIM > Users > Add Users adımları takip edilerek “AdministratorAccess” yetkilerine sahip kullanıcı oluşturulur. Kullanıcı oluşturma adımında oluşan csv dosyasını mutlaka saklayın.
EC2 > Network & Security > Key Pairs > Create Key pair adımları key oluşturulur. Key oluşturma aşamasında .pem dosyası otomatik olarak bilgisayarınıza inecektir bunu mutlaka saklayın.
Öncelikle aws cli ile aws hesabımıza bağlantı yapmamız gerekiyor. Bunun için AIM user oluştururken indirdiğiniz cvs dosyasındaki bilgileri kullanacağız. Bağlantıyı sağlamak için aşağıdaki komutu kullanabilirsiniz.
aws configure
1.1 AWS EKS Cluster Oluşturma
EKS ortamımız için VPC tanımlaması yapmamız gerekiyor.
aws cloudformation create-stack --region eu-west-1 --stack-name my-eks-vpc-stack --template-url https://amazon-eks.s3.us-west-2.amazonaws.com/cloudformation/2020-10-29/amazon-eks-vpc-private-subnets.yaml
EKS için role oluşturmamız gerekiyor. “cluster-role-trust-policy.json” adında bir json dosyası açılır, aşağıdaki konfigürasyon eklenir ve role oluşturulur.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "eks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}aws iam create-role --role-name myAmazonEKSClusterRole --assume-role-policy-document file://"cluster-role-trust-policy.json"aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy --role-name myAmazonEKSClusterRole
EKS > Create Cluster adımları takip edilerek EKS cluster oluşturulur.
Create işlemini başlattıktan sonra 10–15dk beklemek gerekecek ve Kubernetes cluster kurulumu tamamlanmış olacak.
Ve sonuç başarılı. Kubeclt ile görüntülemeden önce config ayarlamasını yapmamız gerekiyor.
aws eks update-kubeconfig --region eu-west-1 --name Kubernetes-Cluster-Arif
Aslında biz AWS tarafından yönetilen master node kurulumu yapmış olduk. Şimdi Worker node kurmak için öncelikle role tanımlamamız gerekiyor. Bu işlem için “node-role-trust-policy.json” adında bir json dosyası oluşturulur ve aşağıdaki konfigürasyon eklenir..
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}aws iam create-role --role-name myAmazonEKSNodeRole --assume-role-policy-document file://"node-role-trust-policy.json"aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy --role-name myAmazonEKSNodeRoleaws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly --role-name myAmazonEKSNodeRoleaws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy --role-name myAmazonEKSNodeRole
Role tanımlama işlemi tamamlandıktan sonra cluster içerisinde Configuration > Compute > Add Node Group adımı izlenir. Node Group ekranından Name ve Node IAM Role bilgileri girilir Next ile devam edilir.
Devam eden ekranda Worker Node’ların Instance bilgileri ve Scale bilgileri giriliri default bırakılır ve devam edilir.
Network ayarları default bırakılır ve devam edilir ve create işlemi başlatılır.
İşlem tamamlandıktan sonra kontrol edilir.
1.2 EKSCTL ile EKS Cluster Oluşturma.
“eks-cluster.yaml” adında bir dosya oluşturulur ve aşağıdaki cluster bilgileri içerisine kaydedilir.
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfigmetadata:
name: Kubernetes-Cluster-Arif
region: eu-west-1nodeGroups:
- name: Kubernetes-Cluster-Node-Group
instanceType: t2.micro
desiredCapacity: 3
ssh: # use existing EC2 key
publicKeyName: arif
Cluster oluşturma işlemi başlatılır. Bu işlem 10–15dk kadar sürecektir.
eksctl create cluster -f eks-cluster.yaml
Ve sonuç başarılı. Kubeclt ile görüntülemeden önce config ayarlamasını yapmamız gerekiyor.
aws eks update-kubeconfig --region eu-west-1 --name Kubernetes-Cluster-Arif
EC2 servisinden EKS için oluşturulan EC2 instance görebiliriz.
EC2 altında security group altında EKS için oluşturulan gruplar, VPC servisi altında EKS için oluşturulan VPC bilgileri gibi birçok işlem otomatik yapıldı. Aslında bunları bizim yerimize yine AWS’nin bir servisi olan CloudFormation servisi halletti. CloudFormation aslında rutin olarak yapılan işlemleri sizin yerinize yapan bir servistir. Bu servis ile ister kendiniz template oluşturur istediğiniz zaman tetikleyerek ortamlarınızı oluşturur ister bizim kullandığımız gibi hazır templateleri kullanarak ortam oluşturabilirsiniz. (Belki başka bir yazıda CloudFormation servisinin detaylarına girebiliriz.)
3. Hadi Biraz Pratik Yapalım
Eğer Kubernetes hakkında daha temel bilgileri öğrenmek istiyorsanız aşağıdaki yazımı okuyabilirsiniz.
2.1 İlk Pod Oluşturuluyor…
Pod
Kubectl ile ilk olarak nginx pod oluşturalım ve kurduğumuz sistemde sorunsuz çalışıyor mu kontrol edelim.
kubectl run firstpod --image=nginx --restart=Neverkubectl.exe describe pods firstpod
İlk pod oluşturuldu ortam sorunsuz çalışıyor, artık bu pod’u silebiliriz.
kubectl delete pods firstpod
Şimdi declarative yani yaml kullanarak bir pod oluşturalım hem de yaml formatını tanımış olalım.
apiVersion: v1 # K8s kubectl ile ve kendi component’leri ile api aracılığı ile iletişim kurduğu için işlem yapacağımız “kind” hangi api üzerinde çalışıyorsa onun api bilgisini girmemiz gerekiyor. Pod’un api bilgisini öürenmek için “ kubectl explain pods ” komutunu kullanabiliriz.
kind: Pod # Objenin tanımlandığı alnadır.(Pod,Deployment,Service…)
metadata: # Oluşturacağımız objenin bilgilerini tanımladığımız alandır.
name: firstpod
spec: # Oluşturulacak objenin özellikleri belirtilir. Opsiyonel olan bir alandır.
Örnek yaml
apiVersion: v1
kind: Pod
metadata:
name: firstpod
labels:
app: front-end
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Bunu kubeclt ile çalıştıralım. İşte bu yöntem yani kubectl’in hangi özelliklerde ne yapacağını yazdığımız yaml dosyasına declarative yöntem diyoruz ve genellikle bu şekilde ilerleyeceğiz.
kubectl.exe apply -f objectbasetemplate.yamlkubectl get pods firstpod -o wide
Declarative yöntem ile biz pod oluşturduk ve bir adet daha label eklemek istiyoruz. Bunu sağlamak için yaml dosyamızda bir adet daha label ekleriz ve tekrar apply komutunu çalıştırırız. Kubectl burada bir değişiklik işlemi olduğunu anlayacak ve daha önce oluşturduğumuz Pod’u güncelleyecek.
Elimizde yaml dosyası yok ama edit yapmak istiyoruz, ne yapacağız? Aşağıdaki komut işimizi çözecektir. Bu işlem size Pod’un yaml formatını tanımlı editör ile açacaktır. Burada değişikliği yapıp kaydederseniz işlem gerçekleştirilmiş olur.
kubectl edit pods firstpod
Hap Bilgi 1
Pod oluşturken o Pod’un çalıştığı süre içerisinde manuel yada bir hata durumunda kapanırsa ne yapması gerektiğini yazabiliriz.
Always : Pod hangi sebeple kapanırsa kapansın Pod’u tekrar başlatılır. (Default)
On-Failure : Eğer pod bir hata sebebi ile kapandıysa tekrar başlatılır.
Never : Pod hangi sebeple kapanırsa kapansın tekrar başlatılmaz.
Genellikle pod kavramını tanımlarken Docker ortamlardan aşina olduğumuz pod eşittir container diye tanımlarız ve her pod içerisinde 1 adet uygulama çalışmasının doğru olduğunu savunuruz. Ama biz bir pod içerisinde 2 adet uygulama çalıştırmak istiyorsak? Mevcut durumumuz bunu gerektiriyorsa? Bu da mümkün, hadi bu seneryo için podmulticontainer.yaml adında bir dosya oluşturalım aşağıdaki örneği inceleyelim.
apiVersion: v1
kind: Pod
metadata:
name: mc1
spec:
volumes:
- name: html
emptyDir: {}
containers:
- name: 1st
image: nginx
ports:
- containerPort: 80
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 15;
done
Yukarıdaki örnekte neler yapılmış;
- Objemizi pod olarak belirliyoruz api ve metadata bilgilerini giriyoruz.
- html adında volume oluşturuyoruz.
- 1st container nginx image kullanıyor. html volume /usr/share/nginx/html dizinine mount ediliyor. Containerport tanımı olarak 80 belirledik.
- 2nd container debian image kullanıyor. html volume /html dizinine mount ediliyor. 2nd container 15 aralıklarla date çıktısınını /html/index.html içerisine yazıyor.
Pod içerisindeki 1st ve 2nd containerlar volume aracılığı ile index.html dosyasını ortak kullanabildiler ve 1 pod içerisinde 2 tane container nasıl ayarlanır bunu da görmüş olduk.
Hadi bunu apply edelim.
kubectl apply -f podmulticontainer.yaml
Evet gördüğünüz gibi biz tek pod içerisinde birden fazla container çalıştırabiliyoruz.
kubectl delete -f podmulticontainer.yaml
Yukarıdaki komut ile oluşturduğumuz Pod’u silelim. Ya biz oluşturuyoruz, denemeden siliyoruz bu ne biçim iştir diyebilirsiniz. Service ve LB kısımlarına gelmediğimiz için bu kısımları geçiyorum. Eğer K8S ortamını on-prime yapıya kursaydık master sunucu üzerinde port-forward yaparak hızlıca erişim sağlayabilirdik. O nedenle şimdilik çalıştığını görmek yeterli olacak bize. Birazdan LB tanımları ile oluşturduğumuz örnekleri test etme fırsatımız olacak.
Pod içerisinde multicontainer çalıştırma konusunun bir yöntemi daha var. Bu yönteme init container diyoruz. Init container Pod içerisindeki container çalışmadan önce çalışan, işlemini bitirdikten sonra Pod içerisindeki asıl container’ın çalıştığı yapıdır. Biraz karmaşık oldu bunu yaml ile anlatayım.
apiVersion: v1
kind: Pod
metadata:
name: mydb
labels:
app: db
spec:
initContainers:
- name: fetch
image: mwendler/wget
command: ["wget","--no-check-certificate","https://sample-videos.com/sql/Sample-SQL-File-1000rows.sql","-O","/docker-entrypoint-initdb.d/dump.sql"]
volumeMounts:
- mountPath: /docker-entrypoint-initdb.d
name: dump
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "example"
volumeMounts:
- mountPath: /docker-entrypoint-initdb.d
name: dump
volumes:
- emptyDir: {}
name: dump
- Klasik api kind ve metadata bilgilerini girdik.
- Spec içerisinde init container tanımı yaptık. Bu init container dump volume(
/docker-entrypoint-initdb.d
) içerisine external kaynaktan .sql dosyasını çekti. Herhangibir döngü olmadığı için init container işlemini bitirdi. - mysql adında bir container oluşturduk dump volume mountpath olarak tanımlandı ve mysql container pod içerisinde çalışmaya başladı.
Neden gerekli bu init container? Yukarıdaki durumdan anlatacak olursam, biz mysql container oluşturmak istiyoruz ama mysql tablolarının da oluşturulması gerekiyor, bu tablolarını da Pod içerisinde container oluşmadan önce container içerisine almamız gerekiyor. Bu tür Pod içersinde container çalışmadan önce yapılması gereken adımlar var ise init container kullanabiliriz.
Pod konusu bu kadar yeter.
2.2 Daha Fazla Bilgi, Daha Fazla Pratik.
Label/Selector
Hap Bilgi 2
Label kubernetes ortamlarında oluşturulan objeleri gruplandırmak veya tanımlamak için kullanılan yapıdır. Yaml dosyasında yada kubectl kullanarak sonradan da atabanilir veya silinebilir. Ve en önemlisi kubernetes’de servis ve objeler arasındaki bağlantı da label’lar ile yapılır. Bir objenin hangi label’a sahip obje ile ilişkilendirileceğini ise selector ile yapıyoruz.
Şu ilişkilendir konusu havada kalmasın aşağıdaki yaml ile anlatayım.
apiVersion: v1
kind: Pod
metadata:
name: label-demo
labels:
environment: production
app: mysql
spec:
containers:
- name: mysql
image: mysql:latest
ports:
- containerPort: 80
nodeSelector:
hddtype: ssd
Örneğin bizim 3 Worker 1 Master’lı k8s cluster yapımız var. Worker sunuculardan sadece 1 tanesi ssd diske kurulmuş. Biz production uygulamalarımızı hızlı çalışmasını istediğimiz için bu Worker üzerinden çalıştırmak istiyoruz. İlk önce 1 node üstünden hddtype:ssd label tanımı yaparız.
kubectl label nodes ip-192-168-50-119.eu-west-1.compute.internal hddtype=ssdkubectl get nodes --show-labels
İstediğimiz Label’ı ekledik. Yukarıdaki Pod artık hddtype:ssd olan yani bizim seçtiğimiz/seçtiklerimiz Worker’lar üzerinde çalışacak.
Annotation
Hap Bilgi 3
Label vs Annotation. Genel anlamda ikisi de objeyi tanımlamak için kullanılır. Ama Label ← → Selector ilişkisinden dolayı her bilgiyi label olarak eklemek, label değiştirmek silmek sorunlaya yol açabilir. Bu nedenle selector bağımlılıklarını etkilememek için annotation kullanılır. Örnek için aşağıdaki yaml dosyasını inceleyebilirsiniz.
apiVersion: v1
kind: Pod
metadata:
name: annotationpod
labels:
environment: production
app: nginx
annotations:
owner: "Arif Kızıltepe"
releasedate: "12.20.2021"
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Namespace
Namespace Kubernetes ekosistemindeki mantıksal bölümlere verilen addır. Biz objeleri herhangibir sebepten mantıksal olarak birbirinden ayırmak istiyorsak namespace kullanabiliriz. Aşağıdaki komut ile default namespace’leri görebiliriz.
kubectl get namespaces
Hap Bilgi 4
Çalıştırdığımız her kubectl komutu default namespace içerisinde çalışır. Default namespace yani çalıştırdığımız her komutun xxx namespace içerisinde çalışmasını istiyorsak .kube/config içerisindeki config dosyamızdan değiştirebiliriz.
apiVersion: v1
kind: Namespace
metadata:
name: arifwashere
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: arifwashere
labels:
environment: production
app: nginx
annotations:
owner: "Arif Kızıltepe"
releasedate: "12.20.2021"
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Öncelikle yaml dosyası içerisinde “ — — — ” ne yapıyor derseniz. Biz bu ifade ile birden fazla declarative(yaml) dosyalarını tek bir yaml dosyasından yazabiliyoruz.
Neler yapmışız yaml içerisinde;
- Namespace için obje oluşturmuşuz. Bu obje bize ArifWasHere adında bir namespace oluşturacak.
- Pod için bir obje oluşturmuşuz. Metadata alanında da bu pod sen git arifwashere namespace altında çalış demişiz.
Bakalım görelim;
Nginx adlı pod objemizi mantıksal olarak ayırmış olduk.
Deployment
Biz bu zamana kadar containerları Pod için yaml oluşturarak çalıştırdık ve bu şekilde kullandık ama genelde bu şekilde kullanım doğru değildir. Çünkü bu podların çalışdıktan sonra da yönetilmeye ihtiyacı olabilir bu nedenle biz denellikle deployment objesini kullanırız. Deployment bir veya birden fazla Pod’u bizim istediğimiz yapıya göre oluşturan ve mevcut durumu sürekli kontrol ederek yapının bozulmamasını sağlayan obje tipidir.
Hadi örneğimiz ile anlamaya çalışalım deployment kavramını;
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
- Deployment objesi, api ve metadata bilgileri yazıldı.
- Spec içerisindeki replicas ile deployment kaç tane pod oluşturacağı yazılır.
- Daha önce selector konusunu anlatırken selector label tanımlarını kullanarak objeleri kullanmamızı sağlar. Burada da gördüğümüz gibi 3 tane replica oluşturacağız ama app: nginx label tanımlı template’den oluşturacağım.
- Template kısmına ise Deployment objesinin hangi özelliklerde pod oluşturacağı yazılır. Aslında dikkatli bakarsanız çok tanıdık gelecek template tanımı altında kalan kısım Pod oluştururken kullandığımız ile aynı tanımları içeriyor.
Deployment objesini oluşturalım.
kubectl apply -f nginx-deployment.yaml
Deployment objesi replicaset ile bize 3 tane Pod oluşturdu ve bunları 3 node üzerine dağıttı. Biz bunu pod objesi ile de yapardık ne farkı var derseniz;
Listedeki 1 pod’u silmeyi deneyin
kubectl delete pod nginx-deployment-66b6c48dd5-c5fz6
Deployment arkadaşım sen bana declarative olarak 3 tane oluştur dedin biri geldi sildi, ben yeniden oluşturuyorum dedi ve create etmeye başladı. Yukarıdaki örnek çok temel bir örnek ama replica sayısını arttırmak autoscale için de tanımlar yapabiliyoruz.
ReplicaSet
ReplicaSet en kısa tabirle Deployment objesinin template’lerden replicaları oluşturan alt objesidir. Biz deployment objesi tanımladığımızda buna ait bir replicaset objesi oluşur ve replicaları oluşturur/kontrol eder.
Siz deployment yaml içerisinde bir değişiklik yaparsanız, deployment objesi yeni bir replicaset oluşturacak ve eski replicaset eski podları temizlerken yeni replicaset ise güncel bilgilere göre podları oluşturacak. Rollout işlemi yapabiliriz diye eski replicaset kendini silmeyecektir.
Deployment oluşturmasın ben oluşturayım dersen yaml burada;
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Zaten kind harici herşeyi ile aynı. Neden mi biz Deployment objesini kullanıyoruz. Biz deployment objesinde bir değişiklik yapıp apply ettiğimizde Deployment objesi değişikliği algıladı ve kendine yeni replicaset oluşturdu, eski replicaset’e eski podları silmesini söyledi. Ama biz manuel Replicaset oluşturur içerisinde değişiklik yaparsak eski podları replicaset güncellemeyecek sadece yeni podları oluştururken yeni özellikleri kullanacaktır. Deployment objesinin desteklediği rollout ve rollback işlemlerini yapamayacağız.
Rollout ve Rollback
apiVersion: apps/v1
kind: Deployment
metadata:
name: rcdeployment
labels:
team: development
spec:
replicas: 3
selector:
matchLabels:
app: recreate
strategy:
type: Recreate
template:
metadata:
labels:
app: recreate
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Bu yaml dosyasında farklı olarak gördüğünüz gibi strategy type olarak recreate seçilmiş. Bu ifade ben deployment içerisinde bir değişiklik yaparsam mevcut olan tüm podları sil daha sonra değişikliğe göre yeni podları oluştur diye declarative olarak tanımlamış olduk. Bu yapıya K8S dünyasında rollout diyoruz.
apiVersion: apps/v1
kind: Deployment
metadata:
name: rolldeployment
labels:
team: development
spec:
replicas: 10
selector:
matchLabels:
app: rolling
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 2
maxSurge: 2
template:
metadata:
labels:
app: rolling
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Burada strategy olarak RollingUpdate belirtilmiş. RollingUpdate recreate’in tam tersidir. Ben bir değişik yaptığım zaman bu işi aşamalı olarak yap. Yani benim deployment ile 10 pod oluşturulmuştu ben bunu update ettim maxUnavailable değeri kadar sil update edilmiş yeni podları oluştur. MaxSurge değeri ise update işlemi sırasında sistemde max update edilmemiş kaç adet pod kalacağını belirtir. Bu şekilde ortamda kesinti olmadan güncelleme yapabiliyoruz. Aynı zamanda RollingUpdate parametredir. Eğer biz deployment yaml içerisinde strategy kullanmazsak default RollingUpdate kullanır.
Bu yaml dosyasını apply edelim ama şu komutla :)
kubectl apply -f deployment-rollingupdate.yaml --record=true
RollingUpdate testi için deployment içerisinde bir değişiklik yapalım.
kubectl set image deployment rolldeployment nginx=httpd:alpine --record=true
#imperative olarak image değiştirdik.
Bu olmadı nginx:1.14.2 olarak değiştirelim image değerini
kubectl set image deployment rolldeployment nginx=nginx:1.14.2 --record=true
#imperative olarak image değiştirdik.
Biz niye değiştirip duruyoruz, neden record diye bir parametre ekliyoruz diye düşünebilirsiniz. Rollout konusunu anlatmak için öncelikle değişiklikler öncesinde değişiklikleri kaydetmemiz gerekiyordu. Bunu görüntülemek için
kubectl rollout history deployment
Değişikliğin id numarasına göre daha detaylı incelmeek için
kubectl rollout history deployment --revision=2
Image bilgileri değiştirdik, işlemleri de kaydettik yok? Evet eğer değişikliği geriye almak istiyorsak şu komutu;
kubectl rollout undo deployment rolldeployment
İstediğimiz bir revision anına dönmek istiyorsak da şunu kullanırız;
kubectl rollout undo deployment rolldeployment --to-revision=1
Rollout komutunun diğer kullanım şekilleri
kubectl rollout status deployment rolldeployment -w
#Deployment aşamalarını izlemek içinkubectl rollout pause deployment rolldeployment
#Deployment işlemini durdurmak içinkubectl rollout resume deployment rolldeployment
#Deployment işlemini devam ettirmek için
Service
Artık erişim konusuna geldik. Kubernetes service bizim podlara yada podların kendi aralarında nasıl iletişim kuracağını belirlediğimiz 4 farklı tipi olan objedir.
ClusterIP : Podların sadece bulundukları cluster içinde tanımlı bir ip set etmek için kullanılır. ClusterIP service oluşturulup label selector ile Pod’lara atama yapılabilir.(Default)
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: tomcat:8.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: frontendlb
spec:
type: ClusterIP
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 8080
NodePort : Worker node ip adresi Pod’lara erişim sağlamak için oluşturulur.
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: wordpress
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: frontendlb
spec:
type: NodePort
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 80
LoadBalancer : EKS ortamında aktif olarak kullanacağımız. AWS EKS’in bizim yerimeize LB tanımının yaptığı servis tipidir.
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: tomcat:8.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: frontendlb
spec:
type: LoadBalancer
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 8080
Biz AWS EKS üzerinde çalıştığımız için LoadBalancer yaml apply edelim. ve service listesine bakalım.
AWS EKS bize bir LoadBalancer balancer tanımı oluşturdu ve frontend app uygulamalarına yönelndirdi. EC2 > Loab Balancer yolunu takip edelim. Burada LoadBalancer bilgilerini göreceğiz.
Şimdi LB adresine erişerek test edebiliriz.
Deployment konusunda biz Deployment objesi oluşturduğumuz zaman ReplicaSet objesinin otomatik olarak oluştuğunu söylemiştim. Service için de benzer bir durum var biz Service tanımı oluşturunca arka planda endpoint tanımı oluşuyor.
Liveness Probe
Biz en son tomcat ayağa kaldırdık ve tomcat üzerine uygulama kurduğumuzu düşünelim. Eğer ortamdan bir pod kapanır veya pod seviyesinde bir hata olursa k8s yeni bir pod ayağa kaldırıyor ama tomcat içerisinde uygulamada problem varsa? Kubernetes bu durumda pod’un düzgün çalıştığını düşünerek bir işlem yapmayacaktır.
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
---
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
---
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
Yukarıda 3 farklı livenessprobe örneği var bunları inceleyebiliriz.
Readiness Probe
Yine örnek verelim. Prod ortam için deployment hazırladınız güzel de trafik alıyor uygulamanız RollingUpdate tanımı var update ettik. Ama bizim yeni oluşacak podlarımız oluştuk sonra trafik alabilmesi için belli hazırlıklar yapması gerekiyorsa? İşte onunda çözümü Readiness Probe
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: tomcat:8.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /healthcheck
port: 80
initialDelaySeconds: 20
periodSeconds: 3
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 20
periodSeconds: 3
Yukarıdaki örneği inceleyebilirsiniz. /ready path uygulamamızda olduğunu varsayalım.
Hap Bilgi 5
RollingUpdate tanımlı deploymen içerisinde bir update işlemi başlattığımızda, eğer eski pod bir işlem yapıyorsa “terminationgraceperiodseconds” değeri (30sn) kadar işlemi tamamlanması beklenir. Daha sonra eski pod terminate edilir yeni pod oluşur. Bu süre içerisinde eski pod’a yeni istek gönderilmez.
Resource Limit
Podlar biz ayarlamadığımız sürece ihtiyaçları olduğu zaman çalıştıkları sunucu kaynaklarından istedikleri kadar kullanırlar. Bunun tanımlamasını da şöyle yaparız.
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: tomcat:8.0
resources:
requests:
memory: "64M"
cpu: "250m"
limits:
memory: "256M"
cpu: "0.5"
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /healthcheck
port: 80
initialDelaySeconds: 20
periodSeconds: 3
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 20
periodSeconds: 3
env:
- name: User
value: Arif
- name: Team
value: Devops
Yukarıdaki örnekte ek olarak env tanımının nasıl yapıldığını da ekledim.
2.3 Son Düzlük
Volume
Docker ortamlarından aşina olduğumuz bir konu aslında bu. Container içerisindeki datalar container kapandığı zaman sıfırlanır. Bazı durumlarda bu bizim için sorun değildir(stateless) ama içerisindeki veriler bizim için önemli ise(stateful) volume oluşturmamız gerekir. Şu örnek ile anlamaya çalışalım.
apiVersion: v1
kind: Pod
metadata:
name: emptydir
spec:
containers:
- name: frontend
image: tomcat:8.0
ports:
- containerPort: 80
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: {}
Burada kubernetes bize node üzerinde boş volume oluşturur. Pod silinirse volume silinir bu tür volume Ephemeral volume denir. Birden fazla container içerisinde kullanılabilir. Ama unutmayım Pod objesi silinirse cache-val silinecektir.
apiVersion: v1
kind: Pod
metadata:
name: hostpath
spec:
containers:
- name: hostpathcontainer
image: tomcat:8.0
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
Bu yaml örneğinde ise node üzerinde var olan bir dizini container üzerine mount etmek için kullanılır.
Secret
Kubernetes ortamlarında kullanacağımız hassas bilgileri store etmek için kullanırız. Pod ve deployment gibi kubernetes objesidir. Secret örneğini şu yaml ile inceleyebiliriz.
apiVersion: v1
kind: Secret
metadata:
name: db
type: Opaque
stringData:
db_server: db.arif.com
db_username: admin
db_password: arifwashere
- Secret ile secret verilerini kullanacağımız obje aynı namespace içerisinde olmalıdır.
- Yukarıdaki gibi farklı amaçlar için secret type vardır.
- StringData yerine Data kullanabiliriz. Data ile yaparsak base64 encoded yazabiliriz.
apiVersion: v1
kind: Pod
metadata:
name: secretpodenvall
spec:
containers:
- name: secretcontainer
image: tomcat:8.0
envFrom:
- secretRef:
name: db
Burada Pod secret bilgilerini nasıl env olarak alıyor inceleyebilirsiniz.
ConfigMap
Secret gibi key value değerlerini tutar. Gizli olmayan şifrelemeden yazabileceğimiz değerleri tutmak için kullanırız.
kind: ConfigMap
apiVersion: v1
metadata:
name: example-configmap
data:
# Configuration values can be set as key-value properties
database: mongodb
database_uri: mongodb://localhost:27017
---
kind: Pod
apiVersion: v1
metadata:
name: pod-env-var
spec:
containers:
- name: env-var-configmap
image: nginx:1.7.9
envFrom:
- configMapRef:
name: example-configmap
Affinity
Podlarımızın çalışacakları Worker Nodeları belirleyebileceğimiz alt objedir. Aslında Selector ile a benzer işi yapar ama daha fazla opsiyon sunar. Bu konu çok fazla parametreye sahip şu yazıyı okumanızı tavsiye ederim.
Taint ve Toleration
Affinity tanımları yaparken Pod nerede çalışacak ayarlarını yaparken Taint ve Toleratin tanımları ile Node üzerinde taint eklenerek, Yaml içerisinde tolerations işlemleri ile yapılır.
Yazı oldukça uzun olduğu ve yukarıdaki 2 konuda çok parametreye sahip olduğu için referans belirtme hakkımı kullanıyorum. Taint kısmında şunu unutmayalım. , elimizdeki Node neleri tolere edebilir bunların tanımını yapıyoruz. Pod tanımlarında ise tolere edebileceği özellikleri veriyoruz.
DaemonSet
DaemonSet deployment objesine oldukça benzeyen belirttiğimiz Pod tanımına göre podlar oluşturan objedir. DaemonSet her Node üzerinde pod oluşturmamız gerektiği zamanda kullanırız.(Filtrelenebilir) Genellikle log toplama uygulamaları gibi uygulamar için kullanılır.
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
Hap Bilgi 6
DaemonSet her Node üzerinde pod oluşturur demiştik. Bunlara master node da dahil olabilir ama Master Node üzerinde node-role.kubernetes.io/master:NoSchedule Taint vardır. Bu yüzden eğer DaemonSet Master node üzerinde de Pod oluşturmasını isterseniz Tolerations eklemeniz gerekir.
Persistent Volume(PV) ve Persistent Volme Claim(PVC)
Volume kısmında oluşturduğumuz emptyDir volue Ephemeral olarak tanımlanır yani Deployment yada Pod objesi silinirse volume silinecektir. Uygulamanın cache tmp dosyalarını tutmak için bu yöntem işimizi görüyordu ama database gibi bir uygulamamız varsa ise? Bizim bu volumeleri cluster dışında bulunan bir alanda oluştuurp datalarımızı güvenle saklamamız gerekiyor, işte onun çözümü de burada.
K8S’in desteklediği PV çeşitleri şöyledir.
AWS EBS örneği yazacağım…
Oluşturduğumuz PV objelerini direk olarak Pod yada Deployment gibi herhangi bir objeye bağlayamıyoruz . Bunun için PVC tanımı da yapmamız gerekiyor.
Devamı geliyor …
Kaynaklar;