Dapr Statestore konusunu ile ilgili burada bir yazı paylaşmıştım. Daha önce paylaştığım bu yazımda statestore kavramından bahsettim. Dapr’ a bir statestore Docker-Compose ile nasıl entegre edilir öğrendik. Şimdi de bu işlemi Kubernetes ile gerçekleştirmek istiyorum. Bu işlemi başarılı bir şekilde gerçekleştirmek için aşağıdaki adımları izleyeceğim.
- Öncelikle Kubernetes Cluster’rına Dapr kurulumu yapacağım.
- Dapr’a statestore olarak entegre edeceğim “Redis Enterprise Cluster” kurulumunu yapacağım.
- Redis’e yazdığım dataları görüntüleyebilmek için “Redisinsight” kurulumu yapacağım.
- Dapr’ı Redis ile bağlayan component configurasyonlarını hazırlayıp Kubertnetes’e göndereceğim.
- Microservislerin Dockerfile ile bir image hazırlayıp Container Registery‘ye yollayacağım.
- Kubernetes Deployment providerını kullanabilmek için “yaml” hazırlayacağım.
- Hazırlamış olduğum Yaml dosyalarının çalışması ile ortaya çıkacak podlara “Dapr Sidecar” entegre edebilmek için “Deployment” Yaml’larına “Dapr annotations” ekleyeceğim.
- Son olarak hazırlamış olduğum Deployment Yaml’larını Kubernetes clusterımda çalıştırdığımda, microservislerimin statestore bağlanabildiğini ve Redisinsight ilede dataları görebilmem gerekiyor.
Yukarıda adım adım sıraladığım gerçekleştirmeye başlayalım.
1. Kubernetes Cluster’ına Dapr Kurulumu.
Bu konuyu daha önce bir yazımda işlemiştim. Kurulum için farklı yöntemler mevcut. Biz, Dapr CLI yeteneklerini kullanarak “dapr init -k” komutu ile basit ve hızlı bir kurulum gerçekleştireceğiz.Kurulum bittiğinde Kubernetes Cluster’ında “dapr-system” isimli bir namespacede aşağıdaki gibi podların oluşmasını bekliyoruz.

2. Redis Enterprise Cluster Kurulumu
Redis’i çok daha basit bir yapıda da kurabilirdim fakat Redis Enterprise Cluster yapısına da değinmek, böyle bir yapının da olduğunu vurgulamak maksadıyla bu kurulumu seçtim. Kubernetes ortamında bu kurulumu yapmak için takip edeceğim dökümantasyon linki burada. Öncelikle kurulumu yapmak istediğim namespace belirleyelim.
kubectl create ns redis-enterprise-cluster
Bu işlemi gerçekleştirdikten sonra “operator bundle” yüklememiz gerekiyor. Bu işlem için aşağıdaki komutları çalıştıralım.
$VERSION = (Invoke-RestMethod -Uri "https://api.github.com/repos/RedisLabs/redis-enterprise-k8s-docs/releases/latest").tag_name
kubectl -n redis-enterprise-cluster apply -f https://raw.githubusercontent.com/RedisLabs/redis-enterprise-k8s-docs/$VERSION/bundle.yaml
Kurulumu yaptıktan sonra aşağıdaki komutu çalıştırarak kurulumu gerçekleştiğini doğrulayalım.
kubectl -n redis-enterprise-cluster get deployment redis-enterprise-operator
Komutun sonucunda görmemiz gereken çıktı şu şekilde.

Redis Enterprise Operator kurulumu tamamlandıktan sonra, RedisEnterpriseCluster
Redis Enterprise Cluster (REC) özelliklerini içeren özel bir kaynaktan oluşturacağız. Bu kaynağa ait yaml dosyası “redisenterprisecluster.yaml” ismi ile kaydettiğinizi farz ediyorum.
apiVersion: app.redislabs.com/v1
kind: RedisEnterpriseCluster
metadata:
name: redis
namespace: redis-enterprise-cluster
spec:
nodes: 3
redisEnterpriseNodeResources:
limits:
cpu: 1
memory: 16Gi
requests:
cpu: 1
memory: 16Gi
Bu yaml dosyasını aşağıdaki komut ile çalıştırmalısınız.
kubectl apply -f redisenterprisecluster.yaml
Bu kurulumda tamamlandığında aşağıdaki gibi podların “redis-enterprise-cluster” namespacesinde çalıştığını aşağıdaki resimde olduğu gibi görmemiz gerekmekte.

Kurulum tamamlandıktan sonra Redis Enterprise Operator kurulumu ile birlikte gelen “redisenterprise UI” ulaşıp bir database oluşturmamız gerekiyor. “redisenterprise UI” ulaşabilmek için Kubernetes’te “port-forward” yöntemini kullanacağız. Öncelikle Redis’ eait Kubernetes Service’lerini görüntüleyelim.
kubectl get svc -n redis-enterprise-cluster

Bu komut ile gelen Service listesinde “redis-ui” olduğunu görüyoruz. Bu service “port-forward” yöntemini uygularsak ara yüze ulaşabiliriz.
kubectl -n redis-enterprise-cluster port-forward service/redis-ui 28015:8443

Arayüze “https://localhost:28015” urli ile aşağıda görüldüğü gibi ulaşabilirsiniz.

Şimdi sırada bu ara yüzü kullanarak bir Redis Database oluşturmakta. Peki username, password bilgilerine nasıl ulaşırım. Redis Enterprise Operator kurulumu ile birlikte gelen bu UI ait username, password bilgileride Kubernetes Secret’larda saklanmakta. Aşağıdaki komut ile görevbiliriz.
kubectl get secret -n redis-enterprise-cluster

“redis” isimli Secret’ ta saklana username, password bilgilerini Base64 formatında aşağıdaki komut ile görüntüleyebiliriz.
kubectl edit secret redis -n redis-enterprise-cluster

Base64 ile encode edilmiş username, password bilgisini aşağıdaki powershell script ile decode edebilirsiniz.
$encodedText = "ZGVtb0ByZWRpcy5jb20="
$decodedText = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encodedText))
Write-Host $decodedText
Artık username, password öğrendiğimize göre arayüze giriş yapıp database oluşturabiliriz.

Görüldüğü gibi “statestore” isimli bir database oluşturuyorum. Active düğmesine tıkladığımızda database aktif edilmiş olacak. Bu işlemin sonunda aşağıdaki resimde görüldüğü gibi “redis-enterprise-cluster” namespacesinde “statestore” isimli bir Service oluşmalı. Bu database Kubernete içerisinden “http://statestore.redis-enterprise-cluster.svc.cluster.local:12004” adresi ile ulaşabiliriz. Yani artık bağlanmamız gerek redis host adresi tam olarak biraz önce verdiğim URL.

Artık Redis Database hazır. Bir sonraki aşama olan dataları görüntüleyebileceğimiz “Redisinsight” kurulumuna başlayabiliriz. Bu kurulum için Deployment ve Service yaml dosyasını paylaşacağım. Aşağıdaki yaml formatını “redisinsight.yaml” ismiyle kaydettiğinizi farz ediyorum.
apiVersion: apps/v1
kind: Deployment
metadata:
name: redisinsight
labels:
app: redisinsight
namespace: redis-enterprise-cluster
spec:
replicas: 1
selector:
matchLabels:
app: redisinsight
template:
metadata:
labels:
app: redisinsight
spec:
imagePullSecrets:
- name: default-secret
containers:
- name: redisinsight
image: redislabs/redisinsight:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: db
mountPath: /db
ports:
- containerPort: 8001
protocol: TCP
resources:
limits:
cpu: 500m
memory: 1024Mi
requests:
cpu: 500m
memory: 1024Mi
volumes:
- name: db
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: redisinsight-service
namespace: redis-enterprise-cluster
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8001
selector:
app: redisinsight
Aşağıdaki komutu çalıştırarak “redisinsight” hazır hale getirebilirsiniz.
kubectl apply -f redisinsight.yaml
Redisenterprise UI görüntülemek için kullandığımız “port-forward” yöntemini “redisinsight” içinde uygulayarak arayüzüne ulaşabiliriz. Arayüzde gördüğünüz port değişiklik gösterebilir. Kubernetes Cluster’ında “statestore” isimli servisin portunu temsil etmekte.
kubectl -n redis-enterprise-cluster port-forward service/redisinsight-service 28016:8080


Redisinsight kurulumunu da tamamladığımıza göre bir sonraki aşama olan Dapr statestore componentini oluşturabiliriz. Aşağıdaki componentin detaylarını paylaştığım makalem burada.
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: daprdotnetjourney
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: statestore.redis-enterprise-cluster.svc.cluster.local:12004
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
scopes:
- basket-api
Oluşturduğumuz componenti Kubernetes’e aşağıda komut ile göndeceğiz.
kubectl apply -f statestore.yaml
Oluşturduğumuz component Kubernetes’te Custom Resource Defination olarak isimlendirilen bir yapı. Görüntüleyebilmek için aşağıdaki komutu kullanabilirsiniz.
kubectl get components.dapr.io

Şimdi sırada microservisleri Container Registery göndermekte. Bunun için öncelikle uygulamaya özel Dockerfile kullanarak bir image oluşturmak. Ben Basket Api’ ye ait Dockerfile “Dockerfile.BasketApi” ismi ile, Aggragator Api’sine ait Dockerfile ise “Dockerfile.Aggregator” ismi ile oluşturdum.
Aşağıdaki komutları kullanarak image oluşturuyorum.
docker build -f Dockerfile.BasketApi -t cahityusuf/basket-api:v1.0.3 .
docker build -f Dockerfile.Aggregator -t cahityusuf/aggregator-api:v1.0.2 .
Şimdi oluşturduğumuz imageları aşağıdaki komut ile Container Registry yollayabiliriz.
docker push docker.io/cahityusuf/basket-api:v1.0.3
docker push docker.io/cahityusuf/aggregator-api:v1.0.2
Image’ları Public Container Registery yolladığımıza göre artık bir sonraki aşama olan Kubernetes Deployment yaml hazırlamakta. Daha önce service invocation yöntemini anlattığım bir makalemde bu yamlların detaylarını anlattım makalem burada.
Aggregator Api Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: aggregator-deployment
namespace: daprdotnetjourney
labels:
app: aggregator
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
selector:
matchLabels:
service: aggregator
template:
metadata:
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: aggregator-api
dapr.io/app-port: "80"
dapr.io/config: "config"
labels:
app: aggregator
service: aggregator
spec:
containers:
- name: aggregator
image: docker.io/cahityusuf/aggregator-api:v1.0.2
imagePullPolicy: Always
ports:
- containerPort: 80
protocol: TCP
resources:
requests:
memory: "500Mi"
cpu: "0.3"
limits:
memory: "500Mi"
cpu: "0.5"
env:
- name: ASPNETCORE_ENVIRONMENT
value: Development
- name: ASPNETCORE_URLS
value: http://+:80
Aggregator Api Service
apiVersion: v1
kind: Service
metadata:
name: aggregator-svc
namespace: daprdotnetjourney
labels:
app: aggregator
service: aggregator
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
service: aggregator
Basket Api Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: basketapi-deployment
namespace: daprdotnetjourney
labels:
app: basketapi
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
selector:
matchLabels:
service: basketapi
template:
metadata:
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: basket-api
dapr.io/app-port: "80"
dapr.io/config: "config"
labels:
app: basketapi
service: basketapi
spec:
containers:
- name: basketapi
image: docker.io/cahityusuf/basket-api:v1.0.3
imagePullPolicy: Always
ports:
- containerPort: 80
protocol: TCP
resources:
requests:
memory: "500Mi"
cpu: "0.3"
limits:
memory: "500Mi"
cpu: "0.5"
env:
- name: ASPNETCORE_ENVIRONMENT
value: Development
- name: ASPNETCORE_URLS
value: http://+:80
Basket Api Service
apiVersion: v1
kind: Service
metadata:
name: basket-svc
namespace: daprdotnetjourney
labels:
app: basketapi
service: basketapi
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
service: basketapi
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: daprdotnetjourney-ingress
namespace: daprdotnetjourney
annotations:
ingress.class: "nginx"
spec:
ingressClassName: nginx
rules:
- host: basket.daprdotnetjourney.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: basket-svc
port:
number: 80
- host: aggregator.daprdotnetjourney.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: aggregator-svc
port:
number: 80
Yukarıda vermiş olduğum configurasyonları Kubernetes’e gönderdiğinizde uygulamalar ayağa kalkacaktır. Uygulamalar ile iletişim kurabilmek için Service ve Ingress configurasyonlarıda yaptığımı fark etmişsinizdir. Aşağıdaki komut ile mevcut Ingress’leri görüntüleyeceğiz ve karşımıza çıkan listedeki host name ile adress bilgilerini local host dosyamıza kaydettiğimizde apilere ulaşmış olacağız.
kubectl get ingress -n daprdotnetjourney

Bu işlemi kısa yoldan daha önce de yaptığımız gibi port-forward yöntemi ile de yapabilirdik fakat ingress tanımını da göstermek istedim. Host kaydımızı yaptıysak “http://aggregator.daprdotnetjourney.com/swagger/index.html” adresinden ilgili endpointi bulalım var Aggragator Api üzerinden Basket Api’ ye isteğimizi atalım. Basket datasının belirlediğimiz statestora yazıldığını görmeliyiz.

Resimde görüldüğü gibi isteğimizi attık ve aşağıda görüldüğü gibi Basket datasını Redis statestora Dapr aracılığı ile yazıldı.

Tüm yazı sonunda öncelikle Kubernetes ortamında Redis Enterprise Cluster nasıl kurulur, Dapr ile bağlantısı nasıl sağlanır ve yine Dapr aracılığı ile datamızı statestore nasıl yazarız öğrenmiş olduk.
Projenin linkini buraya bırakıyorum.
Tekrar görüşmek dileğiyle.