Containerized Jenkins Deployment with Docker Compose
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"
}
}
}