๐ AlmaLinux CI/CD: Complete DevOps Pipeline Guide with Jenkins, GitLab & Docker
Hey there, DevOps champion! ๐ Ready to transform your development workflow from manual chaos into a smooth, automated pipeline that deploys code like magic? Today weโre building a complete CI/CD pipeline on AlmaLinux that will make your team more productive and your deployments bulletproof! ๐ช
Whether youโre a solo developer or part of a large team, this guide will turn your AlmaLinux system into a DevOps powerhouse that automates everything from code commits to production deployments! ๐
๐ค Why is CI/CD Important?
Picture this: youโre still manually testing, building, and deploying code while your competitors are shipping features 10x faster with automated pipelines! ๐ฑ Without CI/CD, youโre basically trying to win a Formula 1 race with a bicycle!
Hereโs why CI/CD on AlmaLinux is absolutely game-changing:
- โก Lightning-Fast Deployments - From code to production in minutes, not hours
- ๐ Reliable Quality - Automated testing catches bugs before customers do
- ๐ Consistent Builds - Every deployment works exactly the same way
- ๐ Instant Feedback - Know immediately if something breaks
- ๐ก๏ธ Rollback Safety - Easy to undo changes if something goes wrong
- ๐ฅ Team Collaboration - Everyone follows the same proven process
- ๐ Continuous Improvement - Metrics and insights to optimize your workflow
- ๐ฏ Focus on Features - Spend time coding, not on manual deployment tasks
๐ฏ What You Need
Before we start building your CI/CD empire, letโs make sure you have everything ready:
โ AlmaLinux 9.x system (with sufficient resources) โ Git repository (GitHub, GitLab, or Bitbucket) โ Docker installed and running properly โ Internet connection for downloading tools โ sudo privileges for system configuration โ Basic understanding of Git workflows โ Sample application to deploy (weโll create one!) โ Patience and enthusiasm for automation! ๐ค
๐ Step 1: Install Jenkins CI/CD Server
Letโs start by setting up Jenkins as our primary CI/CD orchestrator! ๐ฏ
# Update the system first
sudo dnf update -y
# Install Java (required for Jenkins)
sudo dnf install -y java-17-openjdk java-17-openjdk-devel
# Add Jenkins repository
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
# Install Jenkins
sudo dnf install -y jenkins
# Start and enable Jenkins
sudo systemctl start jenkins
sudo systemctl enable jenkins
# Check Jenkins status
sudo systemctl status jenkins
# Get initial admin password
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Expected output:
โ jenkins.service - Jenkins Continuous Integration Server
Loaded: loaded (/lib/systemd/system/jenkins.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2025-09-17 10:00:00 UTC; 30s ago
Perfect! Jenkins is running on port 8080! ๐
๐ง Step 2: Configure Jenkins with Essential Plugins
Now letโs configure Jenkins with the plugins we need for a complete pipeline:
# Install additional tools for our pipeline
sudo dnf install -y git maven nodejs npm python3-pip
# Configure firewall for Jenkins
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
# Create Jenkins user for Docker access
sudo usermod -aG docker jenkins
sudo systemctl restart jenkins
Access Jenkins web interface at http://your-server:8080
and install these essential plugins:
- Pipeline - For pipeline as code
- Git - For Git integration
- Docker Pipeline - For Docker integration
- Blue Ocean - For modern UI
- Workspace Cleanup - For maintaining clean workspaces
- Build Timeout - For preventing stuck builds
Create a sample Jenkins pipeline script:
# Create Jenkinsfile
cat > Jenkinsfile << 'EOF'
pipeline {
agent any
environment {
DOCKER_IMAGE = 'my-app'
DOCKER_TAG = "${BUILD_NUMBER}"
REGISTRY = 'localhost:5000'
}
stages {
stage('Checkout') {
steps {
echo '๐ Checking out source code...'
checkout scm
}
}
stage('Build') {
steps {
echo '๐จ Building application...'
script {
if (fileExists('package.json')) {
sh 'npm install'
sh 'npm run build'
} else if (fileExists('pom.xml')) {
sh 'mvn clean package'
} else {
sh 'echo "No specific build tool detected, using Docker"'
}
}
}
}
stage('Test') {
steps {
echo '๐งช Running tests...'
script {
if (fileExists('package.json')) {
sh 'npm test'
} else if (fileExists('pom.xml')) {
sh 'mvn test'
} else {
sh 'echo "No tests found, skipping"'
}
}
}
}
stage('Docker Build') {
steps {
echo '๐ณ Building Docker image...'
script {
def image = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
docker.withRegistry('http://localhost:5000') {
image.push()
image.push('latest')
}
}
}
}
stage('Deploy') {
steps {
echo '๐ Deploying application...'
sh '''
docker stop my-app || true
docker rm my-app || true
docker run -d --name my-app -p 3000:3000 ${REGISTRY}/${DOCKER_IMAGE}:${DOCKER_TAG}
'''
}
}
}
post {
always {
echo '๐งน Cleaning up workspace...'
cleanWs()
}
success {
echo 'โ
Pipeline completed successfully!'
}
failure {
echo 'โ Pipeline failed!'
}
}
}
EOF
Excellent! Your Jenkins pipeline is configured! ๐
๐ Step 3: Set Up GitLab CI/CD
Letโs also set up GitLab CI/CD as an alternative/complementary solution:
# Install GitLab Runner
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
sudo dnf install -y gitlab-runner
# Register GitLab Runner (you'll need your GitLab token)
# sudo gitlab-runner register
# Create GitLab CI configuration
cat > .gitlab-ci.yml << 'EOF'
# GitLab CI/CD Pipeline Configuration
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE
DOCKER_TAG: $CI_COMMIT_SHORT_SHA
stages:
- build
- test
- security
- deploy
# Build stage
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- echo "๐จ Building application..."
- docker build -t $DOCKER_IMAGE:$DOCKER_TAG .
- docker tag $DOCKER_IMAGE:$DOCKER_TAG $DOCKER_IMAGE:latest
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker push $DOCKER_IMAGE:$DOCKER_TAG
- docker push $DOCKER_IMAGE:latest
only:
- main
- develop
# Test stage
test:
stage: test
image: node:16-alpine
script:
- echo "๐งช Running tests..."
- npm install
- npm run test:unit
- npm run test:integration
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
# Security scanning
security_scan:
stage: security
image: docker:latest
services:
- docker:dind
script:
- echo "๐ Running security scans..."
- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image $DOCKER_IMAGE:$DOCKER_TAG
allow_failure: true
# Deploy to staging
deploy_staging:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- echo "๐ Deploying to staging..."
- curl -X POST "$STAGING_WEBHOOK_URL" -H "Content-Type: application/json" -d "{\"image\":\"$DOCKER_IMAGE:$DOCKER_TAG\"}"
environment:
name: staging
url: https://staging.example.com
only:
- develop
# Deploy to production
deploy_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- echo "๐ Deploying to production..."
- curl -X POST "$PRODUCTION_WEBHOOK_URL" -H "Content-Type: application/json" -d "{\"image\":\"$DOCKER_IMAGE:$DOCKER_TAG\"}"
environment:
name: production
url: https://example.com
when: manual
only:
- main
EOF
Create a local Docker registry for testing:
# Set up local Docker registry
docker run -d -p 5000:5000 --name local-registry registry:2
# Test the registry
curl http://localhost:5000/v2/_catalog
Fantastic! Your GitLab CI/CD is ready to rock! ๐ฏ
โ Step 4: Create Sample Application
Letโs create a sample Node.js application to demonstrate our pipeline:
# Create sample application directory
mkdir -p /opt/sample-app
cd /opt/sample-app
# Initialize Node.js application
npm init -y
# Install dependencies
npm install express helmet compression morgan
# Create main application file
cat > app.js << 'EOF'
const express = require('express');
const helmet = require('helmet');
const compression = require('compression');
const morgan = require('morgan');
const app = express();
const PORT = process.env.PORT || 3000;
// Security and performance middleware
app.use(helmet());
app.use(compression());
app.use(morgan('combined'));
app.use(express.json());
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
version: process.env.APP_VERSION || '1.0.0'
});
});
// Main endpoint
app.get('/', (req, res) => {
res.json({
message: '๐ Welcome to AlmaLinux CI/CD Demo App!',
environment: process.env.NODE_ENV || 'development',
version: process.env.APP_VERSION || '1.0.0'
});
});
// API endpoint
app.get('/api/status', (req, res) => {
res.json({
api: 'running',
database: 'connected',
cache: 'available',
services: ['auth', 'payment', 'notification']
});
});
// Start server
app.listen(PORT, () => {
console.log(`๐ Server running on port ${PORT}`);
console.log(`๐ Health check: http://localhost:${PORT}/health`);
});
module.exports = app;
EOF
# Create test file
mkdir -p test
cat > test/app.test.js << 'EOF'
const request = require('supertest');
const app = require('../app');
describe('Application Tests', () => {
test('Health check endpoint', async () => {
const response = await request(app).get('/health');
expect(response.status).toBe(200);
expect(response.body.status).toBe('healthy');
});
test('Main endpoint', async () => {
const response = await request(app).get('/');
expect(response.status).toBe(200);
expect(response.body.message).toContain('CI/CD Demo');
});
test('API status endpoint', async () => {
const response = await request(app).get('/api/status');
expect(response.status).toBe(200);
expect(response.body.api).toBe('running');
});
});
EOF
# Install test dependencies
npm install --save-dev jest supertest
# Update package.json scripts
cat > package.json << 'EOF'
{
"name": "almalinux-cicd-demo",
"version": "1.0.0",
"description": "Sample app for CI/CD pipeline demonstration",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "NODE_ENV=development node app.js",
"test": "jest",
"test:watch": "jest --watch",
"build": "echo 'Build completed successfully'",
"lint": "echo 'Linting completed'"
},
"dependencies": {
"express": "^4.18.2",
"helmet": "^7.0.0",
"compression": "^1.7.4",
"morgan": "^1.10.0"
},
"devDependencies": {
"jest": "^29.6.1",
"supertest": "^6.3.3"
},
"jest": {
"testEnvironment": "node"
}
}
EOF
# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application files
COPY . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Change ownership
RUN chown -R nextjs:nodejs /app
USER nextjs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Start application
CMD ["npm", "start"]
EOF
# Create .dockerignore
cat > .dockerignore << 'EOF'
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.nyc_output
test
EOF
Perfect! Your sample application is ready for the pipeline! ๐
๐ฎ Quick Examples
Example 1: Multi-Stage Pipeline with Quality Gates
# Create advanced pipeline configuration
cat > advanced-pipeline.yml << 'EOF'
pipeline:
name: "Advanced CI/CD Pipeline"
triggers:
- push:
branches: [main, develop]
- pull_request:
branches: [main]
variables:
NODE_VERSION: "18"
DOCKER_IMAGE: "my-app"
QUALITY_GATE_COVERAGE: "80"
stages:
- name: "Code Quality"
jobs:
- name: "Lint and Format"
script:
- npm install
- npm run lint
- npm run format:check
- name: "Security Scan"
script:
- npm audit --audit-level high
- npm run security:scan
- name: "Build and Test"
jobs:
- name: "Unit Tests"
script:
- npm run test:unit
- npm run test:coverage
artifacts:
- coverage/
quality_gates:
- coverage: ">= 80%"
- name: "Integration Tests"
script:
- docker-compose up -d test-db
- npm run test:integration
- docker-compose down
- name: "Build"
jobs:
- name: "Docker Build"
script:
- docker build -t $DOCKER_IMAGE:$BUILD_NUMBER .
- docker tag $DOCKER_IMAGE:$BUILD_NUMBER $DOCKER_IMAGE:latest
- name: "Deploy"
jobs:
- name: "Deploy to Staging"
environment: staging
script:
- ./scripts/deploy.sh staging $BUILD_NUMBER
when: branch == 'develop'
- name: "Deploy to Production"
environment: production
script:
- ./scripts/deploy.sh production $BUILD_NUMBER
when:
- branch == 'main'
- manual_approval: true
EOF
This creates a comprehensive quality-gated pipeline! ๐ฏ
Example 2: Blue-Green Deployment Script
# Create blue-green deployment script
cat > scripts/blue-green-deploy.sh << 'EOF'
#!/bin/bash
# Blue-Green Deployment Script
set -e
ENVIRONMENT=$1
VERSION=$2
APP_NAME="my-app"
if [ -z "$ENVIRONMENT" ] || [ -z "$VERSION" ]; then
echo "Usage: $0 <environment> <version>"
exit 1
fi
echo "๐ Starting Blue-Green deployment for $APP_NAME v$VERSION to $ENVIRONMENT"
# Determine current active color
CURRENT_COLOR=$(docker ps --filter "name=${APP_NAME}-" --format "{{.Names}}" | grep -E "(blue|green)" | head -1 | cut -d'-' -f2)
NEW_COLOR="blue"
if [ "$CURRENT_COLOR" = "blue" ]; then
NEW_COLOR="green"
fi
echo "๐ Current active: $CURRENT_COLOR, Deploying to: $NEW_COLOR"
# Deploy new version
echo "๐ Deploying new version..."
docker run -d \
--name "${APP_NAME}-${NEW_COLOR}" \
--network app-network \
-e NODE_ENV=$ENVIRONMENT \
-e APP_VERSION=$VERSION \
my-app:$VERSION
# Health check
echo "๐ฅ Performing health checks..."
sleep 10
for i in {1..10}; do
if docker exec "${APP_NAME}-${NEW_COLOR}" curl -f http://localhost:3000/health; then
echo "โ
Health check passed"
break
fi
echo "โณ Waiting for application to be ready... ($i/10)"
sleep 5
done
# Switch traffic
echo "๐ Switching traffic to $NEW_COLOR deployment..."
docker exec nginx-proxy nginx -s reload
# Cleanup old deployment
if [ -n "$CURRENT_COLOR" ]; then
echo "๐งน Cleaning up old deployment..."
sleep 30 # Grace period
docker stop "${APP_NAME}-${CURRENT_COLOR}" || true
docker rm "${APP_NAME}-${CURRENT_COLOR}" || true
fi
echo "๐ Blue-Green deployment completed successfully!"
EOF
chmod +x scripts/blue-green-deploy.sh
This enables zero-downtime deployments! ๐
Example 3: Monitoring and Alerting Integration
# Create monitoring integration
cat > scripts/monitor-pipeline.sh << 'EOF'
#!/bin/bash
# Pipeline Monitoring and Alerting
PIPELINE_NAME="$1"
BUILD_STATUS="$2"
BUILD_NUMBER="$3"
BUILD_URL="$4"
# Metrics collection
send_metrics() {
local metric_name="$1"
local metric_value="$2"
local labels="$3"
# Send to Prometheus (example)
curl -X POST http://localhost:9091/metrics/job/cicd \
-d "${metric_name}{${labels}} ${metric_value}"
}
# Slack notification
send_slack_notification() {
local message="$1"
local color="$2"
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\", \"color\":\"$color\"}" \
$SLACK_WEBHOOK_URL
}
# Process pipeline results
case "$BUILD_STATUS" in
"SUCCESS")
send_metrics "pipeline_builds_total" "1" "status=success,pipeline=$PIPELINE_NAME"
send_slack_notification "โ
Pipeline $PIPELINE_NAME #$BUILD_NUMBER completed successfully! ๐" "good"
;;
"FAILURE")
send_metrics "pipeline_builds_total" "1" "status=failure,pipeline=$PIPELINE_NAME"
send_slack_notification "โ Pipeline $PIPELINE_NAME #$BUILD_NUMBER failed! Check: $BUILD_URL" "danger"
;;
"STARTED")
send_metrics "pipeline_builds_total" "1" "status=started,pipeline=$PIPELINE_NAME"
send_slack_notification "๐ Pipeline $PIPELINE_NAME #$BUILD_NUMBER started" "warning"
;;
esac
echo "๐ Monitoring data sent for pipeline: $PIPELINE_NAME"
EOF
chmod +x scripts/monitor-pipeline.sh
This provides comprehensive pipeline monitoring! ๐
๐จ Fix Common Problems
Problem 1: Pipeline Failing Due to Permission Issues
Symptoms: Docker commands fail with permission denied
# Fix Docker permissions for Jenkins
sudo usermod -aG docker jenkins
sudo systemctl restart jenkins
# Fix workspace permissions
sudo chown -R jenkins:jenkins /var/lib/jenkins/workspace
# Fix Docker socket permissions
sudo chmod 666 /var/run/docker.sock
# Verify permissions
docker ps
groups jenkins
Problem 2: Tests Timing Out or Failing
Symptoms: Tests fail randomly or take too long
# Increase test timeouts
cat > jest.config.js << 'EOF'
module.exports = {
testTimeout: 30000,
setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
testEnvironment: 'node',
collectCoverage: true,
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html']
};
EOF
# Create test setup file
cat > test/setup.js << 'EOF'
// Global test setup
jest.setTimeout(30000);
// Mock external dependencies
jest.mock('external-service', () => ({
callApi: jest.fn().mockResolvedValue({ status: 'ok' })
}));
EOF
# Run tests with better error reporting
npm test -- --verbose --detectOpenHandles
Problem 3: Docker Build Failing
Symptoms: Docker builds fail with dependency or network issues
# Debug Docker build
docker build --no-cache --progress=plain -t my-app .
# Fix common Dockerfile issues
cat > Dockerfile << 'EOF'
FROM node:18-alpine
# Install build dependencies
RUN apk add --no-cache python3 make g++
WORKDIR /app
# Copy package files first (for better caching)
COPY package*.json ./
# Install dependencies with specific npm version
RUN npm ci --only=production --no-audit --no-fund
# Copy application code
COPY . .
# Remove build dependencies to reduce image size
RUN apk del python3 make g++
USER node
EXPOSE 3000
CMD ["npm", "start"]
EOF
# Clean Docker cache if needed
docker system prune -a
Problem 4: Deployment Rollback Needed
Symptoms: New deployment is causing issues in production
# Quick rollback script
cat > scripts/rollback.sh << 'EOF'
#!/bin/bash
# Emergency rollback script
LAST_GOOD_VERSION=$(docker images --format "table {{.Tag}}" my-app | grep -v latest | grep -v TAG | head -2 | tail -1)
echo "๐ Rolling back to version: $LAST_GOOD_VERSION"
# Stop current deployment
docker stop my-app
# Start previous version
docker run -d --name my-app-rollback -p 3000:3000 my-app:$LAST_GOOD_VERSION
# Switch traffic
docker rename my-app my-app-failed
docker rename my-app-rollback my-app
echo "โ
Rollback completed to version: $LAST_GOOD_VERSION"
EOF
chmod +x scripts/rollback.sh
# Execute rollback
./scripts/rollback.sh
๐ Simple Commands Summary
Command | Purpose |
---|---|
sudo systemctl start jenkins | Start Jenkins server |
docker build -t app:tag . | Build Docker image |
npm test | Run application tests |
docker run -d --name app app:tag | Deploy container |
jenkins-cli build job-name | Trigger Jenkins build |
gitlab-runner register | Register GitLab runner |
docker logs container-name | Check deployment logs |
curl http://localhost:3000/health | Health check endpoint |
docker system prune | Clean up Docker resources |
git push origin main | Trigger pipeline via Git push |
๐ก Tips for Success
๐ฏ Start Simple: Begin with basic build-test-deploy before adding complexity
๐ Monitor Everything: Set up comprehensive logging and monitoring
๐ Measure Performance: Track build times, test coverage, and deployment frequency
๐ก๏ธ Security First: Scan for vulnerabilities at every stage
๐ Automate Gradually: Add automation incrementally, not all at once
๐ Document Processes: Keep runbooks for troubleshooting and operations
๐ Practice Recovery: Regularly test rollback and disaster recovery procedures
โก Optimize Continuously: Regularly review and improve pipeline performance
๐ What You Learned
Congratulations! Youโve successfully mastered CI/CD pipelines on AlmaLinux! ๐
โ Set up Jenkins with complete pipeline configuration โ Configured GitLab CI/CD with advanced workflows โ Created sample application with comprehensive testing โ Implemented Docker integration for containerized deployments โ Built quality gates for reliable software delivery โ Established monitoring and alerting for pipeline health โ Learned troubleshooting techniques for common issues โ Mastered deployment strategies including blue-green deployments
๐ฏ Why This Matters
CI/CD is the backbone of modern software development! ๐ With your AlmaLinux-powered pipeline, you now have:
- 10x faster deployments compared to manual processes
- Bulletproof quality assurance with automated testing
- Zero-downtime deployments using advanced deployment strategies
- Complete traceability from code commit to production
- Foundation for DevOps excellence and team collaboration
Youโre now equipped to build, test, and deploy software like the worldโs top tech companies! Your pipeline will help your team ship features faster, with higher quality, and greater confidence! ๐
Keep automating, keep improving, and remember โ every deployment should be boring (in the best possible way)! Youโve got this! โญ๐