Jenkins CI/CD Guide

Introduction

Jenkins is an open-source automation server that enables developers to build, test, and deploy their software. It provides hundreds of plugins to support building, deploying, and automating any project.

Key Features:

  • Easy installation and configuration
  • Rich plugin ecosystem
  • Distributed builds
  • Pipeline as Code
  • Extensive monitoring
  • Community support

Setup & Configuration

Docker Installation

# Dockerfile for Jenkins with Docker support
FROM jenkins/jenkins:lts

USER root

# Install Docker CLI
RUN apt-get update && \
    apt-get -y install apt-transport-https ca-certificates curl software-properties-common && \
    curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - && \
    add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \
    apt-get update && \
    apt-get -y install docker-ce-cli

# Install Docker Compose
RUN curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" \
    -o /usr/local/bin/docker-compose && \
    chmod +x /usr/local/bin/docker-compose

USER jenkins

Configuration as Code

# jenkins.yaml
jenkins:
  systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin"
  numExecutors: 2
  scmCheckoutRetryCount: 2
  mode: NORMAL

  securityRealm:
    local:
      allowsSignup: false
      users:
        - id: "admin"
          password: "${ADMIN_PASSWORD}"

  authorizationStrategy:
    globalMatrix:
      permissions:
        - "Overall/Administer:admin"
        - "Overall/Read:authenticated"

  nodes:
    - permanent:
        name: "agent1"
        remoteFS: "/home/jenkins"
        launcher:
          ssh:
            host: "agent1.example.com"
            credentialsId: "agent-ssh-key"

Pipeline Design

Declarative Pipeline

pipeline {
    agent {
        docker {
            image 'node:16-alpine'
            args '-v $HOME/.npm:/root/.npm'
        }
    }
    
    environment {
        CI = 'true'
        STAGING_SERVER = 'staging.example.com'
        PRODUCTION_SERVER = 'prod.example.com'
    }
    
    stages {
        stage('Install') {
            steps {
                sh 'npm ci'
            }
        }
        
        stage('Test') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'npm test'
                    }
                }
                stage('Lint') {
                    steps {
                        sh 'npm run lint'
                    }
                }
            }
        }
        
        stage('Build') {
            steps {
                sh 'npm run build'
                archiveArtifacts artifacts: 'dist/**/*'
            }
        }
        
        stage('Deploy to Staging') {
            when {
                branch 'develop'
            }
            steps {
                withCredentials([sshUserPrivateKey(credentialsId: 'ssh-key', keyFileVariable: 'SSH_KEY')]) {
                    sh """
                        scp -i \$SSH_KEY dist/* user@\${STAGING_SERVER}:/var/www/html/
                        ssh -i \$SSH_KEY user@\${STAGING_SERVER} 'sudo systemctl restart nginx'
                    """
                }
            }
        }
        
        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            steps {
                input message: 'Deploy to production?'
                withCredentials([sshUserPrivateKey(credentialsId: 'ssh-key', keyFileVariable: 'SSH_KEY')]) {
                    sh """
                        scp -i \$SSH_KEY dist/* user@\${PRODUCTION_SERVER}:/var/www/html/
                        ssh -i \$SSH_KEY user@\${PRODUCTION_SERVER} 'sudo systemctl restart nginx'
                    """
                }
            }
        }
    }
    
    post {
        always {
            cleanWs()
            emailext subject: "Build ${currentBuild.result}: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
                     body: """Build: ${currentBuild.result}
                             Job: ${env.JOB_NAME}
                             Build Number: ${env.BUILD_NUMBER}
                             Build URL: ${env.BUILD_URL}""",
                     recipientProviders: [[$class: 'DevelopersRecipientProvider']]
        }
    }
}

Shared Libraries

// vars/dockerBuild.groovy
def call(String imageName, String tag = 'latest') {
    sh """
        docker build -t ${imageName}:${tag} .
        docker push ${imageName}:${tag}
    """
}

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

pipeline {
    agent any
    stages {
        stage('Build Docker Image') {
            steps {
                dockerBuild('my-app', env.BUILD_NUMBER)
            }
        }
    }
}

Security & Best Practices

Security Configuration:

  • Enable security
  • Use HTTPS
  • Implement RBAC
  • Secure credentials
  • Regular updates
  • Audit logging

Credentials Management

pipeline {
    agent any
    environment {
        AWS_CREDS = credentials('aws-credentials')
        DOCKER_REGISTRY = credentials('docker-registry')
    }
    stages {
        stage('Deploy') {
            steps {
                withCredentials([
                    usernamePassword(
                        credentialsId: 'app-credentials',
                        usernameVariable: 'APP_USER',
                        passwordVariable: 'APP_PASS'
                    )
                ]) {
                    sh """
                        docker login -u $DOCKER_REGISTRY_USR -p $DOCKER_REGISTRY_PSW
                        aws configure set aws_access_key_id $AWS_CREDS_USR
                        aws configure set aws_secret_access_key $AWS_CREDS_PSW
                        ./deploy.sh --user $APP_USER --password $APP_PASS
                    """
                }
            }
        }
    }
}

Essential Plugins

Core Plugins:

  • Pipeline
  • Git
  • Docker
  • Credentials
  • Configuration as Code
  • Blue Ocean

Testing & Quality:

  • JUnit
  • SonarQube Scanner
  • Code Coverage API
  • Checkstyle

Deployment:

  • Kubernetes
  • AWS
  • Azure
  • SSH Agent

Automation Examples

Multi-Branch Pipeline

// Jenkinsfile for multi-branch pipeline
pipeline {
    agent any
    
    options {
        buildDiscarder(logRotator(numToKeepStr: '5'))
        durabilityHint('PERFORMANCE_OPTIMIZED')
        timeout(time: 1, unit: 'HOURS')
    }
    
    triggers {
        pollSCM('H/15 * * * *')
    }
    
    environment {
        BRANCH_NAME = "${env.BRANCH_NAME}"
        ENVIRONMENT = getBranchEnvironment("${env.BRANCH_NAME}")
    }
    
    stages {
        stage('Build') {
            steps {
                script {
                    def buildNumber = env.BUILD_NUMBER
                    def branchName = env.BRANCH_NAME.replaceAll('/', '-')
                    
                    docker.build("myapp:${branchName}-${buildNumber}")
                }
            }
        }
        
        stage('Test') {
            steps {
                parallel(
                    "Unit Tests": {
                        sh 'npm test'
                    },
                    "Integration Tests": {
                        sh 'npm run test:integration'
                    }
                )
            }
        }
        
        stage('Deploy') {
            when {
                expression { ENVIRONMENT != null }
            }
            steps {
                script {
                    deploy(ENVIRONMENT)
                }
            }
        }
    }
}

def getBranchEnvironment(branch) {
    switch(branch) {
        case 'main':
            return 'production'
        case 'develop':
            return 'staging'
        case ~/^release\/.*$/:
            return 'qa'
        default:
            return null
    }
}

def deploy(environment) {
    echo "Deploying to ${environment}"
    // Deployment logic here
}