Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Containerized Jenkins Deployment with Docker Compose

Tech May 13 1

Docker Environment Setup (CentOS)

sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
sudo systemctl enable --now docker

Workspace and Configuration Layout

mkdir -p /opt/ci/jenkins
cd /opt/ci/jenkins

Target directory hierarchy:

  • /opt/ci/jenkins/
  • ├── Dockerfile
  • ├── docker-compose.yml
  • ├── data/ # Persistent Jenkins storage
  • ├── ansible_cfg/ # Ansible inventory and roles
  • ├── logs/ # Runtime logs (optional)
  • └── backups/ # Data archives (optional)

Custom Dockerfile

FROM jenkins/jenkins:2.440-lts-jdk21

USER root

RUN apt-get update && apt-get install -y \
    git \
    openssh-client \
    curl \
    wget \
    zip \
    unzip \
    rsync \
    ansible \
    nano \
    ca-certificates \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

USER jenkins

Docker Compose Configuration

version: '3.8'
services:
  ci_server:
    build: .
    user: "root:root"
    networks:
      - ci_net
    container_name: jenkins_ci
    restart: always
    privileged: true
    ports:
      - "9090:8080"
    environment:
      TZ: Asia/Shanghai
    volumes:
      - /opt/ci/jenkins/data:/var/jenkins_home
      - /opt/ci/jenkins/ansible_cfg:/etc/ansible
networks:
  ci_net:

Service Lifecycle Management

# Initialize and start containers
docker compose up -d

# Tear down containers
docker compose down

Jenkins Initializasion Procedure

Retrieving the Administrator Secret

cat /opt/ci/jenkins/data/secrets/initialAdminPassword

Web Interface Access

Navigate to http://<host-ip>:9090 in you're browser.

Setup Wizard

  • Input the unlocked administrator password.
  • Select "Install suggested plugins".
  • Create a new administrative user account.
  • Finalize the configuration.

Essential Plugins for CI/CD

Access via: Manage Jenkins → Plugins

Core Automation Plugins

  • Pipeline
  • Pipeline: Stage View
  • Git
  • GitHub Integration
  • Docker Pipeline
  • Credentials Binding
  • SSH Agent

Alerting Plugins (Optional)

  • DingTalk
  • Email Extension

Declarative Pipeline Blueprint

pipeline {
    agent any

    options {
        skipDefaultCheckout(true)
        timestamps()
    }

    parameters {
        choice(name: 'TARGET_APP', choices: ['project_alpha', 'project_beta'], description: 'Select the application target')
        choice(name: 'GIT_REF', choices: ['main', 'develop'], description: 'Specify the branch or tag')
    }

    environment {
        GIT_REMOTE = 'http://10.0.0.5/org/core_engine.git'
        INVENTORY_PATH = '/etc/ansible/hosts'
        APP_PRIMARY_DIR = '/opt/app/core_main'
        APP_GATEWAY_DIR = '/opt/app/core_gateway'
        TELEGRAM_KEY = credentials('telegram-bot-token')
        TELEGRAM_GROUP = '-1002145419289'
    }

    stages {
        stage('Purge Workspace') {
            steps {
                cleanWs()
            }
        }

        stage('Fetch Source') {
            steps {
                script {
                    echo "Cloning repository: ${env.GIT_REMOTE}, ref: ${params.GIT_REF}"
                    git branch: params.GIT_REF,
                        url: env.GIT_REMOTE,
                        credentialsId: 'gitlab-access'
                }
            }
        }

        stage('Archive Artifacts') {
            steps {
                script {
                    env.BUILD_TS = sh(script: 'date +%Y%m%d%H%M', returnStdout: true).trim()
                    env.ARCHIVE_PATH = "/tmp/${params.TARGET_APP}_${env.BUILD_TS}.zip"

                    sh """
                    echo "Compressing workspace into ${ARCHIVE_PATH}"
                    cd "${WORKSPACE}"
                    zip -rq "${ARCHIVE_PATH}" . \
                      -x config/local_settings.lua \
                      -x scripts/run_daemon.lua \
                      -x start.sh
                    echo "Compression finished"
                    """
                }
            }
        }

        stage('Distribute Bundle') {
            steps {
                script {
                    sh """
                    echo "Transferring archive to target nodes"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m copy -a "src=${ARCHIVE_PATH} dest=/tmp/"
                    """
                }
            }
        }

        stage('Unpack Release') {
            steps {
                script {
                    sh """
                    echo "Extracting archive on remote hosts"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "unzip -o ${ARCHIVE_PATH} -d ${APP_PRIMARY_DIR}" > /dev/null
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "unzip -o ${ARCHIVE_PATH} -d ${APP_GATEWAY_DIR}" > /dev/null
                    """
                }
            }
        }

        stage('Adjust Permissions & Config') {
            steps {
                script {
                    sh """
                    echo "Modifying gateway port configuration"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -b -m replace -a "path=${APP_GATEWAY_DIR}/scripts/bootstrap.lua regexp='5808' replace='5809'"

                    echo "Setting execution privileges"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "chmod 755 ${APP_PRIMARY_DIR}/engine_bin"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "chmod 755 ${APP_GATEWAY_DIR}/engine_bin"

                    echo "Assigning ownership to service user"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "chown -R appuser:appgroup ${APP_PRIMARY_DIR}"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "chown -R appuser:appgroup ${APP_GATEWAY_DIR}"
                    """
                }
            }
        }

        stage('Rollout Services') {
            steps {
                script {
                    sh """
                    echo "Restarting application daemons"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "supervisorctl restart core_main"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "supervisorctl restart core_gateway"
                    """
                }
            }
        }

        stage('Validate Process Status') {
            steps {
                script {
                    sh """
                    echo "Verifying process existence"
                    ansible -i "${INVENTORY_PATH}" "${params.TARGET_APP}" \
                      -m shell -a "pgrep engine_bin"
                    """
                }
            }
        }
    }

    post {
        always {
            echo "Pipeline execution concluded"
        }
    }
}

Tags: docker

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

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