
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-policydeğeriniallkeys-lruolarak 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
GOMAXPROCSenvironment variable’ını pod limitlerine eşit olacak şekilde (örneğin limit 2000m ise GOMAXPROCS=2) set edin. - Server-Side Apply: Kubernetes 1.22+ üzerindeyseniz,
Applicationsync seçeneklerinde Server-Side Apply opsiyonunu aktif edin.kubectl applytabanlı 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
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.

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.
