diff --git a/apps/demo-nginx/jenkins1 b/apps/demo-nginx/jenkins1 new file mode 100644 index 0000000..efae0ff --- /dev/null +++ b/apps/demo-nginx/jenkins1 @@ -0,0 +1,348 @@ +// Declarative Jenkins Pipeline definition +pipeline { + + // Jenkins can execute this pipeline on any available agent/node + agent any + + // Global environment variables available in all stages + environment { + + // Logical application name + // Used in Docker image name, Kubernetes deployment, labels, logs + APP_NAME = 'demo-nginx' + + // Kubernetes namespace where the application is deployed + NAMESPACE = 'demo-app' + + // Docker registry hostname + DOCKER_REGISTRY = 'docker.io' + + // Docker Hub repository / namespace (Docker Hub username or org) + DOCKER_REPO = 'vladcrypto' + + // Internal Gitea service URL (cluster-internal DNS) + GITEA_URL = 'http://gitea-http.gitea.svc.cluster.local:3000' + + // Git repository path inside Gitea + GITEA_REPO = 'admin/k3s-gitops' + + // Branch used for GitOps updates + GITEA_BRANCH = 'main' + + // Jenkins build number (unique per run) + BUILD_TAG = "${env.BUILD_NUMBER}" + + // Image tag composed of: + // - branch name (e.g. main, feature-x) + // - Jenkins build number + // Ensures traceability and uniqueness + IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}" + } + + stages { + + // ============================ + // STAGE: Generate application source + // ============================ + stage('Checkout Source') { + steps { + + // Log message in Jenkins console + echo "Checking out application source code..." + + // Shell block to generate Dockerfile dynamically + sh ''' + # Create Dockerfile in workspace + cat > Dockerfile << 'EOF' + +# Use lightweight official Nginx Alpine image +FROM nginx:1.25.3-alpine + +# Replace default Nginx index page +# Embed Jenkins build number and image tag directly into HTML +RUN echo "
\ +Environment: Production
\ +Version: ${IMAGE_TAG}
\ +" > /usr/share/nginx/html/index.html + +# Copy custom Nginx configuration into container +COPY nginx.conf /etc/nginx/nginx.conf + +# Expose HTTP port (documentation only, runtime handled by Kubernetes) +EXPOSE 80 + +# Run Nginx in foreground (required for containers) +CMD ["nginx", "-g", "daemon off;"] + +EOF + ''' + + // Shell block to generate nginx.conf dynamically + sh ''' + # Create Nginx configuration file + cat > nginx.conf << 'EOF' + +# User under which Nginx worker processes run +user nginx; + +# Automatically scale workers to CPU cores +worker_processes auto; + +# Error log configuration +error_log /var/log/nginx/error.log warn; + +# PID file location +pid /var/run/nginx.pid; + +# Event handling configuration +events { + # Maximum number of simultaneous connections per worker + worker_connections 1024; +} + +http { + # Load MIME types + include /etc/nginx/mime.types; + + # Default MIME type + default_type application/octet-stream; + + # Define access log format + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + # Enable access logging + access_log /var/log/nginx/access.log main; + + # Enable zero-copy file transfers + sendfile on; + + # Keepalive timeout for client connections + keepalive_timeout 65; + + # HTTP server definition + server { + + # Listen on port 80 + listen 80; + + # Default catch-all server name + server_name _; + + # Root location serving static content + location / { + root /usr/share/nginx/html; + index index.html; + } + + # Health endpoint for Kubernetes probes + location /health { + # Disable access logs for health checks + access_log off; + + # Always return HTTP 200 + return 200 "healthy\n"; + + # Explicit content type + add_header Content-Type text/plain; + } + } +} + +EOF + ''' + } + } + + // ============================ + // STAGE: Build Docker image + // ============================ + stage('Build Docker Image') { + steps { + script { + + // Log which image is being built + echo "Building Docker image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG}" + + // Build Docker image + sh """ + docker build \ + # Versioned image tag + -t ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} \ + # Latest tag for convenience + -t ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest \ + # Build context = current workspace + . + """ + + // Success marker + echo "✅ Image built successfully!" + } + } + } + + // ============================ + // STAGE: Push image to registry + // ============================ + stage('Push to Registry') { + + // Execute this stage ONLY on main branch + when { branch 'main' } + + steps { + script { + + echo "Pushing image to registry..." + + // Inject Docker registry credentials from Jenkins + withCredentials([usernamePassword( + credentialsId: 'docker-registry-credentials', + usernameVariable: 'DOCKER_USER', + passwordVariable: 'DOCKER_PASS' + )]) { + + sh """ + # Login to Docker registry using stdin (secure) + echo "\${DOCKER_PASS}" | docker login ${DOCKER_REGISTRY} \ + -u "\${DOCKER_USER}" --password-stdin + + # Push versioned image + docker push ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} + + # Push latest tag + docker push ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest + + # Logout for security hygiene + docker logout ${DOCKER_REGISTRY} + """ + } + + echo "✅ Image pushed successfully!" + } + } + } + + // ============================ + // STAGE: Update GitOps manifests + // ============================ + stage('Update GitOps Manifests') { + + // GitOps updates only from main branch + when { branch 'main' } + + steps { + script { + + echo "Updating Kubernetes manifests..." + + // Inject Gitea credentials + withCredentials([usernamePassword( + credentialsId: 'gitea-credentials', + usernameVariable: 'GIT_USER', + passwordVariable: 'GIT_PASS' + )]) { + + sh """ + # Remove previous repo clone if exists + rm -rf k3s-gitops || true + + # Clone GitOps repository with credentials + git clone http://\${GIT_USER}:\${GIT_PASS}@gitea-http.gitea.svc.cluster.local:3000/admin/k3s-gitops.git + + # Enter repository directory + cd k3s-gitops + + # Configure Git identity for Jenkins commits + git config user.name "Jenkins" + git config user.email "jenkins@thedevops.dev" + + # Update image field in Kubernetes Deployment manifest + sed -i 's|image: .*|image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG}|' \ + apps/demo-nginx/deployment.yaml + + # Stage changed file + git add apps/demo-nginx/deployment.yaml + + # Commit change (ignore if no diff) + git commit -m "chore(demo-nginx): Update image to ${IMAGE_TAG}" || echo "No changes" + + # Push change to main branch + git push origin main + """ + } + + echo "✅ Manifests updated!" + } + } + } + + // ============================ + // STAGE: Verify Kubernetes deployment + // ============================ + stage('Verify Deployment') { + + // Only verify deployments from main + when { branch 'main' } + + steps { + script { + + echo "Verifying deployment..." + + sh """ + # Give Kubernetes time to start rollout + sleep 30 + + # Check rollout status (do not fail pipeline hard) + kubectl rollout status deployment/${APP_NAME} \ + -n ${NAMESPACE} --timeout=300s || true + + # List pods for visibility + kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} + """ + + echo "✅ Deployment completed!" + } + } + } + } + + // ============================ + // POST actions (always executed) + // ============================ + post { + + // On successful pipeline execution + success { + echo """ + ✅ Pipeline SUCCESS! + Image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} + Namespace: ${NAMESPACE} + """ + } + + // On pipeline failure + failure { + echo "❌ Pipeline failed!" + } + + // Always executed (success or failure) + always { + + // Cleanup local Docker images to free disk space + sh """ + docker rmi ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} || true + docker rmi ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest || true + + # Stop and remove any temporary test containers + docker stop test-${BUILD_NUMBER} 2>/dev/null || true + docker rm test-${BUILD_NUMBER} 2>/dev/null || true + """ + + // Clean Jenkins workspace directory + cleanWs() + } + } +}