Bir CI/CD Anatomisi: Flask ve Redis Mikroservislerini Kubernetes Üzerinde Tam Otomasyonla Canlıya Almak ve izlemek


Neden Monolitik Yapıları Terk Ediyoruz?

Günümüzün modern operasyonlarında, sistemlerin sadece “çalışıyor” olması yeterli değildir; aynı zamanda hataya dayanıklı, saniyeler içinde ölçeklenebilir ve insan müdahalesi olmadan güncellenebilir olmaları şarttır. Bu yazımızdaki vizyonunu kurgularken, en baştan geleneksel ve hantal “Monolitik” (tek parça) mimariyi reddederek yola çıktık.

Çünkü monolitik bir uygulamada, sistemin herhangi bir noktasındaki ufak bir çöküş, tüm operasyonun durması anlamına gelir. Bunun yerine, “Single Responsibility” prensibine sadık kalarak, uygulamamızı bağımsız çalışan, küçük ve çevik mikroservislere böldük. Bu yazıda, linux tabanlı bir sunucudan başlayıp, kodun GitHub’a push edildiği andan itibaren “Zero-Touch” olarak da bildiğimiz sıfır insan temasıyla Kubernetes kümesinde canlıya alınmasına kadar uzanan tam otomatize CI/CD üretim hattımızın anatomisini kuracağız.


Mimari Tasarım ve İzolasyon: Docker Containerization

Sistemin kalbinde, birbirinden tamamen izole edilmiş iki ana mikroservis yer alıyor:

  • Stateless Web Katmanı (Python/Flask): Kullanıcıdan gelen anlık trafik isteklerini karşılayan ön yüz. Bu katman “stateless” olarak tasarlandı. Yani kendi üzerinde hiçbir hayati veri tutmuyor. Bu stratejik karar, ani bir trafik artışında web sunucularımızı saniyeler içinde 1’den 100’e yatayda ölçekleyebilmemize (Horizontal Pod Autoscaling) olanak tanıyor.
  • Stateful Veri Katmanı (Redis): Web katmanının arkasında, sadece izole bir iç ağ (Private Network) üzerinden erişilebilen, verilerin (ziyaretçi sayaçları vb.) tutulduğu bellek içi veri deposu.

Bu iki servisi aynı sunucuya çıplak kurmak yerine, Docker kullanarak konteynerize edeceğiz. Yazdığımız özel Dockerfile ile Python uygulamamızı işletim sistemi, kütüphaneleri (dependencies) ve kaynak kodlarıyla birlikte tek bir “İmaj” kapsülüne mühürleyeceğiz. Bu sayede, “Benim bilgisayarımda çalışıyordu, sunucuda neden çöktü?” problemini tarihe gömerek, ortamdan bağımsız %100 tutarlı bir çalışma garantisi (Immutable Infrastructure) elde etmiş olacağız.


Veri Akış ŞEması

Az sonra yapacağımız işlemlerin akış şeması şu şekilde olacaktır:


Kod ve İzolasyon

Öncelikle GitHub profilimiz üzerinden yeni bir repository oluşturalım ve terminalimize dönelim. Dosyalarımızın bulunacağı klasörü oluşturduktan sonra git init komutu ile klasöre git versiyon kontrol sistemini dahil edelim. Ardından ana branchimizin ismini main yapalım.

Bu klasörün içine app.py adlı bir dosya açıp uygulamamızın kodlarını yazalım:

import redis
from flask import Flask
app = Flask(__name__)
# host olarak IP adresi değil, diğer mikro servisi yazıyoruz
cache = redis.Redis(host='redis', port=6379)
@app.route('/')
def hello():
# ziyaret sayısını Redis'ten al ve 1 artır
sayac = cache.incr('ziyaret')
return f"""
<h1>Bu web sitesine {sayac} kez giris yapildi.</h1><br>
<p>Bu mimari, "stateless" (durumsuz) bir web servisi ile "stateful" (durumlu) bir in-memory veri <br>
deposunun birbirinden izole edildiği temel bir mikroservis yapısıdır. İstemciden (tarayıcıdan) gelen <br>
HTTP istekleri, ana makinenin (host) 8000 portuna ulaşır ve Docker Compose tarafından oluşturulan <br>
özel köprü ağı (bridge network) üzerinden Flask tabanlı web konteynerinin 5000 portuna yönlendirilir. <br>
Web servisi, uygulama durumunu (state) kendi belleğinde tutmak yerine, aynı izole ağ içinde yer alan <br>
ve Docker'ın dahili DNS çözünürlüğü sayesinde doğrudan servis adıyla erişebildiği Redis konteynerine <br>
(6379 portuna) iletir. Redis, anahtar-değer (key-value) eşleşmesindeki sayacı artırıp sonucu döndürdükten <br>
sonra web servisi bu veriyi işleyerek istemciye sunar. Uygulama katmanı ile veri katmanının bu şekilde <br>
ayrıştırılması (decoupling), web servisinin yatayda bağımsız olarak ölçeklenebilmesine (horizontal scaling) <br>
olanak tanır.</p>
"""
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

Bu kodda fark etmemiz gereken önemli bir detay var. host=’redis’ olarak yazılmış; IP adresi direkt verilmemiş. Bunun sebebi, redis servisinin bir başka konteynerde olacağı ve konteyner adının DNS gibi çalışması.

Şimdi ise gerekli olan kütüphaneler için requirements.txt dosyamızı yazalım.

Geliştirme ortamındaki tutarsızlıkları önlemek adına web servisimizi bir Dockerfile ile izole edelim.

Dosyalarımız hazır ancak bunu GitHub’a pushlamamız gerekiyor. Bunun için öncelikle dosyalarımızın durumunu git status ile kontrol edelim ve eklenmemiş dosyaları git add ile ekleyelim.

Hazırladığımız dosyaları git geçmişine git commit komutu ile kalıcı olarak kaydedelim ve git log komutu ile kontrol edelim.

Şimdi ise GitHub’daki repositorymize buradaki dosyaları aktaralım.

En sonda da bir docker imajı yaratalım ve docker hub’a gönderelim.


Kubernetes ile Orkestrasyon

Bu projenin altyapısını tasarlarken, yerel geliştirme ortamlarında harikalar yaratan docker-compose aracını üretim aşamasında bilerek oyun dışı bıraktık. Çünkü sistemin sadece çalışması değil, “hayatta kalması” gerekir. Docker Compose, tek bir sunucu üzerindeki konteynerleri koordine etmekte başarılı olsa da, kurumsal çapta bir esneklik sunamaz. Kubernetes’i (ve bu projedeki hafifletilmiş versiyonu olan K3s’i) seçmemizin arkasında yatan temel mühendislik kararları şunlardır:

  • İstenen Durum (Desired State) ve Kendi Kendini İyileştirme (Self-Healing): Kubernetes’e “Bana her zaman 2 adet Flask veznesi ver” (replicas: 2) emrini verdiğimizde, sistem bu durumu anlık olarak izler. Sunuculardan biri yansa, pod’lardan biri çökse veya yanlışlıkla silinse bile Kubernetes bunu milisaniyeler içinde fark eder ve insan müdahalesine gerek kalmadan anında yeni bir pod üreterek sistemi “İstenen Duruma” geri getirir.
  • Yerleşik Yük Dengeleme (Native Load Balancing): Artan trafiği karşılamak için Nginx veya HAProxy gibi harici yük dengeleyiciler kurup karmaşık ayarlar yapmak yerine, Kubernetes’in Service objesini kullanacağız. Dışarıdan gelen kullanıcı trafiği, arka planda çalışan pod’lar arasında otomatik ve kusursuz bir şekilde paylaştırılır.
  • Sıfır Kesintili Güncellemeler (Zero-Downtime Rolling Updates): Jenkins CI/CD sürecimizde uygulamamıza yeni bir özellik eklediğimizde, Kubernetes eski versiyonları anında kapatmaz. Yeni versiyon pod’ları kademeli olarak ayağa kaldırır, trafik testini yapar ve başarılıysa eskileri siler. Bu sayede uygulamanın müşterileri, arka planda devasa bir güncelleme yapılırken bile hiçbir kesinti hissetmez.
  • Bulut Bağımsızlığı ve Geleceğe Yatırım (Vendor Lock-in Önleme): Bugün K3s ile tek bir makinede kurduğumuz bu standart YAML dosyaları, yarın projeyi Amazon Web Services (AWS EKS), Google Cloud (GKE) veya Azure (AKS) üzerine taşımak istediğimizde hiçbir kod değişikliğine uğramadan çalışmaya devam edecektir.

Şimdi, klasörümüzün içinde “k3s” adında yeni bir klasör oluşturalım ve redis servisimiz için gerekli olan YAML dosyasını yazalım.

apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-deployment
spec:
replicas: 1 # burası kesinlikle 1 olmalı
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis # yaka etiketi: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379 # redisin çalışacağı port
---
apiVersion: v1
kind: Service
metadata:
name: redis # app.py içindeki host='redis' kısmı
spec:
selector:
app: redis # yaka etiketi: redis olan podlara istekleri gönder
ports:
- port: 6379 # python kodunun aradığı port
targetPort: 6379 # redisin çalışacağı port

Ardından, flask servisimiz için de gerekli YAML dosyasını yazalım.

apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-deployment
spec:
replicas: 2 # iki adet web sunucusu servisi ayağa kalkacak
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask # yaka etiketi: flask
spec:
containers:
- name: flask-app
image: norcholly/mikro-servis:v1 # kendi oluşturduğumuz docker imajı
ports:
- containerPort: 5000 # flask, 5000 portunu sever
---
apiVersion: v1
kind: Service
metadata:
name: flask-service
spec:
type: NodePort # bu servis, dışarıdan gelen istekleri de karşılayacak
selector:
app: flask # yaka etiketi: flask olan pod'lara götür
ports:
- port: 80
targetPort: 5000 # konteynerin portuna yönlendir ->
nodePort: 30005 # bu porttan gelenleri <-

Artık dosyalarımız hazır (Bu dosyaları da git push komutu ile GitHub deposuna yollamayı unutmayalım). Jenkins ine CI/CD otomasyonuna geçmeden önce, herhangi bir sorun olup olmadığını kontrol etmek amacıyla servislerimizi manuel olarak çalıştıralım.

Kontrol için http://IP-ADRESI/30005 adresine gidelim.

Başarılı!


Jenkins ile Uçtan Uca CI/CD Otomasyonu (Pipeline as Code)

“Bir işi iki defa manuel yapıyorsan, onu otomatize etmelisin.” DevOps felsefesinin bu altın kuralını projemizin kalbine yerleştireceğiz. Geliştirme sürecini insan hatalarından arındırmak ve dağıtım hızımızı artırmak için süreçlerimizi Jenkins ile otomatize edeceğiz.

Geleneksel UI tıklamaları yerine, altyapımızı kod olarak yönetme (Infrastructure as Code) vizyonuyla bir Jenkinsfile (Declarative Pipeline) inşa edeceğiz. Bu üretim hattı 4 ana aşamadan oluşacaktır:

  1. Checkout: Geliştirici ana dala (main branch) kod gönderdiği an, Jenkins tetiklenir ve en güncel kodları deposundan çeker.
  2. Build & Tagging: Statik v1, v2 etiketleri yerine Jenkins’in ortam değişkeni olan ${env.BUILD_NUMBER} kullanılarak her imaja benzersiz bir tanımlayıcı (ID) atanır. Bu strateji, versiyon takibini kusursuzlaştırır ve gerektiğinde eski sürüme dönüşleri (Rollback) saniyeler içinde yapabilmemizi sağlar.
  3. Push: Güvenlik amacıyla kimlik bilgileri Jenkins Credentials Vault (Kasa) içinde şifrelenerek saklanır ve imaj global depoya (Docker Hub) yüklenir.
  4. Deploy (K3s Rolling Update): Jenkins, Kubernetes API’si ile konuşarak kubectl set image komutunu gönderir. K3s kümemiz, eski pod’ları hemen öldürmek yerine, trafiği kesmeden yeni versiyon pod’ları kademeli olarak ayağa kaldırır (Zero-Downtime Deployment).

Sonuç olarak; bir satır kodun GitHub’a itilmesinden, kullanıcının ekranında yayına girmesine kadar geçen tüm süreç el değmeden, güvenli ve denetlenebilir bir şekilde gerçekleşmektedir.

Öncelikle Jenkins arayüzünden yeni bir Item oluşturmak için gerekli adımları uygulayalım.

Şimdi ise, groovy formatındaki, Jenkinsfile dediğimiz scripti yazalım.

pipeline {
agent any
environment {
DOCKER_USER = "norcholly"
IMAGE_NAME = "mikro-servis"
TAG = "v${env.BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
echo "GitHub deposundan en güncel kodlar çekiliyor..."
git branch: 'main', url: 'https://github.com/norcholly/micro-service-project.git'
}
}
stage('Docker Build') {
steps {
echo "Yeni versiyon imajı (${TAG}) üretiliyor..."
// github deposundaki (varsa yenilikleri) yeni imaj olarak üretir
sh 'docker build -t ${DOCKER_USER}/${IMAGE_NAME}:${TAG} .'
}
}
stage('Docker Push') {
steps {
echo "İmaj Docker Hub'a gönderiliyor..."
// docker'a yeni versiyon imajını yükler
withCredentials([string(credentialsId: 'docker-hub-sifrem', variable: 'DOCKER_PASS')]) {
sh 'echo $DOCKER_PASS | docker login -u ${DOCKER_USER} --password-stdin'
sh 'docker push ${DOCKER_USER}/${IMAGE_NAME}:${TAG}'
}
}
}
stage('Deploy to Kubernetes') {
steps {
echo "Kubernetes'e yeni versiyonu sahaya sürme emri veriliyor..."
// "sudo" gerektirmeden yetkili bir şekilde çalıştırabilecek
withCredentials([file(credentialsId: 'k3s-gizli-dosya', variable: 'KUBECONFIG')]) {
// eğer bu dosyalarda değişiklik varsa, o değişikliği uygular
dir('k3s') {
sh 'kubectl apply -f redis.yaml'
sh 'kubectl apply -f flask.yaml'
}
// eskileri kaldırırken yenileri ekler
sh 'kubectl set image deployment/flask-deployment flask-app=${DOCKER_USER}/${IMAGE_NAME}:${TAG}'
}
}
}
}
}

Build Now butonuna bastığımızda, sorunsuz bir şekilde çalıştığını gördük.


Prometheus ve Grafana ile Gözlemlenebilirlik (Observability)

“Ölçemediğiniz şeyi yönetemezsiniz.” Kurduğumuz CI/CD boru hattı ve otonom Kubernetes mimarisi kusursuz çalışsa da, arka planda akan trafiği ve sistemin sağlığını anlık olarak izleyememek en son isteyeceğimiz şey. Bu nedenle, bu projenin altyapısının son katmanı olarak “Gözlemlenebilirlik” vizyonunu devreye alacağız.

İlk adım olarak, Python/Flask mikroservisimizin kalbine prometheus-flask-exporter kütüphanesini entegre edeceğiz. Bu sayede uygulamamız sadece kullanıcı isteklerine yanıt vermekle kalmıyor; aynı zamanda saniyedeki HTTP istek sayıları, yanıt süreleri ve hata oranları gibi kritik telemetri verilerini /metrics uç noktasından dışarı yayınlayacak.

Bu ham veriler, altyapımıza kuracağımız Prometheus zaman serisi veritabanı tarafından anlık olarak kazınarak (bir nevi scraping) depolanacaktır. Ardından, bu devasa veri yığınını anlamlı ve eyleme dönüştürülebilir istihbarat raporlarına çevirmek için Grafana‘yı konumlandıracağız. Grafana üzerinden tasarladığımız dashboardlar sayesinde; sistem kaynak tüketimini anlık olarak görselleştirecek, olası bottleneck veya anormal trafik artışlarını sistem çökmeden önce tespit edip alarmlar üretebilecek.

Öncelikle app.py ve requirements dosyasında bir değişiklik yapalım. Bu değişiklikleri git push komutu ile GitHub deposuna yolladığımızda, zaten halihazırda olan Jenkins CI/CD Pipeline’ımız her şeyi (docker imajı üretme, docker hub’a gönderme ve kubernetes ile yeni konteynerler başlatma) otomatik halledecek.

import redis
from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics
import os
app = Flask(__name__)
# uygulamanın tüm trafiğini otomatik ölçecek
metrics = PrometheusMetrics(app)
# redis veritabanına bağlanacak (kubernetes'teki redis servisini arar)
redis_host = os.environ.get('REDIS_HOST', 'redis')
# host olarak IP adresi değil, veritabanını yazıyoruz
cache = redis.Redis(host=redis_host, port=6379)
# statik bilgi (etiket) ekleyelim
metrics.info('app_info', 'Mikro Servis Projesi', version='1.0.0')
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except Exception as exc:
if retries == 0:
raise exc
retries -= 1
import time
time.sleep(0.5)
@app.route('/')
def hello():
# ziyaret sayısını Redis'ten al ve 1 artır
count = get_hit_count()
return f"""
<h1>Bu web sitesine {count} kez giris yapildi.</h1><br>
<p>Bu mimari, "stateless" (durumsuz) bir web servisi ile "stateful" (durumlu) bir in-memory veri <br>
deposunun birbirinden izole edildiği temel bir mikroservis yapısıdır. İstemciden (tarayıcıdan) gelen <br>
HTTP istekleri, ana makinenin (host) 8000 portuna ulaşır ve Docker Compose tarafından oluşturulan <br>
özel köprü ağı (bridge network) üzerinden Flask tabanlı web konteynerinin 5000 portuna yönlendirilir. <br>
Web servisi, uygulama durumunu (state) kendi belleğinde tutmak yerine, aynı izole ağ içinde yer alan <br>
ve Docker'ın dahili DNS çözünürlüğü sayesinde doğrudan servis adıyla erişebildiği Redis konteynerine <br>
(6379 portuna) iletir. Redis, anahtar-değer (key-value) eşleşmesindeki sayacı artırıp sonucu döndürdükten <br>
sonra web servisi bu veriyi işleyerek istemciye sunar. Uygulama katmanı ile veri katmanının bu şekilde <br>
ayrıştırılması (decoupling), web servisinin yatayda bağımsız olarak ölçeklenebilmesine (horizontal scaling) <br>
olanak tanır.</p>
"""
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

Bu işlemleri yaptıktan sonra git push komutu ile GitHub deposuna değişikliklerimizi gönderelim ve Jenkins üzerinden Build Now butonu ile pipeline’ımızı çalıştıralım. http://IP_ADRESİ:30005/metrics bağlantısına gidelim ve yaptığımız değişikliklerin başarılı olup olmadığını kontrol edelim.

Şimdi ise Prometheus için gerekli k3s YAML dosyamızı yazalım.

# bu kısımda, prometheus ayar dosyasını (prometheus.yml) imajın içine gömmek yerine
# dışarıdan dinamik olarak enjekte etme amacımız bulunmaktadır.
# bu sayede ayar değiştirmek istediğimizde imajı tekrardan build etmemize gerek olmaz
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
data:
prometheus.yml: |
global:
scrape_interval: 15s # her 15 saniyede bir tarama yapar
scrape_configs:
# birinci öncelik: kendi uygulamamız
- job_name: 'flask-uygulamasi'
static_configs:
- targets: ['flask-service:80'] # flask.yaml içindeki service adımız ve port
# ikinci öncelik: prometheus'un kendi sağlığı
- job_name: 'prometheus-kendi-metrikleri'
static_configs:
- targets: ['localhost:9090']
---
# bu kısımda prometheus konteynerini ayağa kaldırmak ve çökmesi durumunda kubernetesin
# onu tekrar başlatmasını sağlama amacımız bulunmaktadır.
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-deployment
spec:
replicas: 1 # bir adet yeterli
selector:
matchLabels:
app: prometheus # yaka etiketi: prometheus olan podları yönetecektir
template:
metadata:
labels:
app: prometheus # her bir poda atanan yaka etiketi
spec:
containers:
- name: prometheus
image: prom/prometheus:latest # resmi imaj
ports:
- containerPort: 9090 # prometheus konteyner içinde bu porttan dinleme yapmaktadır
volumeMounts:
- name: config-volume # yukarıdaki configmap'i konteynerin içindeki ilgili dosya yoluna mount etmektedir
mountPath: /etc/prometheus/prometheus.yml
subPath: prometheus.yml
volumes:
- name: config-volume # yukarıdaki ile aynı olmalı
configMap:
name: prometheus-config # yukarıdaki ConfigMap klasör yapılır
---
# burası, trafiği yönlendirmek için vardır.
apiVersion: v1
kind: Service
metadata:
name: prometheus-service
spec:
type: NodePort # dışarıdan girişler için
selector:
app: prometheus # prometheus yaka etiketli pod'lara trafiği yönlendirecektir
ports:
- port: 9090 # cluster içerisindeki servislerin bu servise ulaşacağı port
targetPort: 9090 # trafiğin konteynere iletileceği asıl port
nodePort: 30090 # tarayıcı üzerinden gireceğimiz port

URL’i yazdıktan sonra karşımıza Prometheus arayüzü çıkacak.

Üst menüden Status -> Target Health sekmesine girdiğimizde servisler UP gözüküyorsa, başardık demektir.

Sırada, Grafana ile Prometheus’tan gelen ham verileri profesyonel, canlı güncellenen, renkli grafikler ve dashboardlar oluşturarak gözlemleme yapalım. Bunun için k3s grafana.yaml dosyası oluşturalım.

# şimdilik kalıcı bir volume ekleme atlanmıştır. pod silinirse dashboardlar gidecektir
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana-deployment
spec:
replicas: 1 # bir grafana yeterlidir
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana
image: grafana/grafana:latest # resmi grafana imajı
ports:
- containerPort: 3000 # grafana'nın varsayılan dinleme portu
---
apiVersion: v1
kind: Service
metadata:
name: grafana-service
spec:
type: NodePort
selector:
app: grafana
ports:
- port: 3000
targetPort: 3000
nodePort: 30095 # tarayıcıdan erişeceğimiz port

http://IP_ADRESİ:30095 adresine gittiğimizde karşımıza Grafana’nın login ekranı çıkacak. admin:admin ile giriş yaptıktan sonra parolamızı belirleyelim ve arayüze giriş yapalım.

Sol menüden Connections -> Data Sources -> Add Data Source butonuna tıklayalım ve açılan listeden Prometheus seçelim. Prometheus seçtikten sonra bize prometheus’un IP adresini soracak. Buraya, Kube-DNS mimarisindeki IP adresini, yani http://prometheus-service:9090 adresini girelim.

Ardından en aşağıda bulunan Save & Test butonuna basalım ve başarılı olduğunu görelim. Şimdi Dashboard oluşturmak için sol menüden Dashboards -> Create dashboard -> Add visualization -> prometheus seçelim. Karşımıza çıkan ekranın alt kısmında, Queries sekmesinde metrik olarak flask_http_request_total‘i seçelim ve Run queries butonuna basalım.

Bu dashboard’ı kaydedelim. 30005 portlu flask uygulamamızın çalıştığı adrese gidelim ve birçok kez sayfayı yenileyelim. Dashboard’a baktığımızda:

Artışı görmekteyiz.


SONUÇ

Baştan sonra kurduğumuz mimariyi özetlemek gerekirse:

Sürüm Kontrolü (Git/GitHub): Altyapı ve uygulama kodlarının tek kaynağı (Single Source of Truth) olarak konumlandırıldı.

Sürekli Entegrasyon ve Dağıtım (Jenkins CI/CD): Kod değişiklikleri otomatik olarak algılanıp Docker Hub’a versiyonlanarak (BUILD_NUMBER değişkeni ile) gönderiliyor ve manuel müdahale (ve sudo yetkisi) gerektirmeden Kubernetes kümesine iletiliyor.

Konteyner Orkestrasyonu (K3s): Flask (uygulama katmanı) ve Redis (veri katmanı) mikroservis mantığıyla birbirinden izole edildi. Güncellemelerin sıfır kesinti (Zero-Downtime Deployment) prensibiyle uygulanması sağlandı.

Gözlemlenebilirlik (Prometheus & Grafana): Sistem kör uçuşundan çıkarılarak, Flask uygulamasına gelen HTTP trafikleri ve sistem metrikleri anlık, görsel ve analiz edilebilir veri setlerine (Dashboard) dönüştürüldü.

Yorum bırakın