544 lines
15 KiB
Plaintext
544 lines
15 KiB
Plaintext
|
|
# Multi-Provider Infrastructure Examples
|
||
|
|
# Nickel examples demonstrating multi-provider deployment patterns
|
||
|
|
# References: multi-provider-deployment.md guide
|
||
|
|
|
||
|
|
let hetzner = import "../../extensions/providers/hetzner/nickel/main.ncl" in
|
||
|
|
let upcloud = import "../../extensions/providers/upcloud/nickel/main.ncl" in
|
||
|
|
let aws = import "../../extensions/providers/aws/nickel/main.ncl" in
|
||
|
|
let digitalocean = import "../../extensions/providers/digitalocean/nickel/main.ncl" in
|
||
|
|
|
||
|
|
{
|
||
|
|
# =============================================================================
|
||
|
|
# Example 1: Three-Provider Web Application
|
||
|
|
# =============================================================================
|
||
|
|
# Architecture:
|
||
|
|
# - DigitalOcean: Web servers (cost-effective compute) - NYC region
|
||
|
|
# - AWS: Managed PostgreSQL database (high availability) - US-East region
|
||
|
|
# - Hetzner: Backup storage volumes (low-cost data retention) - Germany
|
||
|
|
#
|
||
|
|
# Total Monthly Cost: ~$77 (vs $150+ for single provider)
|
||
|
|
# Key Benefits: Cost optimization, compute specialization, managed database
|
||
|
|
|
||
|
|
three_provider_web_app = {
|
||
|
|
workspace_name = "three-provider-webapp",
|
||
|
|
description = "Web application leveraging provider strengths",
|
||
|
|
|
||
|
|
# Provider routing and configuration
|
||
|
|
providers = {
|
||
|
|
primary_compute = "digitalocean",
|
||
|
|
database = "aws",
|
||
|
|
backup = "hetzner"
|
||
|
|
},
|
||
|
|
|
||
|
|
# ==========================================================================
|
||
|
|
# Private Networks and VPN Tunnels
|
||
|
|
# ==========================================================================
|
||
|
|
networks = {
|
||
|
|
# DigitalOcean VPC (10.0.0.0/16)
|
||
|
|
do_vpc = digitalocean.VPC & {
|
||
|
|
name = "webapp-vpc",
|
||
|
|
region = "nyc3",
|
||
|
|
ip_range = "10.0.0.0/16"
|
||
|
|
},
|
||
|
|
|
||
|
|
# AWS VPC (10.1.0.0/16)
|
||
|
|
aws_vpc = aws.VPC & {
|
||
|
|
cidr_block = "10.1.0.0/16",
|
||
|
|
enable_dns_hostnames = true,
|
||
|
|
enable_dns_support = true
|
||
|
|
},
|
||
|
|
|
||
|
|
# AWS DB Subnet
|
||
|
|
aws_db_subnet = aws.Subnet & {
|
||
|
|
vpc_id = "{{ networks.aws_vpc.id }}",
|
||
|
|
cidr_block = "10.1.1.0/24",
|
||
|
|
availability_zone = "us-east-1a"
|
||
|
|
},
|
||
|
|
|
||
|
|
# Hetzner Private Network (10.2.0.0/16)
|
||
|
|
hetzner_network = hetzner.Network & {
|
||
|
|
name = "backup-network",
|
||
|
|
ip_range = "10.2.0.0/16"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# VPN Tunnels for secure communication
|
||
|
|
vpn_configuration = {
|
||
|
|
do_to_aws_tunnel = {
|
||
|
|
protocol = "ipsec",
|
||
|
|
encryption = "aes-256",
|
||
|
|
source_networks = ["10.0.0.0/16"],
|
||
|
|
destination_networks = ["10.1.0.0/16"],
|
||
|
|
description = "DO web tier to AWS database"
|
||
|
|
},
|
||
|
|
|
||
|
|
aws_to_hetzner_tunnel = {
|
||
|
|
protocol = "ipsec",
|
||
|
|
encryption = "aes-256",
|
||
|
|
source_networks = ["10.1.0.0/16"],
|
||
|
|
destination_networks = ["10.2.0.0/16"],
|
||
|
|
description = "AWS database to Hetzner backup"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# ==========================================================================
|
||
|
|
# DigitalOcean: Web Tier (3 droplets with load balancer)
|
||
|
|
# ==========================================================================
|
||
|
|
web_tier = {
|
||
|
|
# Application servers
|
||
|
|
droplets = digitalocean.Droplet & {
|
||
|
|
name = "web-server",
|
||
|
|
region = "nyc3",
|
||
|
|
size = "s-2vcpu-4gb",
|
||
|
|
image = "ubuntu-22-04-x64",
|
||
|
|
count = 3,
|
||
|
|
vpc_uuid = "{{ networks.do_vpc.id }}",
|
||
|
|
firewall = {
|
||
|
|
inbound_rules = [
|
||
|
|
# SSH from management
|
||
|
|
{
|
||
|
|
protocol = "tcp",
|
||
|
|
ports = "22",
|
||
|
|
sources = {
|
||
|
|
addresses = ["0.0.0.0/0"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
# HTTP from load balancer
|
||
|
|
{
|
||
|
|
protocol = "tcp",
|
||
|
|
ports = "80",
|
||
|
|
sources = {
|
||
|
|
addresses = ["0.0.0.0/0"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
# HTTPS from load balancer
|
||
|
|
{
|
||
|
|
protocol = "tcp",
|
||
|
|
ports = "443",
|
||
|
|
sources = {
|
||
|
|
addresses = ["0.0.0.0/0"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
# Database access to AWS RDS
|
||
|
|
{
|
||
|
|
protocol = "tcp",
|
||
|
|
ports = "5432",
|
||
|
|
sources = {
|
||
|
|
addresses = ["10.0.0.0/8"] # AWS VPC CIDR
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
outbound_rules = [
|
||
|
|
{
|
||
|
|
protocol = "tcp",
|
||
|
|
destinations = {
|
||
|
|
addresses = ["0.0.0.0/0"]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
protocol = "udp",
|
||
|
|
ports = "53",
|
||
|
|
destinations = {
|
||
|
|
addresses = ["8.8.8.8/32", "8.8.4.4/32"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
tags = ["web", "production"]
|
||
|
|
},
|
||
|
|
|
||
|
|
# Load balancer for web tier
|
||
|
|
load_balancer = digitalocean.LoadBalancer & {
|
||
|
|
name = "web-lb",
|
||
|
|
algorithm = "round_robin",
|
||
|
|
region = "nyc3",
|
||
|
|
forwarding_rules = [
|
||
|
|
{
|
||
|
|
entry_protocol = "http",
|
||
|
|
entry_port = 80,
|
||
|
|
target_protocol = "http",
|
||
|
|
target_port = 80,
|
||
|
|
certificate_id = null
|
||
|
|
},
|
||
|
|
{
|
||
|
|
entry_protocol = "https",
|
||
|
|
entry_port = 443,
|
||
|
|
target_protocol = "http",
|
||
|
|
target_port = 80,
|
||
|
|
certificate_id = "cert-id"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
sticky_sessions = {
|
||
|
|
type = "cookies",
|
||
|
|
cookie_name = "LB_SESSION",
|
||
|
|
cookie_ttl_seconds = 300
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# ==========================================================================
|
||
|
|
# AWS: Database Tier (Managed RDS PostgreSQL)
|
||
|
|
# ==========================================================================
|
||
|
|
|
||
|
|
# Create security group for RDS
|
||
|
|
database_sg = aws.SecurityGroup & {
|
||
|
|
name = "webapp-db-sg",
|
||
|
|
description = "Security group for RDS database",
|
||
|
|
vpc_id = "{{ networks.aws_vpc.id }}",
|
||
|
|
ingress_rules = [
|
||
|
|
{
|
||
|
|
protocol = "tcp",
|
||
|
|
from_port = 5432,
|
||
|
|
to_port = 5432,
|
||
|
|
cidr_blocks = ["10.0.0.0/16"] # Allow from DO web tier via VPN
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
|
||
|
|
database_tier = aws.RDS & {
|
||
|
|
identifier = "webapp-db",
|
||
|
|
engine = "postgres",
|
||
|
|
engine_version = "14.6",
|
||
|
|
instance_class = "db.t3.medium",
|
||
|
|
allocated_storage = 100,
|
||
|
|
storage_type = "gp3",
|
||
|
|
multi_az = true,
|
||
|
|
publicly_accessible = false,
|
||
|
|
db_subnet_group_name = "default",
|
||
|
|
vpc_security_group_ids = ["{{ database_sg.id }}"],
|
||
|
|
backup_retention_days = 30,
|
||
|
|
skip_final_snapshot = false,
|
||
|
|
tags = [
|
||
|
|
{ key = "Environment", value = "production" },
|
||
|
|
{ key = "Application", value = "webapp" },
|
||
|
|
{ key = "Network", value = "private-vpc" }
|
||
|
|
]
|
||
|
|
},
|
||
|
|
|
||
|
|
# ==========================================================================
|
||
|
|
# Hetzner: Backup Storage (Volumes for periodic backups)
|
||
|
|
# ==========================================================================
|
||
|
|
backup_storage = hetzner.Volume & {
|
||
|
|
name = "webapp-backups",
|
||
|
|
size = 500,
|
||
|
|
location = "nbg1",
|
||
|
|
automount = false,
|
||
|
|
format = "ext4",
|
||
|
|
labels = { "purpose" = "backup" }
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# =============================================================================
|
||
|
|
# Example 2: Multi-Region High Availability
|
||
|
|
# =============================================================================
|
||
|
|
# Architecture:
|
||
|
|
# - Region 1 (US): DigitalOcean NYC with primary database
|
||
|
|
# - Region 2 (EU): Hetzner Germany with read replicas
|
||
|
|
# - Region 3 (APAC): AWS Singapore with read replicas
|
||
|
|
#
|
||
|
|
# Features:
|
||
|
|
# - Global load balancing with DNS failover
|
||
|
|
# - Async database replication (300s lag)
|
||
|
|
# - Geographic distribution for low latency
|
||
|
|
# - Automatic failover to secondary regions
|
||
|
|
|
||
|
|
multi_region_ha = {
|
||
|
|
workspace_name = "multi-region-ha",
|
||
|
|
description = "High availability across three geographic regions",
|
||
|
|
|
||
|
|
global_config = {
|
||
|
|
dns_provider = "route53",
|
||
|
|
ttl = 60,
|
||
|
|
health_check_interval = 30,
|
||
|
|
failover_strategy = "geographic"
|
||
|
|
},
|
||
|
|
|
||
|
|
# Primary Region: US East (DigitalOcean)
|
||
|
|
region_us_east = {
|
||
|
|
provider = "digitalocean",
|
||
|
|
region_name = "us-east",
|
||
|
|
location = "nyc3",
|
||
|
|
|
||
|
|
servers = digitalocean.Droplet & {
|
||
|
|
name = "us-app",
|
||
|
|
region = "nyc3",
|
||
|
|
size = "s-2vcpu-4gb",
|
||
|
|
image = "ubuntu-22-04-x64",
|
||
|
|
count = 3,
|
||
|
|
tags = ["primary", "us-east"]
|
||
|
|
},
|
||
|
|
|
||
|
|
database = digitalocean.Database & {
|
||
|
|
name = "us-db",
|
||
|
|
engine = "pg",
|
||
|
|
version = "14",
|
||
|
|
size = "db-s-2vcpu-4gb",
|
||
|
|
region = "nyc3",
|
||
|
|
num_nodes = 3, # 3-node cluster for HA
|
||
|
|
multi_az = true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# Secondary Region: EU Central (Hetzner)
|
||
|
|
region_eu_central = {
|
||
|
|
provider = "hetzner",
|
||
|
|
region_name = "eu-central",
|
||
|
|
location = "nbg1",
|
||
|
|
|
||
|
|
servers = hetzner.Server & {
|
||
|
|
name = "eu-app",
|
||
|
|
server_type = "cx31",
|
||
|
|
image = "ubuntu-22.04",
|
||
|
|
location = "nbg1",
|
||
|
|
count = 3,
|
||
|
|
labels = { "region" = "eu-central" }
|
||
|
|
},
|
||
|
|
|
||
|
|
# Read-only replica database
|
||
|
|
database_replica = {
|
||
|
|
replication_lag_seconds = 300,
|
||
|
|
read_only = true,
|
||
|
|
replica_of = "region_us_east.database"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# Tertiary Region: APAC (AWS)
|
||
|
|
region_asia_southeast = {
|
||
|
|
provider = "aws",
|
||
|
|
region_name = "asia-southeast",
|
||
|
|
location = "ap-southeast-1",
|
||
|
|
|
||
|
|
servers = aws.EC2 & {
|
||
|
|
name = "asia-app",
|
||
|
|
instance_type = "t3.medium",
|
||
|
|
image_id = "ami-xxxxxxxxx",
|
||
|
|
count = 3,
|
||
|
|
region = "ap-southeast-1",
|
||
|
|
tags = [
|
||
|
|
{ key = "Region", value = "asia-southeast" }
|
||
|
|
]
|
||
|
|
},
|
||
|
|
|
||
|
|
# Read-only replica database
|
||
|
|
database_replica = {
|
||
|
|
replication_lag_seconds = 300,
|
||
|
|
read_only = true,
|
||
|
|
replica_of = "region_us_east.database"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# Global DNS configuration
|
||
|
|
global_load_balancing = {
|
||
|
|
dns_zones = ["example.com"],
|
||
|
|
|
||
|
|
records = [
|
||
|
|
{
|
||
|
|
name = "us.example.com",
|
||
|
|
type = "A",
|
||
|
|
ttl = 60,
|
||
|
|
failover_primary = true,
|
||
|
|
health_check = {
|
||
|
|
protocol = "HTTPS",
|
||
|
|
path = "/health",
|
||
|
|
interval = 30
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name = "eu.example.com",
|
||
|
|
type = "A",
|
||
|
|
ttl = 60,
|
||
|
|
failover_secondary = true,
|
||
|
|
health_check = {
|
||
|
|
protocol = "HTTPS",
|
||
|
|
path = "/health",
|
||
|
|
interval = 30
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name = "asia.example.com",
|
||
|
|
type = "A",
|
||
|
|
ttl = 60,
|
||
|
|
failover_tertiary = true,
|
||
|
|
health_check = {
|
||
|
|
protocol = "HTTPS",
|
||
|
|
path = "/health",
|
||
|
|
interval = 30
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
|
||
|
|
geolocation_routing = {
|
||
|
|
north_america = "us.example.com",
|
||
|
|
europe = "eu.example.com",
|
||
|
|
asia_pacific = "asia.example.com"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# =============================================================================
|
||
|
|
# Example 3: Cost-Optimized Deployment
|
||
|
|
# =============================================================================
|
||
|
|
# Architecture:
|
||
|
|
# - Hetzner: Application compute (best price/performance)
|
||
|
|
# - AWS: Managed services (RDS, ElastiCache, SQS)
|
||
|
|
# - DigitalOcean: CDN and edge locations (cost-effective distribution)
|
||
|
|
#
|
||
|
|
# Cost Optimization:
|
||
|
|
# - Compute on Hetzner (€50/month vs $300 on AWS)
|
||
|
|
# - Managed services on AWS (reliability + managed ops)
|
||
|
|
# - CDN on DigitalOcean (cheaper than CloudFront)
|
||
|
|
#
|
||
|
|
# Monthly Cost Breakdown:
|
||
|
|
# - Hetzner: €50 for 5 CPX21 servers
|
||
|
|
# - AWS: $150 for RDS + ElastiCache + SQS
|
||
|
|
# - DigitalOcean: $50 for CDN and edge nodes
|
||
|
|
# Total: ~$250/month (vs $600+ for all-AWS)
|
||
|
|
|
||
|
|
cost_optimized_deployment = {
|
||
|
|
workspace_name = "cost-optimized",
|
||
|
|
description = "Optimized deployment using provider strengths",
|
||
|
|
|
||
|
|
# ==========================================================================
|
||
|
|
# Hetzner: Application Tier (Best compute pricing)
|
||
|
|
# ==========================================================================
|
||
|
|
compute_tier = {
|
||
|
|
# Main application servers
|
||
|
|
primary_servers = hetzner.Server & {
|
||
|
|
name = "app",
|
||
|
|
server_type = "cpx21", # 4 vCPU, 8GB RAM, 10Gbps - €20.90/month
|
||
|
|
image = "ubuntu-22.04",
|
||
|
|
location = "nbg1",
|
||
|
|
count = 3,
|
||
|
|
labels = {
|
||
|
|
"tier" = "application",
|
||
|
|
"cost_center" = "compute"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# Storage node for shared data
|
||
|
|
storage_node = hetzner.Server & {
|
||
|
|
name = "storage",
|
||
|
|
server_type = "cx41", # 4 vCPU, 16GB RAM
|
||
|
|
image = "ubuntu-22.04",
|
||
|
|
location = "nbg1",
|
||
|
|
volumes = [
|
||
|
|
{
|
||
|
|
size = 1000,
|
||
|
|
format = "ext4",
|
||
|
|
automount = true
|
||
|
|
}
|
||
|
|
],
|
||
|
|
labels = {
|
||
|
|
"tier" = "storage",
|
||
|
|
"cost_center" = "storage"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# ==========================================================================
|
||
|
|
# AWS: Managed Services Tier
|
||
|
|
# ==========================================================================
|
||
|
|
managed_services = {
|
||
|
|
# Database
|
||
|
|
database = aws.RDS & {
|
||
|
|
identifier = "app-db",
|
||
|
|
engine = "postgres",
|
||
|
|
engine_version = "14.6",
|
||
|
|
instance_class = "db.t3.small",
|
||
|
|
allocated_storage = 100,
|
||
|
|
multi_az = true
|
||
|
|
},
|
||
|
|
|
||
|
|
# Cache
|
||
|
|
cache = aws.ElastiCache & {
|
||
|
|
cluster_id = "app-cache",
|
||
|
|
engine = "redis",
|
||
|
|
engine_version = "7.0",
|
||
|
|
node_type = "cache.t3.small",
|
||
|
|
num_cache_nodes = 2,
|
||
|
|
automatic_failover_enabled = true
|
||
|
|
},
|
||
|
|
|
||
|
|
# Message Queue
|
||
|
|
queue = aws.SQS & {
|
||
|
|
queue_name = "app-queue",
|
||
|
|
visibility_timeout_seconds = 300,
|
||
|
|
message_retention_seconds = 1209600
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# ==========================================================================
|
||
|
|
# DigitalOcean: CDN and Edge Tier
|
||
|
|
# ==========================================================================
|
||
|
|
cdn_tier = {
|
||
|
|
# CDN distribution
|
||
|
|
cdn = {
|
||
|
|
provider = "digitalocean",
|
||
|
|
endpoints = [
|
||
|
|
{
|
||
|
|
name = "app-cdn",
|
||
|
|
origin = "content.example.com",
|
||
|
|
regions = ["nyc1", "sfo1", "lon1", "sgp1", "blr1"],
|
||
|
|
cache_control = {
|
||
|
|
browser_cache_ttl = 3600,
|
||
|
|
cdn_cache_ttl = 86400
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
|
||
|
|
# Edge nodes for regional content delivery
|
||
|
|
edge_nodes = digitalocean.Droplet & {
|
||
|
|
name = "edge-node",
|
||
|
|
regions = ["nyc3", "sfo3", "lon1", "sgp1"],
|
||
|
|
size = "s-1vcpu-1gb",
|
||
|
|
image = "ubuntu-22-04-x64",
|
||
|
|
count = 1, # 1 per region
|
||
|
|
tags = ["edge", "cdn"]
|
||
|
|
},
|
||
|
|
|
||
|
|
# Spaces for object storage
|
||
|
|
object_storage = {
|
||
|
|
provider = "digitalocean",
|
||
|
|
space_name = "app-content",
|
||
|
|
region = "nyc3",
|
||
|
|
versioning = true,
|
||
|
|
lifecycle_rules = [
|
||
|
|
{
|
||
|
|
id = "delete-old-files",
|
||
|
|
days = 90,
|
||
|
|
action = "delete"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
# Cost model
|
||
|
|
cost_model = {
|
||
|
|
monthly_estimate = {
|
||
|
|
hetzner = {
|
||
|
|
primary_servers = "€62.70", # 3x CPX21 @ €20.90
|
||
|
|
storage_node = "€27.60", # 1x CX41
|
||
|
|
subtotal = "€90.30"
|
||
|
|
},
|
||
|
|
|
||
|
|
aws = {
|
||
|
|
database = "$60",
|
||
|
|
cache = "$25",
|
||
|
|
queue = "$15",
|
||
|
|
subtotal = "$100"
|
||
|
|
},
|
||
|
|
|
||
|
|
digitalocean = {
|
||
|
|
cdn = "$25",
|
||
|
|
edge_nodes = "$24", # 4x $6 droplets
|
||
|
|
object_storage = "$15",
|
||
|
|
subtotal = "$64"
|
||
|
|
},
|
||
|
|
|
||
|
|
total = "€90.30 + $100 + $64 ≈ $280/month"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|