384 lines
14 KiB
Rust
384 lines
14 KiB
Rust
//! Production deployment example demonstrating comprehensive deployment utilities
|
|
//!
|
|
//! This example shows how to:
|
|
//! 1. Set up a model registry with versioning
|
|
//! 2. Configure environment-specific settings with hot-reloading
|
|
//! 3. Implement monitoring and observability
|
|
//! 4. Deploy models with proper health checks
|
|
//!
|
|
//! Run with: cargo run --example production_deployment
|
|
|
|
use anyhow::Result;
|
|
use rtx_config::{create_production_config_manager, ConfigValue, Environment};
|
|
use rtx_hub::{ModelRegistry, RegistryConfig, ModelId, ModelVersion, ModelMetadata, ModelStatus, StorageConfig};
|
|
use rtx_monitoring::{create_production_monitoring, MonitoringSystem};
|
|
use serde_json::json;
|
|
use std::path::PathBuf;
|
|
use std::collections::HashMap;
|
|
use tokio::time::{sleep, Duration};
|
|
use tracing::{info, warn, error};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// Initialize tracing
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter("info,production_deployment=debug")
|
|
.init();
|
|
|
|
info!("🚀 Starting RTX Production Deployment Example");
|
|
|
|
// 1. Initialize configuration management with hot-reloading
|
|
let config_manager = setup_configuration().await?;
|
|
|
|
// 2. Set up model registry for versioning and deployment
|
|
let model_registry = setup_model_registry(&config_manager).await?;
|
|
|
|
// 3. Initialize monitoring and observability
|
|
let monitoring = setup_monitoring(&config_manager).await?;
|
|
|
|
// 4. Deploy and register example models
|
|
deploy_example_models(&model_registry).await?;
|
|
|
|
// 5. Run health checks and monitoring
|
|
run_production_checks(&model_registry, &monitoring).await?;
|
|
|
|
// 6. Demonstrate hot configuration reloading
|
|
demonstrate_hot_reload(&config_manager).await?;
|
|
|
|
// 7. Show metrics and health status
|
|
show_system_status(&monitoring).await?;
|
|
|
|
info!("✅ Production deployment example completed successfully");
|
|
Ok(())
|
|
}
|
|
|
|
/// Set up production configuration with environment-specific settings
|
|
async fn setup_configuration() -> Result<rtx_config::SharedConfigManager> {
|
|
info!("📋 Setting up configuration management...");
|
|
|
|
// Create production configuration manager
|
|
let config_manager = create_production_config_manager().await?;
|
|
|
|
// Set some example production settings
|
|
config_manager.set("database.pool_size", ConfigValue::Integer(20)).await?;
|
|
config_manager.set("model_cache.max_size", ConfigValue::String("10GB".to_string())).await?;
|
|
config_manager.set("inference.batch_size", ConfigValue::Integer(32)).await?;
|
|
config_manager.set("monitoring.metrics_enabled", ConfigValue::Boolean(true)).await?;
|
|
|
|
// Environment-specific overrides
|
|
let environment = config_manager.environment();
|
|
match environment {
|
|
Environment::Production => {
|
|
config_manager.set("log_level", ConfigValue::String("warn".to_string())).await?;
|
|
config_manager.set("debug_mode", ConfigValue::Boolean(false)).await?;
|
|
}
|
|
Environment::Staging => {
|
|
config_manager.set("log_level", ConfigValue::String("info".to_string())).await?;
|
|
config_manager.set("debug_mode", ConfigValue::Boolean(true)).await?;
|
|
}
|
|
_ => {
|
|
config_manager.set("log_level", ConfigValue::String("debug".to_string())).await?;
|
|
config_manager.set("debug_mode", ConfigValue::Boolean(true)).await?;
|
|
}
|
|
}
|
|
|
|
// Subscribe to configuration changes for hot-reloading
|
|
let mut change_receiver = config_manager.subscribe_to_changes();
|
|
let config_clone = std::sync::Arc::clone(&config_manager);
|
|
|
|
tokio::spawn(async move {
|
|
while let Ok(change) = change_receiver.recv().await {
|
|
info!("🔄 Configuration changed: {} = {:?}", change.key, change.new_value);
|
|
|
|
// Handle specific configuration changes
|
|
match change.key.as_str() {
|
|
"inference.batch_size" => {
|
|
info!("📊 Updating inference batch size...");
|
|
// In a real deployment, this would trigger model reload
|
|
}
|
|
"monitoring.metrics_enabled" => {
|
|
info!("📈 Updating metrics collection settings...");
|
|
// Toggle metrics collection
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
});
|
|
|
|
info!("✅ Configuration management initialized for environment: {}", environment);
|
|
Ok(std::sync::Arc::new(config_manager))
|
|
}
|
|
|
|
/// Set up model registry for version management
|
|
async fn setup_model_registry(
|
|
config_manager: &rtx_config::ConfigManager
|
|
) -> Result<std::sync::Arc<ModelRegistry>> {
|
|
info!("🗄️ Setting up model registry...");
|
|
|
|
// Get storage configuration from config manager
|
|
let storage_path = config_manager.get::<String>("storage.models_path")
|
|
.await
|
|
.unwrap_or_else(|_| "/tmp/rtx-models".to_string());
|
|
|
|
let registry_config = RegistryConfig {
|
|
storage: StorageConfig::Local {
|
|
base_path: PathBuf::from(storage_path),
|
|
},
|
|
database_url: config_manager.get::<String>("database.url")
|
|
.await
|
|
.unwrap_or_else(|_| "sqlite::memory:".to_string()),
|
|
enable_validation: true,
|
|
enable_compression: true,
|
|
max_package_size: Some(5 * 1024 * 1024 * 1024), // 5GB
|
|
retention_days: Some(365),
|
|
enable_signing: false,
|
|
registry_name: Some("RTX Production Registry".to_string()),
|
|
description: Some("Production model registry for RTX deployments".to_string()),
|
|
registry_url: None,
|
|
};
|
|
|
|
let registry = ModelRegistry::new(registry_config).await?;
|
|
|
|
info!("✅ Model registry initialized");
|
|
Ok(std::sync::Arc::new(registry))
|
|
}
|
|
|
|
/// Set up comprehensive monitoring and observability
|
|
async fn setup_monitoring(
|
|
_config_manager: &rtx_config::ConfigManager
|
|
) -> Result<std::sync::Arc<MonitoringSystem>> {
|
|
info!("📊 Setting up monitoring and observability...");
|
|
|
|
let monitoring = create_production_monitoring().await?;
|
|
|
|
// In a real deployment, you would:
|
|
// 1. Configure Prometheus endpoints
|
|
// 2. Set up OpenTelemetry tracing
|
|
// 3. Configure alerting rules
|
|
// 4. Set up dashboards
|
|
|
|
info!("✅ Monitoring system initialized");
|
|
info!("📈 Metrics available at: http://localhost:9090/metrics");
|
|
info!("🏥 Health checks available at: http://localhost:8080/health");
|
|
|
|
Ok(monitoring)
|
|
}
|
|
|
|
/// Deploy example models with proper versioning
|
|
async fn deploy_example_models(registry: &ModelRegistry) -> Result<()> {
|
|
info!("🤖 Deploying example models...");
|
|
|
|
// Deploy BERT model v1.0.0
|
|
let bert_id = ModelId::new("huggingface", "bert-base-uncased");
|
|
let bert_v1 = ModelVersion::parse("1.0.0")?;
|
|
let bert_metadata = create_model_metadata(
|
|
bert_id.clone(),
|
|
bert_v1.clone(),
|
|
"BERT Base Uncased",
|
|
"Pre-trained BERT model for natural language understanding",
|
|
"transformer",
|
|
&["nlp", "classification", "embeddings"],
|
|
);
|
|
|
|
registry.register_model(bert_metadata).await?;
|
|
info!("✅ Deployed BERT v1.0.0");
|
|
|
|
// Deploy GPT model v2.1.0
|
|
let gpt_id = ModelId::new("openai", "gpt-3.5-turbo");
|
|
let gpt_v2 = ModelVersion::parse("2.1.0")?;
|
|
let gpt_metadata = create_model_metadata(
|
|
gpt_id.clone(),
|
|
gpt_v2.clone(),
|
|
"GPT-3.5 Turbo",
|
|
"Large language model optimized for chat and instruction following",
|
|
"transformer",
|
|
&["nlp", "generation", "chat"],
|
|
);
|
|
|
|
registry.register_model(gpt_metadata).await?;
|
|
info!("✅ Deployed GPT-3.5 v2.1.0");
|
|
|
|
// Deploy ResNet model v1.2.3
|
|
let resnet_id = ModelId::new("torchvision", "resnet50");
|
|
let resnet_v1 = ModelVersion::parse("1.2.3")?;
|
|
let resnet_metadata = create_model_metadata(
|
|
resnet_id.clone(),
|
|
resnet_v1.clone(),
|
|
"ResNet-50",
|
|
"Deep residual network for image classification",
|
|
"cnn",
|
|
&["vision", "classification", "imagenet"],
|
|
);
|
|
|
|
registry.register_model(resnet_metadata).await?;
|
|
info!("✅ Deployed ResNet-50 v1.2.3");
|
|
|
|
// Show registry statistics
|
|
let stats = registry.get_stats().await?;
|
|
info!("📊 Registry Statistics:");
|
|
info!(" - Total Models: {}", stats.model_count);
|
|
info!(" - Total Versions: {}", stats.version_count);
|
|
info!(" - Total Size: {:.2} MB", stats.total_size as f64 / (1024.0 * 1024.0));
|
|
info!(" - Downloads: {}", stats.download_count);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Run production health checks and monitoring
|
|
async fn run_production_checks(
|
|
registry: &ModelRegistry,
|
|
monitoring: &MonitoringSystem,
|
|
) -> Result<()> {
|
|
info!("🏥 Running production health checks...");
|
|
|
|
// Check model registry health
|
|
let registry_stats = registry.get_stats().await?;
|
|
if registry_stats.model_count > 0 {
|
|
info!("✅ Model registry: {} models available", registry_stats.model_count);
|
|
} else {
|
|
warn!("⚠️ Model registry: No models found");
|
|
}
|
|
|
|
// Check monitoring system health
|
|
let metrics_summary = monitoring.get_metrics_summary().await?;
|
|
info!("✅ Monitoring system: {} active alerts", metrics_summary.active_alerts);
|
|
info!("⏱️ System uptime: {:?}", metrics_summary.uptime);
|
|
|
|
// Simulate some model inference metrics
|
|
simulate_model_usage(®istry, &monitoring).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Demonstrate hot configuration reloading
|
|
async fn demonstrate_hot_reload(config_manager: &rtx_config::ConfigManager) -> Result<()> {
|
|
info!("🔄 Demonstrating hot configuration reload...");
|
|
|
|
// Show current batch size
|
|
let current_batch_size = config_manager.get::<i64>("inference.batch_size").await?;
|
|
info!("Current batch size: {}", current_batch_size);
|
|
|
|
// Simulate configuration change (in production, this would come from file/env changes)
|
|
config_manager.set("inference.batch_size", ConfigValue::Integer(64)).await?;
|
|
|
|
// Wait a moment for the change to propagate
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
let new_batch_size = config_manager.get::<i64>("inference.batch_size").await?;
|
|
info!("Updated batch size: {}", new_batch_size);
|
|
|
|
// Revert back
|
|
config_manager.set("inference.batch_size", ConfigValue::Integer(32)).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Show comprehensive system status
|
|
async fn show_system_status(monitoring: &MonitoringSystem) -> Result<()> {
|
|
info!("📈 System Status Summary:");
|
|
|
|
let summary = monitoring.get_metrics_summary().await?;
|
|
|
|
info!("🖥️ System Metrics:");
|
|
info!(" - CPU Usage: {:.1}%", summary.system_metrics.cpu_usage);
|
|
info!(" - Memory Usage: {:.1}%", summary.system_metrics.memory_usage);
|
|
info!(" - Disk Usage: {:.1}%", summary.system_metrics.disk_usage);
|
|
|
|
info!("🏥 Health Status: {:?}", summary.health_status);
|
|
info!("🚨 Active Alerts: {}", summary.active_alerts);
|
|
info!("⏱️ Uptime: {:?}", summary.uptime);
|
|
|
|
// Show available endpoints
|
|
info!("🌐 Available Endpoints:");
|
|
info!(" - Metrics: http://localhost:9090/metrics");
|
|
info!(" - Health: http://localhost:8080/health");
|
|
info!(" - Model Registry API: http://localhost:8080/api/models");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Simulate model usage for metrics demonstration
|
|
async fn simulate_model_usage(
|
|
registry: &ModelRegistry,
|
|
_monitoring: &MonitoringSystem,
|
|
) -> Result<()> {
|
|
info!("🎯 Simulating model inference requests...");
|
|
|
|
// Get list of available models
|
|
let models = registry.list_models(Default::default()).await?;
|
|
|
|
for model in models.iter().take(3) {
|
|
info!("📊 Processing requests for: {}", model.metadata.id);
|
|
|
|
// Simulate inference requests
|
|
for i in 1..=5 {
|
|
// In a real system, this would:
|
|
// 1. Load the model if not cached
|
|
// 2. Run inference
|
|
// 3. Record metrics (latency, throughput, errors)
|
|
// 4. Update health status
|
|
|
|
sleep(Duration::from_millis(50)).await; // Simulate processing time
|
|
|
|
if i % 10 == 0 {
|
|
info!(" Processed {} requests", i);
|
|
}
|
|
}
|
|
|
|
// Simulate accessing the model (updates access statistics)
|
|
let mut model_info = registry.get_model(&model.metadata.id, Some(&model.metadata.version)).await?;
|
|
info!(" Access count: {}", model_info.access_count);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Create model metadata for registration
|
|
fn create_model_metadata(
|
|
id: ModelId,
|
|
version: ModelVersion,
|
|
title: &str,
|
|
description: &str,
|
|
architecture: &str,
|
|
tags: &[&str],
|
|
) -> ModelMetadata {
|
|
ModelMetadata {
|
|
id,
|
|
version,
|
|
title: title.to_string(),
|
|
description: description.to_string(),
|
|
architecture: architecture.to_string(),
|
|
framework: "rustytorch".to_string(),
|
|
framework_version: "1.0.0".to_string(),
|
|
tags: tags.iter().map(|&s| s.to_string()).collect(),
|
|
author: "RTX Team".to_string(),
|
|
license: Some("MIT".to_string()),
|
|
created_at: chrono::Utc::now(),
|
|
updated_at: chrono::Utc::now(),
|
|
status: ModelStatus::Available,
|
|
size: 1024 * 1024 * 100, // 100MB
|
|
content_hash: format!("sha256:{}", hex::encode(rand::random::<[u8; 32]>())),
|
|
dependencies: vec![],
|
|
schema: rtx_hub::ModelSchema {
|
|
inputs: vec![rtx_hub::TensorSchema {
|
|
name: "input".to_string(),
|
|
dtype: "float32".to_string(),
|
|
shape: vec![None, Some(768)],
|
|
description: Some("Model input tensor".to_string()),
|
|
}],
|
|
outputs: vec![rtx_hub::TensorSchema {
|
|
name: "output".to_string(),
|
|
dtype: "float32".to_string(),
|
|
shape: vec![None, Some(1000)],
|
|
description: Some("Model output tensor".to_string()),
|
|
}],
|
|
config: Some(json!({
|
|
"max_sequence_length": 512,
|
|
"vocab_size": 30522,
|
|
"hidden_size": 768
|
|
})),
|
|
},
|
|
metrics: HashMap::new(),
|
|
metadata: HashMap::new(),
|
|
}
|
|
} |