Jenkins Pipeline Nedir?

Jenkins, açık kaynaklı bir sürekli entegrasyon ve sürekli teslimat (CI/CD) aracıdır. Jenkins Pipeline ise yazılım geliştirme süreçlerini — derleme, test, dağıtım gibi adımları — kod olarak tanımlamanızı sağlayan bir özelliktir. Bu yaklaşım “Pipeline as Code” olarak da bilinir.

Pipeline tanımları Jenkinsfile adlı bir dosyada saklanır ve bu dosya doğrudan kaynak kod deposuna (Git vb.) commit edilir. Böylece CI/CD süreciniz, uygulama kodunuzla birlikte versiyonlanır, gözden geçirilir ve izlenebilir hale gelir. Bir geliştirici hangi commit’te hangi deployment adımının değiştiğini tam olarak görebilir; bu da hata ayıklamayı ve ekip içi iletişimi önemli ölçüde kolaylaştırır.

Neden Jenkins Pipeline Kullanmalısınız?

Geleneksel yöntemde CI/CD adımları Jenkins arayüzünden tıklanarak yapılandırılır. Bu konfigürasyon yalnızca Jenkins sunucusunda yaşar, ekip üyeleri tarafından görülmez ve değişiklik geçmişi tutulmaz. Pipeline yaklaşımı bu sorunu kökten çözer.

Pipeline kullandığınızda Jenkinsfile, projenizin kaynak kodunun ayrılmaz bir parçası olur. Bir ekip üyesi kodu klonladığında CI/CD sürecini de beraberinde getirmiş olur. Jenkins sunucusu yeniden başlasa bile pipeline kaldığı yerden devam edebilir; çünkü durum dışarıda, tanımlı bir yapıda tutulmaktadır. Stage’ler arasındaki akış Jenkins arayüzünde grafik olarak izlenebilir; hangi adımın ne kadar sürdüğü ve nerede hata oluştuğu tek bakışta anlaşılır. Üstelik Shared Library mekanizmasıyla ortak pipeline mantığını tüm projeler arasında paylaşmak ve birden fazla stage’i eş zamanlı çalıştırmak da mümkündür.

Pipeline Türleri: Declarative mi, Scripted mi?

Jenkins’te iki farklı pipeline sözdizimi bulunur ve hangisini tercih edeceğiniz büyük ölçüde ihtiyacınıza göre şekillenir.

Declarative Pipeline, daha yeni ve önerilen yaklaşımdır. pipeline { } bloğu içinde sabit bir yapı sunar: önce hangi ortamda çalışacağını belirtirsiniz, ardından stage’leri sırayla tanımlarsınız, son olarak pipeline bitince ne yapılacağını bildirirsiniz. Bu yapı okunabilirliği artırır, hataları erken yakalar ve ekipteki yeni üyelerin pipeline’ı anlamasını kolaylaştırır. Çoğu kurumsal senaryoda Declarative Pipeline yeterlidir.

Scripted Pipeline ise Groovy’nin tam gücüne dayanır. node { } bloğuyla başlar ve içinde dilediğiniz Groovy kodunu yazabilirsiniz. Koşullu mantık, döngüler ve dinamik stage oluşturma gibi ileri düzey senaryolarda Scripted tercih edilir. Ancak bu esneklik beraberinde karmaşıklık getirir; kodun anlaşılması ve bakımı zorlaşır.

Genel öneri, yeni projelerde Declarative ile başlamaktır. Groovy’nin tam gücüne gerçekten ihtiyaç duyduğunuzda Declarative içinde script { } bloğu açarak iki yaklaşımı birleştirebilirsiniz.

Jenkinsfile Anatomisi

Bir Declarative Jenkinsfile dört temel katmandan oluşur.

En dışta pipeline bloğu yer alır; tüm tanımın kapsayıcısıdır. Hemen içinde agent direktifi gelir ve pipeline’ın hangi ortamda çalışacağını belirtir: herhangi bir ajan, belirli bir etiketli makine, Docker imajı veya Kubernetes pod’u olabilir. Ardından environment bloğuyla ortam değişkenleri tanımlanır; uygulama versiyonu, kayıt defteri adresi gibi pipeline boyunca kullanılacak sabit değerler burada merkezi olarak tutulur.

stages bloğu, pipeline’ın çekirdeğidir. Her stage bağımsız bir adımı temsil eder: derleme, test, Docker imajı oluşturma, dağıtım. Stage’ler sırayla çalışır; biri başarısız olursa sonrakiler tetiklenmez. Son olarak post bloğu, pipeline tamamlandıktan sonra koşullu aksiyonları tanımlar: başarılıysa Slack bildirimi gönder, başarısızsa e-posta at, her durumda test raporlarını arşivle gibi.

Credentials yönetimi de bu yapı içinde çözülür. Jenkins’te güvenli biçimde saklanan bir Docker Hub kullanıcı adı ve şifresi, credentials() fonksiyonuyla ortam değişkeni olarak pipeline’a aktarılır. Bu sayede şifreler asla Jenkinsfile’a yazılmaz, log çıktılarında görünmez.

Jenkins Shared Library Nedir?

Birden fazla projeniz olduğunu düşünün. Her projenin Jenkinsfile’ı aynı Docker build adımlarını, aynı bildirim mantığını veya aynı test kurallarını tekrarlıyor olabilir. Bu kod tekrarı hem bakımı zorlaştırır hem de tutarsızlıklara yol açar: bir projede Docker imajı push etme adımı güncellenir, diğerleri unutulur.

Jenkins Shared Library, bu sorunu merkezi bir Git deposuyla çözer. Ortak pipeline mantığı tek bir yerde yazılır ve tüm projeler bu kütüphaneye referans vererek kullanır. Bir değişiklik yapıldığında tüm projeler otomatik olarak güncel mantığı devralır.

Shared Library Dizin Yapısı

Shared Library deposunun belirli bir klasör yapısı vardır ve Jenkins bu yapıyı doğrudan tanır.

vars/ klasörü, Jenkinsfile’dan doğrudan fonksiyon gibi çağrılan global tanımları barındırır. Her .groovy dosyası bir fonksiyon adına karşılık gelir. Örneğin vars/dockerBuild.groovy dosyası içindeki call() metodu, Jenkinsfile’dan dockerBuild(‘my-app’, ‘1.0.0’) şeklinde çağrılır. Bu, pipeline kodunu son derece okunabilir kılar; çünkü Jenkinsfile artık “ne yapılıyor”u anlatır, “nasıl yapılıyor”u değil.

src/ klasörü, standart Groovy sınıflarını barındırır. vars/ fonksiyonları basit ve tek amaçlı olduğunda yeterliyken, daha karmaşık mantık ve nesne yönelimli yapılar için src/ kullanılır. Örneğin Kubernetes işlemlerini yöneten bir Kubernetes sınıfı burada tanımlanabilir; deploy, rollback, scale gibi metotlar bu sınıfa ait olur. Jenkinsfile’da bu sınıfı import ederek bir nesne oluşturursunuz ve metotları çağırırsınız.

resources/ klasörü, Groovy kodlarından erişilen statik dosyaları barındırır. Kubernetes manifest şablonları, konfigürasyon dosyaları veya pipeline’ın ihtiyaç duyduğu diğer statik kaynaklar buraya konur ve libraryResource() fonksiyonuyla okunur.

Shared Library Nasıl Kurulur?

Kurulum üç adımdan oluşur.

Birinci adımda library deposu hazırlanır. vars/ klasörüne fonksiyonlar yazılır. Her fonksiyon dosyası call() metodu içerir; bu metot Jenkinsfile’dan çağrıldığında devreye girer. Fonksiyonlar parametre alabilir ve pipeline içindeki herhangi bir Jenkins adımını — sh, echo, slackSend gibi — çağırabilir.

İkinci adımda Jenkins’e bu kütüphane tanıtılır. Jenkins yönetim panelinden Global Pipeline Libraries bölümüne gidilir. Kütüphaneye bir isim verilir, varsayılan branch belirlenir ve Git deposunun adresi girilir. Bundan sonra tüm pipeline’lar bu kütüphaneyi ismiyle çağırabilir.

Üçüncü adımda Jenkinsfile’a tek satır eklenir: @Library(‘my-shared-lib’) _. Bu satır, vars/ klasöründeki tüm fonksiyonları global namespace’e yükler. Satır sonundaki alt çizgi bu yüklemenin hemen gerçekleşmesini sağlar; olmadan da çalışır ancak fonksiyonlar tembel (lazy) yüklenir. Bundan sonra Jenkinsfile’da dockerBuild(…) veya notifySlack(…) gibi fonksiyonlar sanki yerel olarak tanımlanmış gibi çağrılabilir.

Gerçek Dünya Senaryosu

Bir fintech şirketinin ödeme servisini ele alalım. Bu servis her gün birden fazla kez güncelleniyor ve production ortamına dağıtılıyor. Ekipte on farklı mikroservis var ve her birinin ayrı Jenkinsfile’ı bulunuyor.

Shared Library olmadan: Docker build mantığı on ayrı Jenkinsfile’da yazılmış, Slack bildirimi her birinde farklı konfigüre edilmiş, Kubernetes deploy komutu kimi yerde günceli olmayan bir versiyonla çalışıyor. Bir güvenlik açığı nedeniyle Docker login yöntemi değiştirilmesi gerektiğinde on Jenkinsfile’ın tek tek düzenlenmesi gerekiyor.

Shared Library ile: dockerBuild() fonksiyonu merkezi depoda yönetiliyor. Docker login değişikliği bir kez yapılıyor, tüm servisler bir sonraki build’de otomatik olarak yeni mantığı kullanıyor. On Jenkinsfile’ın her biri ince ve okunabilir: hangi branch’te deploy edileceğini, hangi Kubernetes namespace’ine gönderileceğini belirtmek yeterli. Nasıl yapıldığı ayrıntısı görünmez.

Bu yaklaşımda pipeline’ın Jenkinsfile’a yansıyan kısmı şu sorulara yanıt verir: hangi uygulama, hangi versiyon, hangi ortam? Teknik detaylar — Docker imajını nasıl push et, kubectl’i nasıl çağır, Slack API’sını nasıl kullan — library’nin içinde gizlidir.

Shared Library Kullanımında En İyi Pratikler

Library’yi versiyonlayın. @Library(‘my-shared-lib@v2.1.0’) _ şeklinde Git tag’i kullanmak, beklenmedik değişikliklerden projeyi korur. main branch’ini kullanırsanız library’ye yapılan bir commit tüm pipeline’ları anında etkiler; bu bazen istenen davranış olsa da üretim pipeline’ları için risklidir. Özellikle kritik servisler için sabit bir versiyon tercih edin.

Fonksiyonları küçük ve tek amaçlı tutun. buildTestAndDeploy() gibi her şeyi yapan “tanrı fonksiyonları” yazmaktan kaçının. Bunun yerine runTests(), buildDockerImage(), deployToKubernetes() gibi ayrı fonksiyonlar tanımlayın. Küçük fonksiyonlar daha kolay test edilir, hata mesajları daha anlaşılırdır ve başka pipeline’larda yeniden kullanılabilir.

Parametreleri adlandırılmış Map ile geçin. Bir fonksiyona dört pozisyonel parametre geçmek okuyucuya hiçbir şey anlatmaz. Bunun yerine anahtar-değer Map kullanın; her parametreye açık bir isim verin. Üç ay sonra kodu okuyan biri her parametrenin ne anlama geldiğini anında kavrar.

Library’nizi test edin. Shared Library tüm projelerinizin kritik altyapısıdır; kırıldığında her şey durur. JenkinsPipelineUnit gibi araçlar, pipeline kodunu gerçek bir Jenkins sunucusu olmadan birim testi olarak çalıştırmanızı sağlar. Library’ye yapılan her değişiklik önce test pipeline’ından geçmeli, ardından production’ı etkileyen branch’e merge edilmelidir.

Credentials yönetimini library içinde merkezileştirin. Docker Hub, Kubernetes, AWS gibi credential’ların nasıl alınacağı ve kullanılacağı library içinde bir kez yazılır. Jenkinsfile’lar sadece credential ID’sini bilir, nasıl alındığını değil. Bu hem güvenliği artırır — Jenkinsfile’larda şifre yönetimi kodu olmaz — hem de credential’ın değişmesi durumunda tek bir yeri güncellemek yeterli olur.

Yazma erişimini sınırlandırın. Library deposunu herkes okuyabilir; ancak main branch’ine push etmek için code review süreci zorunlu olmalıdır. Branch protection rules ve en az bir onay gerektiren pull request politikası, kötü bir değişikliğin tüm pipeline’ları çökertmesinin önüne geçer.

Sonuç

Jenkins Pipeline ve Shared Library, CI/CD dünyasında iki tamamlayıcı kavramdır. Pipeline, yazılım teslimat sürecinizi kod haline getirir; Shared Library ise bu kodu tekrar kullanılabilir ve merkezi olarak yönetilebilir kılar.

Bu ikisini birlikte kullandığınızda ortaya çıkan tablo şudur: her projenin Jenkinsfile’ı kısa, okunabilir ve projeye özgü kararları yansıtır. Ortak altyapı mantığı tek bir depoda yaşar, ekip tarafından sahiplenilir ve sürüm kontrolüne tabidir. Yeni bir mikroservis eklendiğinde Jenkinsfile’ı yazmak birkaç satır alır; karmaşık pipeline mantığı zaten hazırdır, sadece çağrılır.

Küçük bir ekipte bile bu alışkanlığı edinmek, ileride büyüdüğünüzde ciddi zaman ve teknik borç tasarrufu sağlar. Jenkinsfile’larınız ne kadar ince olursa, pipeline’larınız o kadar anlaşılır ve sürdürülebilir olur.

Sıkça Sorulan Sorular

Jenkins Pipeline nedir?
Yazılım geliştirme süreçlerini (build, test, deploy) kod olarak tanımlamanızı sağlayan Jenkins özelliğidir. Jenkinsfile adlı dosyada tanımlanır ve Git’te versiyonlanır; böylece CI/CD süreci uygulama koduyla birlikte izlenebilir hale gelir.

Declarative ile Scripted Pipeline arasındaki fark nedir?
Declarative daha okunabilir ve yapılandırılmış bir sözdizimi sunar; büyük çoğunluğu senaryolar için yeterlidir ve yeni başlayanlar için önerilir. Scripted, Groovy’nin tüm gücünü kullanır ve ileri düzey kontrol akışı gerektiren senaryolarda tercih edilir. İkisi script { } bloğuyla birleştirilebilir.

Jenkins Shared Library neden kullanılır?
Pipeline mantığını tüm projeler arasında merkezi olarak paylaşmak, kod tekrarını ortadan kaldırmak ve bakım maliyetini düşürmek için kullanılır. Bir değişiklik library’de bir kez yapılır, tüm projelere yansır.

@Library(‘name’) _ satırındaki _ ne anlama geliyor?
vars/ klasöründeki tüm Groovy dosyalarını pipeline başlangıcında global fonksiyon olarak yükler. Alt çizgi olmadan da çalışır, ancak fonksiyonlar tembel (lazy) yüklenir; bu bazı durumlarda beklenmedik davranışlara yol açabilir.

Shared Library hangi Jenkins versiyonundan itibaren destekleniyor?
Jenkins 2.0 ve üzeri sürümlerde desteklenmektedir. Pipeline eklentisinin kurulu olması yeterlidir.

Güvenli CI/CD: SonarQube, Trivy, Nexus ve Harbor’ın Pipeline’a Entegrasyonu

Temel bir Jenkins Pipeline, kodu derler, test eder ve dağıtır. Ancak kurumsal ortamlarda bu yeterli değildir. Kodun çalışması ile kodun güvenli, kaliteli ve izlenebilir olması arasında büyük bir fark vardır. İşte bu boşluğu dolduran dört araç devreye girer: SonarQube, Trivy, Nexus ve Harbor.

Bu dört araç birbirini tamamlar. SonarQube kaynak kodu inceler, Trivy Docker imajını tarar, Nexus derleme çıktılarını depolar, Harbor ise imajları güvenli bir kayıt defterinde saklar. Pipeline’a bakış açısıyla bunların her biri bir “kapı” (gate) işlevi görür: bir önceki kapıdan geçemeyen iş yükü bir sonraki aşamaya taşınmaz.

Tam Akış: Hepsi Bir Arada Nasıl Çalışır?

Bu araçların asıl değeri tek tek kullanımlarında değil, bir pipeline içinde ardışık kapılar oluşturduklarında ortaya çıkar. Adımları kronolojik olarak düşünelim.

Birinci adım: Kod alınır ve analiz edilir. Geliştirici kodu Git’e push eder, Jenkins webhook tetiklenir. Pipeline önce kaynak kodu alır, ardından SonarQube analizini başlatır. SonarQube tarama tamamlandıktan sonra Quality Gate sonucunu sorgular. Kapı geçilmediyse — yani kod belirlenen kalite eşiğinin altındaysa — pipeline burada durur. Bir sonraki adıma geçilmez, kaynak harcanmaz.

İkinci adım: Derleme ve artifact üretimi. Quality Gate geçilmişse derleme başlar. Maven, Gradle, npm veya hangi araç kullanılıyorsa kaynak kod artifact’e dönüştürülür. Bu artifact — JAR, WAR, bundle, wheel — build numarası ve commit bilgisiyle etiketlenerek Nexus’a yüklenir. Artık bu derleme çıktısı versiyonlanmış ve izlenebilir haldedir; gerektiğinde herhangi bir artifact tekrar indirilebilir.

Üçüncü adım: İmaj oluşturulur ve taranır. Nexus’a yüklenen artifact, Docker imajına paketlenir. Dockerfile bu artifact’i alır ve üzerine gerekli çalışma ortamını kurar. İmaj build tamamlandıktan hemen sonra Trivy devreye girer. İmajın tüm katmanları — temel işletim sistemi, sistem kütüphaneleri, uygulama bağımlılıkları — CVE veritabanıyla karşılaştırılır. Eğer belirlenen eşiğin üzerinde bir açık bulunursa pipeline yine durur. İmaj hiçbir registry’ye ulaşmaz.

Dördüncü adım: İmaj Harbor’a push edilir. Trivy taramasını geçen temiz imaj, Harbor’a gönderilir. Harbor’ın kendi tarama motoru da bu imajı kayıt sırasında bir kez daha inceler; bu ikinci katman güvencedir. İmaj, production’a çekilmeden önce Harbor’daki güvenlik durumu da kontrol edilebilir. Hangi imajın ne zaman, kim tarafından push edildiği, hangi açıkları içerdiği Harbor arayüzünden izlenebilir.

Beşinci adım: Dağıtım. Yalnızca main veya release branch’inden gelen pipeline’lar bu adıma ulaşır. Kubernetes, Harbor’dan temiz imajı çeker ve production’a dağıtır. Pipeline bittikten sonra Slack bildirimi gider: hangi uygulama, hangi versiyon, hangi sonuçla tamamlandı.

Bu akışın kritik özelliği, her kapının bağımsız bir veto yetkisine sahip olmasıdır. SonarQube’un onaylamadığı kod derlenmez. Trivy’nin onaylamadığı imaj registry’ye gitmez. Harbor politikasının onaylamadığı imaj deploy edilemez. Zincirin herhangi bir halkası koparsa tüm akış durur ve geliştirici nerede, ne tür bir sorun olduğunu açıkça görür.

Bu Araçları Shared Library ile Yönetmek

Bu dört araç pipeline’a eklendiğinde her Jenkinsfile’ın onları ayrı ayrı konfigüre etmesi gerekirse kod tekrarı sorunu yeniden baş gösterir. Shared Library burada ikinci kez değerini kanıtlar.

vars/sonarScan.groovy, vars/trivyScan.groovy, vars/pushToNexus.groovy ve vars/pushToHarbor.groovy gibi fonksiyonlar library’de bir kez yazılır. Her projenin Jenkinsfile’ı bu fonksiyonları çağırır, teknik detayları bilmek zorunda kalmaz. SonarQube sunucu adresi değişirse, Trivy’nin hangi CVE veritabanını kullandığı güncellenirse, Nexus veya Harbor erişim bilgileri döngüsel olarak yenilenirse — tüm bu değişiklikler yalnızca Shared Library’de yapılır. Yüzlerce mikroservis bir sonraki build’de otomatik olarak yeni konfigürasyonu kullanır.

Bu noktada Shared Library artık yalnızca kod yeniden kullanımı mekanizması değil, kurumun CI/CD güvenlik politikasının uygulandığı merkezi noktadır. Hangi ekip hangi projeyi geliştirirse geliştirsin, pipeline aynı güvenlik kapılarından geçmek zorundadır. Bu zorunluluk politikadan değil, mimariden gelir.

Örnek Repo https://github.com/murat-akpinar/devops-jenkins-library

Kategori: