diff --git a/apps/demo-nginx/Jenkinsfile b/apps/demo-nginx/Jenkinsfile new file mode 100644 index 0000000..d1c8d3e --- /dev/null +++ b/apps/demo-nginx/Jenkinsfile @@ -0,0 +1,308 @@ +pipeline { + agent any + + environment { + // Application configuration + APP_NAME = 'demo-nginx' + NAMESPACE = 'demo-app' + + // Docker registry (измени на свой registry) + DOCKER_REGISTRY = 'docker.io' // Или Harbor: harbor.thedevops.dev + DOCKER_REPO = 'vladimiras' // Твой Docker Hub username или Harbor project + + // Gitea configuration + GITEA_URL = 'http://gitea-http.gitea.svc.cluster.local:3000' + GITEA_REPO = 'admin/k3s-gitops' + GITEA_BRANCH = 'main' + + // Build info + BUILD_TAG = "${env.BUILD_NUMBER}" + IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}" + } + + stages { + stage('Checkout Source') { + steps { + echo "Checking out application source code..." + // Если у тебя есть отдельный репозиторий с приложением, клонируй его здесь + // git branch: 'main', url: 'http://gitea-http.gitea.svc.cluster.local:3000/admin/demo-nginx-app' + + // Для примера, создадим простой Dockerfile + sh ''' + cat > Dockerfile << 'EOF' +FROM nginx:1.25.3-alpine + +# Add custom index.html +RUN echo "
Environment: Production
Version: ${IMAGE_TAG}
" > /usr/share/nginx/html/index.html + +# Add custom nginx config (optional) +COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +EOF + ''' + + sh ''' + cat > nginx.conf << 'EOF' +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + 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 /var/log/nginx/access.log main; + + sendfile on; + keepalive_timeout 65; + + server { + listen 80; + server_name _; + + location / { + root /usr/share/nginx/html; + index index.html; + } + + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + } +} +EOF + ''' + } + } + + stage('Build Docker Image') { + steps { + script { + echo "Building Docker image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG}" + + // Build image + sh """ + docker build \ + --build-arg BUILD_NUMBER=${BUILD_NUMBER} \ + --build-arg IMAGE_TAG=${IMAGE_TAG} \ + -t ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} \ + -t ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest \ + . + """ + } + } + } + + stage('Test Image') { + steps { + script { + echo "Testing Docker image..." + + // Run container for testing + sh """ + docker run -d --name test-${BUILD_NUMBER} \ + -p 8888:80 \ + ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} + + sleep 5 + + # Test HTTP response + curl -f http://localhost:8888/ || exit 1 + curl -f http://localhost:8888/health || exit 1 + + # Cleanup + docker stop test-${BUILD_NUMBER} + docker rm test-${BUILD_NUMBER} + """ + + echo "✅ Image tests passed!" + } + } + } + + stage('Push to Registry') { + when { + branch 'main' + } + steps { + script { + echo "Pushing image to registry..." + + // Login to Docker registry + withCredentials([usernamePassword( + credentialsId: 'docker-registry-credentials', + usernameVariable: 'DOCKER_USER', + passwordVariable: 'DOCKER_PASS' + )]) { + sh """ + echo "\${DOCKER_PASS}" | docker login ${DOCKER_REGISTRY} -u "\${DOCKER_USER}" --password-stdin + + docker push ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} + docker push ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest + + docker logout ${DOCKER_REGISTRY} + """ + } + + echo "✅ Image pushed successfully!" + } + } + } + + stage('Update GitOps Manifests') { + when { + branch 'main' + } + steps { + script { + echo "Updating Kubernetes manifests in Gitea..." + + withCredentials([usernamePassword( + credentialsId: 'gitea-credentials', + usernameVariable: 'GIT_USER', + passwordVariable: 'GIT_PASS' + )]) { + sh """ + # Clone GitOps repository + rm -rf k3s-gitops || true + git clone http://\${GIT_USER}:\${GIT_PASS}@gitea-http.gitea.svc.cluster.local:3000/admin/k3s-gitops.git + cd k3s-gitops + + # Configure git + git config user.name "Jenkins" + git config user.email "jenkins@thedevops.dev" + + # Update image tag in deployment + sed -i 's|image: .*|image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG}|' apps/demo-nginx/deployment.yaml + + # Commit and push + git add apps/demo-nginx/deployment.yaml + git commit -m "chore(demo-nginx): Update image to ${IMAGE_TAG}" || echo "No changes to commit" + git push origin main + """ + } + + echo "✅ GitOps manifests updated!" + } + } + } + + stage('Wait for ArgoCD Sync') { + when { + branch 'main' + } + steps { + script { + echo "Waiting for ArgoCD to sync application..." + + timeout(time: 5, unit: 'MINUTES') { + sh """ + # Wait for ArgoCD to sync + for i in {1..30}; do + STATUS=\$(kubectl get application ${APP_NAME} -n argocd -o jsonpath='{.status.sync.status}' 2>/dev/null || echo "Unknown") + HEALTH=\$(kubectl get application ${APP_NAME} -n argocd -o jsonpath='{.status.health.status}' 2>/dev/null || echo "Unknown") + + echo "ArgoCD Sync Status: \${STATUS}, Health: \${HEALTH}" + + if [ "\${STATUS}" = "Synced" ] && [ "\${HEALTH}" = "Healthy" ]; then + echo "✅ Application synced and healthy!" + exit 0 + fi + + sleep 10 + done + + echo "⚠️ Timeout waiting for sync, but continuing..." + """ + } + } + } + } + + stage('Verify Deployment') { + when { + branch 'main' + } + steps { + script { + echo "Verifying deployment in Kubernetes..." + + sh """ + # Check deployment status + kubectl rollout status deployment/${APP_NAME} -n ${NAMESPACE} --timeout=300s + + # Get pods + kubectl get pods -n ${NAMESPACE} -l app=${APP_NAME} + + # Verify image + DEPLOYED_IMAGE=\$(kubectl get deployment ${APP_NAME} -n ${NAMESPACE} -o jsonpath='{.spec.template.spec.containers[0].image}') + echo "Deployed image: \${DEPLOYED_IMAGE}" + + if [[ "\${DEPLOYED_IMAGE}" == *"${IMAGE_TAG}"* ]]; then + echo "✅ Correct image version deployed!" + else + echo "❌ Image version mismatch!" + exit 1 + fi + """ + + echo "✅ Deployment verified successfully!" + } + } + } + } + + post { + success { + echo """ + ✅ Pipeline completed successfully! + + Application: ${APP_NAME} + Image: ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} + Namespace: ${NAMESPACE} + URL: https://demo-nginx.thedevops.dev + + ArgoCD will automatically sync the changes. + """ + + // Optional: Send notification to Slack/Teams/Email + // slackSend(color: 'good', message: "Deployment succeeded: ${APP_NAME}:${IMAGE_TAG}") + } + + failure { + echo """ + ❌ Pipeline failed! + + Please check the logs above for details. + """ + + // Optional: Send notification + // slackSend(color: 'danger', message: "Deployment failed: ${APP_NAME}:${IMAGE_TAG}") + } + + always { + // Cleanup + sh """ + docker rmi ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:${IMAGE_TAG} || true + docker rmi ${DOCKER_REGISTRY}/${DOCKER_REPO}/${APP_NAME}:latest || true + docker system prune -f || true + """ + + cleanWs() + } + } +}