“CI/CD ile Docker” yazımızda GitHub Actions üzerinden bulut tabanlı bir pipeline kurmuştuk. Ama birçok kurumsal ekip hâlâ Jenkins‘i tercih ediyor — kendi sunucunuzda barındırdığınız, yüzlerce eklentiyle her türlü araca entegre olabilen, açık kaynak bir otomasyon sunucusu. Bu yazıda Jenkins’i bizzat bir Docker container’ı olarak ayağa kaldırıp, Docker imajlarını otomatik build eden gerçek bir pipeline kuruyoruz.

Jenkins neden hâlâ yaygın?

GitHub Actions ve GitLab CI gibi bulut tabanlı çözümler daha az yapılandırmayla hızlı sonuç verse de, Jenkins’in tercih edilmesinin somut nedenleri var:

  • Tam kontrol: Kendi sunucunuzda çalışır — kod ve build süreçleri hiçbir zaman üçüncü parti bir buluta çıkmaz. Sıkı veri gizliliği gereksinimleri olan kurumlar için önemli.
  • Eklenti ekosistemi: 1800’den fazla resmi eklenti; neredeyse her araç, versiyon kontrol sistemi ve bildirim kanalıyla (Slack, Jira, SonarQube, Artifactory…) entegre olabilir.
  • Karma altyapı desteği: Şirket içi (on-premise) sunucular, farklı işletim sistemleri, özel donanımlar (GPU build ajanları gibi) üzerinde çalışabilir — bulut tabanlı runner’ların erişemeyeceği ortamlarda.
  • Olgunluk: 2011’den beri geliştiriliyor; büyük kurumsal ortamlarda onlarca yıllık production geçmişi var.

Mimari: Controller ve Agent

Jenkins iki bileşenden oluşur:

  • Controller (eski adıyla master): Web arayüzünü sunar, pipeline tanımlarını ve iş kuyruğunu yönetir, sonuçları saklar. Doğrudan build işlerini çalıştırmak yerine bu işi agent’lara devretmesi önerilir.
  • Agent (eski adıyla slave): Asıl build/test/deploy işlerinin çalıştığı makine veya container. Bir controller’a birden fazla agent bağlanabilir, her biri farklı bir işletim sistemi veya araç setiyle donatılabilir.

Jenkins’i Docker container olarak çalıştırmak

Jenkins’in resmi imajı, kendisini de hızlıca ayağa kaldırmanızı sağlar. Docker imajları build edebilmesi için ana makinenin Docker soketini container içine bağlamamız gerekir:

docker volume create jenkins_home

docker run -d --name jenkins \
  -p 8080:8080 -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $(which docker):/usr/bin/docker \
  --group-add $(stat -c '%g' /var/run/docker.sock) \
  jenkins/jenkins:lts-jdk17

İlk açılışta Jenkins, konsol loglarına bir kurulum şifresi yazdırır (docker logs jenkins ile görebilirsiniz); tarayıcıdan localhost:8080‘a gidip bu şifreyle kurulum sihirbazını tamamlarsınız ve önerilen eklentileri (Docker Pipeline dahil) yükletirsiniz.

i

/var/run/docker.sock‘u container içine bağlamak, Jenkins container’ına ana makinenin Docker daemon’ını doğrudan kontrol etme yetkisi verir — pratikte bu, o container’ın host üzerinde neredeyse root yetkisiyle işlem yapabileceği anlamına gelir. Production’da bu riski azaltmak için Jenkins’i izole, güvenilir bir sunucuda çalıştırın ve container’a erişimi kısıtlayın; alternatif olarak rootless bir Docker-in-Docker (DinD) kurulumu da değerlendirilebilir.

Jenkinsfile: pipeline’ı kod olarak tanımlamak

Jenkins’te bir pipeline, projenizin köküne konan bir Jenkinsfile ile tanımlanır (GitHub Actions’taki .github/workflows/*.yml‘e karşılık gelir). Declarative syntax en okunaklı ve önerilen yöntemdir:

pipeline {
    agent any

    environment {
        IMAGE_NAME = 'kullanici-adi/benim-api'
        DOCKERHUB_CRED = credentials('dockerhub-token')
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Build') {
            steps {
                sh 'docker build -t $IMAGE_NAME:$BUILD_NUMBER .'
            }
        }

        stage('Test') {
            steps {
                sh 'docker run --rm $IMAGE_NAME:$BUILD_NUMBER npm test'
            }
        }

        stage('Zafiyet Taraması') {
            steps {
                sh 'trivy image --severity HIGH,CRITICAL --exit-code 1 $IMAGE_NAME:$BUILD_NUMBER'
            }
        }

        stage('Push') {
            steps {
                sh '''
                    echo $DOCKERHUB_CRED_PSW | docker login -u $DOCKERHUB_CRED_USR --password-stdin
                    docker push $IMAGE_NAME:$BUILD_NUMBER
                    docker tag $IMAGE_NAME:$BUILD_NUMBER $IMAGE_NAME:latest
                    docker push $IMAGE_NAME:latest
                '''
            }
        }

        stage('Deploy') {
            steps {
                sshagent(['prod-sunucu-ssh']) {
                    sh '''
                        ssh [email protected] \
                          "docker pull $IMAGE_NAME:latest && docker compose up -d --no-deps api"
                    '''
                }
            }
        }
    }

    post {
        failure {
            slackSend channel: '#deploy', message: "Build #${BUILD_NUMBER} başarısız oldu."
        }
    }
}

Her stage, Jenkins arayüzünde ayrı bir kutu olarak görünür — hangi adımın ne kadar sürdüğünü ve nerede başarısız olduğunu tek bakışta görebilirsiniz. credentials() fonksiyonu, Jenkins’in kendi şifrelenmiş kimlik bilgisi deposundan (Manage Jenkins → Credentials) değer okur; parolalar hiçbir zaman Jenkinsfile içine açık yazılmaz.

Webhook ile otomatik tetikleme

Her kod push’unda pipeline’ın otomatik başlaması için GitHub reponuzda bir webhook tanımlanır: Settings → Webhooks → Add webhook, Payload URL’e http://jenkins-sunucunuz:8080/github-webhook/ yazılır. Jenkins tarafında ilgili proje için “GitHub hook trigger for GITScm polling” seçeneği işaretlenir. Artık her push, saniyeler içinde yeni bir build tetikler — elle “Build Now” tıklamaya gerek kalmaz.

Jenkins vs GitHub Actions: hangisi ne zaman?

Kriter Jenkins GitHub Actions
Barındırma Kendi sunucunuz (self-hosted) GitHub’ın bulut altyapısı (veya self-hosted runner)
Kurulum yükü Yüksek — sunucu, güncelleme, eklenti yönetimi size ait Sıfır — GitHub reposu varsa hazır
Esneklik / eklenti Çok yüksek (1800+ eklenti) Marketplace action’ları ile orta-yüksek
Maliyet Sunucu maliyeti size ait, yazılım ücretsiz Public repo ücretsiz; private repo’da dakika bazlı ücretlendirme
Karma/özel altyapı Doğal destek Self-hosted runner ile mümkün, ek kurulum gerekir
Öğrenme eğrisi Daha dik (Groovy tabanlı Jenkinsfile, plugin yönetimi) Daha düz (YAML, geniş hazır action kütüphanesi)

Küçük-orta ölçekli, GitHub’da barınan projeler için GitHub Actions genellikle daha az sürtünmeyle işe yarar. Kurumsal, karma altyapılı veya veri gizliliği gereksinimi yüksek ortamlarda Jenkins’in esnekliği ve tam kontrolü öne çıkar. İkisi birbirini dışlamaz — bazı ekipler basit projelerde Actions’ı, kritik/özel altyapı gerektiren projelerde Jenkins’i bir arada kullanır.

Pipeline artık her push’ta otomatik çalışıyor. Bir sonraki adım, sunucularınızın kendisini de elle değil, tutarlı ve tekrarlanabilir şekilde yapılandırmak — bunu bir sonraki yazıda, Ansible ile otomasyon başlığı altında ele alıyoruz.