Kubernetes Kümelerinde KEDA ile AWS CloudWatch Metriklerine Dayalı Yatay Ölçeklendirme

Ramazan Taplamacı
8 min readDec 31, 2022

--

Hey mate, if you are looking for an English version of this post, you can find it here. Enjoy it!!

Günümüzde neredeyse bütün ürün ekipleri geliştirdikleri ürünün iş yüklerini Kubernetes üzerinde koşturmakta veya Kubernetes üzerinde koşturmayı planlamaktadır. Bu noktada ekiplerin neden Kubernetes seçtiğine bakmak gerekirse; Kubernetes ilgili iş yükleri için yönetimi ve entegrasyonları kolaylaştırır aynı zamanda hızlı ve esnek aksiyonlar almaya olanak, kolay ölçeklenebilirlik, esneklik ve tutarlılık sağlar. Ez cümle yapacağınız yönetimsel faaliyetlerin eforunu bir hayli düşürür.

Bu makalede; Kubernetes’in sağladığı ölçeklenebilirlik hususunda Kubernetes’e ilgili kümelerde koşan iş yüklerin AWS CloudWatch üzerinden ulaşılan metrikler doğrultusunda yatayda ölçekleme yeteneğini kazandıran KEDA aracını inceliyor olacağız.

KEDA Nedir?

KEDA
Görsel-1 KEDA

KEDA; açılımı “Kubernetes Event-driven Autoscaling” olan, temelde Kubernetes Kümelerine harici kaynaklar üzerindeki metriklere göre yatayda otomatik ölçeklenebilirlik yeteneği kazandıran açık kaynak bir araçtır. Hali hazırda K8s Kümelerinde yatayda ölçeklenebilirlik sağlamak amacıyla HPA (Horizontal Pod Autoscaler) kullananlar bu araca oldukça aşina olacaklardır, keza KEDA’da temelde HPA oluşturup ilgili HPA’yı yönetmektedir, fakat HPA’nın aksine ilgili küme özelindeki metrikler dışında harici kaynakların metriklerini de baz alarak ilgili ölçekleme işlemini sağlayabilmektedir.

Ayrıca KEDA 12 Mart 2020 itibariyle CNCF Kuluçka projesi olarak kabul edilmiş ve olgunluk seviyesinde olan bir projedir.

KEDA Komponentleri Nelerdir ve Nasıl Çalışır?

Görsel-2 KEDA Komponentleri ve Mimarisi

KEDA; temelde “keda-operator” ve “keda-operator-metrics-apiserver” olarak adlandırılan komponentlerden oluşmaktadır. Ayrıca ilgili iki komponente ek olarak “ScaledObjects”, “ScaledJobs” ve “TriggerAuthentication / ClusterTriggerAuthentication” gibi K8s CRD’lerden (Custom Resource Definition) oluşmaktadır. Görsel-2 üzerinden KEDA mimarisine ulaşabilirsiniz. İlgili komponentleri incelemek gerekirse;

“keda-operator”: Basitçe ölçeklenecek iş yükünün dinamik olarak ölçeklenmesinden sorumludur.

“keda-operator-metrics-apiserver”: Harici kaynaklardan metriklerin okunup K8s “metric-server” gibi ilgili metrikleri sağlamaktan sorumludur. Bu metrikler sayesinde “keda-operator” takip ettiği iş yükünü ölçekler.

“ScaledObjects”: Harici metrik veya olay kaynağı ile dinamik olarak ölçeklenecek olan objenin eşleşmesini sağlar. Bu obje bir Deployment olabileceği gibi; StateFullSet veya CRD de olabilir.

“ScaledJobs”: Harici metrik veya olay kaynağı ile dinamik olarak ölçeklenecek olan K8s Job’ının eşleşmesini sağlar.

“TriggerAuthentication / ClusterTriggerAuthentication”: Bu CRD ise harici metrik veya olay kaynağını izlemek için kullanılması gereken kimlik doğrulama (authentication) konfigürasyonları veya K8s “Secret”ı içerir.

KEDA’nın Desteklediği Olay veya Metrik Kaynakları

KEDA; Amazon Web Services (a.k.a AWS), Microsoft Azure ve Google Cloud Platform (a.k.a GCP) gibi büyük Cloud sağlayıcıların çeşitli araçlarını olay veya metrik kaynağı olarak kullanabildiği gibi, sıklıkla kullanılan Prometheus, Redis, RabbitMQ, PostgreSQL, MongoDB, MYSQL gibi diğer araçları da kaynak olarak kullanılabilmektedir. Güncel olarak desteklenen araçların tamamına aşağıdaki adresten ulaşabilirsiniz:

Örnek Senaryo ve PoC

Bu makale kapsamında daha önce bahsedildiği üzere AWS CloudWatch üzerindeki metrikler baz alınarak ölçekleme işlemi yapılacak. Daha açık olmak gerekirse; AWS üzerinde çalışan neredeyse tüm servisleri izleyebileceğiniz servis olan AWS CloudWatch servisini metrik kaynağı olarak kullanıp temelde bir çok senaryonun uygulanabilir olduğunu görmüş olacağız. Bu PoC’de Amazon SQS üzerindeki item sayısına istinaden Amazon EKS K8s kümesi üzerinde bulunan Consumer iş yüklerimizi dinamik olarak ölçekleyeceğiz. Aşağıda gereksinimler ve uygulanması gereken adımlara ulaşabilirsiniz:

Gereksinimler:

  • Temel AWS IAM bilgisi,
  • AWS hesabı ve Amazon SQS,
  • K8s kümesi (≥ Kubernetes 1.20),
  • Local ortamda kurulu ve konfigüre edilmiş olan “aws-cli” (Amazon EKS kullananlar için), “kubectl” ve “Helm3”

Uygulanacak Adımlar:

  1. AWS CloudWatch Metrics Server için ilgili authentication konfigürasyonlarının yapılması.
  2. KEDA’nın Amazon EKS K8s kümesine deploy edilmesi.
  3. “ScaledObject”in konfigüre edilmesi ve Consumer’ların çalıştığı “namespace”e deploy edilmesi.
  4. Oluşturulan alt yapının test edilmesi.

AWS CloudWatch Metrics Server Erişim Konfigürasyonları

Ben bu PoC’de K8s kümesi olarak Amazon EKS ve worker node olarak AWS Ec2 Instance kullandığımdan dolayı; AWS CloudWatch üzerinden metrikleri okumak amacıyla yapılacak erişimi sağlamak için ilgili instancelara tanımladığım AWS IAM Role’e erişim izni sağlıyor olacağım. Bu işlem için aşağıdaki AWS IAM Policy’i oluşturup bahsi geçen AWS IAM Role’e ekliyorum.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudwatch:GetMetricData"
],
"Resource": "*"
}
]
}

Siz ilgili çalışmayı on-prem ortamda koşan K8s kümesi üzerinde yapacaksanız ilgili kimlik doğrulama ve erişim işlemlerini “ScaledObject”te AWS IAM User AccessKey ile veya “TriggerAuthentication / ClusterTriggerAuthentication” CRD’si ile sağlayabilirsiniz.

KEDA’nın K8s Kümesine Deploy Edilmesi

Deploy işlemi öncesi KEDA’nın koşacağı K8s “namespace”i oluşturuyoruz. Bu işlemi aşağıdaki komut ile sağlayabilirsiniz:

$ kubectl create ns keda
Görsel-3 Namespace oluşturma

İlgili “namespace”i oluşturduktan sonra KEDA’yı kümeye deploy ediyoruz. Ben deploy işlemi için KEDA tarafından sağlanan Helm Chart’ı kullandım, sizde aşağıdaki komutları sırasıyla takip ederek deployment işlemini sağlayabilirsiniz:

$ helm repo add kedacore https://kedacore.github.io/charts
$ helm repo update
$ helm install keda kedacore/keda --namespace keda
Görsel-4 K8s kümesine KEDA deploy etme

Eğer Helm kullanmak istemiyorsanız, YAML konfigürasyonlarına buradan (https://keda.sh/docs/2.8/deploy/#yaml) ulaşabilirsiniz.

Aşağıdaki komut ile birlikte ise deploy edilen “keda-operator” ve “keda-operator-metrics-apiserver” “pod”larını görebilirsiniz.

$ kubectl get pod -n keda
Görsel-5 “keda-operator” ve “keda-operator-metrics-apiserver” podları

ScaledObject’in Konfigüre ve Deploy Edilmesi

Daha önce de belirtildiği üzere “ScaledObject” harici metrik veya olay kaynağı ile dinamik olarak ölçeklenecek olan K8s objesinin eşleşmesini, HPA konfigürasyonlarını ve gerekiyorsa kimlik doğrulama işlemi için tanımların operatöre sağlanmasına yardımcı olur. Kısaca; ölçeklenecek obje, metrik veya olay kaynağı, eşik değerleri, ölçekleme boyutu ve periyodu gibi özelleştirmelerin yapıldığı CRD’dir. Aşağıda örnek bir “ScaledObject” konfigürasyonunu görebilirsiniz.

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: aws-cloudwatch-keda-scaledobject
namespace: rtaplamaci
spec:
scaleTargetRef:
name: keda-poc-consumer
minReplicaCount: 0 # We don't want pods if the queue is empty
maxReplicaCount: 3 # We don't want to have more than 5 replicas
pollingInterval: 30 # How frequently we should go for metrics (in seconds)
cooldownPeriod: 10 # How many seconds should we wait for downscale
triggers:
- type: aws-cloudwatch
metadata:
# Required: namespace
namespace: AWS/SQS
# Optional: Dimension Name
dimensionName: QueueName
# Optional: Dimension Value
dimensionValue: keda-poc-rtaplamaci-sqs
# Optional: Expression query
expression: SELECT MAX(ApproximateNumberOfMessagesVisible) FROM "AWS/SQS" WHERE QueueName = 'keda-poc-rtaplamaci-sqs'
metricName: ApproximateNumberOfMessagesVisible
targetMetricValue: "10"
minMetricValue: "2"
# Required: region
awsRegion: "us-east-1"
identityOwner: operator # Optional. Default: pod
# Optional: Collection Time
metricCollectionTime: "60" # default 300
# Optional: Metric Statistic
metricStat: "Maximum" # default "Average"
# Optional: Metric Statistic Period
metricStatPeriod: "60" # default 300
# Optional: Metric Unit
metricUnit: "Count" # default ""
# Optional: Metric EndTime Offset
metricEndTimeOffset: "60" # default 0

Bu konfigürasyondaki değişkenlerin ne işe yaradığı ve kısıtlamaları aşağıdaki konfigürasyon dosyasından bulabilirsiniz ;

apiVersion: keda.sh/v1alpha1
kind: "CRD türü (ScaledObject)",
metadata:
name: "İlgili ScaledObjecte verilecek isim",
namespace: "ScaledObject ve dinamik olarak ölçeklenecek olan objenin çalıştığı K8s namespace'i",
spec:
scaleTargetRef: "Dinamik olarak ölçeklenecek obje referansı",
apiVersion: "Dinamik olarak ölçeklenecek olan objenin kullandığı api varsiyonu (Opsiyonel, Varsayılan: apps/v1)",
kind: "Dinamik olarak ölçeklenecek olan objenin türü (Opsiyonel, Varsayılan: Deployment)",
name: "Dinamik olarak ölçeklenecek olan objenin adı (Zorunlu, ScaledObject ile aynı namspace'de bulunmalı)",
envSourceContainerName: "Ölçekleme işleminde kullanılacak ortam değişkenlerinin alındığı container (Opsiyonel, Varsayılan: .spec.template.spec.containers[0])",
minReplicaCount: "HPA'nın ölçekleyebileceği minimum pod sayısı (Opsiyonel, Varsayılan: 0)",
maxReplicaCount: "HPA'nın ölçekleyebileceği maksimum pod sayısı (Opsiyonel, Varsayılan: 100)",
pollingInterval: "Ölçekleme işleminin tetiklenmesi için metrik/olay kaynağının kontrol edileceği periyot (Opsiyonel, Varsayılan: 30 saniye)",
cooldownPeriod: "ScaledObject'in metrik/olay kaynağı baz alınarak en son aktif olarak belirtildiği, HPA'nın pod sayısını 0'a düşürmeden önce bekleyeceği süre (Opsiyonel, Varsayılan: 300 saniye)",
triggers: "Metrik/olay kaynağı",
- type: "Trigger tipi",
metadata:
namespace: "AWS CloudWatch Namespace'i (Zorunlu)",
dimensionName: "Metrik (Kaynak) belirteci Anahtar kısmı (Opsiyonel, Expression belirtilmemişse zorunlu)",
dimensionValue: "Metrik (Kaynak) belirteci Değer kısmı (Opsiyonel, Expression belirtilmemişse zorunlu)",
expression: "Metrik sorgusu (Opsiyonel, dimensionName ve/veya dimensionValue belirtilmemişse zorunlu)",
metricName: "Metrik ismi",
targetMetricValue: "Metrik için belirlenen threshold (Opsiyonel, Varsayılan: 0, Ondalık olabilir)",
minMetricValue: "Metrik cevap dönmemesi veya boş dönmesi durumunda kullanılacak olan değer (Opsiyonel, Varsayılan: 0, Ondalık olabilir)",
awsRegion: "Region (Zorunlu)",
identityOwner: "Kimlik doğrulama işlemleri için kullanılacak identity kaynağı (Opsiyonel, Varsayılan: pod, Alabileceği Değerler: pod veya operator)",
metricCollectionTime: "Scaler'ın geçmişe dönük kontrol edeceği periyot (metricStatPeriod'dan büyük olmalıdır, metricStatPeriod'un katıları olacak şekilde değer set edilmesi performansı iyileştirecektir)",
metricStat: "Kullanılacak olan istatistik tipi (Opsiyoneldir, Varsayılan: Avarage, Alabileceği Değerlerin Bazıları: SampleCount, Sum, Average, Minimum, Maximum)",
metricStatPeriod: "İlgili metrik için kullanılacak olan frekans (Opsiyonel, Varsayılan: 300, Alabileceği Değerler: 1, 5, 10, 30, 60 ve 60'ın katları)",
metricUnit: "Metrik birimi (Opsiyonel, Varsayılan: none, Alabileceği Değerler: Bytes, Seconds, Count, ve Percent)",
metricEndTimeOffset: "Belirtilen periyotta kullanılacak olan en son data (Opsiyonel, Varsayılan: 0)"

İlgili “ScaledObject” dosyasını hazırladıktan sonra aşağıdaki komut ile iligli objeyi K8s kümesine deploy edebilirsiniz;

$ kubectl apply -f ScaledObject.yaml
Görsel-6 ScaledObject Deploy etme

Deploy işlemi tamamlandıktan sonra ise aşağıdaki komut ile objenin durumunu kontrol edebilirsiniz;

$ kubectl get scaledobject -n < namespace >
Görsel-7 ScaledObject kontrol etme

Ölçekleme Testlerinin Yapılması

İlgili ölçekleme testleri için olay kaynağı olarak kullanılan Amazon SQS üzerine bir miktar mesaj pushladım, ilgili puhslama sonrası ScaledObject ve ölçeklenecek objenin davranışlarını gözlemledim.

Görsel-8 Amazon SQS Mesaj sayısı grafiği

Yaptığım gözlemlerde ScaledObject’in oluşturduğu HPA objesi belirlenen seviyeleri geçtiğinde “Active” hale gelip ilgili objeyi ölçekledi, ayrıca belirlenen periyot doğrultusunda ilgili ölçeklemeyi arttırdı. ScaledObject’in ve HPA’nın durumunu aşağıdaki komutları kullanarak görebilirsiniz:

$ kubectl get hpa -n < namespace >
$ kubectl get scaledobject -n < namespace >
Görsel-8 ScaledObject ve HPA davranışlarını kontrol etme

İlgili Amazon SQS üzerindeki mesajlar consume edildikten sonra beklendiği üzere “cooldownPeriod” süresi beklendikten sonra ilgili obje sayısı HPA’da belirlenen minimum değere düştü. Ayrıca ilgili ScaledObject ile ilgili detayları görmek için aşağıdaki komutu kullanabilirsiniz;

$ kubectl describe scaledobject < scaledobject-name > -n < namespace >
Görsel-8 ScaledObject detayları

Ortamın Temizlenmesi

PoC kapsamında oluşturulan kaynakları aşağıdaki sıralamayı takip ederek her hangi bir sorun yaşamadan silebilirsiniz;

  1. Oluşturulan ScaledObject’i aşağıdaki komutlardan biri yardımıyla silebilirsiniz;
$ kubectl delete scaledobject < scaledobject-name > -n < namespace >

veya

$ kubectl delete -f ScaledObject.yaml

2. Sonrasında KEDA ve komponentlerini aşağıdaki komut ile K8s kümesinden silebilirsiniz;

$ helm delete keda -n keda

3. Son olarak KEDA için oluşturulan namespace’i aşağıdaki komut ile silebilirsiniz;

$ kubectl delete ns keda
Görsel-9 Ortamın temizlenmesi

Sonuç

Bu makale kapsamında KEDA yardımıyla AWS üzerinde çalışan neredeyse tüm servisleri izleyebileceğiniz servis olan AWS CloudWatch servisini metrik/olay kaynağı olarak kullanıp K8s kümesinde koşan herhangi bir iş yükünün ölçeklenebileceğini görmüş olduk. Bu PoC’de Amazon SQS üzerindeki item sayısına istinaden Amazon EKS K8s kümesi üzerinde bulunan Consumer iş yüklerimizi dinamik olarak ölçekledik.

Ben bu yazıyı yazarken ve PoC’Yi yaparken çok eğlendim, ayrıca KEDA Production ortamında iş yüklerini doğru metriklerle ölçeklemek için çok faydalı oldu, umarım sizde okurken eğlenmiş ve sizler için de faydalı bir yazı olmuştur, bir sonraki makalede görüşmek üzere.

This is the Way…

--

--

Ramazan Taplamacı

Computer Engineer | Cloud Native Engineer | DevOps Engineer | AWS Certified Solution Architect Professional | @rtaplamaci