Eski Jenkins Kurulumlarını Okumak ve Taşımak: Freestyle'dan Pipeline'a, Blue Ocean'dan Pipeline Graph View'a, Java 17'den 21'e
Eski bir Jenkins'i önce envanterle okur, sonra adım adım taşırsın: önce Java 21, sonra Jenkins sürümü, sonra job'lar. 2.580.2'nin Java 17'de açılmayı reddeden gerçek mesajı, controller ve agent için Java takvimi, Freestyle config.xml'inin Jenkinsfile karşılığı, deprecated Blue Ocean yerine Pipeline Graph View, JNLP'den WebSocket'e ve GitHub Actions Importer'a kısa bakış.
Sürüm notu: Bu yazıdaki örnekler Jenkins 2.580.2 LTS (Long-Term Support), Eclipse Temurin 17.0.21 ve 21.0.13, Pipeline Graph View plugin'i 1053 ve Docker 29 ile denenmiştir. GitHub Actions Importer çalıştırılmadı.
Önce Tahmin Et
Devraldığın Jenkins sunucusu Java 17 ile çalışıyor ve sen Jenkins'i 2.580.2'ye yükseltmek istiyorsun. Paketi güncelleyip servisi yeniden başlatıyorsun ama Java'ya dokunmuyorsun. Ne olur: Jenkins açılır ve bazı plugin'ler hata mı verir, yavaş mı çalışır, yoksa hiç açılmaz mı? Açılmazsa, sebebi log'da ne kadar açık yazar?
Eski Bir Jenkins'i Okumak: Envanter
Yıllardır çalışan bir Jenkins'i devraldığında ilk iş bir şey değiştirmek değil, ne olduğunu görmek. Sorulacak sorular: hangi Jenkins ve hangi Java, kaç plugin, kaç job ve hangi türde, agent'lar nasıl bağlanıyor, controller'da executor var mı, credential'lar nerede. Cevapların çoğu tek bir script console script'iyle gelir:
def j = Jenkins.get()
println "Jenkins ${j.version}, Java ${System.getProperty('java.version')}"
println "Controller executors: ${j.numExecutors}"
println "Plugins: ${j.pluginManager.plugins.size()}"
j.getAllItems(hudson.model.Job).groupBy { it.class.simpleName }.each { type, jobs -> println "${type}: ${jobs.size()}" }
j.nodes.each { n -> println "Agent ${n.nodeName}: ${n.launcher.class.simpleName}, labels=[${n.labelString}]" }
Bu serinin controller'ında çıktısı:
Jenkins 2.580.2, Java 21.0.13
Controller executors: 0
Plugins: 96
WorkflowJob: 26
FreeStyleProject: 1
Agent docker-agent-1: JNLPLauncher, labels=[linux docker]
Agent ssh-agent-1: SSHLauncher, labels=[linux ssh]
Gerçek bir eski kurulumda bu tablo çok farklı görünür: yüzlerce FreeStyleProject, controller'da 2 ya da 4 executor, 200'ün üzerinde plugin. WorkflowJob sayısı Pipeline'a geçişin ne kadar ilerlediğini, plugin sayısı upgrade'in ne kadar acı vereceğini söyler. Inbound agent'ın class adının hâlâ JNLPLauncher olması da ilginç bir ayrıntı; buna aşağıda döneceğiz. Script console her şeyi yapabildiği için bu script'i yalnız okuma yapan hâliyle, yetkili bir kullanıcıyla çalıştır.
Java Zaman Çizelgesi ve Upgrade
Baştaki sorunun cevabı: Jenkins açılmıyor. 2.580.2'nin war dosyasını Java 17 ile çalıştırdım:
openjdk version "17.0.21" 2026-10-20
Running with Java 17 from /opt/java/openjdk, which is older than the minimum required version (Java 21).
Supported Java versions are: [21, 25]
See https://jenkins.io/redirect/java-support/ for more information.
Mesaj açık, ama bunu bir upgrade gecesi production'da görmek istemezsin. Jenkins'in Java destek politikasından ilgili satırlar:
| Java | İlk LTS | Son LTS / planlanan bitiş |
|---|---|---|
| 17 | 2.346.1 (Haziran 2022) | 2.541.3 (Mart 2026), artık desteklenmiyor |
| 21 | 2.479.1 (Ekim 2024) | Destek bitişi planı: Eylül 2027 |
| 25 | 2.541.1 (Ocak 2026) | Destek bitişi planı: Eylül 2029 |
2.555.1'in upgrade guide'ı bir ayrıntıyı özellikle vurguluyor: Java 21 veya 25 şartı yalnız controller için değil, agent JVM'leri (Java Virtual Machine) için de geçerli. Agent'ta Java 17 ile build alan bir proje varsa, agent process'i yine Java 21 veya 25 ile çalışmalı, build ise ayrı bir JDK (Java Development Kit) ile (ör. bir Docker agent'ının image'ında). Upgrade sırası bu yüzden şöyle:
- Envanteri çıkar: controller'ın ve her agent'ın Java sürümü (agent'lar için Versions Node Monitors plugin'i işe yarar).
- Önce mevcut Jenkins sürümünde Java'yı 21'e yükselt: Java 21'i destekleyen bir LTS'te (2.479.1 ve sonrası) controller'ı ve agent'ları Java 21 ile yeniden başlat, birkaç gün böyle çalıştır.
- Sonra Jenkins'i yükselt; aynı anda iki şeyi değiştirme ki sorun çıkınca sebebini bil.
- Docker image kullanıyorsan tag'in sonundaki JDK'ye bak: Java 17'li controller image'ları 2.555.1 ile kaldırıldı,
-jdk21veya-jdk25tag'lerine geç.
Freestyle Job'ları Okumak
Eski bir Jenkins'teki tipik Freestyle job'un config.xml'i şuna benzer; bu job'u bu yazı için oluşturdum:
<?xml version='1.1' encoding='UTF-8'?>
<project>
<description>Legacy Bookstore web packaging job</description>
<assignedNode>linux</assignedNode>
<canRoam>false</canRoam>
<scm class="hudson.plugins.git.GitSCM">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<url>http://jenkwork-gitea:3000/bookstore/bookstore.git</url>
<credentialsId>gitea-ci</credentialsId>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec>
<name>*/main</name>
</hudson.plugins.git.BranchSpec>
</branches>
</scm>
<triggers>
<hudson.triggers.SCMTrigger>
<spec>H/5 * * * *</spec>
</hudson.triggers.SCMTrigger>
</triggers>
<builders>
<hudson.tasks.Shell>
<command>echo "Packaging web sources for build $BUILD_NUMBER"
tar -czf web-src-$BUILD_NUMBER.tgz bookstore-web/src
ls -l web-src-*.tgz</command>
</hudson.tasks.Shell>
</builders>
<publishers>
<hudson.tasks.ArtifactArchiver>
<artifacts>web-src-*.tgz</artifacts>
<fingerprint>true</fingerprint>
</hudson.tasks.ArtifactArchiver>
</publishers>
</project>
Build log'undan satırlar:
Started by user admin
Running as SYSTEM
Building remotely on docker-agent-1 (linux docker) in workspace /home/jenkins/agent/workspace/legacy-web-package
Checking out Revision 2dd9e11bc7e6d35170bc3ba6cc3f386519fffcff (refs/remotes/origin/main)
[legacy-web-package] $ /bin/sh -xe /tmp/jenkins14282019625289931251.sh
+ echo Packaging web sources for build 1
Packaging web sources for build 1
+ tar -czf web-src-1.tgz bookstore-web/src
+ ls -l web-src-1.tgz
-rw-r--r-- 1 jenkins jenkins 1722 Nov 8 05:03 web-src-1.tgz
Archiving artifacts
Recording fingerprints
Finished: SUCCESS
Running as SYSTEM satırına dikkat et: ek bir yetkilendirme plugin'i yapılandırılmamışsa build'ler Jenkins'in kendi kimliğiyle çalışır. Freestyle'ın asıl sorunu ise tarifin repository'de değil controller'ın diskinde olması: kim, ne zaman, neyi değiştirdi bilinmez, review yoktur, branch'le birlikte değişmez.
Freestyle'dan Pipeline'a
Taşıma çoğu zaman mekanik: Freestyle'ın her bölümünün Pipeline'da bir karşılığı var.
| Freestyle (config.xml) | Declarative Pipeline |
|---|---|
assignedNode | agent { label 'linux' } |
scm (GitSCM) | multibranch'te otomatik checkout, tek job'da git adımı |
SCMTrigger | triggers { pollSCM(...) }, tercihen webhook |
hudson.tasks.Shell | sh '''...''' |
ArtifactArchiver | post { success { archiveArtifacts ... } } |
| Build discarder ayarı | options { buildDiscarder(...) } |
Aynı job'un Pipeline hâli. Bu deneme tek bir pipeline job'u olarak koştuğu için checkout'u git adımıyla yaptım; repository'deki bir Jenkinsfile'da multibranch bunu kendisi yapar:
pipeline {
agent { label 'linux' }
triggers {
pollSCM('H/5 * * * *')
}
options {
buildDiscarder(logRotator(numToKeepStr: '20'))
}
stages {
stage('Checkout') {
steps {
git url: 'http://jenkwork-gitea:3000/bookstore/bookstore.git', branch: 'main', credentialsId: 'gitea-ci'
}
}
stage('Package') {
steps {
sh '''
echo "Packaging web sources for build $BUILD_NUMBER"
tar -czf web-src-$BUILD_NUMBER.tgz bookstore-web/src
ls -l web-src-*.tgz
'''
}
}
}
post {
success {
archiveArtifacts artifacts: 'web-src-*.tgz', fingerprint: true
}
}
}
+ echo Packaging web sources for build 1
Packaging web sources for build 1
+ tar -czf web-src-1.tgz bookstore-web/src
+ ls -l web-src-1.tgz
-rw-r--r-- 1 jenkins jenkins 1724 Nov 8 05:04 web-src-1.tgz
Archiving artifacts
Recording fingerprints
Finished: SUCCESS
Aynı çıktı, ama tarif artık bir dosya. Taşırken bir job'u değil bir pattern'i taşıdığını unutma: on Freestyle job aynı şeyi küçük farklarla yapıyorsa, onları on Jenkinsfile'a değil, bir multibranch job'a ve gerekirse onuncu yazıdaki gibi bir library step'ine çevir. Bir de sıra önerisi: önce en çok çalışan ve en sık değişen job'lar; yılda bir çalışan job'u taşımak çoğu zaman silmekten daha pahalıdır.
Blue Ocean'dan Pipeline Graph View'a
Blue Ocean, Pipeline için yıllarca "modern arayüz" olarak kuruldu. Jenkins dokümantasyonuna göre Blue Ocean Temmuz 2026'dan beri deprecated; yeni geliştirme durdu ve yalnız ciddi güvenlik sorunları veya hatalar için seçici güncellemeler alacak. Dokümantasyonun önerdiği yerine geçenler aktif bakımı süren Pipeline Graph View plugin'i ve Pipeline: Stage View. Bu seride baştan beri Pipeline Graph View kullanıyoruz; build sayfasındaki grafik ve sekizinci yazıda paralel dalların sürelerini okuduğumuz stages görünümü (/job/bookstore/job/main/5/stages/) ondan geliyor.
Taşıma bir plugin kaldırma işi. Blue Ocean bir plugin ailesi olarak gelir ve kendi bağımlılıklarını çeker; plugins.txt'ten blueocean'ı çıkarıp Pipeline Graph View'ı eklemek, kod olarak yönetilen bir controller'da tek satırlık bir PR (Pull Request). Elle yönetilen eski bir controller'da ise önce kimlerin Blue Ocean linklerini (/blue/...) kullandığına bak: bildirim e-postaları, chat mesajları ve wiki sayfaları bu linklerle dolu olabilir.
JNLP'den Inbound ve WebSocket'e
Eski dokümanlarda ve script'lerde "JNLP (Java Network Launch Protocol) agent" geçer. Bu isim, agent'ın bir zamanlar Java Web Start ile tarayıcıdan başlatılmasından kalma bir isim; bugün aynı şeye inbound agent deniyor. İsmin izleri hâlâ duruyor: envanterdeki JNLPLauncher class'ı ve agent.jar'ın eski seçeneği:
-jnlpUrl URL : instead of talking to the controller
via stdin/stdout, emulate a JNLP
client by making a TCP connection to
the controller. Connection parameters
Eski kurulumlarda inbound agent'lar bu yolla ve 50000 numaralı TCP (Transmission Control Protocol) portu üzerinden bağlanır. Bugünkü yol -url, -name, -secret ve -webSocket seçenekleri: bağlantı controller'ın normal HTTP (Hypertext Transfer Protocol) portundan geçer, ayrı bir port açmak ve reverse proxy'de özel ayar yapmak gerekmez. Dördüncü yazıdaki agent'larımız (JENKINS_WEB_SOCKET=true) böyle bağlanıyordu. Taşırken her agent'ı tek tek WebSocket'e çevirip 50000 portunu en son kapat.
Jenkins'ten GitHub Actions'a mı?
Bazen doğru taşıma Jenkins'in içinde değil, dışına doğrudur. Kodun zaten GitHub'daysa ve birinci yazıdaki "neden Jenkins" sebepleri artık geçerli değilse, GitHub Actions Importer bu işe yardım eden resmi bir araç: gh actions-importer CLI (Command Line Interface) eklentisi. audit komutu Jenkins'teki job'ları tarayıp ne kadarının otomatik dönüştürülebileceğini raporlar, dry-run tek bir pipeline'ı workflow YAML'ına (YAML Ain't Markup Language) çevirir, migrate dönüşümü bir pull request olarak açar. Script'li pipeline'lar, bilinmeyen plugin'ler ve secret'lar elle taşınması gereken kısımlardır. Bu yazıda aracı çalıştırmadım; GitHub Actions tarafını ilerideki GitHub Actions serisinde ele alacağız.
Üretimde Dikkat
Taşımayı küçük adımlarla yap ve her adımı geri alınabilir tut: önce Java, sonra Jenkins sürümü, sonra plugin temizliği, sonra job'lar. Her adımdan önce on birinci yazıdaki gibi bir yedek ve restore testi.
Eski ve yeni job'ları bir süre yan yana çalıştır, çıktılarını karşılaştır (aynı artifact, aynı test sayısı) ve eski job'u ancak ondan sonra devre dışı bırak. Silmeden önce bir süre "disabled" hâlde bekletmek, unutulmuş bir bağımlılığı ortaya çıkarır.
Taşınan her job için sahibini bul. Kimsenin sahiplenmediği bir job, taşımaya değil silmeye adaydır.
Sık Düşülen Tuzaklar
Java'yı ve Jenkins'i aynı gece yükseltmek. Bir şey kırıldığında sebebin hangisi olduğunu bilemezsin.
Agent'ların Java'sını unutmak. Controller Java 21'de açılır ama Java 17'li agent'lar bağlanamaz.
Freestyle job'ları birebir kopyalamak. On benzer job'u on benzer Jenkinsfile'a çevirmek tekrarı yalnızca başka bir yere taşır.
Blue Ocean linklerini unutmak. Plugin kaldırıldığında eski linkler 404 verir; bildirim şablonlarını güncelle.
Taşımayı bir defalık iş sanmak. Taşıma bittiğinde on birinci yazıdaki düzen (JCasC, pin'li plugin'ler, düzenli upgrade) kurulmamışsa, birkaç yıl sonra aynı yazıyı tekrar okursun.
Küçük Bir Deneme
★☆☆ Envanter script'ini kendi Jenkins'inde (yetkili bir kullanıcıyla) çalıştır ve job türlerini, plugin sayısını ve agent'ların bağlantı yöntemlerini bir tabloya yaz.
★★☆ Bir Freestyle job'unu yukarıdaki tabloyu kullanarak Jenkinsfile'a çevir, iki job'u yan yana çalıştır ve artifact'ların fingerprint'lerini karşılaştır.
★★★ Controller'ını ve bir agent'ını Java 21'e geçirmek için bir plan yaz: hangi sırayla, hangi yedekle, nasıl doğrulayarak ve bir şey kırılırsa nasıl geri dönerek. Planı bir test controller'ında uygula.
Seri burada bitiyor. On iki yazıda bir controller'ı Docker'da sıfırdan kurduk, Jenkinsfile'la pipeline yazdık, agent'ları, credential'ları ve multibranch'i yapılandırdık, Bookstore'un .NET API'sini (Application Programming Interface) ve Angular uygulamasını build edip test ettik, onayla iki ortama deploy ettik, ortak kodu bir library'ye taşıdık ve controller'ın kendisini kod olarak yönetip yedekledik. Bir sonraki adımın, kendi ekibinin pipeline'ına bu yazılardan birini alıp uygulamak olsun; CI (Continuous Integration) ve CD (Continuous Delivery) kavramlarının tool'dan bağımsız tarafını merak ediyorsan ilerideki CI/CD Temelleri serisi tam bunun için.