1. Pengenalan Jenkins
Jenkins adalah automation server open-source yang paling banyak digunakan untuk Continuous Integration (CI) dan Continuous Delivery (CD). Dibuat oleh Kohsuke Kawaguchi pada 2011 (fork dari Hudson), Jenkins telah menjadi standar industri untuk CI/CD dengan lebih dari 1,800+ plugins yang tersedia.
Jenkins memungkinkan developer mengotomasi seluruh siklus build, test, dan deployment. Dengan Jenkins, setiap kali developer push kode ke repository, Jenkins secara otomatis mengambil kode tersebut, menjalankan build, menjalankan test, dan men-deploy hasilnya ke environment yang diinginkan.
Mengapa Jenkins?
| Keunggulan | Penjelasan |
|---|---|
| Open Source | Gratis, komunitas besar, dokumentasi lengkap |
| Plugin Ecosystem | 1,800+ plugins untuk integrasi dengan hampir semua tool |
| Extensible | Bisa dikustomisasi dengan Groovy scripting |
| Distributed | Bisa menjalankan jobs di banyak agent/server |
| Pipeline as Code | Define pipeline dalam Jenkinsfile di repository |
| Community | Dukungan komunitas yang sangat besar |
| Self-hosted | Kontrol penuh atas infrastruktur CI/CD |
┌─────────────────────────────────────────────────────────────────┐ │ JENKINS ARCHITECTURE │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ JENKINS CONTROLLER │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ Web UI │ │ Pipeline │ │ Plugin │ │ Job │ │ │ │ │ │ (8080) │ │ Engine │ │ Manager │ │ Scheduler│ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ └──────────┬─────────────┬─────────────┬────────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Agent 1 │ │ Agent 2 │ │ Agent 3 │ │ │ │ (Linux) │ │ (Windows) │ │ (Docker) │ │ │ │ - Build │ │ - Test │ │ - Build │ │ │ │ - Deploy │ │ - Package │ │ - Deploy │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ SCM (Git/SVN) + Docker Registry │ │ │ │ Artifacts Repository (Nexus/Artifactory) │ │ │ └──────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘
2. Instalasi & Setup
Instalasi Jenkins dengan Docker
# =========================== # INSTALL JENKINS (DOCKER) # =========================== # Jalankan Jenkins LTS dengan Docker 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 \ jenkins/jenkins:lts-jdk17 # Cek initial admin password docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword # Buka browser: http://localhost:8080 # Masukkan initial admin password # Install suggested plugins # Buat admin user # =========================== # DOCKER COMPOSE SETUP # =========================== # docker-compose.yml untuk Jenkins
# docker-compose.yml — Jenkins Setup
version: "3.8"
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
container_name: jenkins
restart: unless-stopped
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=true
user: root # Hanya untuk development!
# SonarQube untuk code quality (opsional)
sonarqube:
image: sonarqube:community
container_name: sonarqube
ports:
- "9000:9000"
volumes:
- sonarqube_data:/opt/sonarqube/data
environment:
- SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true
volumes:
jenkins_home:
sonarqube_data:
Instalasi di Ubuntu/Debian
# Install Java (prasyarat) sudo apt update sudo apt install -y openjdk-17-jdk # Tambah Jenkins repository 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 # Install Jenkins sudo apt update sudo apt install -y jenkins # Start Jenkins sudo systemctl start jenkins sudo systemctl enable jenkins # Cek status sudo systemctl status jenkins # Initial password sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Untuk production, jangan jalankan Jenkins sebagai root. Buat user khusus jenkins dengan akses terbatas. Gunakan reverse proxy (Nginx/Apache) di depan Jenkins untuk SSL termination dan keamanan tambahan.
3. Jenkinsfile: Declarative Pipeline
Declarative Pipeline adalah sintaks yang direkomendasikan untuk Jenkins Pipeline. Ditulis dalam format terstruktur dengan Groovy DSL, lebih mudah dibaca dan di-maintain dibanding Scripted Pipeline.
Basic Declarative Pipeline
// Jenkinsfile — Declarative Pipeline
// BeebaneLabs - Jenkins Tutorial
pipeline {
agent any
// Environment variables
environment {
APP_NAME = 'myapp'
DOCKER_IMAGE = "registry.example.com/${APP_NAME}"
NODE_VERSION = '20'
}
// Build tools
tools {
nodejs "${NODE_VERSION}"
}
// Pipeline options
options {
timeout(time: 30, unit: 'MINUTES')
timestamps()
buildDiscarder(logRotator(numToKeepStr: '10'))
disableConcurrentBuilds()
}
// Pipeline triggers
triggers {
pollSCM('H/5 * * * *') // Cek Git setiap 5 menit
}
stages {
// ===== CHECKOUT =====
stage('Checkout') {
steps {
checkout scm
sh 'git log --oneline -5'
}
}
// ===== INSTALL =====
stage('Install Dependencies') {
steps {
sh 'npm ci --cache .npm'
}
}
// ===== BUILD =====
stage('Build') {
steps {
sh 'npm run build'
}
post {
success {
archiveArtifacts artifacts: 'dist/**', fingerprint: true
}
}
}
// ===== TEST =====
stage('Test') {
parallel {
stage('Unit Test') {
steps {
sh 'npm run test:unit -- --coverage'
}
post {
always {
junit 'test-results/*.xml'
publishHTML(target: [
reportDir: 'coverage/lcov-report',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
}
}
// ===== SECURITY SCAN =====
stage('Security Scan') {
steps {
sh 'npm audit --audit-level=high || true'
}
}
// ===== DOCKER BUILD =====
stage('Docker Build') {
when {
branch 'main'
}
steps {
script {
docker.withRegistry("https://registry.example.com", 'registry-credentials') {
def image = docker.build("${DOCKER_IMAGE}:${env.BUILD_NUMBER}")
image.push()
image.push('latest')
}
}
}
}
// ===== DEPLOY =====
stage('Deploy') {
when {
branch 'main'
}
steps {
input message: 'Deploy to production?',
ok: 'Deploy',
submitter: 'admin,devops'
sh '''
ssh deploy@production-server \
"cd /app && docker compose pull && docker compose up -d"
'''
}
}
}
// Pipeline-wide post actions
post {
success {
slackSend(
channel: '#builds',
color: 'good',
message: "✅ ${env.JOB_NAME} #${env.BUILD_NUMBER} - SUCCESS"
)
}
failure {
slackSend(
channel: '#builds',
color: 'danger',
message: "❌ ${env.JOB_NAME} #${env.BUILD_NUMBER} - FAILED"
)
}
always {
cleanWs() // Cleanup workspace
}
}
}
Pipeline Directives Reference
| Directive | Fungsi | Scope |
|---|---|---|
agent | Di mana job dijalankan | pipeline / stage |
stages | Kumpulan stage berurutan | pipeline |
stage | Satu tahap dalam pipeline | stages |
steps | Aksi yang dijalankan | stage |
environment | Environment variables | pipeline / stage |
tools | Build tools (Maven, Node, dll) | pipeline / stage |
options | Pipeline options (timeout, retry) | pipeline / stage |
triggers | Jadwal eksekusi pipeline | pipeline |
when | Kondisi eksekusi stage | stage |
input | Manual approval gate | stage |
parallel | Stage berjalan paralel | stage |
post | Aksi setelah stage/pipeline selesai | pipeline / stage |
4. Scripted Pipeline
Scripted Pipeline menggunakan sintaks Groovy penuh, memberikan fleksibilitas maksimum tetapi lebih kompleks. Cocok untuk pipeline yang memerlukan logika tingkat lanjut.
// Jenkinsfile — Scripted Pipeline
// Fleksibilitas penuh dengan Groovy
node('linux') {
def app
def version
try {
// ===== CHECKOUT =====
stage('Checkout') {
checkout scm
version = sh(script: 'git describe --tags --always', returnStdout: true).trim()
echo "Building version: ${version}"
}
// ===== BUILD =====
stage('Build') {
// Conditional logic
if (env.BRANCH_NAME == 'main') {
sh 'npm ci --production'
} else {
sh 'npm ci'
}
sh 'npm run build'
}
// ===== TEST =====
stage('Test') {
// Parallel execution
parallel(
'Unit Tests': {
sh 'npm run test:unit'
},
'Integration Tests': {
sh 'npm run test:integration'
},
'Lint': {
sh 'npm run lint'
}
)
}
// ===== DOCKER BUILD =====
stage('Docker Build') {
if (env.BRANCH_NAME == 'main') {
docker.withRegistry('https://registry.example.com', 'registry-creds') {
app = docker.build("myapp:${version}")
app.push()
app.push('latest')
}
}
}
// ===== DEPLOY =====
stage('Deploy') {
if (env.BRANCH_NAME == 'main') {
def proceed = input(
message: 'Deploy to production?',
ok: 'Deploy',
parameters: [
string(name: 'ENVIRONMENT', defaultValue: 'production')
]
)
echo "Deploying to: ${proceed}"
sh "./deploy.sh ${proceed} ${version}"
}
}
} catch (e) {
currentBuild.result = 'FAILURE'
throw e
} finally {
// Cleanup
cleanWs()
// Notification
if (currentBuild.result == 'FAILURE') {
slackSend(color: 'danger', message: "Build failed: ${env.JOB_NAME}")
}
}
}
Declarative vs Scripted
| Aspek | Declarative | Scripted |
|---|---|---|
| Sintaks | 🟢 Terstruktur, mudah dibaca | 🟡 Groovy penuh |
| Validasi | 🟢 Bisa di-validate sebelum run | 🔴 Runtime error |
| Flexibility | 🟡 Terbatas pada directive | 🟢 Sangat fleksibel |
| Learning Curve | 🟢 Lebih mudah | 🟡 Butuh Groovy knowledge |
| Rekomendasi | 🟢 Utama | 🟡 Untuk kasus kompleks |
5. Essential Plugins
Jenkins memiliki lebih dari 1,800 plugins. Berikut plugin-plugin yang paling sering digunakan:
Must-Have Plugins
| Plugin | Fungsi | Kategori |
|---|---|---|
Blue Ocean | Modern UI untuk pipeline visualization | UI |
Pipeline | Support untuk Jenkinsfile | Core |
Git | Git integration | SCM |
Docker Pipeline | Build & deploy Docker images | Docker |
SSH Agent | Inject SSH keys ke pipeline | Security |
Credentials Binding | Inject credentials ke build | Security |
SonarQube Scanner | Code quality analysis | Quality |
Slack Notification | Notify ke Slack | Notification |
Publish HTML | Publish HTML reports | Reporting |
JUnit | Test report integration | Testing |
NodeJS | Node.js tool installation | Build Tools |
Job DSL | Programmatically create jobs | Automation |
Role-Based Authorization | Access control per role | Security |
Parameterized Trigger | Trigger other jobs with parameters | Pipeline |
Install Plugins
# Install plugins via Jenkins UI: # 1. Buka Manage Jenkins > Plugins > Available plugins # 2. Cari plugin yang diinginkan # 3. Centang dan klik "Install without restart" # 4. Restart Jenkins setelah semua plugin terinstall # Atau via Jenkins CLI: java -jar jenkins-cli.jar -s http://localhost:8080/ \ -auth admin:password \ install-plugin blueocean \ docker-workflow \ pipeline-stage-view \ slack \ sonar \ nodejs # Restart setelah install java -jar jenkins-cli.jar -s http://localhost:8080/ \ -auth admin:password safe-restart
6. Distributed Agents/Nodes
Jenkins mendukung distributed builds — menjalankan jobs di server terpisah (agents/nodes). Ini memungkinkan Anda menjalankan build di berbagai OS, membagi beban, dan mengisolasi environment.
Agent Types
| Agent Type | Konfigurasi | Cocok Untuk |
|---|---|---|
any | Gunakan agent mana saja yang tersedia | Simple jobs |
agent { label 'linux' } | Agent dengan label tertentu | Targeted jobs |
agent { docker { ... } } | Jalankan di Docker container | Isolated environment |
agent { node { label '...' } } | Specific node | Hardware-specific |
Setup Distributed Agent
# =========================== # SETUP REMOTE AGENT # =========================== # 1. Di Jenkins UI: Manage Jenkins > Nodes > New Node # 2. Isi nama node, pilih "Permanent Agent" # 3. Set: # - Remote root directory: /var/jenkins # - Labels: linux, docker # - Launch method: Launch agents via SSH # - Host: agent-host.example.com # - Credentials: SSH key # Atau via command line (agent side): # Download agent.jar dari Jenkins controller curl -O http://jenkins:8080/jnlpJars/agent.jar # Connect agent java -jar agent.jar \ -url http://jenkins:8080/ \ -secret @secret-file \ -name "my-agent" \ -workDir "/var/jenkins" # =========================== # DOCKER AGENT # =========================== # Jenkinsfile: Jalankan di Docker container
Using Docker Agents di Jenkinsfile
// Jalankan pipeline di Docker container
pipeline {
agent {
docker {
image 'node:20-alpine'
args '-v /tmp:/tmp -e NODE_ENV=test'
}
}
stages {
stage('Test') {
steps {
sh 'node --version'
sh 'npm ci'
sh 'npm test'
}
}
}
}
// Multiple agents per stage
pipeline {
agent none
stages {
stage('Build') {
agent {
docker { image 'node:20-alpine' }
}
steps {
sh 'npm run build'
}
}
stage('Test') {
agent {
docker { image 'cypress/included:13.6.0' }
}
steps {
sh 'npx cypress run'
}
}
stage('Deploy') {
agent {
label 'deploy-server'
}
steps {
sh './deploy.sh'
}
}
}
}
Gunakan label yang deskriptif untuk agents: linux, docker, gpu, deploy. Ini memudahkan penempatan jobs ke agent yang tepat. Gunakan Docker agents untuk isolasi yang lebih baik — setiap build mendapat container bersih.
7. Credentials Management
Jenkins Credentials menyimpan secrets secara terenkripsi dan menyediakannya ke pipeline tanpa mengekspos ke logs.
Jenis Credentials
| Tipe | Penggunaan | Contoh |
|---|---|---|
| Username with Password | Git auth, Docker registry | credentials('git-creds') |
| SSH Username with Key | SSH ke server | sshagent(['ssh-key']) |
| Secret Text | API keys, tokens | credentials('api-token') |
| Secret File | JSON key, certificate | credentials('service-account') |
Menggunakan Credentials di Pipeline
// Menggunakan credentials di Jenkinsfile
pipeline {
agent any
environment {
// Secret text → environment variable
API_TOKEN = credentials('my-api-token')
// Username/password → USERN dan PASSW
REGISTRY_CREDS = credentials('docker-registry')
}
stages {
stage('Git Clone') {
steps {
// Git credentials via credential-id
git url: 'https://gitlab.com/private/repo.git',
branch: 'main',
credentialsId: 'git-ssh-key'
}
}
stage('Docker Push') {
steps {
// Username/password credentials
sh 'echo $REGISTRY_CREDS_PSW | docker login -u $REGISTRY_CREDS_USR --password-stdin'
sh 'docker build -t myapp .'
sh 'docker push myapp'
}
}
stage('Deploy') {
steps {
// SSH credentials
sshagent(credentials: ['deploy-ssh-key']) {
sh 'ssh deploy@server "cd /app && ./update.sh"'
}
}
}
stage('API Call') {
steps {
// Secret text credential
sh '''
curl -H "Authorization: Bearer $API_TOKEN" \
https://api.example.com/deploy
'''
}
}
stage('Using Secret File') {
steps {
withCredentials([file(credentialsId: 'gcloud-key', variable: 'GCloud_KEY')]) {
sh 'gcloud auth activate-service-account --key-file=$GCloud_KEY'
}
}
}
}
}
Jangan pernah echo atau print credentials di pipeline script. Jenkins otomatis menyensor credential values di logs, tetapi pastikan Anda juga tidak menyimpannya ke file yang bisa diakses. Gunakan withCredentials block agar credentials hanya tersedia di scope yang tepat.
8. Shared Libraries
Shared Libraries memungkinkan Anda reuse pipeline code di antara banyak project. Ini sangat penting untuk organisasi dengan banyak repositories.
Struktur Shared Library
# Shared Library Repository Structure (jenkins-shared-library/) ├── vars/ # Global variables/functions │ ├── buildNodeApp.groovy # Callable dari Jenkinsfile │ ├── deployToK8s.groovy │ ├── sendNotification.groovy │ └── standardPipeline.groovy ├── src/ # Groovy classes │ └── com/ │ └── beebanelabs/ │ └── ci/ │ └── Docker.groovy ├── resources/ # Non-Groovy files │ └── templates/ │ └── k8s-deployment.yml └── README.md
// vars/buildNodeApp.groovy — Reusable build function
def call(Map config = [:]) {
def nodeVersion = config.nodeVersion ?: '20'
def buildCommand = config.buildCommand ?: 'npm run build'
pipeline {
agent {
docker { image "node:${nodeVersion}-alpine" }
}
environment {
NODE_ENV = config.environment ?: 'production'
}
stages {
stage('Install') {
steps {
sh 'npm ci --cache .npm'
}
}
stage('Build') {
steps {
sh buildCommand
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
}
post {
always {
cleanWs()
}
}
}
}
// vars/sendNotification.groovy
def call(String status = 'SUCCESS', String channel = '#builds') {
def color = status == 'SUCCESS' ? 'good' : 'danger'
def emoji = status == 'SUCCESS' ? '✅' : '❌'
slackSend(
channel: channel,
color: color,
message: "${emoji} ${env.JOB_NAME} #${env.BUILD_NUMBER} - ${status}"
)
}
Menggunakan Shared Library
// Jenkinsfile — Menggunakan shared library
// Konfigurasi library: Manage Jenkins > System > Global Pipeline Libraries
@Library('jenkins-shared-library@main') _
// Method 1: Langsung gunakan fungsi
buildNodeApp(
nodeVersion: '20',
buildCommand: 'npm run build:prod',
environment: 'production'
)
// Method 2: Custom pipeline dengan shared function
pipeline {
agent any
stages {
stage('Build') {
steps {
// Fungsi dari shared library
buildAndTest(nodeVersion: '20')
}
}
stage('Deploy') {
steps {
deployToK8s(
namespace: 'production',
imageTag: env.BUILD_NUMBER
)
}
}
}
post {
failure {
sendNotification('FAILURE')
}
success {
sendNotification('SUCCESS')
}
}
}
9. Docker Integration
Jenkins memiliki integrasi Docker yang sangat baik. Anda bisa build images, push ke registry, dan bahkan menjalankan pipeline di dalam Docker containers. Lihat tutorial Docker Dasar dan Docker Compose Advanced untuk konsep dasar.
// Docker integration di Jenkinsfile
pipeline {
agent any
environment {
REGISTRY = 'registry.example.com'
IMAGE_NAME = "${REGISTRY}/myapp"
}
stages {
stage('Build Image') {
steps {
script {
// Build Docker image
def image = docker.build("${IMAGE_NAME}:${env.BUILD_NUMBER}",
"--build-arg VERSION=${env.BUILD_NUMBER} .")
// Push ke registry
docker.withRegistry("https://${REGISTRY}", 'registry-credentials') {
image.push()
image.push('latest')
}
}
}
}
stage('Test in Container') {
steps {
// Jalankan test di container
docker.image('node:20-alpine').inside {
sh 'npm ci'
sh 'npm test'
}
}
}
stage('Deploy with Docker Compose') {
steps {
sshagent(['deploy-key']) {
sh """
ssh deploy@server '
cd /app
export IMAGE_TAG=${env.BUILD_NUMBER}
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
docker system prune -f
'
"""
}
}
}
stage('Multi-Container Test') {
steps {
// Jalankan dengan service containers
docker.image('node:20-alpine').inside(
'--link postgres:postgres --link redis:redis'
) {
sh 'npm run test:integration'
}
}
}
}
post {
always {
// Cleanup Docker images lama
sh 'docker image prune -f'
}
}
}
10. Best Practices & Tips
Jenkinsfile Best Practices
// ===========================
// BEST PRACTICES
// ===========================
pipeline {
agent any
// 1. SELALU set timeout
options {
timeout(time: 30, unit: 'MINUTES')
timestamps()
buildDiscarder(logRotator(
artifactNumToKeepStr: '5',
numToKeepStr: '20'
))
disableConcurrentBuilds()
}
// 2. SELALU gunakan post actions
post {
always {
// Cleanup workspace
cleanWs()
// Publish test results
junit allowEmptyResults: true, testResults: '**/test-results/*.xml'
}
failure {
// Notifikasi
emailext(
subject: "FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: '${DEFAULT_CONTENT}',
to: 'team@example.com'
)
}
}
stages {
stage('Build') {
steps {
// 3. JANGAN hardcode credentials
// ❌ sh 'docker login -u admin -p secret'
// ✅ withCredentials([...]) { sh 'docker login ...' }
// 4. JANGAN swallow errors
// ❌ sh 'npm test || true'
// ✅ sh 'npm test' (biarkan fail jika test gagal)
// 5. SELALU archive artifacts penting
sh 'npm run build'
}
post {
success {
archiveArtifacts artifacts: 'dist/**', fingerprint: true
}
}
}
stage('Test') {
steps {
// 6. Parallel test untuk kecepatan
parallel(
'Unit': { sh 'npm run test:unit' },
'Integration': { sh 'npm run test:integration' },
'Lint': { sh 'npm run lint' }
)
}
}
}
}
Jenkins Administration Tips
| Area | Tip | Prioritas |
|---|---|---|
| Backup | Backup JENKINS_HOME secara berkala | 🔴 Kritis |
| Security | Enable CSRF protection, matrix auth | 🔴 Kritis |
| Monitoring | Monitor disk space, executor usage | 🟡 Tinggi |
| Performance | Rotate build logs, limit artifact retention | 🟡 Tinggi |
| Updates | Update Jenkins & plugins secara berkala | 🟡 Tinggi |
| Access | Implementasikan RBAC (Role-Based Access Control) | 🟡 Tinggi |
11. Quiz Pemahaman
Uji pemahaman Anda tentang Jenkins:
1. Apa perbedaan utama antara Declarative dan Scripted Pipeline?
2. Apa fungsi dari Jenkins Agent/Node?
3. Mengapa credentials tidak boleh di-hardcode di Jenkinsfile?
4. Apa fungsi Shared Libraries di Jenkins?
5. Apa fungsi keyword input di Jenkinsfile?