DevOps

Jenkins

Master Jenkins CI/CD: architecture, installation, pipelines, and creating both declarative and scripted pipelines for automation.

By TechCoder TeamLast updated: 2026-06-02
In a Nutshell

Master Jenkins CI/CD: architecture, installation, pipelines, and creating both declarative and scripted pipelines for automation. This hands-on tutorial focuses on practical implementation of jenkins concepts.

Jenkins

Jenkins is the most widely used open-source automation server for building, testing, and deploying software.

Jenkins Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      Jenkins Architecture                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   ┌──────────────┐                                             │
│   │   Jenkins    │                                             │
│   │   Master     │                                             │
│   │  (Controller)│                                             │
│   │              │                                             │
│   │ ┌──────────┐ │                                             │
│   │ │ Web UI   │ │                                             │
│   │ │ REST API │ │                                             │
│   │ │  Queue   │ │                                             │
│   │ └──────────┘ │                                             │
│   └──────┬───────┘                                             │
│          │                                                       │
│          │ delegates jobs                                        │
│          ▼                                                       │
│   ┌──────────────┐    ┌──────────────┐    ┌──────────────┐     │
│   │   Agent 1    │    │   Agent 2    │    │   Agent 3    │     │
│   │  (Linux)     │    │  (Windows)   │    │  (Docker)    │     │
│   │              │    │              │    │              │     │
│   │ • Build      │    │ • .NET apps  │    │ • Containers │     │
│   │ • Test       │    │ • PowerShell │    │ • Isolated   │     │
│   │ • Deploy     │    │              │    │              │     │
│   └──────────────┘    └──────────────┘    └──────────────┘     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Master (Controller)

  • Web Interface: Manage jobs, view builds
  • Job Scheduling: Queue and distribute work
  • Plugin Management: Extend functionality
  • Configuration: Store job and system config

Agents (Nodes)

  • Execute build jobs delegated by master
  • Can run on different OS (Linux, Windows, macOS)
  • Label-based job assignment
  • Scale horizontally

Installation

# Run Jenkins
docker run -d \
  -p 8080:8080 \
  -p 50000:50000 \
  -v jenkins_home:/var/jenkins_home \
  --name jenkins \
  jenkins/jenkins:lts

# Get initial admin password
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

# Access at http://localhost:8080

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      containers:
      - name: jenkins
        image: jenkins/jenkins:lts
        ports:
        - containerPort: 8080
        - containerPort: 50000
        volumeMounts:
        - name: jenkins-home
          mountPath: /var/jenkins_home
      volumes:
      - name: jenkins-home
        persistentVolumeClaim:
          claimName: jenkins-pvc

Jenkinsfile

Declarative Pipeline

pipeline {
    agent any
    
    environment {
        APP_NAME = 'my-app'
        VERSION = '1.0.0'
    }
    
    options {
        buildDiscarder(logRotator(numToKeepStr: '10'))
        disableConcurrentBuilds()
        timeout(time: 30, unit: 'MINUTES')
    }
    
    triggers {
        cron('H 2 * * *')  // Daily at 2 AM
        pollSCM('*/5 * * * *')  // Poll every 5 minutes
    }
    
    parameters {
        string(name: 'ENVIRONMENT', defaultValue: 'dev', description: 'Target environment')
        booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: 'Skip tests?')
        choice(name: 'DEPLOY_STRATEGY', choices: ['blue-green', 'canary', 'rolling'], description: 'Deployment strategy')
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                sh 'git log --oneline -5'
            }
        }
        
        stage('Build') {
            when {
                not { params.SKIP_TESTS }
            }
            steps {
                sh 'mvn clean compile'
            }
        }
        
        stage('Test') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'mvn test'
                    }
                    post {
                        always {
                            junit '**/target/surefire-reports/*.xml'
                        }
                    }
                }
                stage('Integration Tests') {
                    steps {
                        sh 'mvn integration-test'
                    }
                }
            }
        }
        
        stage('Security Scan') {
            steps {
                sh 'mvn dependency-check:check'
            }
        }
        
        stage('Package') {
            steps {
                sh 'mvn package -DskipTests'
                archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
            }
        }
        
        stage('Deploy') {
            when {
                anyOf {
                    branch 'main'
                    branch 'release/*'
                }
            }
            steps {
                script {
                    if (params.DEPLOY_STRATEGY == 'blue-green') {
                        sh './deploy-blue-green.sh'
                    } else if (params.DEPLOY_STRATEGY == 'canary') {
                        sh './deploy-canary.sh'
                    } else {
                        sh './deploy-rolling.sh'
                    }
                }
            }
        }
    }
    
    post {
        always {
            echo 'Pipeline completed'
            cleanWs()
        }
        success {
            slackSend(color: 'good', message: "Build succeeded: ${env.JOB_NAME} - ${env.BUILD_NUMBER}")
        }
        failure {
            slackSend(color: 'danger', message: "Build failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}")
            mail to: 'team@example.com',
                 subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
                 body: "Check console output at ${env.BUILD_URL}"
        }
        unstable {
            echo 'Build is unstable'
        }
        changed {
            echo 'Build status changed'
        }
    }
}

Scripted Pipeline

node('linux && docker') {
    try {
        stage('Prepare') {
            checkout scm
            def commit = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
            echo "Building commit: ${commit}"
        }
        
        stage('Build') {
            def image = docker.build("myapp:${env.BUILD_NUMBER}")
        }
        
        stage('Test') {
            docker.image('myapp:${env.BUILD_NUMBER}').inside {
                sh 'npm test'
            }
        }
        
        stage('Push') {
            docker.withRegistry('https://registry.example.com', 'registry-credentials') {
                docker.image('myapp:${env.BUILD_NUMBER}').push()
                docker.image('myapp:${env.BUILD_NUMBER}').push('latest')
            }
        }
        
        stage('Deploy') {
            if (env.BRANCH_NAME == 'main') {
                sh 'kubectl set image deployment/myapp myapp=myapp:${env.BUILD_NUMBER}'
            }
        }
        
        currentBuild.result = 'SUCCESS'
    } catch (Exception e) {
        currentBuild.result = 'FAILURE'
        throw e
    } finally {
        cleanWs()
    }
}

Key Directives

Agent

// Run on any available agent
agent any

// Specific agent label
agent { label 'linux && docker' }

// Docker agent
agent {
    docker {
        image 'maven:3.9-eclipse-temurin-17'
        args '-v /root/.m2:/root/.m2'
    }
}

// Kubernetes agent
agent {
    kubernetes {
        yaml """
            apiVersion: v1
            kind: Pod
            spec:
              containers:
              - name: maven
                image: maven:3.9-eclipse-temurin-17
                command: ['cat']
                tty: true
        """
    }
}

When Conditions

stage('Production Deploy') {
    when {
        branch 'main'
    }
    steps {
        sh './deploy-prod.sh'
    }
}

stage('Release Deploy') {
    when {
        buildingTag()
    }
    steps {
        sh './deploy-release.sh'
    }
}

stage('Manual Gate') {
    when {
        expression { params.ENVIRONMENT == 'prod' }
    }
    steps {
        input message: 'Deploy to production?', ok: 'Deploy'
        sh './deploy.sh'
    }
}

Environment Variables

environment {
    // Static values
    AWS_DEFAULT_REGION = 'us-east-1'
    
    // Dynamic values
    GIT_COMMIT_SHORT = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
    
    // Credentials from Jenkins
    AWS_CREDENTIALS = credentials('aws-credentials')
    // Creates: AWS_CREDENTIALS, AWS_CREDENTIALS_USR, AWS_CREDENTIALS_PSW
}

Input

stage('Approval') {
    steps {
        input message: 'Proceed with deployment?',
              ok: 'Deploy',
              submitter: 'admin,deploy-team',
              submitterParameter: 'APPROVER',
              parameters: [
                  choice(name: 'ENVIRONMENT', choices: ['staging', 'production']),
                  text(name: 'NOTES', description: 'Deployment notes')
              ]
    }
}

Shared Libraries

Creating a Shared Library

// vars/buildJava.groovy
def call(Map config = [:]) {
    pipeline {
        agent { label 'linux' }
        
        stages {
            stage('Build') {
                steps {
                    sh "mvn clean package -DskipTests=${config.skipTests ?: false}"
                }
            }
            
            stage('Test') {
                when { expression { !config.skipTests } }
                steps {
                    sh 'mvn test'
                }
                post {
                    always {
                        junit '**/target/surefire-reports/*.xml'
                    }
                }
            }
        }
    }
}
// vars/deployToK8s.groovy
def call(String image, String namespace = 'default') {
    sh """
        kubectl set image deployment/app \
            app=${image} \
            -n ${namespace}
        kubectl rollout status deployment/app -n ${namespace}
    """
}

Using Shared Libraries

// Jenkinsfile
@Library('my-shared-library') _

buildJava(skipTests: false)

// Or call specific function
deployToK8s('myapp:1.0', 'production')

Essential Plugins

PluginPurpose
PipelineCore pipeline functionality
GitGit integration
Docker PipelineDocker in pipelines
KubernetesKubernetes agents
Blue OceanModern UI
SonarQube ScannerCode quality
Nexus Artifact UploaderArtifact management
Slack NotificationNotifications
CredentialsSecure credential storage
Parameterized TriggerTrigger jobs with params

Best Practices

Pipeline Organization

project-root/
├── Jenkinsfile              # Main pipeline
├── jenkins/
│   ├── vars/               # Shared library vars
│   ├── src/                # Library source
│   └── resources/          # Templates/configs
├── scripts/
│   ├── build.sh
│   ├── test.sh
│   └── deploy.sh
└── src/                    # Application source

Security

  1. Use credentials plugin: Never hardcode secrets
  2. Master agent separation: Run builds on agents, not master
  3. Job-based permissions: Restrict access per team
  4. CSRF protection: Enable cross-site request forgery protection
  5. Regular updates: Keep Jenkins and plugins updated

Performance

  1. Lightweight checkout: options { checkoutToSubdirectory('src') }
  2. Stash/unstash: Share files between stages
  3. Parallel execution: Run independent stages in parallel
  4. Agent caching: Mount volumes for dependencies (m2, npm cache)
  5. Discard old builds: buildDiscarder(logRotator(...))

Quiz

Quiz

Question 1 of 5

What is the difference between Declarative and Scripted pipelines in Jenkins?

Scripted is newer than Declarative
Declarative has a structured syntax; Scripted is Groovy-based and flexible
Declarative doesn't support Docker; Scripted does
Scripted is for Windows only

Next Steps

Now let's explore GitHub Actions and GitLab CI—cloud-native CI/CD alternatives.