Operations 11 min read

Implementing CI/CD Pipelines with Jenkins, GitLab Webhooks, and GitOps for Microservices

This article demonstrates how to build CI and CD pipelines for microservices using Jenkins, GitLab webhooks, and GitOps, covering repository structures, pipeline stages, code snippets, and deployment automation with Helm, including environment configuration.

DevOps Cloud Academy
DevOps Cloud Academy
DevOps Cloud Academy
Implementing CI/CD Pipelines with Jenkins, GitLab Webhooks, and GitOps for Microservices

The guide starts by distinguishing single‑repository and multi‑repository layouts for microservice projects, then outlines the essential steps of a CI pipeline: detecting which service a commit belongs to, checking out the code, compiling, testing, scanning, and building Docker images.

It explains how to use GitLab WebHook to trigger a Jenkins scheduler job. When code is pushed to GitLab, a POST request containing JSON data is sent to Jenkins, where the Generic WebHook Trigger plugin extracts the payload, parses the JSON, and determines the affected service modules.

CI‑Scheduler Job

pipeline {
    agent any

    stages {
        stage("GetData") {
            steps {
                script {
                    echo "${webHookData}"
                    data = readJSON text: "${webHookData}"
                    println(data)
                    env.branchName = data.ref - "refs/heads/"
                    env.commitId = data.checkout_sha
                    env.projectId = data.project_id
                    commits = data["commits"]
                    // Determine changed services
                    changeServices = []
                    for (commit in commits) {
                        // added, modified, removed handling ...
                    }
                }
            }
        }
        stage('DefineService') {
            steps {
                script {
                    services = ['service02', 'service01']
                    for (service in services) {
                        if (changeServices.indexOf(service) != -1) {
                            jobName = "microservicecicd-${service}-service-CI"
                            build job: jobName, wait: false, parameters: [
                                string(name: 'branchName', value: "${env.branchName}"),
                                string(name: 'commitId', value: "${env.commitId}"),
                                string(name: 'projectId', value: "${env.projectId}")
                            ]
                        }
                    }
                }
            }
        }
    }
}

The CI job for each microservice receives three string parameters (branch name, commit ID, project ID) and runs a Jenkinsfile that checks out the specific module, builds the Maven project, runs unit tests, performs SonarQube scanning, and builds & pushes a Docker image.

String branchName = "${env.branchName}"
String moduleName = "${JOB_NAME}".split("/")[1].split("-")[1]
String srcUrl = "http://gitlab.idevops.site/microservicecicd/microservicecicd-demo-service.git"
String commitId = "${env.commitId}"
String projectId = "${env.projectId}"

pipeline {
    agent { node { label "build" } }
    stages {
        stage('GetCode') {
            steps { script { checkout([...]) } }
        }
        stage('Build&Test') {
            steps { script { sh "cd ${moduleName} && mvn clean package" } }
            post { always { junit "${moduleName}/target/surefire-reports/*.xml" } }
        }
        stage('SonarScan') {
            steps { script { /* SonarScanner command */ } }
        }
        stage('BuildImage') {
            steps { script { /* Docker build & push */ } }
        }
    }
}

To extend CI with GitOps, a new stage "PushFile" updates the environment repository's Helm values file after the image is pushed. It fetches the YAML file via GitLab API, modifies the image version and commit fields, encodes the updated content, and commits it back.

stage("PushFile") {
    steps {
        script {
            if ("${env.branchName}".contains("RELEASE-")) {
                env.branchName = "master"
            } else {
                env.branchName = "feature"
            }
            for (i = 0; i < 3; i++) {
                response = GetRepoFile(40, "${moduleName}%2fvalues.yaml", "${env.branchName}")
                yamlData = readYaml text: "${response}"
                yamlData.image.version = "${releaseVersion}-${env.nowDate}"
                yamlData.image.commit = "${commitId}"
                writeYaml file: 'test.yaml', data: yamlData
                newYaml = sh returnStdout: true, script: 'cat test.yaml'
                base64Content = newYaml.bytes.encodeBase64().toString()
                try {
                    UpdateRepoFile(40, "${moduleName}%2fvalues.yaml", base64Content, "${env.branchName}")
                    break
                } catch (e) {
                    sh "sleep 2"
                    continue
                }
            }
        }
    }
}

def HttpReq(reqType, reqUrl, reqBody) { /* ... */ }

def GetRepoFile(projectId, filePath, branchName) { /* ... */ }

def UpdateRepoFile(projectId, filePath, fileContent, branchName) { /* ... */ }

The CD side mirrors the CI approach. A CD‑scheduler job receives GitLab webhook events from the environment repository, extracts the changed services, and triggers corresponding CD jobs.

pipeline {
    agent any
    stages {
        stage('GetCommitService') {
            steps { script { /* parse webhook JSON */ } }
        }
        stage('DefineService') {
            steps { script { /* trigger CD jobs per service */ } }
        }
    }
}

Each CD job checks out the environment code, creates a Kubernetes namespace, and deploys the service with Helm. It attempts a fresh install and falls back to an upgrade if the release already exists, then lists the Helm releases and history.

String serviceName = "${JOB_NAME}".split("-")[1]
String nameSpace = "${JOB_NAME}".split("-")[0].split("/")[-1]

pipeline {
    agent { node { label "k8s" } }
    stages {
        stage('GetCode') { steps { script { checkout([...]) } } }
        stage('HelmDeploy') {
            steps { script { sh "kubectl create ns ${nameSpace}-uat || echo false; helm install ${serviceName} --namespace ${nameSpace}-uat ./... || helm upgrade ${serviceName} --namespace ${nameSpace}-uat ./...; helm list --namespace ${nameSpace}-uat; helm history ${serviceName} --namespace ${nameSpace}-uat" } }
        }
    }
}

Overall, the article provides a complete, end‑to‑end example of automating microservice builds, tests, image publishing, and continuous delivery using Jenkins pipelines, GitLab webhooks, and GitOps practices.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ci/cdMicroservicesDevOpsGitLabJenkins
DevOps Cloud Academy
Written by

DevOps Cloud Academy

Exploring industry DevOps practices and technical expertise.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.