2024-12-02

Kyverno ile Pod Disruption Budget'ları Otomatize Etmek

KubernetesDevOpsKyvernoKarpenter
K
<div class="toc"> <ul> <li><a href="#sorun-unutulan-pdb">Sorun: "Unutulan" PDB</a></li> <li><a href="#cozum-kyverno-ile-policy-as-code">Çözüm: Kyverno ile Policy as Code</a></li> <li><a href="#akilli-policy">Akıllı Policy</a></li> <li><a href="#sihri-cozmek">Sihri Çözmek</a></li> <li><a href="#terraform-ile-dagitim">Terraform ile Dağıtım</a></li> <li><a href="#izinler-sorunu">İzinler Sorunu (Gotcha)</a></li> <li><a href="#aggregated-clusterroles-nedir">Aggregated ClusterRoles Nedir?</a></li> <li><a href="#sonuc">Sonuç</a></li> </ul> </div> <p>Zencity.io'daki platformumuz, yerel yönetimler ile sakinleri arasında kritik bir köprü görevi görüyor. Platformumuzun "her zaman açık" (always-on) olması gerekiyor.</p> <p>Kubernetes üzerinde ölçekli bir mikroservis mimarisi çalıştırmak belirli zorlukları beraberinde getiriyor. Cluster verimliliğimiz için en büyük kazançlarımızdan biri, otomatik ölçeklendirme için <a href="https://aws.amazon.com/karpenter/">Karpenter</a> kullanmaya başlamamız oldu (Tercihen AWS resmi dokümantasyonu). Karpenter, nodeları agresif bir şekilde konsolide ederek (mümkün olduğunda podları daha az instance üzerine sıkıca paketleyerek) maliyetleri düşürmede mükemmeldir.</p> <p>Ancak bu verimlilik bir risk de getirdi. Karpenter sürekli nodeları konsolide ediyor. Bir mikroservisin tüm replikaları aynı noda (genellikle açık pod anti-affinity kuralları olmadığı için) planlanmışsa ve o node konsolidasyon için seçildiyse, Karpenter o node'u boşaltır (drain). Güvenlik ağı olmadan bu işlem tüm replikaları aynı anda kapatır ve servis için anında kesintiye neden olur.</p> <p>İşte o güvenlik ağı Pod Disruption Budget (PDB)'dir. Basitçe ifade etmek gerekirse PDB, bakım veya güncellemeler için gerçekleştirilen gönüllü kesintiler sırasında, erişilebilir olması gereken minimum (veya erişilemez olabilecek maksimum) replika sayısını tanımlamaya olanak tanır.</p> <p>En az 2 replikasının çevrimiçi kalması gereken bir uygulama için temel bir PDB şöyledir:</p> <pre><code class="language-yaml"> apiVersion: policy.io/v1beta1 kind: PodDisruptionBudget metadata: name: example-pdb namespace: example-namespace spec: minAvailable: 2 selector: matchLabels: app: example-app </code></pre> <h2 id="sorun-unutulan-pdb">Sorun: "Unutulan" PDB</h2> <p>PDB standart bir Kubernetes kaynağıdır, ancak manuel olarak tanımlanması gerekir. Onlarca mikroservisimiz genelinde şu klasik operasyonel boşluğu yaşadık:</p> <ol> <li><strong>Karpenter Faktörü:</strong> Otomatik node konsolidasyonu "nadir" bakım olaylarını sık ve otomatik eylemlere dönüştürdü.</li> <li><strong>İnsan Hatası:</strong> Geliştiriciler yeni bir mikroservis oluşturur ama PDB tanımlamayı unuturlar.</li> <li><strong>Angarya (Toil):</strong> Her servisin PDB'ye sahip olup olmadığını manuel olarak denetlemek verimsizdi.</li> </ol> <h2 id="cozum-kyverno-ile-policy-as-code">Çözüm: Kyverno ile Policy as Code</h2> <p>Kubernetes için özel tasarlanmış bir politika motoru olan <a href="https://kyverno.io/" target="_blank">Kyverno</a>'yu seçtik. Rego gibi ayrı bir dil gerektiren diğer araçların aksine Kyverno, Kubernetes'e özgü YAML yapılarını kullanır. Amacımız basitti: Eğer bir Deployment veya StatefulSet birden fazla (>= 2) replikaya sahipse ve eşleşen bir PDB YOKSA, otomatik olarak oluştur.</p> <p><em>Not: Bu özelliklerin çalışabilmesi için Kubernetes v1.20+ ve Kyverno v1.9+ sürümleri gerekebilir (Bu kısım kaynakta net belirtilmediğinden doğrulanamadı).</em></p> <h2 id="akilli-policy">Akıllı Policy</h2> <p>Gerçek zorluk sadece bir PDB üretmek değildi; aynı zamanda zaten özel ayarlara sahip PDB'leri kopyalamamak veya onlara müdahale etmemekti. <code>maxUnavailable</code> değerini %20 olarak ayarladık, böylece bir kesinti sırasında replikaların büyük çoğunluğu çevrimiçi kalır.</p> <pre><code class="language-yaml"> apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: auto-generate-pdb spec: rules: - name: generate-pdb-for-deployments match: any: - resources: kinds: - Deployment - StatefulSet context: - name: existing_pdb_count apiCall: urlPath: "/apis/policy/v1/namespaces/{{request.namespace}}/poddisruptionbudgets" jmesPath: "items[?spec.selector.matchLabels == `{{request.object.spec.selector.matchLabels}}`]" preconditions: any: - key: "{{request.object.spec.replicas}}" operator: GreaterThanOrEquals value: 2 - key: "{{existing_pdb_count | length(@)}}" operator: Equals value: 0 generate: apiVersion: policy/v1 kind: PodDisruptionBudget name: "{{request.object.metadata.name}}-pdb" namespace: "{{request.namespace}}" synchronize: true data: spec: maxUnavailable: 20% selector: matchLabels: "{{request.object.spec.selector.matchLabels}}" </code></pre> <h3 id="sihri-cozmek">Sihri Çözmek</h3> <p>Bu politikanın en güçlü kısmı <code>context</code> bloğudur. PDB'ler isme göre Deployment'larla birebir eşleşmez; etiketlere göre eşleşir. Kyverno'nun <code>apiCall</code> özelliğini kullanarak Namespace'teki mevcut PDB'leri sorguluyor, etiketleri filtreliyor ve eşleşme yoksa (count 0 ise) kaynağı üretiyoruz. Ayrıca geçmişte devreye alınan servisler için PDB doldurma işlemini sağlayan <code>generateExisting: true</code> özelliğini kullanıyoruz.</p> <h2 id="terraform-ile-dagitim">Terraform ile Dağıtım</h2> <p>Altyapımızı kod olarak yönetiyoruz. Bu politikayı ortamlarımıza uygulamak için <a href="https://www.devopsn.cloud/tech/terraform-consultancy">Terraform</a> kullanıyoruz. Örneğin üretim ortamında erişilebilirliği sıkı bir şekilde zorunlu kılmak için dinamik değişkenler belirliyoruz.</p> <pre><code class="language-hcl"> resource "kubernetes_manifest" "kyverno_cluster_policy" { manifest = { "apiVersion" = "kyverno.io/v1" "kind" = "ClusterPolicy" "metadata" = { "name" = "auto-generate-pdb" } "spec" = { # Policy details... } } } </code></pre> <h2 id="izinler-sorunu">İzinler Sorunu (Gotcha)</h2> <p>Kyverno'nun arka planda çalışan ve mevcut kaynakları tarayıp yeni kaynaklar üreten bir kontrolcüsü vardır. Varsayılan kurulumda, PDB'leri yönetmek için gerekli izinlere sahip olmayabilir. Bu nedenle kontrolcünün PDB'ler üzerinde sadece okuma değil tam yönetim (create, delete, update) yetkilerine sahip olması gerekir.</p> <h3 id="aggregated-clusterroles-nedir">Aggregated ClusterRoles Nedir?</h3> <p>Bu izinleri temiz bir şekilde vermek için Kubernetes Aggregated ClusterRoles özelliğinden faydalandık. Bu yapı, tek ve devasa bir ClusterRole'ü değiştirmek yerine, daha küçük roller oluşturup etiketlememizi sağlar. Kubernetes kontrol düzlemi bu küçük rolleri ana role otomatik olarak birleştirir.</p> <pre><code class="language-yaml"> apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: kyverno-pdb-manager labels: app.kubernetes.io/component: background-controller app.kubernetes.io/instance: kyverno app.kubernetes.io/part-of: kyverno spec: rules: - apiGroups: ["policy"] resources: ["poddisruptionbudgets"] verbs: ["create", "update", "delete", "get", "list", "watch"] </code></pre> <h2 id="sonuc">Sonuç</h2> <p>Bu policy sayesinde "umuda dayalı" bir güvenilirlik stratejisinden tam otomatik bir stratejiye geçtik. <a href="https://www.devopsn.cloud/tech/kubernetes-consultancy">Kubernetes</a> ve Kyverno sayesinde artık "acaba bir PDB unutuldu mu?" veya "anti-affinity kuralı eksikliği kesintiye yol açar mı?" gibi sorularla vakit kaybetmiyoruz. <a href="https://www.devopsn.cloud/">DevOpsN</a> süreçlerinizde de benzer <a href="https://www.devopsn.cloud/tech/aws-consultancy">AWS</a> eksenli otomasyonlara başvurarak kaynak verimliliğini güvenle artırabilirsiniz.</p> <p><i>Kaynak / Source: http://freedium-mirror.cfd/https://medium.com/zencity-engineering/automating-pod-disruption-budgets-with-kyverno-0a6bee7bbcca</i></p>