167 lines
9.6 KiB
Groovy
167 lines
9.6 KiB
Groovy
pipeline { // Declarative Jenkins pipeline start
|
|
agent any // Run on any available Jenkins agent
|
|
|
|
environment { // Global environment variables
|
|
APP_NAME = 'demo-nginx' // Application name (Docker, K8s, logs)
|
|
NAMESPACE = 'demo-app' // Kubernetes namespace
|
|
DOCKER_REGISTRY = 'docker.io' // Docker registry hostname
|
|
DOCKER_REPO = 'vladcrypto' // Docker Hub repository / namespace
|
|
GITEA_URL = 'http://gitea-http.gitea.svc.cluster.local:3000' // Internal Gitea URL
|
|
GITEA_REPO = 'admin/k3s-gitops' // GitOps repository path
|
|
GITEA_BRANCH = 'main' // Git branch for GitOps updates
|
|
BUILD_TAG = "${env.BUILD_NUMBER}" // Jenkins build number
|
|
IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}" // Image version tag
|
|
}
|
|
|
|
stages { // Pipeline stages definition
|
|
|
|
stage('Checkout Source') { // Stage: prepare application source
|
|
steps { // Steps executed in this stage
|
|
echo "Checking out application source code..." // Log message
|
|
|
|
sh ''' // Execute shell script
|
|
cat > Dockerfile << 'EOF' # Create Dockerfile
|
|
FROM nginx:1.25.3-alpine # Base Nginx Alpine image
|
|
RUN echo "<html><body><h1>Demo Nginx - Build ${BUILD_NUMBER}</h1><p>Environment: Production</p><p>Version: ${IMAGE_TAG}</p></body></html>" > /usr/share/nginx/html/index.html # Inject build metadata into HTML
|
|
COPY nginx.conf /etc/nginx/nginx.conf # Copy custom nginx configuration
|
|
EXPOSE 80 # Expose HTTP port
|
|
CMD ["nginx", "-g", "daemon off;"] # Run nginx in foreground
|
|
EOF
|
|
'''
|
|
|
|
sh ''' // Generate nginx configuration
|
|
cat > nginx.conf << 'EOF' # Create nginx.conf
|
|
user nginx; # Run workers as nginx user
|
|
worker_processes auto; # Scale workers to CPU cores
|
|
error_log /var/log/nginx/error.log warn; # Error log level and path
|
|
pid /var/run/nginx.pid; # PID file location
|
|
events { worker_connections 1024; } # Max connections per worker
|
|
http { # HTTP configuration block
|
|
include /etc/nginx/mime.types; # Load MIME types
|
|
default_type application/octet-stream; # Default content type
|
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # Access log format
|
|
access_log /var/log/nginx/access.log main; # Enable access logging
|
|
sendfile on; # Enable zero-copy file transfer
|
|
keepalive_timeout 65; # Keepalive timeout
|
|
server { # Server definition
|
|
listen 80; # Listen on port 80
|
|
server_name _; # Catch-all server
|
|
location / { # Root location
|
|
root /usr/share/nginx/html; # Serve static files
|
|
index index.html; # Default index file
|
|
}
|
|
location /health { # Health check endpoint
|
|
access_log off; # Disable logging for health checks
|
|
return 200 "healthy\n"; # Always return HTTP 200
|
|
add_header Content-Type text/plain; # Explicit content type
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
'''
|
|
}
|
|
}
|
|
|
|
stage('Build Docker Image') { // Stage: build Docker image
|
|
steps {
|
|
script { // Scripted block
|
|
echo "Building Docker image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG}" // Log image name
|
|
sh """ // Execute Docker build
|
|
docker build \
|
|
-t ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} \ // Versioned image
|
|
-t ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest \ // Latest tag
|
|
. // Build context
|
|
"""
|
|
echo "✅ Image built successfully!" // Success message
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Push to Registry') { // Stage: push image to registry
|
|
when { branch 'main' } // Execute only on main branch
|
|
steps {
|
|
script {
|
|
echo "Pushing image to registry..." // Log push start
|
|
withCredentials([usernamePassword( // Inject Docker credentials
|
|
credentialsId: 'docker-registry-credentials',
|
|
usernameVariable: 'DOCKER_USER',
|
|
passwordVariable: 'DOCKER_PASS'
|
|
)]) {
|
|
sh """ // Authenticate and push image
|
|
echo "\${DOCKER_PASS}" | docker login ${DOCKER_REGISTRY} -u "\${DOCKER_USER}" --password-stdin // Login securely
|
|
docker push ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} // Push versioned image
|
|
docker push ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest // Push latest image
|
|
docker logout ${DOCKER_REGISTRY} // Logout
|
|
"""
|
|
}
|
|
echo "✅ Image pushed successfully!" // Push success
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Update GitOps Manifests') { // Stage: update GitOps repository
|
|
when { branch 'main' } // Only from main branch
|
|
steps {
|
|
script {
|
|
echo "Updating Kubernetes manifests..." // Log GitOps update
|
|
withCredentials([usernamePassword( // Inject Gitea credentials
|
|
credentialsId: 'gitea-credentials',
|
|
usernameVariable: 'GIT_USER',
|
|
passwordVariable: 'GIT_PASS'
|
|
)]) {
|
|
sh """ // Clone and update manifests
|
|
rm -rf k3s-gitops || true // Remove old repo
|
|
git clone http://\${GIT_USER}:\${GIT_PASS}@gitea-http.gitea.svc.cluster.local:3000/admin/k3s-gitops.git // Clone repo
|
|
cd k3s-gitops // Enter repo
|
|
git config user.name "Jenkins" // Git author name
|
|
git config user.email "jenkins@thedevops.dev" // Git author email
|
|
sed -i 's|image: .*|image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG}|' apps/demo-nginx/deployment.yaml // Update image
|
|
git add apps/demo-nginx/deployment.yaml // Stage change
|
|
git commit -m "chore(demo-nginx): Update image to ${IMAGE_TAG}" || echo "No changes" // Commit if needed
|
|
git push origin main // Push to main
|
|
"""
|
|
}
|
|
echo "✅ Manifests updated!" // GitOps success
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Verify Deployment') { // Stage: verify Kubernetes deployment
|
|
when { branch 'main' } // Only on main
|
|
steps {
|
|
script {
|
|
echo "Verifying deployment..." // Log verification start
|
|
sh """ // Check deployment status
|
|
sleep 30 // Wait for rollout start
|
|
kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=300s || true // Rollout status
|
|
kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} // List pods
|
|
"""
|
|
echo "✅ Deployment completed!" // Verification success
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
post { // Post-pipeline actions
|
|
success { // On success
|
|
echo """ // Success summary
|
|
✅ Pipeline SUCCESS!
|
|
Image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG}
|
|
Namespace: ${NAMESPACE}
|
|
"""
|
|
}
|
|
failure { // On failure
|
|
echo "❌ Pipeline failed!" // Failure message
|
|
}
|
|
always { // Always execute cleanup
|
|
sh """ // Cleanup Docker artifacts
|
|
docker rmi ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} || true // Remove versioned image
|
|
docker rmi ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest || true // Remove latest image
|
|
docker stop test-${BUILD_NUMBER} 2>/dev/null || true // Stop test container
|
|
docker rm test-${BUILD_NUMBER} 2>/dev/null || true // Remove test container
|
|
"""
|
|
cleanWs() // Clean Jenkins workspace
|
|
}
|
|
}
|
|
}
|