Skip to content

Latest commit

 

History

History
1147 lines (928 loc) · 32.1 KB

File metadata and controls

1147 lines (928 loc) · 32.1 KB
layout default
title Chapter 7: Production Deployment
nav_order 7
has_children false
parent Dify Platform Deep Dive

Chapter 7: Production Deployment

Welcome to Chapter 7: Production Deployment. In this part of Dify Platform: Deep Dive Tutorial, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.

Deploying, scaling, and monitoring Dify applications in production environments

🎯 Learning Objectives

By the end of this chapter, you'll be able to:

  • Set up production-ready Dify deployments
  • Implement scaling strategies for high-throughput workloads
  • Configure monitoring and observability
  • Handle security, compliance, and backup requirements
  • Optimize performance and cost efficiency

🏗️ Production Architecture

Multi-Environment Deployment

graph TB
    subgraph "Development"
        Dev[Local Development]
    end

    subgraph "Staging"
        Staging[Dify Staging Environment]
    end

    subgraph "Production"
        LB[Load Balancer]
        App1[Dify App Server 1]
        App2[Dify App Server 2]
        App3[Dify App Server 3]
        DB[(PostgreSQL Cluster)]
        Redis[(Redis Cluster)]
        VDB[(Vector Database)]
        Storage[(Object Storage)]
    end

    subgraph "Monitoring"
        Metrics[Metrics Collection]
        Logs[Log Aggregation]
        Alerts[Alerting System]
    end

    Dev --> Staging
    Staging --> Production

    LB --> App1
    LB --> App2
    LB --> App3

    App1 --> DB
    App2 --> DB
    App3 --> DB

    App1 --> Redis
    App2 --> Redis
    App3 --> Redis

    App1 --> VDB
    App2 --> VDB
    App3 --> VDB

    App1 --> Storage
    App2 --> Storage
    App3 --> Storage

    App1 --> Metrics
    App2 --> Metrics
    App3 --> Metrics

    App1 --> Logs
    App2 --> Logs
    App3 --> Logs

    Metrics --> Alerts
    Logs --> Alerts
Loading

Infrastructure Components

Component Purpose Scaling Strategy
Application Servers Run Dify workflows and API Horizontal scaling with load balancer
Database Store application data and metadata Read replicas, connection pooling
Cache Speed up frequently accessed data Redis cluster with replication
Vector Database Store embeddings for RAG Sharded deployment for large datasets
Object Storage Store files and large documents CDN integration for global access
Message Queue Handle async processing Partitioned queues for high throughput

🚀 Deployment Strategies

Docker-Based Deployment

# docker-compose.prod.yml
version: '3.8'

services:
  dify-web:
    image: langgenius/dify-web:latest
    replicas: 3
    environment:
      - NODE_ENV=production
      - API_URL=https://api.yourdomain.com
    ports:
      - "80:80"
    depends_on:
      - dify-api
    networks:
      - dify-network

  dify-api:
    image: langgenius/dify-api:latest
    replicas: 5
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/dify
      - REDIS_URL=redis://redis:6379
      - VECTOR_DB_URL=http://weaviate:8080
      - SECRET_KEY=your-secret-key
      - WORKER_CONCURRENCY=10
    volumes:
      - ./storage:/app/storage
    depends_on:
      - db
      - redis
      - weaviate
    networks:
      - dify-network

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=dify
      - POSTGRES_USER=dify
      - POSTGRES_PASSWORD=secure-password
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./backup:/backup
    networks:
      - dify-network

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - dify-network

  weaviate:
    image: semitechnologies/weaviate:latest
    environment:
      - QUERY_DEFAULTS_LIMIT=25
      - PERSISTENCE_DATA_PATH='/var/lib/weaviate'
      - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true
    volumes:
      - weaviate_data:/var/lib/weaviate
    networks:
      - dify-network

  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/ssl/certs
    depends_on:
      - dify-web
      - dify-api
    networks:
      - dify-network

volumes:
  postgres_data:
  redis_data:
  weaviate_data:

networks:
  dify-network:
    driver: bridge

Kubernetes Deployment

# dify-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dify-api
  namespace: dify-production
spec:
  replicas: 5
  selector:
    matchLabels:
      app: dify-api
  template:
    metadata:
      labels:
        app: dify-api
    spec:
      containers:
      - name: dify-api
        image: langgenius/dify-api:latest
        ports:
        - containerPort: 5001
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: dify-secrets
              key: database-url
        - name: REDIS_URL
          valueFrom:
            secretKeyRef:
              name: dify-secrets
              key: redis-url
        - name: SECRET_KEY
          valueFrom:
            secretKeyRef:
              name: dify-secrets
              key: secret-key
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 5001
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 5001
          initialDelaySeconds: 5
          periodSeconds: 5
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app: dify-api
              topologyKey: kubernetes.io/hostname

Environment Configuration

# config/production.py
import os

class ProductionConfig:
    """Production environment configuration"""

    # Flask/Dify settings
    SECRET_KEY = os.environ.get('SECRET_KEY')
    DEBUG = False
    TESTING = False

    # Database
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ENGINE_OPTIONS = {
        'pool_size': 20,
        'max_overflow': 30,
        'pool_timeout': 30,
        'pool_recycle': 3600,
        'pool_pre_ping': True
    }

    # Redis
    REDIS_URL = os.environ.get('REDIS_URL')
    CACHE_TYPE = 'redis'
    CACHE_REDIS_URL = REDIS_URL

    # Vector Database
    WEAVIATE_URL = os.environ.get('WEAVIATE_URL')
    VECTOR_DB_CONFIG = {
        'host': WEAVIATE_URL,
        'timeout': 30,
        'retries': 3
    }

    # External APIs
    OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
    ANTHROPIC_API_KEY = os.environ.get('ANTHROPIC_API_KEY')

    # File Storage
    AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
    AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
    AWS_S3_BUCKET = os.environ.get('AWS_S3_BUCKET')
    AWS_REGION = os.environ.get('AWS_REGION', 'us-east-1')

    # Security
    SESSION_COOKIE_SECURE = True
    SESSION_COOKIE_HTTPONLY = True
    SESSION_COOKIE_SAMESITE = 'Lax'

    # Rate Limiting
    RATELIMIT_DEFAULT = "100 per minute"
    RATELIMIT_STORAGE_URL = REDIS_URL

    # Logging
    LOG_LEVEL = 'INFO'
    LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

    # Monitoring
    SENTRY_DSN = os.environ.get('SENTRY_DSN')
    METRICS_ENABLED = True

    # Worker Configuration
    CELERY_BROKER_URL = REDIS_URL
    CELERY_RESULT_BACKEND = REDIS_URL
    CELERY_WORKER_CONCURRENCY = int(os.environ.get('WORKER_CONCURRENCY', 4))
    CELERY_WORKER_MAX_TASKS_PER_CHILD = 1000

    # Feature Flags
    ENABLE_ADVANCED_ANALYTICS = True
    ENABLE_CUSTOM_NODES = True
    ENABLE_TEAM_COLLABORATION = True

📊 Monitoring and Observability

Metrics Collection

# monitoring/metrics.py
from prometheus_client import Counter, Histogram, Gauge
import time

# Request metrics
REQUEST_COUNT = Counter(
    'dify_requests_total',
    'Total number of requests',
    ['method', 'endpoint', 'status']
)

REQUEST_LATENCY = Histogram(
    'dify_request_duration_seconds',
    'Request duration in seconds',
    ['method', 'endpoint']
)

# Workflow metrics
WORKFLOW_EXECUTION_COUNT = Counter(
    'dify_workflow_executions_total',
    'Total number of workflow executions',
    ['workflow_id', 'status']
)

WORKFLOW_EXECUTION_DURATION = Histogram(
    'dify_workflow_execution_duration_seconds',
    'Workflow execution duration',
    ['workflow_id']
)

# Node metrics
NODE_EXECUTION_COUNT = Counter(
    'dify_node_executions_total',
    'Total number of node executions',
    ['node_type', 'status']
)

NODE_EXECUTION_DURATION = Histogram(
    'dify_node_execution_duration_seconds',
    'Node execution duration',
    ['node_type']
)

# Resource metrics
ACTIVE_CONNECTIONS = Gauge(
    'dify_active_connections',
    'Number of active connections'
)

MEMORY_USAGE = Gauge(
    'dify_memory_usage_bytes',
    'Memory usage in bytes'
)

# Business metrics
USER_COUNT = Gauge(
    'dify_active_users',
    'Number of active users'
)

API_COST = Counter(
    'dify_api_cost_total',
    'Total API costs',
    ['provider']
)

class MetricsMiddleware:
    """Middleware to collect metrics for each request"""

    def __init__(self, app):
        self.app = app

    async def __call__(self, scope, receive, send):
        if scope['type'] != 'http':
            return await self.app(scope, receive, send)

        start_time = time.time()

        # Track active connections
        ACTIVE_CONNECTIONS.inc()

        try:
            # Process request
            response_status = None

            async def send_wrapper(message):
                nonlocal response_status
                if message['type'] == 'http.response.start':
                    response_status = message['status']

                await send(message)

            await self.app(scope, receive, send_wrapper)

            # Record metrics
            duration = time.time() - start_time
            method = scope['method']
            path = scope['path']

            REQUEST_COUNT.labels(
                method=method,
                endpoint=path,
                status=response_status
            ).inc()

            REQUEST_LATENCY.labels(
                method=method,
                endpoint=path
            ).observe(duration)

        finally:
            ACTIVE_CONNECTIONS.dec()

Logging Configuration

# config/logging.py
import logging
import logging.config
from pythonjsonlogger import jsonlogger

LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'json': {
            'class': 'pythonjsonlogger.jsonlogger.JsonFormatter',
            'format': '%(asctime)s %(name)s %(levelname)s %(message)s'
        },
        'detailed': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'detailed',
            'level': 'INFO'
        },
        'file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'logs/dify.log',
            'maxBytes': 10485760,  # 10MB
            'backupCount': 5,
            'formatter': 'json',
            'level': 'INFO'
        },
        'error_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'logs/dify-error.log',
            'maxBytes': 10485760,
            'backupCount': 5,
            'formatter': 'json',
            'level': 'ERROR'
        }
    },
    'loggers': {
        'dify': {
            'handlers': ['console', 'file', 'error_file'],
            'level': 'INFO',
            'propagate': False
        },
        'dify.api': {
            'handlers': ['console', 'file', 'error_file'],
            'level': 'INFO',
            'propagate': False
        },
        'dify.workflow': {
            'handlers': ['console', 'file', 'error_file'],
            'level': 'INFO',
            'propagate': False
        }
    },
    'root': {
        'handlers': ['console'],
        'level': 'INFO'
    }
}

def setup_logging():
    """Configure logging for production"""
    logging.config.dictConfig(LOGGING_CONFIG)

    # Suppress noisy third-party logs
    logging.getLogger('urllib3').setLevel(logging.WARNING)
    logging.getLogger('requests').setLevel(logging.WARNING)
    logging.getLogger('werkzeug').setLevel(logging.WARNING)

Health Checks and Alerting

# monitoring/health.py
from flask import Blueprint, jsonify
import psutil
import time

health_bp = Blueprint('health', __name__)

class HealthChecker:
    """Comprehensive health checking"""

    def __init__(self):
        self.start_time = time.time()
        self.last_check = {}

    async def check_database(self):
        """Check database connectivity"""
        try:
            # Test database connection
            from dify.models import db
            db.engine.execute('SELECT 1')
            return {'status': 'healthy', 'response_time': '0.01s'}
        except Exception as e:
            return {'status': 'unhealthy', 'error': str(e)}

    async def check_redis(self):
        """Check Redis connectivity"""
        try:
            from dify.cache import redis_client
            redis_client.ping()
            return {'status': 'healthy'}
        except Exception as e:
            return {'status': 'unhealthy', 'error': str(e)}

    async def check_vector_db(self):
        """Check vector database connectivity"""
        try:
            from dify.vector_db import vector_client
            # Simple connectivity test
            await vector_client.health_check()
            return {'status': 'healthy'}
        except Exception as e:
            return {'status': 'unhealthy', 'error': str(e)}

    async def check_external_apis(self):
        """Check external API connectivity"""
        results = {}

        # Check OpenAI
        try:
            import openai
            # Simple model list call
            openai.Model.list()
            results['openai'] = {'status': 'healthy'}
        except Exception as e:
            results['openai'] = {'status': 'unhealthy', 'error': str(e)}

        return results

    def check_system_resources(self):
        """Check system resource usage"""
        cpu_percent = psutil.cpu_percent(interval=1)
        memory = psutil.virtual_memory()
        disk = psutil.disk_usage('/')

        return {
            'cpu_usage': f"{cpu_percent}%",
            'memory_usage': f"{memory.percent}%",
            'disk_usage': f"{disk.percent}%",
            'cpu_healthy': cpu_percent < 90,
            'memory_healthy': memory.percent < 90,
            'disk_healthy': disk.percent < 90
        }

    async def comprehensive_health_check(self):
        """Run all health checks"""

        checks = await asyncio.gather(
            self.check_database(),
            self.check_redis(),
            self.check_vector_db(),
            self.check_external_apis(),
            asyncio.get_event_loop().run_in_executor(None, self.check_system_resources)
        )

        results = {
            'timestamp': time.time(),
            'uptime': time.time() - self.start_time,
            'database': checks[0],
            'redis': checks[1],
            'vector_db': checks[2],
            'external_apis': checks[3],
            'system': checks[4]
        }

        # Overall health status
        all_healthy = all(
            check.get('status') == 'healthy'
            for check in [results['database'], results['redis'], results['vector_db']]
        ) and all(
            api.get('status') == 'healthy'
            for api in results['external_apis'].values()
        ) and all(results['system'][f'{resource}_healthy'] for resource in ['cpu', 'memory', 'disk'])

        results['overall_status'] = 'healthy' if all_healthy else 'unhealthy'

        self.last_check = results
        return results

@health_bp.route('/health')
async def health_check():
    """Health check endpoint"""
    checker = HealthChecker()
    results = await checker.comprehensive_health_check()

    status_code = 200 if results['overall_status'] == 'healthy' else 503
    return jsonify(results), status_code

@health_bp.route('/ready')
async def readiness_check():
    """Readiness check for Kubernetes"""
    # Simpler check for readiness
    try:
        from dify.models import db
        db.engine.execute('SELECT 1')
        return jsonify({'status': 'ready'}), 200
    except:
        return jsonify({'status': 'not ready'}), 503

@health_bp.route('/metrics')
def metrics():
    """Prometheus metrics endpoint"""
    from prometheus_client import generate_latest
    return generate_latest()

🔒 Security and Compliance

Security Best Practices

# security/config.py
class SecurityConfig:
    """Security configuration for production"""

    # Authentication
    JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
    JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 15
    JWT_REFRESH_TOKEN_EXPIRE_DAYS = 30

    # Password policy
    PASSWORD_MIN_LENGTH = 12
    PASSWORD_REQUIRE_UPPERCASE = True
    PASSWORD_REQUIRE_LOWERCASE = True
    PASSWORD_REQUIRE_DIGITS = True
    PASSWORD_REQUIRE_SPECIAL_CHARS = True

    # Rate limiting
    RATE_LIMIT_REQUESTS = 100
    RATE_LIMIT_WINDOW = 60  # seconds

    # CORS
    CORS_ORIGINS = os.environ.get('CORS_ORIGINS', '').split(',')
    CORS_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
    CORS_HEADERS = ['Content-Type', 'Authorization']

    # Content Security Policy
    CSP = {
        'default-src': "'self'",
        'script-src': "'self' 'unsafe-inline' 'unsafe-eval'",
        'style-src': "'self' 'unsafe-inline'",
        'img-src': "'self' data: https:",
        'font-src': "'self'",
        'connect-src': "'self' https://api.openai.com https://api.anthropic.com"
    }

    # Encryption
    ENCRYPTION_KEY = os.environ.get('ENCRYPTION_KEY')
    ENCRYPT_SENSITIVE_DATA = True

    # Audit logging
    AUDIT_LOG_ENABLED = True
    AUDIT_LOG_RETENTION_DAYS = 365

    # Data protection
    DATA_RETENTION_DAYS = 2555  # 7 years for compliance
    AUTO_DELETE_OLD_DATA = True

Compliance Features

# compliance/gdpr.py
class GDPRCompliance:
    """GDPR compliance features"""

    def __init__(self, db):
        self.db = db

    async def handle_data_deletion_request(self, user_id):
        """Handle GDPR right to erasure"""

        # Log deletion request
        await self.audit_log('data_deletion_requested', user_id=user_id)

        # Anonymize user data instead of deleting
        await self.anonymize_user_data(user_id)

        # Delete personal data
        await self.delete_personal_data(user_id)

        # Notify user
        await self.send_deletion_confirmation(user_id)

    async def anonymize_user_data(self, user_id):
        """Anonymize user data for retention"""

        # Replace personal information with anonymized versions
        anonymized_email = f"deleted_user_{user_id}@anonymized.local"
        anonymized_name = f"User {user_id}"

        await self.db.execute("""
            UPDATE users
            SET email = ?, name = ?, anonymized = true
            WHERE id = ?
        """, (anonymized_email, anonymized_name, user_id))

    async def export_user_data(self, user_id):
        """Handle GDPR data portability"""

        # Collect all user data
        user_data = await self.collect_user_data(user_id)

        # Format as JSON
        export_data = {
            'user_profile': user_data['profile'],
            'workflows': user_data['workflows'],
            'executions': user_data['executions'],
            'export_date': datetime.now().isoformat(),
            'gdpr_compliant': True
        }

        # Encrypt export
        encrypted_data = await self.encrypt_export(export_data)

        return encrypted_data

    async def audit_log(self, action, **kwargs):
        """Log all data operations for audit"""

        audit_entry = {
            'timestamp': datetime.now().isoformat(),
            'action': action,
            'user_id': kwargs.get('user_id'),
            'ip_address': kwargs.get('ip_address'),
            'user_agent': kwargs.get('user_agent'),
            'details': kwargs
        }

        await self.db.execute("""
            INSERT INTO audit_log (timestamp, action, details)
            VALUES (?, ?, ?)
        """, (audit_entry['timestamp'], audit_entry['action'], json.dumps(audit_entry)))

📈 Performance Optimization

Caching Strategies

# performance/cache.py
from cachetools import TTLCache, LRUCache
import asyncio

class IntelligentCache:
    """Multi-level caching system"""

    def __init__(self):
        # Fast in-memory cache for frequently accessed data
        self.l1_cache = TTLCache(maxsize=10000, ttl=300)  # 5 minutes

        # Larger Redis cache for shared data
        self.redis_cache = RedisCache()

        # Persistent cache for expensive computations
        self.persistent_cache = PersistentCache()

    async def get(self, key, fetch_func=None):
        """Get value with multi-level fallback"""

        # Check L1 cache first
        if key in self.l1_cache:
            return self.l1_cache[key]

        # Check Redis cache
        redis_value = await self.redis_cache.get(key)
        if redis_value is not None:
            # Populate L1 cache
            self.l1_cache[key] = redis_value
            return redis_value

        # Check persistent cache
        persistent_value = await self.persistent_cache.get(key)
        if persistent_value is not None:
            # Populate higher caches
            self.l1_cache[key] = persistent_value
            await self.redis_cache.set(key, persistent_value)
            return persistent_value

        # Fetch from source if provided
        if fetch_func:
            value = await fetch_func()
            await self.set(key, value)
            return value

        return None

    async def set(self, key, value, ttl=None):
        """Set value in all cache levels"""

        self.l1_cache[key] = value
        await self.redis_cache.set(key, value, ttl)
        await self.persistent_cache.set(key, value)

    async def invalidate(self, key):
        """Invalidate cache entry"""

        if key in self.l1_cache:
            del self.l1_cache[key]

        await self.redis_cache.delete(key)
        await self.persistent_cache.delete(key)

    async def preload_frequent_queries(self):
        """Preload frequently accessed data"""

        frequent_queries = await self.analyze_access_patterns()

        for query in frequent_queries:
            # Prefetch and cache results
            result = await self.execute_query(query)
            cache_key = f"query:{hash(query)}"
            await self.set(cache_key, result, ttl=3600)  # 1 hour

Database Optimization

# performance/database.py
from sqlalchemy import text
from sqlalchemy.pool import QueuePool

class DatabaseOptimizer:
    """Database performance optimization"""

    def __init__(self, db):
        self.db = db

    async def optimize_connection_pool(self):
        """Configure optimal connection pool settings"""

        pool_config = {
            'poolclass': QueuePool,
            'pool_size': 20,
            'max_overflow': 30,
            'pool_timeout': 30,
            'pool_recycle': 3600,
            'pool_pre_ping': True
        }

        # Apply configuration
        self.db.engine.pool = pool_config

    async def create_indexes(self):
        """Create performance indexes"""

        indexes = [
            "CREATE INDEX IF NOT EXISTS idx_workflows_user_id ON workflows(user_id)",
            "CREATE INDEX IF NOT EXISTS idx_executions_workflow_id ON executions(workflow_id)",
            "CREATE INDEX IF NOT EXISTS idx_executions_status ON executions(status)",
            "CREATE INDEX IF NOT EXISTS idx_nodes_workflow_id ON nodes(workflow_id)",
            "CREATE INDEX IF NOT EXISTS idx_audit_log_timestamp ON audit_log(timestamp)",
        ]

        for index_sql in indexes:
            await self.db.execute(text(index_sql))

    async def setup_read_replicas(self):
        """Configure read replicas for load distribution"""

        # Primary database for writes
        self.write_db = self.db

        # Read replicas for reads
        self.read_dbs = [
            create_engine(os.environ.get('READ_REPLICA_1_URL')),
            create_engine(os.environ.get('READ_REPLICA_2_URL')),
        ]

    def get_read_connection(self):
        """Get connection from read replica pool"""

        # Simple round-robin selection
        replica = self.read_dbs[self.read_index % len(self.read_dbs)]
        self.read_index += 1

        return replica

    async def archive_old_data(self):
        """Archive old execution data for performance"""

        # Move old executions to archive table
        cutoff_date = datetime.now() - timedelta(days=90)

        await self.db.execute(text("""
            INSERT INTO executions_archive
            SELECT * FROM executions
            WHERE created_at < :cutoff_date
        """), {'cutoff_date': cutoff_date})

        # Delete from main table
        await self.db.execute(text("""
            DELETE FROM executions
            WHERE created_at < :cutoff_date
        """), {'cutoff_date': cutoff_date})

🚀 Deployment Pipeline

CI/CD Configuration

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.9'
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
      - name: Run tests
        run: pytest --cov=dify --cov-report=xml
      - name: Upload coverage
        uses: codecov/codecov-action@v3

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Build Docker image
        run: |
          docker build -t dify-api:${{ github.sha }} .
          docker tag dify-api:${{ github.sha }} dify-api:latest

  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Deploy to staging
        run: |
          kubectl set image deployment/dify-api dify-api=dify-api:${{ github.sha }}
          kubectl rollout status deployment/dify-api

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy to production
        run: |
          kubectl set image deployment/dify-api dify-api=dify-api:${{ github.sha }}
          kubectl rollout status deployment/dify-api
      - name: Run smoke tests
        run: |
          # Run automated smoke tests against production
          python scripts/smoke_test.py

  rollback:
    needs: deploy-production
    runs-on: ubuntu-latest
    if: failure()
    steps:
      - name: Rollback deployment
        run: |
          kubectl rollout undo deployment/dify-api

🧪 Hands-On Exercise

Estimated Time: 120 minutes

  1. Set Up Production Infrastructure:

    • Configure Docker Compose for local production-like environment
    • Set up monitoring with Prometheus and Grafana
    • Implement health checks and alerting
  2. Implement Security Measures:

    • Configure HTTPS with SSL certificates
    • Set up authentication and authorization
    • Implement rate limiting and DDoS protection
  3. Optimize Performance:

    • Set up caching layers (Redis, in-memory)
    • Configure database connection pooling
    • Implement request queuing for high load
  4. Deploy and Monitor:

    • Create automated deployment scripts
    • Set up log aggregation and analysis
    • Implement automated rollback procedures

🎯 Key Takeaways

  1. Infrastructure Design: Scalable architecture with proper separation of concerns
  2. Deployment Automation: CI/CD pipelines for reliable releases
  3. Monitoring: Comprehensive observability with metrics, logs, and alerts
  4. Security: Defense-in-depth approach with authentication, encryption, and compliance
  5. Performance: Multi-layer caching, database optimization, and load balancing
  6. Reliability: Health checks, auto-scaling, and automated recovery

🏁 Tutorial Complete!

Congratulations! You've completed the comprehensive Dify Platform Deep Dive tutorial. You now have the knowledge to:

  • Build complex LLM workflows with visual and programmatic approaches
  • Implement RAG systems for enhanced question answering
  • Create autonomous agents with tool integration and reasoning
  • Extend Dify's capabilities with custom nodes
  • Deploy production-ready applications with monitoring and scaling

Next Steps in Your Learning Journey

  1. Explore Other Tutorials: Check out the other tutorials in this collection
  2. Build Real Applications: Apply your knowledge to create production LLM apps
  3. Contribute Back: Help improve this tutorial or create new ones
  4. Stay Updated: Follow the latest developments in the LLM and AI space

Resources for Continued Learning

Thank you for completing this tutorial! Your feedback and contributions help make technical education better for everyone.


Part of the Awesome Code Docs collection - transforming complex systems into accessible learning experiences

What Problem Does This Solve?

Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for self, dify, name so behavior stays predictable as complexity grows.

In practical terms, this chapter helps you avoid three common failures:

  • coupling core logic too tightly to one implementation path
  • missing the handoff boundaries between setup, execution, and validation
  • shipping changes without clear rollback or observability strategy

After working through this chapter, you should be able to reason about Chapter 7: Production Deployment as an operating subsystem inside Dify Platform: Deep Dive Tutorial, with explicit contracts for inputs, state transitions, and outputs.

Use the implementation notes around status, environ, results as your checklist when adapting these patterns to your own repository.

How it Works Under the Hood

Under the hood, Chapter 7: Production Deployment usually follows a repeatable control path:

  1. Context bootstrap: initialize runtime config and prerequisites for self.
  2. Input normalization: shape incoming data so dify receives stable contracts.
  3. Core execution: run the main logic branch and propagate intermediate state through name.
  4. Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
  5. Output composition: return canonical result payloads for downstream consumers.
  6. Operational telemetry: emit logs/metrics needed for debugging and performance tuning.

When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.

Source Walkthrough

Use the following upstream sources to verify implementation details while reading this chapter:

  • Dify Why it matters: authoritative reference on Dify (github.com).

Suggested trace strategy:

  • search upstream code for self and dify to map concrete implementation paths
  • compare docs claims against actual runtime/config code before reusing patterns in production

Chapter Connections