Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Creating A Basic Jenkins Pipeline

This tutorial is tied to this template GitHub repository I created, for projects that require a Jenkins Pipeline.

Required Plugins

Steps

Create your repository, or clone the the template on GitHub I created.

Create one of each of the following files (you will see they already exist in the template repository):

  • Jenkinsfile
  • Dockerfile

Create branches for production and staging

git branch production && git push -u origin production
git branch staging && git push -u origin staging

Create a project access token and use that in the url for authentication before plugging into jenkins.

Fill the Jenkinsfile with the following content:

pipeline {
    agent none

    stages {
        stage("build") {
            agent {
                docker { image 'docker:dind' }
            }
            steps {
                echo "building docker image..."

                configFileProvider([configFile(fileId: 'fc6ad96f-c364-4b59-b24b-5eddee5b41f6', variable: 'BRANCH_SETTINGS')]) {
                    echo "Branch ${env.BRANCH_NAME}"
                    echo "Branch Settings: ${BRANCH_SETTINGS}"

                    script {
                        def config = readJSON file:"$BRANCH_SETTINGS"
                        def branchConfig = config."${env.BRANCH_NAME}"

                        if (branchConfig) {
                            echo "using config for branch ${env.BRANCH_NAME}"

                            def DOCKER_REGISTRY = branchConfig.DOCKER_REGISTRY
                            def dockerImage = docker.build(branchConfig.IMAGE_NAME)

                            docker.withRegistry("https://${branchConfig.DOCKER_REGISTRY}", 'docker-registry-credentials') {
                                dockerImage.push("${env.BUILD_NUMBER}")
                                dockerImage.push("latest")
                            }
                        }
                        else {
                            error("Build failed because failed to fetch settings for branch")
                        }
                    }
                }
            }
        }

        stage("deploy") {
            agent {
                docker { image 'ubuntu:focal' }
            }
            steps {
                sh "apt-get update && apt-get install ssh -y"

                configFileProvider([configFile(fileId: 'fc6ad96f-c364-4b59-b24b-5eddee5b41f6', variable: 'BRANCH_SETTINGS')]) {
                    echo "Branch ${env.BRANCH_NAME}"
                    echo "Branch Settings: ${BRANCH_SETTINGS}"

                    script {
                        def config = readJSON file:"$BRANCH_SETTINGS"

                        def branchConfig = config."${env.BRANCH_NAME}"

                        if (branchConfig) {
                            echo "using config for branch ${env.BRANCH_NAME}"

                            def SSH_USER = branchConfig.SSH_USER
                            def DOCKER_HOST = branchConfig.DOCKER_HOST
                            def DOCKER_REGISTRY = branchConfig.DOCKER_REGISTRY
                            def IMAGE_NAME = branchConfig.IMAGE_NAME

                            sshagent(credentials : ['master.pem']) {
                                withCredentials([usernamePassword(credentialsId: 'docker-registry-credentials', passwordVariable: 'DOCKER_REGISTRY_PASSWORD', usernameVariable: 'DOCKER_REGISTRY_USER')]) {
                                    sh 'ssh -o StrictHostKeyChecking=no ' + branchConfig.SSH_USER + '@' + branchConfig.DOCKER_HOST + ' "docker login -u ' + DOCKER_REGISTRY_USER + ' -p ' + DOCKER_REGISTRY_PASSWORD + ' ' + branchConfig.DOCKER_REGISTRY + '"'
                                    sh 'ssh -o StrictHostKeyChecking=no ' + branchConfig.SSH_USER + '@' + branchConfig.DOCKER_HOST + ' "docker pull ' + branchConfig.DOCKER_REGISTRY + '/' + IMAGE_NAME + ' && docker kill hello-world || true && docker rm hello-world || true && docker run -d --name hello-world -p80:80 ' + branchConfig.DOCKER_REGISTRY + '/' + branchConfig.IMAGE_NAME + '"'
                                }
                            }
                        }
                        else {
                            error("Build failed because failed to fetch settings for branch")
                        }
                    }
                }

            }
        }

    }
}

In your Jenkins Server, create a new multi-branch project for this codebase.

Branch Settings Config File

Create a config file in that project with the name: my-config-file.json, or use an alternative name, and update the Jenkinsfile in this codebase accordingly.

That config file should have the following contents (a block per environment branch).

{
    "staging": {
        "DOCKER_HOST": "my.staging-server.com",
        "IMAGE_NAME": "my-docker-image",
        "DOCKER_REGISTRY": "docker-registry.mydomain.com",
        "SSH_USER": "programster"
    },
    "production": {
        "DOCKER_HOST": "my.production-server.com",
        "IMAGE_NAME": "my-docker-image",
        "DOCKER_REGISTRY": "docker-registry.mydomain.com",
        "SSH_USER": "programster"
    }
}

Create Credentials

Create a credential of type usernamePassword called "docker-registry-credentials" for which you provide the username and password to your docker registry.

Create a credential of type file called master.pem that contains the private SSH key that will allow logging into the remote server (DOCKER_HOST).

Configure Triggering From GitHub

Now we have a pipeline that will build and deploy our project, it would be ideal if this would get automatically triggered whenever a change was made. In order to do this, follow this post on how to Integrate Jenkins Multibranch Pipeline With Git / GitHub

Last updated: 29th June 2021
First published: 29th June 2021