GitLab Deploy Pipeline | Complete CI/CD Deployment Guide
GitLab CI/CD deployment pipelines automate the process of deploying applications to various environments. This guide covers deploying Angular UIs, .NET Core microservices, and databases using Liquibase.
🅰️ How to Deploy Angular UI
Section titled “🅰️ How to Deploy Angular UI”Deploying Angular applications involves building the production bundle and serving it through web servers or cloud platforms.
Basic Angular Deployment to Static Hosting
Section titled “Basic Angular Deployment to Static Hosting”stages: - build - deploy
variables: NODE_VERSION: "18"
deploy_angular_staging: stage: deploy image: node:${NODE_VERSION} dependencies: - build_angular script: - npm install -g @angular/cli - echo "Deploying to staging environment" - aws s3 sync dist/ s3://$STAGING_S3_BUCKET --delete - aws cloudfront create-invalidation --distribution-id $STAGING_CLOUDFRONT_ID --paths "/*" environment: name: staging url: https://staging.myapp.com only: - develop
deploy_angular_production: stage: deploy image: node:${NODE_VERSION} dependencies: - build_angular script: - npm install -g @angular/cli - echo "Deploying to production environment" - aws s3 sync dist/ s3://$PRODUCTION_S3_BUCKET --delete - aws cloudfront create-invalidation --distribution-id $PRODUCTION_CLOUDFRONT_ID --paths "/*" environment: name: production url: https://myapp.com when: manual only: - main
Angular Deployment with Docker
Section titled “Angular Deployment with Docker”deploy_angular_docker: stage: deploy image: docker:20.10.16 services: - docker:20.10.16-dind variables: DOCKER_TLS_CERTDIR: "/certs" script: # Build Docker image - docker build -t $CI_REGISTRY_IMAGE/angular-ui:$CI_COMMIT_SHA . - docker tag $CI_REGISTRY_IMAGE/angular-ui:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE/angular-ui:latest
# Push to registry - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker push $CI_REGISTRY_IMAGE/angular-ui:$CI_COMMIT_SHA - docker push $CI_REGISTRY_IMAGE/angular-ui:latest
# Deploy to Kubernetes - kubectl set image deployment/angular-ui angular-ui=$CI_REGISTRY_IMAGE/angular-ui:$CI_COMMIT_SHA - kubectl rollout status deployment/angular-ui environment: name: production url: https://myapp.com
Angular Deployment with Environment-Specific Configurations
Section titled “Angular Deployment with Environment-Specific Configurations”.deploy_angular_template: &deploy_angular image: node:18 dependencies: - build_angular script: - echo "Deploying Angular UI to $ENVIRONMENT" - | if [ "$ENVIRONMENT" = "staging" ]; then ng build --configuration=staging else ng build --configuration=production fi - aws s3 sync dist/ s3://$S3_BUCKET --delete - aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_ID --paths "/*"
deploy_angular_staging: <<: *deploy_angular stage: deploy variables: ENVIRONMENT: "staging" S3_BUCKET: $STAGING_S3_BUCKET CLOUDFRONT_ID: $STAGING_CLOUDFRONT_ID environment: name: staging url: https://staging.myapp.com only: - develop
deploy_angular_production: <<: *deploy_angular stage: deploy variables: ENVIRONMENT: "production" S3_BUCKET: $PRODUCTION_S3_BUCKET CLOUDFRONT_ID: $PRODUCTION_CLOUDFRONT_ID environment: name: production url: https://myapp.com when: manual only: - main
Angular Deployment with Nginx
Section titled “Angular Deployment with Nginx”deploy_angular_nginx: stage: deploy image: nginx:alpine dependencies: - build_angular script: - cp -r dist/* /usr/share/nginx/html/ - | cat > /etc/nginx/conf.d/default.conf << 'EOF' server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html;
location / { try_files $uri $uri/ /index.html; }
location /api/ { proxy_pass http://backend-service:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } EOF - nginx -g 'daemon off;' environment: name: production url: https://myapp.com
🔷 How to Deploy .NET Core Microservices
Section titled “🔷 How to Deploy .NET Core Microservices”Deploying .NET Core microservices involves containerization, orchestration, and proper service mesh configuration.
Basic .NET Core Deployment
Section titled “Basic .NET Core Deployment”deploy_dotnet_service: stage: deploy image: mcr.microsoft.com/dotnet/sdk:8.0 dependencies: - build_dotnet script: - cd publish/ - dotnet MyService.dll & - echo "Service deployed successfully" environment: name: production url: https://api.myapp.com
.NET Core Microservices with Docker Deployment
Section titled “.NET Core Microservices with Docker Deployment”variables: DOCKER_REGISTRY: "registry.gitlab.com/mygroup/myproject" SERVICES: "userservice orderservice paymentservice"
.deploy_microservice_template: &deploy_microservice stage: deploy image: docker:20.10.16 services: - docker:20.10.16-dind variables: DOCKER_TLS_CERTDIR: "/certs" before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - | for service in $SERVICES; do echo "Deploying $service..."
# Build and tag image docker build -f src/$service/Dockerfile -t $DOCKER_REGISTRY/$service:$CI_COMMIT_SHA src/$service/ docker tag $DOCKER_REGISTRY/$service:$CI_COMMIT_SHA $DOCKER_REGISTRY/$service:latest
# Push to registry docker push $DOCKER_REGISTRY/$service:$CI_COMMIT_SHA docker push $DOCKER_REGISTRY/$service:latest
# Deploy to Kubernetes kubectl set image deployment/$service $service=$DOCKER_REGISTRY/$service:$CI_COMMIT_SHA kubectl rollout status deployment/$service done
deploy_microservices_staging: <<: *deploy_microservice environment: name: staging url: https://staging-api.myapp.com only: - develop
deploy_microservices_production: <<: *deploy_microservice environment: name: production url: https://api.myapp.com when: manual only: - main
Kubernetes Deployment with Helm
Section titled “Kubernetes Deployment with Helm”deploy_with_helm: stage: deploy image: alpine/helm:3.10.0 dependencies: - build_dotnet script: - helm repo add stable https://charts.helm.sh/stable - helm repo update - | helm upgrade --install my-microservices ./helm-chart \ --set image.tag=$CI_COMMIT_SHA \ --set environment=$ENVIRONMENT \ --set ingress.host=$INGRESS_HOST \ --namespace $NAMESPACE \ --create-namespace - kubectl get pods -n $NAMESPACE environment: name: $ENVIRONMENT url: https://$INGRESS_HOST
Blue-Green Deployment Strategy
Section titled “Blue-Green Deployment Strategy”deploy_blue_green: stage: deploy image: kubectl:latest script: - | # Check current active color CURRENT_COLOR=$(kubectl get service myapp-service -o jsonpath='{.spec.selector.color}') if [ "$CURRENT_COLOR" = "blue" ]; then NEW_COLOR="green" else NEW_COLOR="blue" fi
echo "Deploying to $NEW_COLOR environment"
# Deploy to inactive color kubectl set image deployment/myapp-$NEW_COLOR myapp=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA kubectl rollout status deployment/myapp-$NEW_COLOR
# Health check kubectl run health-check --image=curlimages/curl --rm -it --restart=Never \ -- curl -f http://myapp-$NEW_COLOR-service/health
# Switch traffic kubectl patch service myapp-service -p '{"spec":{"selector":{"color":"'$NEW_COLOR'"}}}'
echo "Traffic switched to $NEW_COLOR" environment: name: production url: https://api.myapp.com
Canary Deployment with Istio
Section titled “Canary Deployment with Istio”deploy_canary: stage: deploy image: istio/istioctl:1.18.0 script: - | # Deploy canary version kubectl set image deployment/myapp-canary myapp=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA kubectl rollout status deployment/myapp-canary
# Configure traffic split (10% to canary) cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: myapp-vs spec: http: - match: - headers: canary: exact: "true" route: - destination: host: myapp-canary-service - route: - destination: host: myapp-service weight: 90 - destination: host: myapp-canary-service weight: 10 EOF
echo "Canary deployment with 10% traffic split completed" environment: name: production url: https://api.myapp.com
🗄️ Deploy Database using Liquibase
Section titled “🗄️ Deploy Database using Liquibase”Database deployments with Liquibase ensure consistent schema changes across environments.
Basic Liquibase Deployment
Section titled “Basic Liquibase Deployment”deploy_database: stage: deploy image: liquibase/liquibase:4.23 variables: DB_URL: "jdbc:postgresql://postgres:5432/myapp" DB_USERNAME: "dbuser" DB_PASSWORD: "dbpass" script: - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD update - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD status environment: name: production
Multi-Environment Database Deployment
Section titled “Multi-Environment Database Deployment”.deploy_database_template: &deploy_database image: liquibase/liquibase:4.23 script: - echo "Deploying database changes to $ENVIRONMENT" - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD validate - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD update - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD status
deploy_database_staging: <<: *deploy_database stage: deploy variables: ENVIRONMENT: "staging" DB_URL: $STAGING_DB_URL DB_USERNAME: $STAGING_DB_USERNAME DB_PASSWORD: $STAGING_DB_PASSWORD environment: name: staging only: - develop
deploy_database_production: <<: *deploy_database stage: deploy variables: ENVIRONMENT: "production" DB_URL: $PRODUCTION_DB_URL DB_USERNAME: $PRODUCTION_DB_USERNAME DB_PASSWORD: $PRODUCTION_DB_PASSWORD environment: name: production when: manual only: - main
Database Deployment with Rollback Support
Section titled “Database Deployment with Rollback Support”deploy_database_with_rollback: stage: deploy image: liquibase/liquibase:4.23 variables: DB_URL: $PRODUCTION_DB_URL DB_USERNAME: $PRODUCTION_DB_USERNAME DB_PASSWORD: $PRODUCTION_DB_PASSWORD script: # Create rollback script before deployment - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD rollbackSQL $(date +"%Y-%m-%d") > rollback-script.sql
# Validate changes - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD validate
# Generate update SQL for review - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD updateSQL > update-script.sql
# Apply changes - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD update
# Verify deployment - liquibase --url=$DB_URL --username=$DB_USERNAME --password=$DB_PASSWORD status artifacts: paths: - rollback-script.sql - update-script.sql expire_in: 1 week environment: name: production
Database Deployment with Health Checks
Section titled “Database Deployment with Health Checks”deploy_database_with_health_check: stage: deploy image: liquibase/liquibase:4.23 services: - postgres:13 variables: POSTGRES_DB: myapp POSTGRES_USER: dbuser POSTGRES_PASSWORD: dbpass DB_URL: "jdbc:postgresql://postgres:5432/myapp" script: # Wait for database to be ready - | for i in {1..30}; do if liquibase --url=$DB_URL --username=$POSTGRES_USER --password=$POSTGRES_PASSWORD status; then echo "Database is ready" break fi echo "Waiting for database... ($i/30)" sleep 10 done
# Deploy changes - liquibase --url=$DB_URL --username=$POSTGRES_USER --password=$POSTGRES_PASSWORD update
# Run health checks - | cat > health_check.sql << 'EOF' SELECT table_name, column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position; EOF
- psql $DB_URL -f health_check.sql environment: name: production
Database Migration with Multiple Databases
Section titled “Database Migration with Multiple Databases”deploy_multi_database: stage: deploy image: liquibase/liquibase:4.23 parallel: matrix: - DATABASE: [userdb, orderdb, inventorydb] script: - | case $DATABASE in userdb) DB_URL=$USER_DB_URL DB_USER=$USER_DB_USERNAME DB_PASS=$USER_DB_PASSWORD ;; orderdb) DB_URL=$ORDER_DB_URL DB_USER=$ORDER_DB_USERNAME DB_PASS=$ORDER_DB_PASSWORD ;; inventorydb) DB_URL=$INVENTORY_DB_URL DB_USER=$INVENTORY_DB_USERNAME DB_PASS=$INVENTORY_DB_PASSWORD ;; esac
- echo "Deploying $DATABASE database changes" - liquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASS --changeLogFile=changelog-$DATABASE.xml update - liquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASS status environment: name: production
🔧 Advanced Deployment Strategies
Section titled “🔧 Advanced Deployment Strategies”Progressive Deployment with Monitoring
Section titled “Progressive Deployment with Monitoring”deploy_with_monitoring: stage: deploy script: - echo "Deploying application with monitoring" - kubectl apply -f deployment.yaml - kubectl rollout status deployment/myapp
# Setup monitoring - | for i in {1..10}; do HEALTH_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://myapp-service/health) if [ "$HEALTH_STATUS" = "200" ]; then echo "Health check passed" break fi echo "Health check failed, retrying... ($i/10)" sleep 30 done
# Monitor metrics for 5 minutes - | for i in {1..5}; do ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(http_requests_total{status=~'5..'}[5m])" | jq -r '.data.result[0].value[1]') if (( $(echo "$ERROR_RATE > 0.05" | bc -l) )); then echo "Error rate too high: $ERROR_RATE" kubectl rollout undo deployment/myapp exit 1 fi echo "Monitoring... Error rate: $ERROR_RATE" sleep 60 done environment: name: production url: https://myapp.com
Zero-Downtime Deployment
Section titled “Zero-Downtime Deployment”deploy_zero_downtime: stage: deploy script: - | # Scale up new version kubectl scale deployment myapp-new --replicas=3 kubectl rollout status deployment myapp-new
# Health check new version kubectl run health-check --image=curlimages/curl --rm -it --restart=Never \ -- curl -f http://myapp-new-service/health
# Gradually shift traffic for weight in 25 50 75 100; do echo "Shifting $weight% traffic to new version" kubectl patch service myapp-service -p "{\"spec\":{\"selector\":{\"version\":\"new\",\"weight\":\"$weight\"}}}" sleep 120
# Monitor during traffic shift ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(http_requests_total{status=~'5..'}[2m])" | jq -r '.data.result[0].value[1]') if (( $(echo "$ERROR_RATE > 0.02" | bc -l) )); then echo "Error rate increased, rolling back" kubectl patch service myapp-service -p '{"spec":{"selector":{"version":"old"}}}' exit 1 fi done
# Scale down old version kubectl scale deployment myapp-old --replicas=0 environment: name: production url: https://myapp.com
This comprehensive deployment guide covers the essential aspects of deploying Angular UIs, .NET Core microservices, and databases using Liquibase in GitLab CI/CD pipelines.