Takip et

Kubernetes’e Geçiş Sonrası 3 Dakikadan 15 Dakikaya Çıkan CI Süreçleri: İşte Nasıl Düzelttik!

Kubernetes, modern uygulama dağıtımının ve yönetiminin vazgeçilmez bir parçası haline geldi. Ölçeklenebilirlik, esneklik ve kaynak verimliliği va…

Kubernetes’e Geçiş Sonrası 3 Dakikadan 15 Dakikaya Çıkan CI Süreçleri: İşte Nasıl Düzelttik!

Kubernetes, modern uygulama dağıtımının ve yönetiminin vazgeçilmez bir parçası haline geldi. Ölçeklenebilirlik, esneklik ve kaynak verimliliği vaatleriyle birçok ekibi kendine çekiyor. Biz de bu avantajlardan faydalanmak amacıyla CI/CD altyapımızı Kubernetes üzerine taşıma kararı aldık. Ancak bu geçişin beklenmedik bir yan etkisi oldu: 3 dakika süren CI pipeline’larımız aniden 15 dakikaya fırladı! Bu durum, geliştirme hızımızı ciddi şekilde düşürdü ve ekibin moralini bozdu. Neyse ki, kapsamlı bir inceleme ve bir dizi optimizasyon sayesinde bu sorunu çözmeyi başardık. Bu makalede, karşılaştığımız zorlukları, kök nedenleri ve pipeline sürelerimizi tekrar kabul edilebilir seviyelere çekmek için uyguladığımız çözümleri detaylı bir şekilde paylaşacağız.

1. Performans Düşüşünün Kök Nedenlerini Anlamak

İlk şokun ardından, sorunun tam olarak nerede yattığını anlamak için kapsamlı bir analiz sürecine girdik. Kubernetes’in karmaşıklığı, birçok farklı bileşenin performans üzerinde etkili olabileceği anlamına geliyordu. İşte tespit ettiğimiz başlıca kök nedenler:

1.1. Yanlış Kaynak Tahsisi ve Kubernetes Scheduler Etkisi

Kubernetes’te pod’lara CPU ve bellek için requests (istekler) ve limits (limitler) tanımlamak kritik öneme sahiptir. Başlangıçta, bu ayarları ya hiç yapmamış ya da çok düşük tutmuştuk. Bu durum, CI pod’larımızın yeterli kaynağı alamamasına veya küme üzerinde “noisy neighbor” etkisi yaratmasına neden oluyordu. Düşük requests değerleri, Kubernetes Scheduler’ın pod’ları yetersiz kaynaklara sahip node’lara yerleştirmesine yol açarak performansı olumsuz etkiliyordu.

1.2. Büyük Docker İmajları ve İmaj Çekme Süreleri

CI pipeline’larımızın her adımında kullanılan Docker imajlarının boyutları oldukça büyüktü. Bu imajlar, her CI job’ı başladığında Kubernetes node’larına çekilmek zorundaydı. Her seferinde yüzlerce megabaytlık (hatta gigabaytlık) imajların indirilmesi, pipeline’ın başlangıç süresini önemli ölçüde artırıyordu. Özellikle çok sayıda bağımlılığı olan monolitik uygulamaların imajları bu konuda başı çekiyordu.

1.3. Önbellekleme Eksikliği ve Tekrarlanan İşlemler

Eski CI sistemimizde yerel disk önbellekleme gibi mekanizmalarla bağımlılık indirme ve ara derleme çıktılarını önbelleğe alabiliyorduk. Kubernetes’e geçişle birlikte bu önbellekleme stratejileri kaybolmuştu. Her CI job’ı, sıfırdan başlıyor, tüm bağımlılıkları yeniden indiriyor ve tüm derleme adımlarını tekrarlıyordu. Bu, özellikle Node.js projelerinde node_modules klasörünün her seferinde yeniden indirilmesi veya Maven/Gradle bağımlılıklarının tekrar tekrar çekilmesi gibi durumlarda büyük zaman kayıplarına yol açıyordu.

1.4. Ağ Gecikmeleri ve Harici Bağımlılıklar

CI pipeline’larımız, dış Maven/NPM depoları, veritabanları veya harici API’ler gibi birçok harici servisle iletişim kuruyordu. Kubernetes kümesinin ağ yapılandırması, DNS çözümleme süreleri veya harici servislere erişimdeki gecikmeler, pipeline’ın toplam süresine ekleniyordu. Özellikle DNS sorgularının yavaşlaması, her bağımlılık indirme veya API çağrısında hissedilir bir gecikme yaratıyordu.

2. Docker İmaj Optimizasyonu ve Katman Önbellekleme

Pipeline sürelerimizi kısaltmanın ilk ve en etkili adımlarından biri, Docker imajlarımızı optimize etmek oldu. İmaj boyutunu küçültmek ve katman önbelleklemesini doğru kullanmak, imaj çekme sürelerini dramatik şekilde azalttı.

2.1. Çok Aşamalı (Multi-stage) Derlemeler

Uygulama derleme ve çalıştırma ortamlarını ayırmak için çok aşamalı Dockerfile’lar kullanmaya başladık. Bu sayede, derleme için gerekli olan büyük araçlar (SDK’lar, derleyiciler vb.) nihai imaja dahil edilmedi. Sonuç olarak, sadece uygulamanın çalışması için gerekli olan bağımlılıkları içeren çok daha küçük imajlar elde ettik.


# İlk aşama: Derleme ortamı
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# İkinci aşama: Çalışma zamanı ortamı
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json
CMD ["npm", "start"]

2.2. Daha Küçük Temel İmajlar Kullanımı

Büyük taban imajlar yerine (örneğin ubuntu:latest), Alpine Linux tabanlı daha minimalist imajlara geçiş yaptık. Alpine imajları, boyut olarak çok daha küçük olduğu için indirme sürelerini önemli ölçüde kısalttı.

2.3. Akıllı Katman Önbellekleme

Dockerfile'daki komutların sırasını optimize ederek Docker'ın katman önbellekleme mekanizmasından daha iyi faydalandık. Sık değişmeyen katmanları (örneğin bağımlılık kurulumu) Dockerfile'ın üst kısımlarına taşıdık ve daha sık değişen kod kopyalama gibi adımları alt kısımlara yerleştirdik. Bu sayede, sadece değişen katmanların yeniden derlenmesini sağladık.

3. Kubernetes Kaynak Yönetimi ve Planlayıcı Optimizasyonu

Kubernetes'in kaynak yönetimini doğru yapmak, CI pod'larımızın istikrarlı ve hızlı çalışması için hayati önem taşıyordu.

3.1. Doğru Kaynak İstekleri (Requests) ve Limitleri (Limits)

Her CI pod'u için CPU ve bellek requests ve limits değerlerini dikkatlice ayarladık. requests değerlerini, pod'un minimum ihtiyaçlarını karşılayacak şekilde belirledik ki Kubernetes Scheduler pod'u uygun kaynaklara sahip bir node'a yerleştirebilsin. limits değerlerini ise, pod'un aşırı kaynak tüketerek diğer pod'ları etkilemesini engellemek için kullandık. Bu ayarları, CI pod'larının gerçek tüketimlerini gözlemleyerek kademeli olarak optimize ettik.


resources:
  requests:
    memory: "512Mi"
    cpu: "1000m" # 1 CPU çekirdeği
  limits:
    memory: "2Gi"
    cpu: "2000m" # 2 CPU çekirdeği

3.2. Özel CI Node'ları ve Node Selector'lar

Kritik CI iş yüklerimizi, diğer uygulamalarımızdan izole etmek için belirli node'ları "CI node'ları" olarak etiketledik ve bu node'lara özel taints (kirlilikler) uyguladık. CI pod'larımıza da bu taints'lere uygun tolerations (toleranslar) ve node selector'lar ekleyerek sadece bu özel node'larda çalışmasını sağladık. Bu, CI işlerinin diğer iş yüklerinden etkilenmemesini ve her zaman yeterli kaynağa sahip olmasını garanti etti.

3.3. Pod Anti-Affinity Kullanımı

Aynı anda çalışan birden fazla CI job'ının aynı node üzerinde yoğunlaşarak performans düşüşüne neden olmasını engellemek için pod anti-affinity kuralları uyguladık. Bu sayede, aynı tipteki CI pod'larının farklı node'lara dağıtılmasını sağlayarak kaynak çakışmalarını minimuma indirdik.

4. Akıllı Önbellekleme Stratejileri

Tekrarlanan işlemleri ortadan kaldırmak ve bağımlılık indirme sürelerini kısaltmak için çeşitli önbellekleme stratejileri uyguladık.

4.1. Bağımlılık Önbellekleme (Shared Volumes veya S3)

Node.js (node_modules), Maven (.m2), Python (pip cache) gibi bağımlılıkları, CI job'ları arasında paylaşılabilen bir mekanizma ile önbelleğe aldık. Bunun için iki ana yaklaşım kullandık:

  • Persistent Volume Claims (PVC): Kubernetes kümesi içinde kalıcı bir depolama birimi oluşturarak bağımlılıkları burada sakladık. Her CI job'ı bu PVC'yi mount ederek önbelleğe erişebildi. Ancak bu yaklaşım, farklı node'larda çalışan pod'lar arasında performans sorunları yaratabildiğinden dikkatli kullanılması gerekir.
  • Harici Depolama (S3/MinIO): Bağımlılık önbelleğini Amazon S3 veya küme içinde çalışan MinIO gibi nesne depolama servislerinde tuttuk. CI job'ları, başlangıçta önbelleği indirip sonunda güncelleyerek geri yükledi. Bu yöntem, dağıtık sistemler için daha esnek ve ölçeklenebilirdi.

4.2. Docker Katman Önbellekleme (BuildKit ve Kaniko)

Kubernetes ortamında Docker derleme işlemleri için BuildKit veya Kaniko gibi araçları kullandık. Bu araçlar, Docker daemon'a ihtiyaç duymadan container imajlarını derleyebilir ve Docker katman önbelleklemesini verimli bir şekilde yönetebilir. Özellikle Kaniko, derleme çıktısını bir registry'ye doğrudan gönderebildiği için CI pipeline'larında çok faydalı oldu.

4.3. Derleme Çıktılarını Önbellekleme

Büyük projelerde ara derleme çıktıları (örneğin, derlenmiş JAR dosyaları veya frontend build çıktıları) da önemli zaman alabilir. Bu çıktıları da S3 gibi harici depolama birimlerinde önbelleğe alarak, sadece değişen kısımların yeniden derlenmesini sağladık.

5. Ağ Performansı ve Harici Bağımlılıklar

Ağ gecikmelerini azaltmak ve harici bağımlılıklara erişimi hızlandırmak için adımlar attık.

5.1. Yerel veya Özel İmaj Kayıt Defterleri (Registry)

Docker imajlarımızı Docker Hub yerine, kendi Kubernetes kümemizin içinde veya kümemize çok yakın bir konumda çalışan özel bir imaj kayıt defterinde (örneğin Harbor veya küme içi Docker Registry) barındırmaya başladık. Bu, imaj çekme sürelerini önemli ölçüde azalttı çünkü ağ gecikmesi ve bant genişliği sorunları ortadan kalktı.

5.2. DNS Optimizasyonu

CoreDNS yapılandırmamızı inceledik ve harici DNS sorgularının gecikmesini azaltmak için gerekli optimizasyonları yaptık. Gerekirse, daha hızlı yanıt veren DNS sunucularını kullanmak üzere CoreDNS'i yapılandırdık.

5.3. Harici Servislere Erişim Optimizasyonu

CI pipeline'larımızda sıkça erişilen harici veritabanları veya API'ler için, Kubernetes kümesi ile bu servisler arasında daha hızlı ve güvenli bağlantılar (örneğin, VPC peering veya özel ağ geçitleri) kurduk. Bu, her bir API çağrısının veya veritabanı sorgusunun daha hızlı tamamlanmasını sağladı.

6. CI Süreçlerinin Paralelleştirilmesi ve İyileştirilmesi

Pipeline'ın iç yapısını gözden geçirerek daha verimli hale getirdik.

6.1. Paralel Adımlar

CI pipeline'larımızdaki bağımsız derleme, test veya linting adımlarını paralel olarak çalışacak şekilde yeniden düzenledik. Örneğin, farklı modüllerin testlerini veya farklı linting kontrollerini aynı anda başlatarak toplam süreyi kısalttık. CI/CD aracımızın (Jenkins, GitLab CI, Argo Workflows vb.) paralel yürütme özelliklerinden tam olarak faydalandık.

6.2. Monolitik Derlemeleri Parçalama

Büyük monolitik uygulamaları, mümkün olduğunca mikroservis veya modüler yapılar halinde parçaladık. Her bir mikroservisin veya modülün kendi bağımsız CI pipeline'ı olması, sadece değişen bileşenlerin derlenip test edilmesini sağlayarak genel CI süresini azalttı.

6.3. Daha Hızlı Derleme Araçları

Mümkün olan yerlerde, geleneksel derleme araçları yerine Esbuild, SWC gibi daha hızlı ve modern derleyicileri veya bundler'ları kullanmayı değerlendirdik. Özellikle frontend projelerinde bu tür araçlar ciddi hız artışları sağlayabiliyor.

7. İzleme ve Gözlemlenebilirlik

Optimizasyon sürecimiz boyunca ve sonrasında, performansı sürekli olarak izlemek ve darboğazları tespit etmek için güçlü izleme araçlarına güvendik.

7.1. Kubernetes Metrikleri

Prometheus ve Grafana gibi araçlarla Kubernetes kümemizin CPU, bellek, ağ I/O gibi temel metriklerini sürekli olarak izledik. Özellikle CI pod'larımızın kaynak tüketimini yakından takip ederek, kaynak istekleri ve limitleri için en uygun değerleri bulduk.

7.2. CI Aracı Logları ve Analizi

CI/CD aracımızın (örneğin Jenkins, GitLab CI) sağladığı detaylı logları ve süre raporlarını analiz ettik. Hangi adımın ne kadar sürdüğünü belirleyerek, iyileştirme için en yüksek potansiyele sahip alanları tespit ettik.

7.3. Dağıtık İzleme (Distributed Tracing)

Bazı durumlarda, pipeline içindeki karmaşık etkileşimleri ve ağ çağrılarını izlemek için Jaeger veya Zipkin gibi dağıtık izleme araçlarını kullandık. Bu, özellikle harici servislere yapılan çağrıların gecikmelerini anlamak için faydalı oldu.

Sonuç

Kubernetes'e geçiş, CI pipeline'larımız için başlangıçta beklenmedik bir performans düşüşüyle sonuçlansa da, bu süreci derinlemesine bir öğrenme ve optimizasyon fırsatına dönüştürdük. Docker imaj optimizasyonundan Kubernetes kaynak yönetimine, akıllı önbellekleme stratejilerinden ağ performans iyileştirmelerine kadar birçok alanda yaptığımız değişiklikler sayesinde, pipeline sürelerimizi 15 dakikadan tekrar 3-5 dakika aralığına çekmeyi başardık. Bu süreç bize, Kubernetes'in gücünden tam olarak faydalanmak için sadece geçiş yapmakla kalmayıp, platformun dinamiklerini iyi anlamak ve sürekli optimizasyon yapmak gerektiğini öğretti. Her projenin kendine özgü ihtiyaçları olsa da, burada paylaştığımız stratejilerin benzer sorunlarla karşılaşan ekiplere yol göstereceğini umuyoruz. Unutmayın, performans optimizasyonu tek seferlik bir iş değil, sürekli bir yolculuktur.

Sık Sorulan Sorular (SSS)

Kubernetes'e geçiş neden CI'ı yavaşlatabilir?

Kubernetes, pod'ların başlaması, imajların çekilmesi, ağ yapılandırması ve kaynak tahsisi gibi ek katmanlar getirir. Yanlış yapılandırıldığında (büyük imajlar, yetersiz kaynaklar, önbellekleme eksikliği), bu katmanlar CI süreçlerine önemli gecikmeler ekleyebilir.

Multi-stage Dockerfile'lar gerçekten ne kadar fark yaratır?

Çok aşamalı Dockerfile'lar, nihai imaj boyutunu önemli ölçüde küçülterek imaj çekme sürelerini azaltır. Bu da her CI job'ının daha hızlı başlamasına olanak tanır. Özellikle derleme bağımlılıkları büyük olan projelerde (örneğin Java, Node.js) büyük fark yaratır.

CI/CD için Persistent Volume kullanmak iyi bir fikir mi?

Evet, özellikle bağımlılık önbellekleme veya derleme çıktılarını saklamak için Persistent Volume'lar faydalı olabilir. Ancak, farklı node'larda çalışan pod'lar arasında önbellek tutarlılığı ve performans sorunları yaşanmaması için dikkatli planlama (örneğin, ReadWriteOnce yerine ReadWriteMany destekleyen depolama çözümleri) gereklidir. S3 gibi nesne depolama çözümleri genellikle daha esnek bir alternatif sunar.

Hangi metrikleri izlemeliyim?

Kubernetes tarafında CPU/bellek kullanımı, ağ I/O, pod başlangıç süreleri ve disk I/O gibi metrikleri izlemelisiniz. CI/CD aracınızda ise her bir pipeline adımının süresi, toplam pipeline süresi ve başarısızlık oranları gibi metrikler kritik öneme sahiptir.

Bu optimizasyonlar maliyeti artırır mı?

Bazı optimizasyonlar (örneğin, özel CI node'ları, daha fazla depolama alanı) başlangıçta maliyeti artırabilir. Ancak, daha hızlı CI süreçleri, geliştirici verimliliğini artırır, bekleme sürelerini azaltır ve daha hızlı ürün teslimatına olanak tanır. Uzun vadede, bu verimlilik artışı genellikle maliyet artışını telafi eder ve hatta toplam maliyeti düşürebilir.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Mobil sürümden çık