Blue Ocean ile Modern Jenkins Pipeline Görselleştirme

Jenkins’i ilk kurduğumda pipeline’ları yönetmek gerçekten bir kabustu. Onlarca stage, paralel çalışan joblar, hata ayıklamak için log dosyalarını satır satır okumak… Sonra Blue Ocean ile tanıştım ve her şey değişti. Eğer hala klasik Jenkins arayüzünde boğuşuyorsanız, bu yazı tam size göre.

Blue Ocean Nedir ve Neden Önemlidir

Blue Ocean, Jenkins için geliştirilen modern bir kullanıcı arayüzü eklentisidir. Atlassian, CloudBees ve Jenkins topluluğunun birlikte yürüttüğü bu proje, klasik Jenkins’in karmaşık ve kullanıcı dostu olmayan arayüzünün yerine görsel, sezgisel bir deneyim sunmayı hedefler.

Klasik Jenkins arayüzünde bir pipeline hatası aldığınızda ne yaparsınız? Muhtemelen “Console Output” linkine tıklar, binlerce satır logu okumaya çalışırsınız. Blue Ocean’da ise hangi stage’in patladığını, tam olarak hangi adımda durduğunu ve hata mesajını saniyeler içinde görebilirsiniz.

Blue Ocean’ın getirdiği temel yenilikler:

  • Pipeline’ları görsel akış diyagramı olarak göstermek
  • Paralel stage’leri yan yana görselleştirmek
  • Her step için ayrı log görünümü
  • Git entegrasyonu ile branch ve PR yönetimi
  • Modern ve responsive bir arayüz

Kurulum ve Ön Hazırlık

Blue Ocean’ı kurmadan önce Jenkins’inizin hazır olduğundan emin olun. Ben genellikle LTS sürümünü öneriyorum, çünkü eklenti uyumluluğu çok daha istikrarlı.

# Jenkins LTS kurulumu (Ubuntu/Debian)
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee 
    /usr/share/keyrings/jenkins-keyring.asc > /dev/null

echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] 
    https://pkg.jenkins.io/debian-stable binary/ | sudo tee 
    /etc/apt/sources.list.d/jenkins.list > /dev/null

sudo apt-get update
sudo apt-get install -y jenkins

# Java 17 kurulumu (Jenkins için gerekli)
sudo apt-get install -y openjdk-17-jdk

# Jenkins servisini başlat
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins

Jenkins kurulumu tamamlandıktan sonra Blue Ocean eklentisini yüklemenin birkaç yolu var. En kolay yol Jenkins Plugin Manager üzerinden yapmak, ama production ortamlarında CLI üzerinden yapmayı tercih ediyorum.

# Jenkins CLI ile Blue Ocean kurulumu
# Önce jenkins-cli.jar dosyasını indir
wget http://localhost:8080/jnlpJars/jenkins-cli.jar

# Blue Ocean eklenti paketini kur
java -jar jenkins-cli.jar -s http://localhost:8080/ 
    -auth admin:YOUR_API_TOKEN 
    install-plugin blueocean

# Jenkins'i yeniden başlat
java -jar jenkins-cli.jar -s http://localhost:8080/ 
    -auth admin:YOUR_API_TOKEN 
    safe-restart

Eğer Docker ile Jenkins kullanıyorsanız, ki production dışı ortamlar için bu yöntemi şiddetle tavsiye ederim, aşağıdaki yaklaşım işinizi görecektir:

# Docker ile Blue Ocean dahil Jenkins
docker run -d 
    --name jenkins-blueocean 
    --restart=on-failure 
    -p 8080:8080 
    -p 50000:50000 
    -v jenkins_home:/var/jenkins_home 
    -v /var/run/docker.sock:/var/run/docker.sock 
    jenkinsci/blueocean:latest

# Container loglarını kontrol et
docker logs -f jenkins-blueocean

# Jenkins initial admin password'ü al
docker exec jenkins-blueocean 
    cat /var/jenkins_home/secrets/initialAdminPassword

İlk Pipeline’ı Oluşturmak

Blue Ocean’a erişmek için tarayıcınızda http://YOUR_JENKINS_URL/blue adresine gidin. Klasik arayüzden de “Open Blue Ocean” butonuna tıklayabilirsiniz.

Şimdi gerçek dünya senaryosuna geçelim. Diyelim ki bir Node.js uygulamanız var ve bunu Blue Ocean üzerinden görselleştirmek istiyorsunuz. Önce Jenkinsfile’ınızı hazırlayalım:

// Jenkinsfile - Node.js Uygulama Pipeline
pipeline {
    agent {
        docker {
            image 'node:18-alpine'
            args '-p 3000:3000'
        }
    }
    
    environment {
        NODE_ENV = 'production'
        DOCKER_REGISTRY = 'your-registry.example.com'
        APP_NAME = 'nodejs-app'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                echo "Branch: ${env.GIT_BRANCH}"
                echo "Commit: ${env.GIT_COMMIT}"
            }
        }
        
        stage('Install Dependencies') {
            steps {
                sh 'npm ci --prefer-offline'
                sh 'npm audit --audit-level=high'
            }
        }
        
        stage('Lint ve Format Check') {
            parallel {
                stage('ESLint') {
                    steps {
                        sh 'npm run lint'
                    }
                }
                stage('Prettier') {
                    steps {
                        sh 'npm run format:check'
                    }
                }
                stage('TypeScript Check') {
                    steps {
                        sh 'npm run type-check'
                    }
                }
            }
        }
        
        stage('Test') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'npm run test:unit -- --ci --coverage'
                    }
                    post {
                        always {
                            publishHTML([
                                allowMissing: false,
                                alwaysLinkToLastBuild: true,
                                keepAll: true,
                                reportDir: 'coverage',
                                reportFiles: 'index.html',
                                reportName: 'Coverage Report'
                            ])
                        }
                    }
                }
                stage('Integration Tests') {
                    steps {
                        sh 'npm run test:integration'
                    }
                }
            }
        }
        
        stage('Build') {
            steps {
                sh 'npm run build'
                archiveArtifacts artifacts: 'dist/**/*', fingerprint: true
            }
        }
        
        stage('Docker Build ve Push') {
            when {
                branch 'main'
            }
            steps {
                script {
                    def image = docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${env.BUILD_NUMBER}")
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-credentials') {
                        image.push()
                        image.push('latest')
                    }
                }
            }
        }
        
        stage('Deploy to Staging') {
            when {
                branch 'main'
            }
            steps {
                withCredentials([sshUserPrivateKey(
                    credentialsId: 'staging-server-key',
                    keyFileVariable: 'SSH_KEY'
                )]) {
                    sh """
                        ssh -i ${SSH_KEY} -o StrictHostKeyChecking=no 
                            [email protected] 
                            'docker pull ${DOCKER_REGISTRY}/${APP_NAME}:${env.BUILD_NUMBER} && 
                             docker stop ${APP_NAME} || true && 
                             docker rm ${APP_NAME} || true && 
                             docker run -d --name ${APP_NAME} -p 3000:3000 
                             ${DOCKER_REGISTRY}/${APP_NAME}:${env.BUILD_NUMBER}'
                    """
                }
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        success {
            slackSend(
                channel: '#deployments',
                color: 'good',
                message: "Pipeline başarılı: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            )
        }
        failure {
            slackSend(
                channel: '#deployments',
                color: 'danger',
                message: "Pipeline başarısız: ${env.JOB_NAME} #${env.BUILD_NUMBER}nDetay: ${env.BUILD_URL}blue"
            )
        }
    }
}

Bu pipeline’ı Blue Ocean’da çalıştırdığınızda, özellikle paralel stage’lerin görsel olarak yan yana gösterildiğini göreceksiniz. “Lint ve Format Check” aşamasında üç farklı kontrol aynı anda çalışırken Blue Ocean bunları paralel sütunlar halinde gösterir. Hangi column’ın ne kadar sürdüğünü, hangisinin hata verdiğini anlık olarak takip edebilirsiniz.

Blue Ocean Pipeline Editörü

Blue Ocean’ın en güçlü özelliklerinden biri görsel pipeline editörüdür. Jenkinsfile yazmadan, sürükle bırak mantığıyla pipeline oluşturabilirsiniz. Ancak şunu söylemeliyim: bu editör basit pipeline’lar için harika, karmaşık iş akışları için hala kod yazmak daha pratik.

Pipeline editörüne erişmek için Blue Ocean ana sayfasında “New Pipeline” butonuna tıklayın. Buradan Git repository’nizi bağlayabilir, branch’leri seçebilir ve görsel olarak stage’lerinizi tanımlayabilirsiniz.

Editörde yapabilecekleriniz:

  • Stage ekleme ve silme
  • Her stage’e step atama
  • Environment variable tanımlama
  • Agent seçimi yapma
  • Post-build aksiyon tanımlama

Multibranch Pipeline ile Branch Yönetimi

Blue Ocean, Multibranch Pipeline ile gerçek gücünü gösterir. Her branch için ayrı pipeline çalıştırabilir, PR’ları otomatik olarak test edebilirsiniz.

// Multibranch Pipeline için geliştirilmiş Jenkinsfile
pipeline {
    agent any
    
    options {
        buildDiscarder(logRotator(numToKeepStr: '10'))
        timeout(time: 30, unit: 'MINUTES')
        skipDefaultCheckout(true)
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                script {
                    // Branch tipine göre değişken ata
                    env.IS_PR = env.CHANGE_ID ? 'true' : 'false'
                    env.IS_MAIN = env.BRANCH_NAME == 'main' ? 'true' : 'false'
                    env.IS_RELEASE = env.BRANCH_NAME.startsWith('release/') ? 'true' : 'false'
                    
                    echo "Branch: ${env.BRANCH_NAME}"
                    echo "PR: ${env.IS_PR}"
                    echo "Main: ${env.IS_MAIN}"
                }
            }
        }
        
        stage('Build ve Test') {
            steps {
                sh 'make build'
                sh 'make test'
            }
        }
        
        stage('Security Scan') {
            when {
                anyOf {
                    branch 'main'
                    changeRequest()
                }
            }
            steps {
                sh 'trivy fs --exit-code 1 --severity HIGH,CRITICAL .'
            }
        }
        
        stage('Deploy Preview') {
            when {
                changeRequest()
            }
            steps {
                script {
                    def previewUrl = deployPreview(env.CHANGE_ID)
                    // PR'a yorum olarak preview URL ekle
                    pullRequest.comment("Preview ortamı hazır: ${previewUrl}")
                }
            }
        }
        
        stage('Deploy Production') {
            when {
                branch 'main'
            }
            steps {
                input message: 'Production deployment onaylıyor musunuz?', ok: 'Deploy Et'
                sh 'make deploy-prod'
            }
        }
    }
}

Gerçek Dünya Senaryosu: Microservice Mimarisinde Blue Ocean

Şirkette çalıştığım dönemde 12 microservice’ten oluşan bir sistemi yönetiyorduk. Her servisin kendi repository’si, kendi test suite’i vardı. Blue Ocean olmadan bu kaosta kaybolmak an meselesiydi.

Çözüm olarak her servis için standart bir Jenkinsfile şablonu oluşturduk ve bunu Shared Library olarak tanımladık:

// vars/microservicePipeline.groovy
// Jenkins Shared Library
def call(Map config = [:]) {
    def serviceName = config.serviceName ?: error("serviceName zorunludur")
    def dockerRegistry = config.dockerRegistry ?: 'registry.company.com'
    def k8sNamespace = config.k8sNamespace ?: 'production'
    
    pipeline {
        agent {
            kubernetes {
                yaml """
                apiVersion: v1
                kind: Pod
                spec:
                  containers:
                  - name: build
                    image: golang:1.21-alpine
                    command:
                    - sleep
                    args:
                    - infinity
                  - name: docker
                    image: docker:24-dind
                    securityContext:
                      privileged: true
                """
            }
        }
        
        stages {
            stage("${serviceName} Build") {
                steps {
                    container('build') {
                        sh "go build -v -o ${serviceName} ./cmd/${serviceName}"
                        sh "go test ./... -v -coverprofile=coverage.out"
                        sh "go vet ./..."
                    }
                }
            }
            
            stage("${serviceName} Docker Build") {
                steps {
                    container('docker') {
                        sh """
                            docker build 
                                --build-arg SERVICE_NAME=${serviceName} 
                                --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') 
                                --build-arg GIT_COMMIT=${env.GIT_COMMIT} 
                                -t ${dockerRegistry}/${serviceName}:${env.BUILD_NUMBER} 
                                -t ${dockerRegistry}/${serviceName}:latest 
                                .
                        """
                    }
                }
            }
            
            stage("${serviceName} Deploy") {
                when { branch 'main' }
                steps {
                    container('build') {
                        withKubeConfig([credentialsId: 'k8s-config']) {
                            sh """
                                kubectl set image deployment/${serviceName} 
                                    ${serviceName}=${dockerRegistry}/${serviceName}:${env.BUILD_NUMBER} 
                                    -n ${k8sNamespace}
                                kubectl rollout status deployment/${serviceName} 
                                    -n ${k8sNamespace} 
                                    --timeout=300s
                            """
                        }
                    }
                }
            }
        }
    }
}

Her microservice’in Jenkinsfile’ı bu sayede sadece birkaç satıra indi:

// Her microservice'in Jenkinsfile'ı
@Library('company-shared-lib') _

microservicePipeline(
    serviceName: 'user-service',
    dockerRegistry: 'registry.company.com',
    k8sNamespace: 'production'
)

Blue Ocean bu yapıyla çalışırken her servisin pipeline’ını ayrı ayrı görselleştirir, hangi servisin hangi aşamada olduğunu dashboard üzerinden anlık takip edebilirsiniz.

Blue Ocean API Kullanımı

Blue Ocean’ın REST API’si, pipeline durumlarını harici sistemlerle entegre etmek için kullanışlıdır. Monitoring dashboardlarına veri çekmek, Slack veya Teams entegrasyonu yapmak için bu API’yi aktif olarak kullanıyorum.

# Blue Ocean REST API ile pipeline bilgilerini çek
JENKINS_URL="http://jenkins.example.com"
JENKINS_USER="admin"
JENKINS_TOKEN="your-api-token"
JOB_NAME="my-multibranch-pipeline"

# Tüm branch pipeline'larını listele
curl -s -u "${JENKINS_USER}:${JENKINS_TOKEN}" 
    "${JENKINS_URL}/blue/rest/organizations/jenkins/pipelines/${JOB_NAME}/branches/" 
    | python3 -m json.tool

# Son build'in detaylarını al
curl -s -u "${JENKINS_USER}:${JENKINS_TOKEN}" 
    "${JENKINS_URL}/blue/rest/organizations/jenkins/pipelines/${JOB_NAME}/branches/main/runs/latest/" 
    | python3 -m json.tool

# Belirli bir build'in stage'lerini görüntüle
BUILD_NUMBER="42"
curl -s -u "${JENKINS_USER}:${JENKINS_TOKEN}" 
    "${JENKINS_URL}/blue/rest/organizations/jenkins/pipelines/${JOB_NAME}/branches/main/runs/${BUILD_NUMBER}/nodes/" 
    | python3 -m json.tool

# Pipeline loglarını çek (belirli bir node/stage için)
NODE_ID="15"
curl -s -u "${JENKINS_USER}:${JENKINS_TOKEN}" 
    "${JENKINS_URL}/blue/rest/organizations/jenkins/pipelines/${JOB_NAME}/branches/main/runs/${BUILD_NUMBER}/nodes/${NODE_ID}/log/?start=0"

Bu API’yi kullanarak yazdığım basit bir monitoring scripti şirketimizde günde düzinelerce pipeline’ı izlemek için hala aktif olarak çalışıyor.

Performans ve Optimizasyon İpuçları

Blue Ocean’ın arayüzü zengin olduğu için tarayıcıda biraz ağır açılabiliyor. Özellikle onlarca pipeline’ı olan büyük ortamlarda bu can sıkıcı hale gelebilir. Bir kaç pratik ipucu:

Jenkins JVM ayarları için:

# /etc/default/jenkins dosyasını düzenle
# veya systemd service dosyasına ekle

JAVA_OPTS="-Xms2g -Xmx4g 
    -XX:+UseG1GC 
    -XX:MaxGCPauseMillis=200 
    -XX:+UnlockExperimentalVMOptions 
    -XX:+UseCGroupMemoryLimitForHeap 
    -Djava.awt.headless=true 
    -Djenkins.install.runSetupWizard=false 
    -Dblueocean.features.CACHE_RESPONSES=true"

Blue Ocean cache ayarları için Jenkins system groovy:

// init.groovy.d/blueocean-config.groovy
import jenkins.model.Jenkins

def instance = Jenkins.getInstance()

// Blue Ocean için executor thread sayısını artır
instance.setNumExecutors(4)

// Build history'yi sınırla (Blue Ocean daha hızlı yüklensin)
def strategy = new jenkins.model.BuildDiscarder()
instance.save()

println "Blue Ocean konfigürasyonu tamamlandı"

Pipeline optimizasyonu:

  • Paralel stage’leri mümkün olduğunca kullanın, Blue Ocean bunları zaten güzel görselleştirir
  • skipDefaultCheckout(true) ile gereksiz checkout işlemlerinden kaçının
  • buildDiscarder ile eski build’leri temizleyin, Blue Ocean’ın yüklenme hızı buna çok bağlı
  • Docker agent kullanıyorsanız image’ları önceden pull etmiş olduğunuzdan emin olun

Yaygın Sorunlar ve Çözümleri

“Blue Ocean yüklenmiyor” sorunu:

Genellikle eklenti bağımlılık çakışmasından kaynaklanır. Şu şekilde tanımlayabilirsiniz:

# Jenkins eklenti sorunlarını kontrol et
java -jar jenkins-cli.jar -s http://localhost:8080/ 
    -auth admin:TOKEN 
    list-plugins | grep -E "(blueocean|pipeline)"

# Tüm eklentileri güncelle
java -jar jenkins-cli.jar -s http://localhost:8080/ 
    -auth admin:TOKEN 
    install-plugin 
    blueocean 
    workflow-aggregator 
    git 
    credentials-binding 
    --restart

Pipeline görselleştirilmiyor: Declarative Pipeline syntax kullanmanız gerekiyor. Scripted Pipeline’lar Blue Ocean’da tam görselleştirilemiyor. Eğer eski scripted pipeline’larınız varsa, declarative’e geçişi planlayın.

GitHub/GitLab bağlantı sorunları: Blue Ocean’ın Git entegrasyonu için personal access token kullanıyor. Bu token’ın repo, webhook ve pull request yetkilerine sahip olduğundan emin olun.

Sonuç

Blue Ocean, Jenkins’i sadece bir CI aracı olmaktan çıkarıp gerçek anlamda bir DevOps platformuna dönüştürüyor. Özellikle takım olarak çalışırken, herkesin pipeline durumunu kolayca görebilmesi, PR’ların test sonuçlarını anlık takip edebilmesi inanılmaz bir verimlilik artışı sağlıyor.

Kendi deneyimimden söyleyebilirim ki Blue Ocean’a geçtikten sonra “pipeline neden patladı” sorusuna cevap bulmak için harcanan zaman ciddi ölçüde azaldı. Geliştiriciler artık bana “Jenkins’te bir şeyler oldu, bakabilir misin?” diye gelmek yerine kendileri hangi adımın neden başarısız olduğunu görebiliyorlar.

Eğer hala klasik Jenkins arayüzünde çalışıyorsanız, Blue Ocean’a geçiş için bir hafta sonu yeterli. Kurulum basit, öğrenme eğrisi düşük. En kötü ihtimalle iki arayüzü paralel kullanabilirsiniz, zira Blue Ocean klasik arayüzü devre dışı bırakmıyor. Deneyin, bence memnun kalacaksınız.

Bir yanıt yazın

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