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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
DevOps Cloud Academy
Exploring industry DevOps practices and technical expertise.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
