Episode penutup series ini menyajikan studi kasus arsitektur pipeline enterprise yang terpadu, dari webhook SCM, provisioning pod build dinamis di Kubernetes, hingga deployment production dan notifikasi Slack. Kita juga menulis Jenkinsfile lengkap delapan tahap serta checklist security hardening untuk Jenkins controller.

Ini episode terakhir series Belajar Jenkins. Di 19 episode sebelumnya kita membangun keterampilan dari instalasi, pipeline as code, agent, secrets, Docker, shared library, JCasC, hingga troubleshooting. Sekarang kita merangkai semuanya menjadi satu arsitektur pipeline enterprise yang utuh — alur yang benar-benar dipakai tim DevOps di perusahaan berskala besar.
Kita akan membangun pipeline yang memenuhi alur berikut:
Kuncinya adalah pemisahan tanggung jawab. Jenkins controller hanya mengorkestrasi; semua pekerjaan berat berjalan di pod agent Kubernetes yang dibuat dan dihapus per build (episode 3). Sumber kebenaran untuk deployment ada di chart Helm (episode 16), kualitas dikontrol SonarQube (episode 14), dan keamanan image dijaga Trivy. Setiap tahap bisa gagal dan menghentikan alur lebih awal — jangan pernah melanjutkan ke production jika kualitas atau keamanan belum lulus.
Berikut Jenkinsfile utuh untuk alur di atas. Repository aplikasi berisi source code Node.js, folder chart/ berisi Helm chart, dan pipeline dijalankan sebagai Multibranch Pipeline.
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: build
image: node:20-alpine
command: ['sleep', 'infinity']
- name: sonar
image: sonarsource/sonar-scanner-cli:11
command: ['sleep', 'infinity']
- name: docker
image: docker:27
command: ['sleep', 'infinity']
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
'''
}
}
options {
timestamps()
timeout(time: 60, unit: 'MINUTES')
disableConcurrentBuilds()
}
environment {
IMAGE = 'registry.example.com/my-app'
TAG = "1.0.${BUILD_NUMBER}"
}
stages {
stage('Checkout & SonarQube Scan') {
steps {
checkout scm
container('sonar') {
sh 'sonar-scanner -Dsonar.projectKey=my-app'
}
}
}
stage('Quality Gate') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'echo "Menunggu quality gate..."'
}
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
stage('Test & Build') {
steps {
container('build') {
sh 'npm ci'
sh 'npm run test:coverage'
sh 'npm run build'
}
}
post {
success {
junit 'coverage/report.xml'
jacoco execPattern: '**/jacoco.exec'
}
}
}
stage('Push Image & Trivy Scan') {
steps {
container('docker') {
docker.withRegistry('https://registry.example.com', 'registry-creds') {
sh "docker build -t ${IMAGE}:${TAG} ."
sh "docker push ${IMAGE}:${TAG}"
}
}
container('build') {
sh "trivy image --severity HIGH,CRITICAL --exit-code 1 ${IMAGE}:${TAG}"
}
}
}
stage('Deploy Staging & E2E') {
steps {
withKubeConfig(credentialsId: 'kubeconfig-staging', serverUrl: 'https://k8s-staging.example.com') {
sh "helm upgrade --install my-app ./chart --namespace staging --set image.repository=${IMAGE} --set image.tag=${TAG}"
}
sh 'npm run test:e2e -- --base-url=https://staging.example.com'
}
}
stage('Manual Approval') {
when { branch 'main' }
steps {
input message: 'Approve production deployment?', submitter: 'release-manager'
}
}
stage('Deploy Production') {
when { branch 'main' }
steps {
withKubeConfig(credentialsId: 'kubeconfig-prod', serverUrl: 'https://k8s-prod.example.com') {
sh "helm upgrade --install my-app ./chart --namespace production --set image.repository=${IMAGE} --set image.tag=${TAG} --wait"
}
}
}
}
post {
success {
slackSend(color: 'good', message: "Deploy ${env.JOB_NAME} #${env.BUILD_NUMBER} berhasil ke production")
}
failure {
emailext(to: 'release@example.com', subject: "Pipeline gagal: ${env.JOB_NAME}", body: "Cek log: ${env.BUILD_URL}")
}
cleanup {
cleanWs()
}
}
}build (default), sonar, dan docker. Setelah build selesai, pod otomatis dihancurkan, sehingga tidak ada resource yang tertinggal.waitForQualityGate memblokir pipeline jika SonarQube memberi hasil gagal. Atur quality gate SonarQube agar auto-pass pada project non-prod, supaya pipeline tidak menggantung selamanya.--exit-code 1 membuat pipeline gagal jika ditemukan kerentanan HIGH atau CRITICAL.input menghentikan pipeline di branch main sampai release manager menyetujui.helm upgrade --install dengan flag --wait melakukan rolling update dan menunggu pod siap sebelum pipeline dinyatakan sukses, lalu Slack memberi kabar.Warning
Mount host docker.sock di pod agent memudahkan build image, tapi memberi akses root ke node. Di environment yang ketat, gunakan Docker-in-Docker (DinD) atau runner khusus ber-privilege, dan pastikan pod agent tidak pernah menerima job dari pengguna tidak tepercaya.
Terakhir, checklist yang wajib dipenuhi sebelum Jenkins controller dinyatakan production-ready:
withCredentials dan jangan pernah menyimpan secret di Jenkinsfile.Di episode ini kita merangkai semuanya menjadi studi kasus pipeline enterprise:
Dari episode 0 hingga 20, kalian sekarang memiliki bekal lengkap membangun, mengamankan, memantau, dan memecahkan masalah Jenkins di skala enterprise. Jangan berhenti di sini — coba terapkan di proyek nyata, otomasi hal kecil dulu, lalu naikkan kompleksitasnya. Selamat membangun pipeline production-grade kalian sendiri!