Kubernetes projelerinizde klasör karmaşasından sıkıldınız mı? Özellikle büyük ve karmaşık yapılarla uğraşırken, manifest dosyalarının kaybolduğu, ortamlar arası konfigürasyonların karıştığı anlar yaşanabilir. Bu detaylı rehberde, en iyi uygulamaları takip ederek düzenli, sürdürülebilir ve yönetilebilir bir Kubernetes proje klasör yapısı oluşturmayı adım adım öğrenecek, karmaşıklığı ortadan kaldırarak verimliliğinizi artıracaksınız. Haydi, projelerinizi düzene sokmaya başlayalım!
Modern uygulama geliştirme süreçlerinde, mikroservis mimarileri ve DevOps prensipleriyle birlikte Kubernetes, dağıtık sistemlerin yönetiminde vazgeçilmez bir araç haline geldi. Ancak Kubernetes’in gücü, doğru bir yapılandırma ve yönetim stratejisi olmadan kolayca bir karmaşaya dönüşebilir. İşte tam da bu noktada, iyi düşünülmüş bir proje klasör yapısı devreye girer. Bir düşünün, yeni bir ekip üyesi projenize katıldığında veya siz acil bir hatayı düzeltmek için hızlıca ilgili manifest dosyasına ulaşmanız gerektiğinde, aradığınızı anında bulabilmek ne kadar değerli olurdu? Klasör yapısı, projenin okunabilirliğini, yönetilebilirliğini ve en önemlisi ölçeklenebilirliğini doğrudan etkileyen temel bir unsurdur.
Düzenli bir yapı, farklı ortamlar (geliştirme, test, üretim) için ayrılmış konfigürasyonları kolayca ayırt etmenizi sağlar. Bu, yanlış bir manifest dosyasını yanlış bir ortama dağıtma riskini minimize ederken, güvenlik açıklarını ve sistem kesintilerini önlemeye yardımcı olur. Ayrıca, bir projenin yaşam döngüsü boyunca birçok değişiklikten geçtiği düşünüldüğünde, iyi bir klasörleme stratejisi, versiyon kontrol sistemleriyle (Git gibi) daha uyumlu çalışır ve değişiklikleri takip etmeyi basitleştirir. Örneğin, bir Helm chart kullanıyorsanız, chart’ın şablonlarını, değerlerini ve bağımlılıklarını belirli bir düzende tutmak, hem chart’ın bakımını kolaylaştırır hem de başkalarının onu kullanmasını anlaşılır hale getirir. Kısacası, Kubernetes projelerinizdeki düzen, sadece estetik bir tercih değil, aynı zamanda operasyonel mükemmelliğin ve hızlı teslimatın temel taşıdır. Karmaşık yapılar genellikle hatalara, gecikmelere ve yüksek bakım maliyetlerine yol açarken, basit ve anlaşılır bir yapı, ekibin daha verimli çalışmasını sağlayarak inovasyona odaklanmasına olanak tanır. Unutmayın, iyi bir yapılandırma, projenizin gelecekteki büyümesini ve adaptasyonunu destekler.
Peki, bu düzeni nasıl sağlamalıyız? Kubernetes dünyasında standart bir klasör yapısı olmamakla birlikte, sektörde kabul görmüş “en iyi uygulamalar” bulunmaktadır. Bu uygulamalar, çoğu projenin ihtiyaçlarına uyacak esneklikte tasarlanmıştır ve projenizin ölçeği veya karmaşıklığı ne olursa olsun size yol gösterebilir. Aşağıdaki bölümlerde, bu pratik yaklaşımları adım adım inceleyeceğiz. Amacımız, sadece düzeni sağlamak değil, aynı zamanda CI/CD süreçleriyle entegrasyonu kolaylaştırmak ve takım içi işbirliğini güçlendirmektir. Unutulmamalıdır ki, iyi bir başlangıç, uzun vadede zaman ve kaynak tasarrufu demektir.
Temel Kubernetes Proje Yapılandırması: Nereden Başlamalıyım?
Her büyük yolculuk gibi, Kubernetes proje klasör yapınızı oluşturmaya başlamak da temel adımlarla başlar. Genellikle, bir Kubernetes projesi, birden fazla uygulamayı veya tek bir mikroservisi barındırabilir. Bu ilk aşamada, projenizin genel mimarisini göz önünde bulundurarak, manifest dosyalarınızı ve ilgili konfigürasyonlarınızı nasıl gruplandıracağınızı düşünmelisiniz. En yaygın ve anlaşılır yaklaşımlardan biri, her uygulama veya mikroservis için ayrı bir kök klasör oluşturmak ve bu klasörlerin altında Kubernetes kaynaklarını organize etmektir.
Öncelikle, projenizin ana dizininde bir k8s/ veya kubernetes/ klasörü oluşturmak iyi bir başlangıçtır. Bu klasör, tüm Kubernetes ile ilgili manifest ve konfigürasyon dosyalarını içerecektir. Ardından, bu ana klasörün altında, projenizin servislerini veya uygulamalarını temsil eden alt klasörler oluşturun. Örneğin, bir “api-gateway” ve bir “user-service” mikroservisleriniz varsa, yapı aşağıdaki gibi görünebilir:
my-kubernetes-project/
├── k8s/
│ ├── api-gateway/
│ │ └── base/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── ingress.yaml
│ ├── user-service/
│ │ └── base/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── configmap.yaml
│ └── common/
│ ├── namespace.yaml
│ └── rbac.yaml
├── src/
│ ├── api-gateway/
│ │ └── ... (uygulama kodları)
│ └── user-service/
│ └── ... (uygulama kodları)
└── README.md
Bu temel yapılandırmada, her servis kendi klasörü altında barındırılır. base/ klasörü, uygulamanın temel, ortamlardan bağımsız Kubernetes manifest dosyalarını içerir. Bu yaklaşım, manifest dosyalarının mantıksal olarak ayrılmasını ve her bir servisin kendi yaşam döngüsüne sahip olmasını kolaylaştırır. common/ klasörü ise, tüm proje veya birden fazla servis tarafından kullanılan ortak kaynakları (örneğin, namespace tanımları, RBAC rolleri) barındırabilir. Bu, tekrarı önler ve ortak konfigürasyonları tek bir yerden yönetmenize olanak tanır.
Bu ilk düzenleme, projenizin başlangıcında bile büyük bir fark yaratacaktır. Ekip üyeleri, belirli bir servisin dağıtım manifestlerine hızla ulaşabilir ve değişiklik yapabilir. Ayrıca, bu yapı, daha sonra Kustomize veya Helm gibi araçlarla dinamik konfigürasyon yönetimine geçiş yaparken size sağlam bir zemin sunar. Manifest dosyalarınıza ad verirken de tutarlı olmak önemlidir; örneğin, deployment.yaml, service.yaml gibi standart isimlendirmeler kullanmak, okunabilirliği artırır.
Unutmayın, iyi bir klasör yapısı aynı zamanda iyi bir dokümantasyonla birleşmelidir. README.md dosyası, projenin genel yapısını, hangi servisin nerede bulunduğunu ve nasıl dağıtılacağını açıklamalıdır. Bu, özellikle yeni ekip üyeleri için öğrenme eğrisini önemli ölçüde azaltır. Projeniz büyüdükçe bu temel yapının üzerine inşa edilecek çok şey olacaktır, ancak sağlam bir temel her zaman en önemli adımdır. Şimdilik, bu basit ve etkili başlangıç noktasını benimseyerek, Kubernetes projenizin düzenini sağlam bir şekilde kurmuş olacaksınız. Bu sayede, gelecekteki değişiklikler ve genişlemeler için hazırlıklı olursunuz.
Uygulama Katmanı ve Manifest Dosyaları Nasıl Düzenlenir?
Kubernetes projelerinde manifest dosyalarının düzenlenmesi, projenin genel sağlığı ve sürdürülebilirliği için kritik bir öneme sahiptir. Her bir uygulama veya mikroservis için ayrı bir dizin oluşturmak, mantıksal bir ayrım sağlar ve ilgili kaynakların bir arada bulunmasını garanti eder. Bu yaklaşım, özellikle çok sayıda servisi olan büyük monorepo projelerinde veya birden fazla mikroservisi yönetirken iş akışını basitleştirir. Örneğin, api-gateway adında bir servisiniz varsa, tüm Kubernetes manifestleri k8s/api-gateway/ altında yer almalıdır.
Bu dizinin içinde, base/ adında bir alt dizin oluşturmak, ortama özel değişiklikler yapmadan önce varsayılan, temel konfigürasyonları saklamak için harika bir yoldur. base/ dizini tipik olarak aşağıdaki manifest türlerini içerir:
deployment.yaml: Uygulamanızın pod'larını nasıl çalıştıracağınızı tanımlar.service.yaml: Uygulamanızın pod'larına nasıl erişileceğini belirler.ingress.yaml(isteğe bağlı): Dış dünyadan gelen trafiği uygulamanıza yönlendirmek için kullanılır.configmap.yaml: Uygulamanızın yapılandırma verilerini (ortam değişkenleri, ayar dosyaları) içerir.secret.yaml(güvenlik nedeniyle genellikle başka bir yöntemle yönetilir, ancak bazen burada placeholder olarak bulunabilir): Hassas verileri barındırır.pvc.yaml(kalıcı depolama gerekiyorsa): Pod'lar için kalıcı depolama talebini tanımlar.
Bu dosyaların her biri, ilgili Kubernetes kaynağının amacını açıkça belirtmeli ve mümkün olduğunca jenerik olmalıdır. Yani, bu base/ manifestleri, farklı dağıtım ortamlarında (dev, staging, prod) herhangi bir değişiklik yapılmadan doğrudan dağıtılabilecek şekilde tasarlanmalıdır. Örneğin, bir deployment.yaml dosyası, resim etiketi olarak my-app:latest yerine, daha esnek bir şablonlama değişkeni (örneğin Helm'de {{ .Values.image.tag }} veya Kustomize'da yamayı kolaylaştıracak bir placeholder) kullanabilir. Bu, base/ konfigürasyonunun genel geçerliliğini korur.
Ayrıca, her bir manifest dosyası, okunabilirliği artırmak ve bakımı kolaylaştırmak için yorumlarla desteklenmelidir. Özellikle karmaşık veya alışılmadık konfigürasyonlar için kısa açıklamalar eklemek, gelecekteki kendinize veya ekip arkadaşlarınıza büyük kolaylık sağlayacaktır. Örneğin:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
labels:
app: my-app
spec:
replicas: 3 # Üretim ortamı için varsayılan kopya sayısı
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: my-registry/my-app:1.0.0 # Temel imaj etiketi
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: my-app-config
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
Bu yapı, uygulamanın temel ihtiyaçlarını net bir şekilde ortaya koyar ve geliştiricilerin, operasyon ekiplerinin ve otomasyon araçlarının projeyi anlamasını ve yönetmesini kolaylaştırır. Tekrar eden konfigürasyonları önlemek için common/ gibi paylaşılan kaynak klasörleri kullanmak da akıllıca bir yaklaşımdır. Bu sayede, örneğin tüm servislerinizin kullanacağı bir Kubernetes namespace'ini veya genel RBAC politikalarını tek bir yerden yönetebilirsiniz. Bu düzenleme, projenizin büyüklüğünden bağımsız olarak, sürekli bir düzen ve verimlilik artışı sağlayacaktır.
Ortama Özel Konfigürasyonları Yönetmek İçin En İyi Uygulamalar Nelerdir?
Kubernetes projelerinde ortama özel konfigürasyonları yönetmek, başarılı bir dağıtım stratejisinin temel direklerinden biridir. Geliştirme, test (staging) ve üretim (production) gibi farklı ortamların kendine özgü gereksinimleri vardır; bu, genellikle farklı kopya sayıları, kaynak limitleri, etiketler, Ingress kuralları veya ConfigMap değerleri anlamına gelir. Bu farklılıkları yönetmek için en etkili yol, temel (base) manifest dosyalarınızın üzerine "overlays" (kaplamalar) oluşturmaktır.
Bu yaklaşımı uygulamak için, her uygulama dizininin altında, environments/ veya overlays/ adında bir klasör oluşturmak yaygın bir uygulamadır. Bu klasörün içinde, her ortam için ayrı bir alt klasör bulunur (örneğin, dev/, staging/, prod/). Bu alt klasörler, base/ klasöründeki manifestleri hedef alan ve ortama özel değişiklikleri içeren manifest dosyalarını barındırır.
Bir örnekle açıklayalım:
my-kubernetes-project/
├── k8s/
│ ├── api-gateway/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── ingress.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ ├── kustomization.yaml
│ │ │ └── replica-patch.yaml
│ │ ├── staging/
│ │ │ ├── kustomization.yaml
│ │ │ └── ingress-host.yaml
│ │ └── prod/
│ │ ├── kustomization.yaml
│ │ └── resource-limits.yaml
│ └── user-service/
│ └── ...
└── ...
Bu yapıda, api-gateway/base/ dizininde uygulamanın varsayılan, jenerik manifestleri yer alır. Daha sonra overlays/ içindeki her ortam klasörü, base/ manifestlerini alıp üzerine o ortama özel yamaları (patches) uygular. Bu yamalar genellikle küçük YAML dosyalarıdır ve sadece değiştirilmesi gereken alanları içerir. Örneğin, prod/replica-patch.yaml dosyası sadece üretim ortamı için replika sayısını artırabilir.
Her bir overlays// klasöründe bulunan kustomization.yaml dosyası, Kustomize'ın hangi base/ manifestlerini kullanacağını ve hangi yamaları uygulayacağını belirler. Bu sayede, tek bir komutla ilgili ortama özel manifestler oluşturulabilir:
# Geliştirme ortamı manifestlerini oluşturmak için:
kubectl apply -k k8s/api-gateway/overlays/dev/
# Üretim ortamı manifestlerini oluşturmak için:
kubectl apply -k k8s/api-gateway/overlays/prod/
Bu yöntem, manifest dosyasında tekrar eden kod miktarını en aza indirir ve ortamlar arası tutarlılığı garanti eder. Ayrıca, bir değişiklik yapıldığında, bu değişikliğin sadece belirli bir ortamı mı yoksa tüm ortamları mı etkileyeceğini anlamak kolaylaşır. Örneğin, bir servisin genel bir özelliği değiştiğinde sadece base/ dosyası güncellenirken, bir ortamın kaynak limitleri değiştiğinde sadece o ortamın yaması güncellenir. Bu, dağıtım süreçlerini basitleştirir, hata olasılığını azaltır ve CI/CD (Sürekli Entegrasyon/Sürekli Dağıtım) boru hatlarının daha güvenilir çalışmasını sağlar. Her ortam için ayrı bir Git branch veya tag kullanmak yerine, aynı branch üzerinde farklı ortam konfigürasyonlarını yönetebilmek, GitOps prensipleriyle de daha uyumlu bir yaklaşım sunar.
Son olarak, hassas verileri (şifreler, API anahtarları) doğrudan Git deposunda saklamaktan kesinlikle kaçınılmalıdır. Bunun yerine, Kubernetes Secret'ları, HashiCorp Vault veya sızdırmazlık araçları (Sealed Secrets gibi) kullanılmalıdır. Manifestlerde Secret referansları bulunabilir, ancak Secret'ın kendisi asla depoya commit edilmemelidir. Bu, güvenlik açısından kritik bir uygulamadır ve her zaman öncelikli olarak değerlendirilmelidir. Ortama özel konfigürasyonların doğru bir şekilde yönetilmesi, projenizin olgunluğunu ve profesyonelliğini gösteren önemli bir işarettir.
Monorepo Yaklaşımı ile Çoklu Uygulamaları Yönetmek: Avantajları Nelerdir?
Modern yazılım geliştirmede, projenizin büyüklüğüne ve takımınızın yapısına bağlı olarak iki ana kod yönetimi stratejisi öne çıkar: polirepo (her mikroservis/uygulama için ayrı bir depo) ve monorepo (tüm mikroservisler/uygulamalar tek bir depoda). Kubernetes ortamında, monorepo yaklaşımının kendine özgü avantajları ve iyi düşünülmüş bir klasör yapısıyla birleştiğinde sunduğu faydalar bulunmaktadır.
Monorepo, adından da anlaşılacağı gibi, birden fazla projenin veya uygulamanın tek bir Git deposunda barındırıldığı bir yaklaşımdır. Kubernetes bağlamında, bu, hem uygulama kaynak kodunu hem de ilgili Kubernetes manifestlerini aynı depoda tutmak anlamına gelir. Klasik bir monorepo yapısı şu şekilde görünebilir:
my-monorepo-project/
├── apps/
│ ├── api-gateway/
│ │ ├── src/
│ │ └── k8s/
│ │ ├── base/
│ │ └── overlays/
│ ├── user-service/
│ │ ├── src/
│ │ └── k8s/
│ │ ├── base/
│ │ └── overlays/
│ └── ... (diğer uygulamalar)
├── libs/
│ ├── common-utils/
│ └── ... (paylaşılan kütüphaneler)
├── k8s-infra/
│ ├── common/
│ │ ├── namespace.yaml
│ │ └── rbac.yaml
│ └── environments/
│ ├── dev/
│ │ └── kustomization.yaml
│ └── prod/
│ └── kustomization.yaml
└── README.md
Bu yapıda, apps/ dizini altında her uygulama kendi kaynak kodunu (src/) ve Kubernetes konfigürasyonlarını (k8s/) barındırır. libs/ ise uygulamalar arasında paylaşılan kod kütüphanelerini içerir. En dikkat çekici kısım ise k8s-infra/ dizinidir. Bu dizin, tüm monorepo genelinde kullanılan ortak Kubernetes kaynaklarını veya üst düzey ortam tanımlarını barındırabilir. Örneğin, tüm uygulamaların konuşlandığı namespace'ler, genel ağ politikaları veya küme genelindeki rol tanımları burada yer alabilir.
Peki, bu yaklaşımın avantajları nelerdir? Öncelikle, bağımlılık yönetimi basitleşir. Uygulamalar arası kod paylaşımı daha kolay hale gelir, çünkü tüm kod aynı depoda bulunur. Bir kütüphanede yapılan değişiklikler, bağımlı olan tüm uygulamalarda anında görünür hale gelir ve CI/CD süreçleri bu değişikliklerin tüm sistemi nasıl etkilediğini daha kolay test edebilir. İkinci olarak, atomik değişiklikler (atomic changes) yapma yeteneği büyük bir avantajdır. Bir özellik, birden fazla mikroserviste değişiklik gerektirdiğinde, bu değişiklikler tek bir Git commit'i içinde yapılabilir ve tüm ilgili servisler aynı anda dağıtılabilir. Bu, dağıtım koordinasyonunu büyük ölçüde basitleştirir ve "deploy hell" sendromunu azaltır.
Üçüncü olarak, tutarlılık ve kod tekrarı azalır. Ortak Kubernetes manifest şablonları, CI/CD boru hatları ve geliştirme ortamı konfigürasyonları merkezi bir şekilde yönetilebilir. Bu, farklı mikroservislerde aynı veya benzer konfigürasyonları tekrar tekrar yazma ihtiyacını ortadan kaldırır. Dördüncü olarak, CI/CD süreçleri daha entegre ve verimli hale gelebilir. Tüm kodun tek bir yerde olması, tek bir boru hattının birden fazla uygulamayı derlemesini, test etmesini ve dağıtmasını sağlar. Akıllı CI/CD sistemleri, sadece değişen uygulamaları tespit edip onları dağıtabilir, bu da kaynak kullanımını optimize eder.
src/ veya k8s/ dizinleri) tarayarak ilgili uygulamaları yeniden dağıtmasını sağlayacak akıllı trigger'lar kurmak, dağıtım sürelerini kısaltır ve gereksiz kaynak tüketimini önler. Bu, özellikle büyük repolarda çok önemlidir.
Elbette, monorepo'nun da zorlukları vardır; örneğin, depo boyutu büyüyebilir, klonlama süreleri uzayabilir ve tek bir depoda çok fazla geliştiricinin çalışması merge çakışmalarına yol açabilir. Ancak, iyi bir klasör yapısı, güçlü bir versiyon kontrol stratejisi (örneğin, trunk-based development) ve akıllı CI/CD araçlarıyla bu zorlukların üstesinden gelinebilir. Özellikle küçük ve orta ölçekli takımlar için, monorepo, Kubernetes projelerinde verimliliği ve koordinasyonu artıran güçlü bir yaklaşımdır. Karmaşık dağıtık sistemlerin yönetimini basitleştirme potansiyeli göz ardı edilmemelidir.
Helm ve Kustomize ile Dinamik Konfigürasyon Yönetimi Nasıl Yapılır?
Kubernetes manifest dosyalarının manuel olarak düzenlenmesi ve sürdürülmesi, projenizin boyutu arttıkça giderek daha zahmetli ve hata yapmaya açık hale gelir. İşte bu noktada Helm ve Kustomize gibi araçlar devreye girerek dinamik konfigürasyon yönetimi için güçlü çözümler sunar. Bu araçlar, temel klasör yapımızı tamamlayarak, ortama özel değişiklikleri, şablonlamayı ve paketlemeyi kolaylaştırır. Her ikisinin de kendi kullanım senaryoları ve avantajları bulunmaktadır.
Helm: Kubernetes Paket Yöneticisi ve Şablonlama Gücü
Helm, Kubernetes için bir paket yöneticisi olarak öne çıkar. Uygulamaları "chart" adı verilen önceden yapılandırılmış paketler halinde tanımlamanızı, kurmanızı ve yönetmenizi sağlar. Bir Helm chart'ı, bir uygulamanın tüm Kubernetes kaynaklarını (Deployment, Service, Ingress vb.) içerir ve bu kaynakları Go şablonlama diliyle dinamik hale getirir. Klasör yapımız içinde Helm'i kullanmak istediğimizde, her bir uygulamamız için ayrı bir Helm chart'ı oluşturabiliriz:
my-kubernetes-project/
├── charts/
│ ├── api-gateway/
│ │ ├── Chart.yaml
│ │ ├── values.yaml
│ │ ├── templates/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── _helpers.tpl
│ │ └── README.md
│ ├── user-service/
│ │ └── ... (Helm chart yapısı)
├── k8s/
│ └── ... (varsa Kustomize overlays veya diğer manifestler)
└── ...
Bu yapıda, charts/ dizini altında her uygulama için bir Helm chart bulunur. values.yaml dosyası, chart'ın varsayılan yapılandırma değerlerini içerir ve bu değerler dağıtım sırasında ortam bazında geçersiz kılınabilir. templates/ dizini ise uygulamanın Kubernetes manifest şablonlarını barındırır. Helm'in şablonlama yetenekleri sayesinde, bir kez şablonladığınız bir manifest dosyasını farklı ortamlar için farklı değerlerle (örneğin, replika sayıları, imaj etiketleri, kaynak limitleri) dağıtabilirsiniz:
# charts/api-gateway/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "api-gateway.fullname" . }}
labels:
{{- include "api-gateway.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }} # replicaCount değeri values.yaml'dan gelir
selector:
matchLabels:
{{- include "api-gateway.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "api-gateway.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
{{- toYaml .Values.resources | nindent 12 }}
Helm, bir uygulamanın tüm bağımlılıklarını ve yaşam döngüsünü yönetmek için idealdir. Bir veritabanı veya kuyruk sistemi gibi karmaşık bağımlılıkları olan uygulamaları tek bir Helm chart'ı aracılığıyla kolayca dağıtabilirsiniz.
Kustomize: Yamalarla Manifestleri Özelleştirme
Kustomize, Helm'den farklı olarak şablonlama motoru kullanmaz. Bunun yerine, mevcut, jenerik Kubernetes manifestlerini "yamalar" (patches) kullanarak özelleştirmenizi sağlar. Bu, "template-free" (şablonsuz) yaklaşım olarak bilinir ve genellikle daha basit ve anlaşılır bir yapı sunar. Kustomize, özellikle base/ ve overlays/ klasör yapımızla mükemmel bir uyum içindedir:
my-kubernetes-project/
├── k8s/
│ ├── api-gateway/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── kustomization.yaml # Base için kustomization.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ ├── kustomization.yaml
│ │ │ └── dev-replica-patch.yaml
│ │ ├── prod/
│ │ │ ├── kustomization.yaml
│ │ │ └── prod-resource-limits.yaml
│ └── user-service/
│ └── ...
└── ...
Her overlays// dizini, kendi kustomization.yaml dosyasını içerir. Bu dosya, base/ dizinini referans alır ve bu temel manifestler üzerine uygulanacak yamaları, configMap ve Secret jeneratörlerini veya diğer özelleştirmeleri tanımlar. Örneğin, prod-resource-limits.yaml sadece üretim ortamına özgü CPU/bellek limitlerini güncelleyen küçük bir yama olabilir.
# k8s/api-gateway/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base # base klasöründeki kaynakları kullan
patches:
- path: prod-resource-limits.yaml # Bu yamayı uygula
target:
kind: Deployment
name: api-gateway-deployment # Hedef deployment
# Ortam bazında etiket ekleme
commonLabels:
env: prod
# k8s/api-gateway/overlays/prod/prod-resource-limits.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway-deployment
spec:
template:
spec:
containers:
- name: api-gateway-container
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "1024Mi"
Kustomize'ın ana gücü, "diff" tabanlı bir yaklaşım sunarak sadece değişen kısımları yönetmesidir. Bu, şablonlamaya göre daha az karmaşık olabilir ve mevcut manifestleri düzenlemeniz gerektiğinde daha doğrudan bir kontrol sağlar. Hem Helm hem de Kustomize, CI/CD boru hatlarınızla entegre edilebilir ve Kubernetes dağıtımlarınızı otomatikleştirmek için kullanılabilir. Seçiminiz, projenizin karmaşıklığına, ekibinizin alışkanlıklarına ve belirli gereksinimlerinize bağlı olacaktır. Bazı projelerde her iki aracın bir kombinasyonu bile kullanılabilir; örneğin, Helm chart'ları Kustomize ile özelleştirilebilir. Bu araçlar, iyi bir klasör yapısıyla birleştiğinde, Kubernetes konfigürasyon yönetimini bambaşka bir seviyeye taşır.
GitOps ve CI/CD Entegrasyonu İçin Klasör Yapısının Rolü Nedir?
Modern DevOps dünyasında, GitOps ve Sürekli Entegrasyon/Sürekli Dağıtım (CI/CD) kavramları Kubernetes ile el ele gider. GitOps, operasyonel altyapının durumunu yönetmek için Git depolarını tek doğru kaynak (single source of truth) olarak kullanır. Bu yaklaşım, altyapıyı kod olarak ele alır ve Git'in versiyon kontrol, değişiklik izleme ve işbirliği özelliklerini altyapı yönetimine taşır. CI/CD boru hatları ise, kod değişikliklerinin otomatik olarak derlenmesini, test edilmesini ve dağıtılmasını sağlar. Bu güçlü entegrasyonun temelinde, iyi düşünülmüş ve sistematik bir klasör yapısı yatar.
Klasör yapınız, GitOps ve CI/CD süreçlerinizin verimliliğini ve güvenilirliğini doğrudan etkiler. Eğer manifest dosyalarınız dağınık, tutarsız veya ortama özel değişiklikleri yönetmekte zorlanıyorsa, otomasyon süreçlerinizde hatalar meydana gelmesi kaçınılmazdır. İyi bir klasör yapısı, CI/CD araçlarının doğru manifestleri bulmasını, ortamlar arası farklılıkları uygulamasını ve dağıtımın güvenli bir şekilde gerçekleşmesini sağlar.
GitOps İçin Klasör Yapısının Önemi
GitOps'ta, Kubernetes kümenizin mevcut durumu (beklenen durum) Git deposundaki manifest dosyalarıyla temsil edilir. Git deposunda yapılan her değişiklik (commit), küme üzerinde bir değişime yol açar. Bu modelde, klasör yapınızın aşağıdaki özellikleri taşıması kritik önemdedir:
- Açık ve Anlaşılır Ortam Ayırımı: Her ortam (dev, staging, prod) için ayrı dizinlerin bulunması, GitOps operatörlerinin (Argo CD, Flux CD gibi) belirli bir ortama özgü manifestleri kolayca senkronize etmesini sağlar. Örneğin,
k8s/overlays/proddizini, üretim ortamı için beklenen durumu içerir. - Modülerlik: Her uygulama veya mikroservis için ayrı bir dizin ve içinde
base/ileoverlays/yapıları, GitOps araçlarının sadece değişen bileşenleri hedef almasına olanak tanır. Bu, gereksiz yeniden dağıtımları önler ve dağıtım süreçlerini hızlandırır. - Versiyon Kontrol Entegrasyonu: Klasör yapınız, Git'in doğasına uygun olmalıdır. Örneğin, bir uygulamaya ait tüm manifestler aynı dizinde olduğunda, o uygulama ile ilgili tüm değişiklikler tek bir Git commit'inde toplanabilir. Bu, değişikliklerin izlenmesini ve geri alınmasını kolaylaştırır.
Bir GitOps akışında, geliştirici bir kod değişikliği yapar, bu değişiklik testlerden geçer ve bir Pull Request (PR) ile Git deposuna merge edilir. Kubernetes manifest dosyaları da aynı şekilde (veya ayrı bir depoda) yönetilir. GitOps operatörü, Git deposundaki manifest değişikliklerini sürekli olarak izler ve kümenin durumunu bu değişikliklere göre günceller. Eğer klasör yapısı karmaşıksa, bu izleme ve senkronizasyon süreçleri başarısız olabilir veya yanlış kaynakları güncelleyebilir.
CI/CD Entegrasyonu İçin Klasör Yapısının Rolü
CI/CD boru hatları, kod ve konfigürasyon değişikliklerinin hızlı ve güvenilir bir şekilde dağıtımını sağlamak için tasarlanmıştır. Klasör yapınız, CI/CD adımlarınızın mantıksal olarak düzenlenmesine ve optimize edilmesine yardımcı olur:
- Kolay Manifest Erişimi: CI/CD script'leri, dağıtım için gerekli manifest dosyalarına kolayca erişebilmelidir. Örneğin,
kubectl apply -f k8s/my-app/base/veyakustomize build k8s/my-app/overlays/prod | kubectl apply -f -gibi komutlar, iyi bir klasör yapısıyla çok daha okunabilir ve yönetilebilir hale gelir. - Ortama Duyarlı Dağıtım: CI/CD boru hattı, dağıtım hedefi olan ortama (dev, staging, prod) göre farklı komutlar veya parametreler kullanabilir. Klasör yapısı, bu ortamları açıkça ayırarak, boru hattının doğru konfigürasyon setini seçmesini sağlar.
- Sadece Değişenleri Dağıtma (Optimizasyon): Monorepo senaryolarında, iyi bir klasör yapısı ve akıllı CI/CD araçları sayesinde, sadece kodu veya manifestleri değişen uygulamaların yeniden derlenmesi ve dağıtılması mümkündür. Bu, CI/CD sürelerini kısaltır ve kaynak kullanımını optimize eder. Örneğin, bir Jenkinsfile veya GitHub Actions workflow'u, sadece
k8s/user-service/klasöründe bir değişiklik algıladığındauser-service'i yeniden dağıtabilir.
# .github/workflows/deploy.yml
name: Kubernetes Deploy
on:
push:
branches:
- main
paths:
- 'k8s/api-gateway/**' # Sadece api-gateway manifestleri değiştiğinde tetikle
- 'k8s/user-service/**' # Sadece user-service manifestleri değiştiğinde tetikle
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Deploy API Gateway to Production
if: contains(github.event.commits[0].message, '[deploy-api-gateway]')
run: |
# Ortama özel kustomize build ve kubectl apply
kustomize build k8s/api-gateway/overlays/prod | kubectl apply -f -
- name: Deploy User Service to Production
if: contains(github.event.commits[0].message, '[deploy-user-service]')
run: |
kustomize build k8s/user-service/overlays/prod | kubectl apply -f -
Yukarıdaki örnekte, GitHub Actions'ın paths anahtar kelimesi ve commit mesajı kontrolü ile sadece ilgili servislerin dağıtılması sağlanmıştır. Bu tür akıllı CI/CD yapıları, düzgün bir klasörleme olmasa uygulanamazdı. Özetle, iyi bir Kubernetes proje klasör yapısı, sadece geliştiricilerin işini kolaylaştırmakla kalmaz, aynı zamanda GitOps prensiplerinin ve CI/CD otomasyonunun tam potansiyelini ortaya çıkararak daha hızlı, daha güvenilir ve daha sürdürülebilir bir yazılım teslimatı sağlar. Bu, modern DevOps kültürünün temel bir parçasıdır.
Vaka Analizi: Büyük Ölçekli Bir Projede Başarılı Klasör Yapısı Uygulaması
Teorik bilgilerin ötesine geçerek, gerçek dünya senaryolarında iyi bir Kubernetes klasör yapısının nasıl bir fark yarattığını anlamak önemlidir. Hayal edelim ki, "GlobalConnect" adında, dünya genelinde milyonlarca kullanıcıya hizmet veren bir e-ticaret platformu geliştiriyorsunuz. Bu platform, mikroservis mimarisinde tasarlanmış olup, 50'den fazla farklı mikroservis, çeşitli üçüncü taraf entegrasyonları ve farklı coğrafi bölgelerde konuşlandırılmış Kubernetes kümeleri içermektedir. Proje başlangıcında, ekip küçükken manifestler tek bir dizinde tutulmuş, ancak büyüme ile birlikte kontrol edilemez bir karmaşa ortaya çıkmıştır.
Sorun: Yönetilemez Karmaşa ve Hata Oranları
GlobalConnect'in ilk aşamalarında, tüm deployment.yaml, service.yaml ve diğer manifest dosyaları tek bir kubernetes/ dizini altında düz bir şekilde duruyordu. Farklı ortamlar için (dev, test, staging, prod) manuel kopyala-yapıştır ile konfigürasyonlar çoğaltılmıştı. Bu durum, aşağıdaki sorunlara yol açtı:
- Yüksek Hata Oranı: Bir geliştirici, üretim ortamı için tasarlanmış bir manifesti yanlışlıkla geliştirme ortamına dağıttı ve bu, test veritabanında istenmeyen değişikliklere neden oldu.
- Yavaş Dağıtım Süreçleri: Her yeni özellik veya hata düzeltmesi için ilgili manifestleri bulmak, ortamlar arası farkları manuel olarak düzenlemek uzun zaman alıyordu.
- Yetersiz İşbirliği: Farklı ekipler aynı manifestler üzerinde çakışmalar yaşıyor, değişikliklerin kim tarafından ve neden yapıldığı net değildi.
- Güvenlik Açıkları: Hassas veriler (Secret'lar) bazen yanlışlıkla Git deposuna commit ediliyordu.
Çözüm: Yeniden Yapılandırılmış Klasör ve Araç Entegrasyonu
Bu sorunları çözmek için GlobalConnect ekibi, kapsamlı bir yeniden yapılandırma projesi başlattı ve aşağıdaki klasör yapısını benimsedi:
globalconnect-monorepo/
├── services/
│ ├── order-service/
│ │ ├── src/
│ │ └── k8s/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── kustomization.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ └── kustomization.yaml
│ │ ├── staging/
│ │ │ └── kustomization.yaml
│ │ └── prod/
│ │ └── kustomization.yaml
│ ├── payment-service/
│ │ └── ... (aynı yapı)
│ └── ... (diğer 48 servis)
├── shared-libs/
│ └── ...
├── infrastructure/ # Küme genelindeki altyapı manifestleri
│ ├── cluster-roles/
│ ├── namespaces/
│ └── network-policies/
├── helm-charts/ # Bazı karmaşık uygulamalar için Helm
│ └── analytics-dashboard/
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
├── ci-cd/
│ ├── argocd-applications/ # Argo CD uygulama tanımları
│ │ ├── dev.yaml
│ │ ├── staging.yaml
│ │ └── prod.yaml
│ └── pipelines/ # Jenkins/GitHub Actions pipeline tanımları
└── README.md
Bu yeni yapılandırma ile birlikte aşağıdaki iyileştirmeler yapıldı:
- Kustomize Benimsenmesi: Her servis,
base/manifestlerini ve ortama özeloverlays/klasörlerini kullanan bir Kustomize yapısına geçti. Bu, manuel kopyala-yapıştır hatasını tamamen ortadan kaldırdı ve ortamlar arası tutarlılığı sağladı. - Argo CD (GitOps Operatörü) Entegrasyonu:
ci-cd/argocd-applications/dizininde, her ortam için Argo CD'nin izleyeceği uygulamaların tanımları oluşturuldu. Argo CD, Git deposundaki değişiklikleri otomatik olarak algılayıp Kubernetes kümelerine senkronize etmeye başladı. Bu, dağıtım sürecini tamamen otomatik ve gözlemlenebilir hale getirdi. - Paylaşılan Altyapı Manifestleri:
infrastructure/dizini, tüm kümeler veya birden fazla servis için geçerli olan RBAC kuralları, Namespace tanımları ve ağ politikaları gibi ortak altyapı konfigürasyonlarını içeriyordu. Bu, tekrarı önledi ve merkezi yönetim sağladı. - Helm Chart Kullanımı: Analitik panosu gibi karmaşık, üçüncü taraf uygulamalar için Helm chart'ları
helm-charts/dizininde yönetildi. Bu, bu uygulamaların dağıtımını ve konfigürasyonunu basitleştirdi. - CI/CD Pipelines:
ci-cd/pipelines/dizininde Jenkinsfile'lar veya GitHub Actions workflow'ları, sadece değişen servislerin Kubernetes manifestlerinin Kustomize ile derlenmesini ve Argo CD'ye senkronizasyon tetiklemesini sağlayacak şekilde optimize edildi.
Sonuçlar ve Kazanımlar
Bu yeniden yapılandırma sayesinde GlobalConnect ekibi önemli kazanımlar elde etti:
- %90 Hata Azalması: Manuel müdahalenin azalması ve otomasyonun artmasıyla dağıtım hataları neredeyse sıfıra indi.
- %70 Dağıtım Hızı Artışı: Otomatikleştirilmiş GitOps akışları sayesinde, bir değişikliğin üretime geçme süresi dakikalara indi.
- Geliştirilmiş İşbirliği: Her servis kendi manifest dizininde çalıştığı için, farklı ekipler aynı anda farklı servisler üzerinde çalışabilir ve merge çakışmaları azaldı.
- Daha İyi Güvenlik: Secret'lar Git deposundan tamamen kaldırıldı ve Kubernetes Secret Management araçları ile yönetilmeye başlandı.
- Yüksek Şeffaflık: Tüm altyapı konfigürasyonu Git'te versiyonlandığı için, her değişikliğin kaydı tutuldu ve denetlenebilirlik arttı.
Bu vaka analizi, iyi bir Kubernetes proje klasör yapısının, araç entegrasyonları (Kustomize, Helm, Argo CD) ve otomatize CI/CD süreçleriyle birleştiğinde, büyük ölçekli ve karmaşık projelerde dahi yönetilebilirliği, verimliliği ve güvenilirliği nasıl artırabileceğini açıkça göstermektedir. Başarılı bir yapı, sadece kodun değil, tüm operasyonel yaşam döngüsünün temelini oluşturur.
Sonuç: Temiz Bir Yapı ile Kubernetes Projelerinizi Ölçeklendirin
Kubernetes ekosisteminde proje klasör yapısının önemi, çoğu zaman göz ardı edilen ancak projenizin uzun vadeli başarısı ve sürdürülebilirliği için kritik bir faktördür. Bu rehber boyunca ele aldığımız gibi, iyi düşünülmüş bir yapılandırma, sadece başlangıçtaki düzeni sağlamakla kalmaz, aynı zamanda projenizin büyümesiyle ortaya çıkabilecek karmaşıklıkları yönetme yeteneğinizi de artırır. Geliştirme sürecinin hızından, operasyonel verimliliğe, hata oranlarının düşürülmesinden ekip içi işbirliğinin güçlendirilmesine kadar birçok alanda olumlu etkiler yaratır.
Başlangıçta basit bir base/ ve overlays/ yapısı ile manifestlerinizi düzenleyerek temel bir düzen kurabilir, ardından Kustomize'ın yama tabanlı yaklaşımı veya Helm'in şablonlama ve paketleme gücüyle dinamik konfigürasyon yönetimine geçebilirsiniz. Monorepo yaklaşımı, birden fazla mikroservisi tek bir çatı altında yönetme esnekliği sunarken, GitOps prensipleri ve entegre CI/CD boru hatları, dağıtımlarınızı otomatikleştirerek güvenilirliği ve hızı artırır. Unutmayın, bu yaklaşımların tamamı veya bir kısmı, projenizin özel ihtiyaçlarına ve ekibinizin yetkinliklerine göre uyarlanabilir.
Herhangi bir projenin "tek doğru" klasör yapısı olmasa da, bu rehberde sunulan "en iyi uygulamalar", deneyimlerden damıtılmış ve sektörde geniş kabul görmüş prensiplere dayanır. Bu prensipleri kendi projenize uygulayarak, Kubernetes kaynaklarınızın okunabilirliğini, bakımını ve ölçeklenebilirliğini önemli ölçüde geliştirebilirsiniz. Temiz, modüler ve iyi organize edilmiş bir klasör yapısı, projenizin sadece bugününü değil, gelecekteki büyümesini ve adaptasyonunu da destekleyen güçlü bir temel oluşturur. Böylece, karmaşıklığı yönetilebilir hale getirirken, ekipleriniz inovasyona ve değer yaratmaya odaklanabilirler. Hadi, projelerinizi bugün düzenlemeye başlayın ve Kubernetes'in tüm potansiyelini ortaya çıkarın!
Sıkça Sorulan Sorular
-
Kubernetes klasör yapısı için "tek doğru" bir standart var mı?
Hayır, Kubernetes ekosisteminde evrensel olarak kabul görmüş tek bir standart klasör yapısı yoktur. Ancak bu makalede ele aldığımız
base/veoverlays/gibi yaklaşımlar, Kustomize ve Helm ile entegrasyonu kolaylaştıran sektördeki en iyi uygulamalardır. Projenizin ölçeği, ekibinizin büyüklüğü ve kullanılan araçlara göre yapınızı adapte etmeniz en doğrusudur. -
Helm mi Kustomize mi kullanmalıyım? İkisini bir arada kullanabilir miyim?
Seçim, projenizin ihtiyaçlarına bağlıdır. Helm, karmaşık uygulamaların paketlenmesi ve şablonlama için güçlüdür; Kustomize ise mevcut manifestleri yamalarla özelleştirmek için daha basittir ve şablonlama gerektirmez. Evet, ikisini bir arada kullanmak mümkündür. Örneğin, Kustomize'ı kullanarak bir Helm chart'ın çıktısını özelleştirebilir veya Helm chart'larınızı Kustomize'ın
resourcesalanında bir kaynak olarak gösterebilirsiniz. Bu, size her iki aracın avantajlarından yararlanma esnekliği sunar. -
Monorepo yaklaşımı büyük projeler için her zaman en iyi seçenek midir?
Monorepo'nun birçok avantajı (basitleştirilmiş bağımlılık yönetimi, atomik değişiklikler) olsa da, büyük projelerde kendi zorlukları da vardır (depo boyutu, klonlama süreleri, merge çakışmaları). Genellikle, küçük ve orta ölçekli ekipler için idealdir. Çok büyük ve bağımsız ekiplerle çalışılan projelerde, iyi yönetilen polirepo (birden fazla depo) yaklaşımı daha uygun olabilir. Karar verirken ekibinizin kültürünü, araç setini ve projenizin karmaşıklığını göz önünde bulundurmalısınız.
-
Kubernetes Secret'ları klasör yapısında nasıl yönetmeliyim?
Hassas verileri (Secret'lar) doğrudan Git deposunda, yani klasör yapınızda asla açık metin olarak saklamamalısınız. Bunun yerine, Kubernetes Secret'ları güvenli bir şekilde oluşturmak ve yönetmek için çeşitli araçlar ve yöntemler vardır. Örneğin, Bitnami'nin Sealed Secrets, HashiCorp Vault veya Kubernetes'in kendi Secret yönetimini kullanabilir ve manifest dosyalarınızda sadece bu Secret'lara referans verebilirsiniz. Bu, güvenlik açısından hayati öneme sahiptir ve her zaman öncelikli olmalıdır.
-
Bir uygulama için hem kod hem de Kubernetes manifestlerini aynı dizinde mi tutmalıyım?
Bu, genellikle monorepo yaklaşımıyla birlikte tercih edilen bir yapıdır. Eğer uygulamanızın kaynak kodu (
src/) ve Kubernetes manifestleri (k8s/) aynı üst dizinde yer alırsa, ilgili değişiklikler daha kolay ilişkilendirilebilir ve CI/CD süreçleri bu iki bileşen arasındaki bağımlılığı daha iyi yönetebilir. Bu, özellikle bir mikroservis mimarisinde, her servisin kendi "kapsülünde" izole edilmesi gerektiğinde oldukça etkilidir. Ancak, ayrı repolarda çalışıyorsanız, Kubernetes manifestleri için ayrı bir Git deposu veya sadece manifestleri içeren bir yapı tercih edilebilir.
