Cloud Native 16 min read

Implementing DevOps with Container Orchestration: CI/CD Pipeline, Docker Image Optimization, and Automated Operations

This article explains how to build a DevOps workflow using container orchestration, covering agile development with Docker image slimming, a Jenkins‑based CI/CD pipeline, simplified Gitflow, container monitoring with cAdvisor‑InfluxDB‑Grafana, and auto‑scaling via Marathon, while separating automated deployment from production release.

Hujiang Technology
Hujiang Technology
Hujiang Technology
Implementing DevOps with Container Orchestration: CI/CD Pipeline, Docker Image Optimization, and Automated Operations

With the rise of DevOps and SRE, developers are moving from traditional deployment to a continuous loop model, which the article divides into three parts: agile development, continuous integration and delivery, and automated operations.

Agile Development emphasizes speed and micro‑services, recommending Docker image slimming using a Dockerfile based on java:8-jre-alpine and including timezone configuration. The resulting image is about 80 MB and starts in ~5 seconds.

FROM java:8-jre-alpine
# add timezone and set to Shanghai
RUN apk --update add --no-cache tzdata
ENV TZ=Asia/Shanghai

RUN mkdir -p /app/log
COPY ./target/xxx.jar /app/xxx.jar
EXPOSE 9999
VOLUME ["/app/log"]
WORKDIR /app/

ENTRYPOINT ["java","-Xms2048m","-Xmx2048m","-Xss512k","-jar","xxx.jar"]
CMD []

All environment configuration files are packaged into the image, enabling the same image to run on any Docker host. A typical project structure is shown.

├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   │       ├── application.yaml
│   │       ├── application-dev.yaml
│   │       ├── application-qa.yaml
│   │       ├── application-yz.yaml
│   │       ├── application-prod.yaml
│   │       └── logback.xml
│   └── test
├── scripts
│   ├── Dockerfile
│   └── InitDB.sql
└── pom.xml

Continuous Integration & Delivery uses Jenkins Pipeline with plugins (Pipeline, Git, SonarQube, Docker Pipeline, Marathon). The pipeline stages include checkout, Maven build, SonarQube analysis, Docker image build & push, deployment to test, automated tests, optional manual tests, and production deployment.

stage('Check out')
    git branch: "dev", credentialsId: "deploy-key", url: gitUrl

stage('Build')
    sh "${mvnHome}/bin/mvn -U clean install"

stage('SonarQube analysis')
    def scannerHome = tool 'SonarQube.Scanner-2.8'
    withSonarQubeEnv('SonarQube-Prod') {
        sh "${scannerHome}/bin/sonar-scanner -e -Dsonar.links.scm=${gitUrl} -Dsonar.sources=. -Dsonar.projectKey=lms-barrages -Dsonar.projectName=xxx"
    }

stage('Build image')
    docker.withRegistry('https://dockerhub.xxx.com','dockerhub-login') {
        docker.build('dockerhub.xxx.com/xxxx').push('test')
    }

stage('Deploy on Test')
    sh "mkdir -pv deploy"
    dir('./deploy') {
        git branch: 'dev', credentialsId: 'deploy-key', url: '[email protected]:lms/xxx-deploy.git'
        marathon docker: imageName, dockerForcePull: true, forceUpdate: true, url: marathon_url, filename: 'qa-deploy.json'
    }

stage('Test')
    parallel(
        autoTests: {
            sh "docker run -it --rm -v $PWD:/code nosetests -s -v -c conf\\run\\api_test.cfg --attr safeControl=1"
        },
        manualTests: { sleep 30000 }
    )

stage('Deploy on Prod')
    input "Do tests OK?"
    dir('./deploy') {
        marathon docker: imageName, dockerForcePull: true, forceUpdate: true, url: 'http://marathon-prod', filename: 'prod-deploy.json'
    }

The article also proposes a simplified Gitflow with three branches (master, dev, feature) to reduce merge overhead for small teams.

Automated Operations covers container monitoring (cAdvisor + InfluxDB + Grafana) and auto‑scaling. Metrics from cAdvisor are stored in InfluxDB and visualized in Grafana. An auto‑scaler reads metrics and uses the Marathon API to scale services when CPU or memory usage is high.

node('docker-qa'){
    if (ReleaseVersion=="") { echo "Release version cannot be empty"; return }
    stage('Prepare image') {
        def moduleName = "${ApplicationModule}".toLowerCase()
        def resDockerImage = "${imageName}:latest"
        def desDockerImage = "${imageName}:${ReleaseVersion}"
        if (GenDockerVersion=="true") {
            sh "docker pull ${resDockerImage}"
            sh "docker tag ${resDockerImage} ${desDockerImage}"
            sh "docker push ${desDockerImage}"
            sh "docker rmi -f ${resDockerImage} ${desDockerImage}"
        }
    }
    stage('Deploy on Mesos') {
        git branch: 'dev', credentialsId: 'deploy-key', url: '[email protected]:lms/xxx-test.git'
        // determine marathon_url based on DeployDC and DeployEnv
        marathon docker: imageName, dockerForcePull: true, forceUpdate: true, url: marathon_url, filename: "${DeployEnv}-deploy.json"
    }
}

Finally, the article stresses separating automated deployment from production release, assigning appropriate permissions, and acknowledges that the presented examples may need adaptation for different environments.

CI/CDAuto ScalingJenkinsContainer Orchestration
Hujiang Technology
Written by

Hujiang Technology

We focus on the real-world challenges developers face, delivering authentic, practical content and a direct platform for technical networking among developers.

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.