ArgoCD Reconciliation Gecikmesini 12 Dakikadan 4 Saniyeye Düşürmek: 2000+ Uygulama Ölçeğinde GitOps Sharding
Blog'a Dön

ArgoCD Reconciliation Gecikmesini 12 Dakikadan 4 Saniyeye Düşürmek: 2000+ Uygulama Ölçeğinde GitOps Sharding

Buğra Şıkel

ArgoCD Reconciliation Gecikmesini 12 Dakikadan 4 Saniyeye Düşürmek: 2000+ Uygulama Ölçeğinde GitOps Sharding

Giriş

Gece 03:00 sularında tetiklenen bir PagerDuty alarmı, Kubernetes (v1.28) altyapımızdaki kritik bir darboğazı gün yüzüne çıkardı. Geliştiricilerin Git deposuna merge ettiği kodların production ortamına yansıması 720 saniyeyi (12 dakika) buluyordu. Kube-state-metrics üzerinden gelen veriler, ArgoCD (v2.8.4) Application Controller pod’unun CPU throttling (%85) yaşadığını ve workqueue derinliğinin 1800’lere ulaştığını gösteriyordu. Toplam 14 cluster üzerinde koşan 2400’den fazla uygulamanın tek bir controller instance’ı üzerinden yönetilmesi sistemin fiziksel sınırlarını zorluyordu.

Mevcut yapıda controller, tüm uygulamaları varsayılan olarak her 180 saniyede bir Git reposuyla senkronize etmeye çalışır. 2400 uygulama için bu, saniyede ortalama 13.3 senkronizasyon (reconciliation) işlemi anlamına gelir. Ancak repo-server tarafındaki Git fetch/pull operasyon süreleri p99’da 1.8 saniyeye ulaştığında, iş kuyruğu (queue) birikmeye başlar. Sürekli artan bu kuyruk, acil production hotfix deployment’larının bile 12 dakika beklemesine neden oldu.

İçindekiler

  • Başlangıç Noktası ve Metrikler
  • Kök Neden Analizi: Application Controller Darboğazı
  • Çözüm Adımı 1: Controller Sharding Mimarisi
  • Çözüm Adımı 2: Webhook Tetikleyicileri ile Polling İptali
  • Trade-off Analizi: Hangi Senaryoda Ne Kullanılmalı?
  • Pratik Öneriler / Production Notları
  • Sık Sorulan Sorular
  • Sonuç

Başlangıç Noktası ve Metrikler

Optimizasyon öncesi Prometheus’tan aldığımız baseline metrikler sorunun boyutunu netleştiriyordu. Amacımız p99 gecikme süresini 5 saniyenin altına indirmek ve kaynak tüketimini lineer yatay büyüme (horizontal scaling) modeline oturtmaktı.

Metrik Optimizasyon Öncesi Hedeflenen Optimizasyon Sonrası
Reconcile Süresi (p99) 720.4 saniye < 5 saniye 4.1 saniye
Workqueue Derinliği 1500 – 1800 < 50 0 – 15
Controller OOMKills Haftada 4-5 kez 0 0
GitHub API Rate Limit 5000 limitinin %92’si (saatlik) < %10 %3

Kök Neden Analizi: Application Controller Darboğazı

ArgoCD mimarisinde application-controller, argocd-repo-server ile konuşarak cluster statüsü ile Git statüsünü karşılaştırır. Varsayılan konfigürasyonda controller tek bir replika olarak çalışır. 2000’den fazla Application CRD’si olan bir ortamda, controller bellek üzerinde devasa bir state tutar.

Aşağıdaki PromQL sorgusuyla controller kuyruk durumunu analiz ettik:

rate(workqueue_adds_total{name="app_reconciliation_queue"}[5m]) 
- rate(workqueue_work_duration_seconds_count{name="app_reconciliation_queue"}[5m])

Sonuç pozitifti; yani kuyruğa giren iş sayısı, işlenen iş sayısından fazlaydı. Controller status.reconciledAt alanını güncelleyemediği için OutOfSync durumları UI’a yansımıyor, geliştirici ekiplerinde kafa karışıklığı yaratıyordu.

Çözüm Adımı 1: Controller Sharding Mimarisi

Tek bir süreci (process) dikeyde büyütmek yerine Controller Sharding mekanizmasını devreye aldık. ArgoCD v2.8+ ile gelen dynamic sharding algoritmasını tercih ettik.

Klasik (legacy) sharding yöntemi cluster ID’sinin hash’ini alarak statik dağıtım yapar. Ancak elimizde 10 uygulamanın çalıştığı bir UAT cluster’ı ve 1800 uygulamanın çalıştığı ana production cluster’ı vardı. Statik hash yöntemi, production cluster’ını tek bir pod’a atayarak darboğazı çözmeyecekti. Round-robin algoritmasına dayalı dinamik sharding ise replica başına düşen uygulama sayısını eşitleyerek asıl yük dengesini kurar.

Konfigürasyon değişikliğini ArgoCD Helm Chart (v5.51.1) values.yaml dosyası üzerinden aşağıdaki gibi gerçekleştirdik:

controller:
  replicas: 6
  sharding:
    enabled: true
    algorithm: "round-robin" # Dinamik uygulama dağıtımı
  env:
    - name: ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS
      value: "50"
    - name: ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS
      value: "25"
  resources:
    limits:
      cpu: 2000m
      memory: 4Gi
    requests:
      cpu: 1000m
      memory: 2Gi

Bu konfigürasyonla birlikte StatefulSet replika sayısını 6’ya çıkarttık. Her replika ortalama 400 uygulamadan sorumlu hale geldi. Memory tüketimi pod başına 8.5GB’tan 1.2GB’a geriledi, OOMKill vakaları tamamen sıfırlandı.

Çözüm Adımı 2: Webhook Tetikleyicileri ile Polling İptali

Sharding işlemi kuyrukları eritti ancak senkronizasyon hala default 3 dakikalık polling döngüsüne bağlıydı. 180 saniyelik bir timeout.reconciliation süresi, bir commit’in cluster’a inmesi için 3 dakika beklenmesi ihtimali demekti. 4 saniye hedefine ulaşmak için polling mekanizmasını pasif hale getirip, GitHub Webhook’larına geçiş yaptık.

Önce ArgoCD API’sini Ingress üzerinden dışarı açarak GitHub webhook’larının ulaşabileceği bir endpoint (/api/webhook) yarattık. Ardından polling döngüsünü kapatmak için Helm konfigürasyonunda şu değişikliği uyguladık:

configs:
  cm:
    timeout.reconciliation: "0s"

Bu değişiklik sonrası ArgoCD, Git reposunu periyodik olarak kontrol etmeyi bıraktı. Sadece GitHub’dan push event’i geldiğinde hedeflenen repodaki uygulamaları senkronize etmeye başladı. repo-server üzerindeki Git fetch operasyonları %98 oranında azaldı.

Trade-off Analizi: Hangi Senaryoda Ne Kullanılmalı?

Sistemi tasarlarken belirli teknik tavizler (trade-off) vermemiz gerekti:

1. Dynamic Sharding vs Legacy Sharding

Karar: round-robin (dinamik) algoritması seçildi.
Neden? legacy yöntem cluster’ları sharding anahtarı olarak kullanır. 14 cluster’dan birine 1800 uygulama yüklü olduğu senaryoda bu yapı çalışmaz. round-robin ise cluster bağımsız, doğrudan application objelerini dağıtır.
Trade-off: round-robin kullanıldığında birden fazla controller pod’u aynı hedef cluster ile iletişim kurar. Bu, Kubernetes API Server üzerinde eşzamanlı bağlantı artışına sebep oldu. Control Plane metriklerini izleyerek kube-apiserver replikalarını 3’ten 5’e çıkarmak zorunda kaldık. Eğer uygulamalarınız cluster’lara eşit dağılmışsa (örneğin 10 cluster, her birinde 200 uygulama), legacy sharding API Server yükünü daha düşük tutacaktır.

2. Mono-repo vs Multi-repo Mimarisi

Karar: Büyük mono-repo parçalanmadı ancak manifest path’leri izole edildi.
Neden? Webhook tetiklendiğinde repo-server mono-repo’nun tamamını taramaya çalışır. Bunu önlemek için Application CRD’lerinde spec.source.path kullanarak sadece değişen dizinlerin taranmasını sağladık (ArgoCD v2.7+ manifest-generate-paths özelliği).
Trade-off: Eğer 2000 uygulamanın Helm manifestleri aynı Git kök dizinindeyse, webhook ile 4 saniye hedefine ulaşmak fiziksel olarak imkansızdır. Her push operasyonunda 2000 uygulamanın render edilmesi gerekir. Uygulama manifest dizinlerini ayrıştırmak bir sefere mahsus 40 saatlik bir refactoring gerektirdi.

Pratik Öneriler / Production Notları

Bu ölçekte bir ArgoCD ortamı yönetiyorsanız aşağıdaki checklist sistem stabilitesi açısından kritiktir:

  • Redis Cache Bağımlılığı: 2000+ uygulama ile çalışıyorsanız dahili Redis (single-node) kullanmayın. HAProxy ile desteklenen Redis Sentinel veya ElastiCache gibi managed bir cluster şarttır. Biz 3 node’lu bir Sentinel yapısına geçtik ve maxmemory-policy değerini allkeys-lru olarak yapılandırdık.
  • GOMAXPROCS Optimizasyonu: ArgoCD pod’larında Go runtime’ını CPU limitlerine göre ayarlayın. Aksi takdirde thread context-switching CPU’yu tüketir. Helm üzerinde GOMAXPROCS environment variable’ını pod limitlerine eşit olacak şekilde (örneğin limit 2000m ise GOMAXPROCS=2) set edin.
  • Server-Side Apply: Kubernetes 1.22+ üzerindeyseniz, Application sync seçeneklerinde Server-Side Apply opsiyonunu aktif edin. kubectl apply tabanlı eski client-side mekanizma çok büyük ConfigMap’lerde (örnek: Grafana dashboardları) “metadata.annotations: Too long” hatası fırlatır.

“Sistemi optimize etmek sadece kaynakları (CPU/RAM) artırmak değildir; sistemin gereksiz iş yapmasını engellemektir. Polling’den webhook’a geçiş, CPU limitlerimizi artırmadan gecikmeyi saniyelere indiren anahtar dokunuş oldu.”

Sık Sorulan Sorular

Sharding aktif edildikten sonra uygulamalarım kayboldu (UI’da görünmüyor), neden?

Eğer sharding sayısını (controller.replicas) değiştirip eski pod’ları gracefully kapatmazsanız, in-memory state senkronizasyonu kısa süreliğine kopabilir. StatefulSet’i scale ederken her zaman ArgoCD Redis cache’ini temizlemenizi (redis-cli flushall) ve application-controller pod’larını rollout restart ile yeniden başlatmanızı öneririm.

Webhook kullanırken GitHub tarafında kesinti olursa ne olur? Fallback stratejisi nedir?

timeout.reconciliation: "0s" ayarı periyodik taramayı tamamen kapatır. GitHub webhook servisi çökerse deployment’lar durur. Bu riski minimize etmek için değeri 0 yapmak yerine 12 saate (43200s) çekebilirsiniz. Böylece dış bir kesinti yaşansa bile sistem günde 2 kez self-heal döngüsüne girer.

Repo Server Parallelism Limit ayarını kaç yapmalıyım?

Varsayılan limit 50’dir. 6 controller replikası ve 2400 uygulamanız varsa (aynı repodan beslenen), bir commit anında yüzlerce eşzamanlı manifest üretim (manifest generation) isteği gelir. ARGOCD_REPO_SERVER_PARALLELISM_LIMIT değerini repo-server CPU çekirdek sayınızla (örneğin 16 core için 16) eşitlemek OOMKill’leri önler. Kuyruk geçici olarak uzar ancak sistem çökmez.

Round-robin algoritması Redis ağ trafiğini artırır mı?

Evet, %400’e varan bir trafik artışı ölçümledik. Her controller replikası kendi state’ini ortak Redis cluster’ına yazar. Redis sunucunuzun network bant genişliğini (Bandwidth) ve IOPS kapasitesini AWS/GCP konsolundan mutlaka izleyin.

Sonuç

12 dakikalık deployment gecikmesini 4 saniyeye düşürmek sadece bir hızlanma değil, operasyonel körlüğü ortadan kaldıran ve developer experience (DX) standartlarını yukarı çeken bir hamleydi. Dinamik sharding ile iş yükünü 6 replikaya dağıtırken, webhook mekanizmasıyla CPU döngülerini ve Git API limitlerini israf etmeyi bıraktık.

Eğer Prometheus metriklerinizde workqueue_depth değeri sürekli tırmanıştayken CPU tüketimi tepe noktasına ulaşıyorsa, ilk aksiyonunuz controller.replicas sayısını artırarak sharding başlatmak olmalıdır. Ardından polling mekanizmasını webhook’larla değiştirerek altyapınızın tepki süresini saniyelere indirebilirsiniz.

Bunları da beğenebilirsiniz

PostgreSQL İndeks Şişmesini Yönetme: Sensör Verileri İçin HOT ve Fillfactor Rehberi
29 Mart 2026

PostgreSQL İndeks Şişmesini Yönetme: Sensör Verileri İçin HOT ve Fillfactor Rehberi

Yüksek hacimli sensör verilerinde PostgreSQL performansını artırmak için indeks şişmesini önleyin. HOT updates ve fillfactor parametreleriyle disk I/O darboğazlarını nasıl aşacağınızı öğrenin.

Devamını Oku
Yüksek Trafikli Sistemlerde Cache Stampede ve Thundering Herd Koruması: İleri Düzey Stratejiler
23 Nisan 2026

Yüksek Trafikli Sistemlerde Cache Stampede ve Thundering Herd Koruması: İleri Düzey Stratejiler

Yüksek trafikli web uygulamalarında sistem çöküşlerini önlemek için AI tabanlı dinamik kilitleme ve probabilistik yazma yöntemlerini kullanarak ölçeklenebilirliği nasıl artıracağınızı keşfedin.

Devamını Oku
vLLM ile LLM Sunumunda Mühendislerin Düştüğü 5 PagedAttention ve VRAM Tuzağı
5 Haziran 2026

vLLM ile LLM Sunumunda Mühendislerin Düştüğü 5 PagedAttention ve VRAM Tuzağı

Production ortamında vLLM kullanan mühendislerin PagedAttention, KV Cache ve VRAM yönetiminde yaptığı 5 kritik konfigürasyon hatası, tespiti ve çözümleri.

Devamını Oku
AI Asistan