Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Automating Java CI/CD Pipelines with Jenkins, Docker, and Git

Notes 1

Infrastructure Layout

  • Node A (10.0.0.10): CentOS 7, Docker, Harbor, Git Server
  • Node B (10.0.0.11): CentOS 7, Docker, Jenkins, Maven, Git Client

The target deployment environment is Node B.

Git Repository Configuration

On Node A, install Git to act as the central version control server.

yum install git -y
useradd gituser
passwd gituser
su - gituser
mkdir -p repos/java-app.git && cd repos/java-app.git
git --bare init

On Node B, install Git as the client, clone a sample Java project, and push it to the private repository.

yum install git -y
git clone gituser@10.0.0.10:/home/gituser/repos/java-app.git
mv java-app java-app-backup

git clone https://github.com/lizhenliang/tomcat-java-demo
cd tomcat-java-demo

Update the remote origin in .git/config to point to the private Git server:

[remote "origin"]
        url = gituser@10.0.0.10:/home/gituser/repos/java-app.git
        fetch = +refs/heads/*:refs/remotes/origin/*

Commit and push the source code:

git config --global user.name "devadmin"
git config --global user.email "devadmin@internal.net"
git add .
git commit -m 'initial project upload'
git push origin master

Configure passwordless SSH access from Node B (Jenkins) to Node A (Git):

ssh-keygen
ssh-copy-id gituser@10.0.0.10

Tomcat Base Image and Harbor Integration

On Node A, create a Dockerfile to build a custom Tomcat base image.

FROM centos:7
LABEL maintainer="admin"

ARG TOMCAT_VER=9.0.50

RUN yum install -y java-1.8.0-openjdk wget tar gzip && \
    yum clean all && \
    rm -rf /var/cache/yum/*

RUN wget https://archive.apache.org/dist/tomcat/tomcat-9/v${TOMCAT_VER}/bin/apache-tomcat-${TOMCAT_VER}.tar.gz && \
    tar -xzf apache-tomcat-${TOMCAT_VER}.tar.gz && \
    mv apache-tomcat-${TOMCAT_VER} /opt/tomcat && \
    rm -rf apache-tomcat-${TOMCAT_VER}.tar.gz /opt/tomcat/webapps/* && \
    echo "ok" > /opt/tomcat/webapps/health.html && \
    sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' /opt/tomcat/bin/catalina.sh && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENV PATH=${PATH}:/opt/tomcat/bin
WORKDIR /opt/tomcat

EXPOSE 8080
CMD ["catalina.sh", "run"]

Build and push the image to the Harbor registry:

docker build -t tomcat-base:latest -f Dockerfile-tomcat .

Configure Docker to trust the private registry on both nodes by updating /etc/docker/daemon.:

{ "registry-mirrors": ["http://f1361db2.m.daocloud.io"], "insecure-registries": ["http://10.0.0.10"] }

Restart Docker and push the image:

systemctl daemon-reload
systemctl restart docker
docker tag tomcat-base:latest 10.0.0.10/library/tomcat-base:latest
docker login 10.0.0.10
docker push 10.0.0.10/library/tomcat-base:latest

Jenkins Setup and Pipeline Configuration

On Node B, install the JDK and deploy Jenkins using Tomcat.

tar zxf jdk-8u45-linux-x64.tar.gz
mv jdk-8u45-linux-x64 /usr/local/jdk1.8

Set environment variables in /etc/profile:

export JAVA_HOME=/usr/local/jdk1.8
export PATH=$PATH:$JAVA_HOME/bin
source /etc/profile

Deploy the Jenkins WAR file:

wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war
tar zxf apache-tomcat-8.5.32.tar.gz
mv apache-tomcat-8.5.32 /usr/local/tomcat-jenkins
rm -rf /usr/local/tomcat-jenkins/webapps/*
unzip jenkins.war -d /usr/local/tomcat-jenkins/webapps/ROOT
/usr/local/tomcat-jenkins/bin/startup.sh

Access the Jenkins dashboard at http://10.0.0.11:8080, copmlete the initial setup, and install the Pipeline and Git plugins.

Create a new Pipeline project and enable "This project is parameterized" with a string paramter named BRANCH_SPEC (defaulting to master).

Configrue the Pipeline script using the Declarative syntax:

pipeline {
    agent any
    environment {
        HARBOR_URL = '10.0.0.10'
        IMAGE_NAME = 'library/java-webapp'
        HARBOR_CREDS = credentials('harbor-login')
    }
    stages {
        stage('Source Checkout') {
            steps {
                git branch: "${params.BRANCH_SPEC}", 
                    credentialsId: 'git-ssh-key', 
                    url: 'gituser@10.0.0.10:/home/gituser/repos/java-app.git'
            }
        }
        stage('Maven Build') {
            steps {
                sh '/usr/local/maven/bin/mvn clean package -DskipTests'
            }
        }
        stage('Docker Build & Push') {
            steps {
                sh """
                docker build -t ${HARBOR_URL}/${IMAGE_NAME}:${params.BRANCH_SPEC} .
                docker login ${HARBOR_URL} -u ${HARBOR_CREDS_USR} -p ${HARBOR_CREDS_PSW}
                docker push ${HARBOR_URL}/${IMAGE_NAME}:${params.BRANCH_SPEC}
                """
            }
        }
        stage('Deploy Container') {
            steps {
                sh """
                docker rm -f webapp-instance || true
                docker rmi ${HARBOR_URL}/${IMAGE_NAME}:${params.BRANCH_SPEC} || true
                docker run -d --name webapp-instance -p 8081:8080 ${HARBOR_URL}/${IMAGE_NAME}:${params.BRANCH_SPEC}
                """
            }
        }
    }
}

A Dockerfile should exist in the project root to assemble the final application image:

FROM 10.0.0.10/library/tomcat-base:latest
COPY target/*.war /opt/tomcat/webapps/ROOT.war

Deployment Verification

Trigger the Jenkins build with the BRANCH_SPEC parameter. Once completed, verify the running container on Node B.

[root@node-b ~]# docker ps
CONTAINER ID   IMAGE                                      COMMAND             CREATED         STATUS         PORTS                    NAMES
a1b2c3d4e5f6   10.0.0.10/library/java-webapp:master        "catalina.sh run"   2 minutes ago   Up 2 minutes   0.0.0.0:8081->8080/tcp   webapp-instance

Check the application logs:

[root@node-b ~]# docker logs webapp-instance
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/9.0.50
...
INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]

Validate the HTTP response from the mapped port:

[root@node-b ~]# curl -I 10.0.0.11:8081
HTTP/1.1 200 
Content-Type: text/html;charset=utf-8
Content-Length: 2538
Date: Sat, 16 Mar 2019 13:36:13 GMT
Tags: Jenkins

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.