Implementing CI/CD Pipelines with Jenkins and Docker

Understanding Continuous Integration and Delivery

Software Development Life Cycle (SDLC)

The SDLC represents the entire workflow of planning, coding, testing, and releasing software. Typical phases include:

  1. Requirement Analysis: Gathering feasibility data, budget estimates, and project goals.
  2. System Design: Defining architecture, UI/UX layouts, and project milestones.
  3. Implementation: Developers write code based on design specs.
  4. Verification: QA teams perform functional, load, and security testing.
  5. Maintenance: Ongoing updates, bug fixes, and feature additions based on user feedback.

Waterfall vs. Agile Models

The Waterfall model follows a strictly linear path. While simple to understand, it creates heavy documentation overhead and delivers the product only at the very end, increasing risk.

Pros Cons
Simple structure High documentation overhead
Clear phase transitions High risk due to late delivery
Defined checkpoints Poor adaptability to changing requirements

Agile development focuses on iterative and incremental progress. Enstead of one massive release, the work is broken into small cycles (sprints).

  • Iterative: Repeatedly refining the product through short cycles (e.g., building a simple prototype before a complex final version).
  • Incremental: Delivering functional pieces one by one (e.g., releasing one feature module at a time).

Benefits include early return on investment (ROI) and reduced risk since feedback is gathered early.

Continuous Integration (CI) Defined

CI is the practice of merging developer code into a shared repository multiple times a day.

Core Components:

  • Version Control System: Git or SVN acting as the source of truth.
  • Automated Pipeline: Scripts that handle code checkout, compilation, testing, and packaging without manual intervention.
  • CI Server: Tools like Jenkins that orchestrate the workflow.

Advantages:

  • Early detection of bugs, reducing fix costs.
  • Automated repetitive tasks (build/test).
  • Always having a deployable artifact ready.

Setting Up the Jenkins Environment

Installing Jenkins

Option A: YUM Repository

wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo --no-check-certificate
rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
yum -y install jenkins

Option B: RPM Package

wget https://pkg.jenkins.io/redhat-stable/jenkins-2.190.1-1.1.noarch.rpm
rpm -ivh jenkins-2.190.1-1.1.noarch.rpm

Configuration: Edit /etc/sysconfig/jenkins:

JENKINS_USER="root"
JENKINS_PORT="16060"

Set permissions and restart:

chown -R root:root /var/lib/jenkins
chown -R root:root /var/cache/jenkins
systemctl restart jenkins

If Java is missing, create a symlink:

ln -s /usr/local/jdk/bin/java /usr/bin/java

Essential Plugins

Install these via Manage Jenkins > Manage Plugins:

  • Maven Integration: For building Java projects.
  • Docker: To manage container lifecycles.
  • GitLab: For repository integration.
  • Publish Over SSH: To deploy artifacts to remote servers.

Tooling Setup (Git, Maven, Docker)

Git Installation:

yum -y install git
git version

Maven Configuration: Extract the binary and set environment variables in /etc/profile:

export MAVEN_HOME=/usr/local/maven/apache-maven-3.3.9
export PATH=$PATH:$MAVEN_HOME/bin

Docker Installation:

yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum -y install docker-ce
systemctl enable docker && systemctl start docker

Private Docker Registry

Launch a local registry to store images:

docker run -d -p 5000:5000 --name local_registry -v /usr/local/docker/registry:/var/lib/registry registry:latest

Configure the Docker client to trust the insecure registry by editing /lib/systemd/system/docker.service:

ExecStart=/usr/bin/dockerd --insecure-registry 192.168.200.100:5000

Reload and restart:

systemctl daemon-reload
systemctl restart docker.service

Production Pipeline Configuration

Infrastructure Services

MySQL Deployment:

docker pull mysql:5.7
docker run -di --name=mysql_db -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7

Job Scheduler (XXL-Job):

  1. Update application.properties to point to the MySQL instance.
  2. Create a Dockerfile:
FROM java:8u111
COPY xxl-job-admin-2.2.0-SNAPSHOT.jar /app.jar
CMD java -jar /app.jar
EXPOSE 8888
  1. Build and run:
docker build -t xxl-job-admin:2.2.0 .
docker run -di --name=xxl-job-admin -p 8888:8888 xxl-job-admin:2.2.0

Multi-Environment Strategy

Define profiles in the parent pom.xml to switch between Dev, Test, and Prod.

<profiles>
    <profile>
        <id>dev</id>
        <activation><activeByDefault>true</activeByDefault></activation>
        <build>
            <filters><filter>maven_dev.properties</filter></filters>
        </build>
    </profile>
    <profile>
        <id>prod</id>
        <build>
            <filters><filter>maven_prod.properties</filter></filters>
        </build>
    </profile>
</profiles>

Example maven_prod.properties:

datasource.url=jdbc:mysql://52.82.20.5:33066/leadnews_admin?useUnicode=true
nacos.server=192.168.200.130:8848

Spring Boot application.yml usage:

spring:
  datasource:
    url: ${datasource.url}
    username: ${datasource.username}

Build with a specific profile:

mvn package -P prod

Dockerizing Microservices

Maven Plugin Configuration (pom.xml):

<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>dockerfile-maven-plugin</artifactId>
    <version>1.3.6</version>
    <configuration>
        <repository>docker_storage/${project.artifactId}</repository>
        <buildArgs>
            <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
        </buildArgs>
    </configuration>
</plugin>

Generic Dockerfile:

FROM java:8
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENV JAVA_OPTS="-Xms256m -Xmx512m"
ENTRYPOINT java ${JAVA_OPTS} -jar /app.jar

Jenkins Pipeline Job

  1. Base Dependency Build: Create a Jenkins job to pull code and run clean install -Dmaven.test.skip=true.
  2. Service Deployment (e.g., leadnews-admin):
    • Build Step (Maven): clean install -Dmaven.test.skip=true -P prod dockerfile:build -f heima-leadnews-admin/pom.xml
    • Post-build Shell Script: Clean up old containers and run new ones using host network mode for connectivity.
if [ -n "$(docker ps -a -f name=heima-$JOB_NAME --format '{{.ID}}' )" ]; then
  docker rm -f $(docker ps -a -f name=heima-$JOB_NAME --format '{{.ID}}' )
fi
docker image prune -f
docker run -d --net=host --name heima-$JOB_NAME docker_storage/heima-$JOB_NAME

Remote Deployment via Registry

To deploy to a remote server, push the image to the private registry first.

Push Script:

image_tag=$docker_registry/docker_storage/heima-$JOB_NAME
docker tag docker_storage/heima-$JOB_NAME $image_tag
docker push $image_tag
docker rmi $image_tag

Remote Pull & Run Script:

docker pull $docker_registry/docker_storage/heima-$JOB_NAME
docker rm -f $(docker ps -a -f name=heima-$JOB_NAME --format '{{.ID}}' )
docker run -d --net=host --name heima-$JOB_NAME $docker_registry/docker_storage/heima-$JOB_NAME

Frontend Deployment (Nginx)

  1. Install Nginx from source or repo.
  2. Build Frontend: Modify API gateway paths and run build scripts to generate the dist folder.
  3. Nginx Config: Serve static files and proxy API requests.
http {
    upstream backend_gateway {
        server localhost:6001;
    }

    server {
        listen 80;
        location / {
            root /root/workspace/admin/dist;
            index index.html;
        }
        location ~/service_6001/(.*) {
            proxy_pass http://backend_gateway/$1;
        }
    }
}

Build Triggers

Configure Jenkins to build automatically:

  • Remote Trigger: Access http://<jenkins-url>/job/<job-name>/build?token=<token>.
  • Cron Jobs: Use syntax like H 18 * * * (Daily at 6 PM) in the Build Periodically section.
  • Poll SCM: Checks the repository for changes at intervals (not recommended due to overhead).

Tags: Jenkins docker CI/CD devops microservices

Posted on Sat, 26 Sep 2026 16:48:40 +0000 by metuin