2026-01-08 09:55:37 +00:00
|
|
|
# Multi-Provider Batch Workflow Examples
|
|
|
|
|
|
2026-01-12 04:42:18 +00:00
|
|
|
This document provides practical examples of orchestrating complex deployments and operations across multiple cloud providers using the batch workflow
|
|
|
|
|
system.
|
2026-01-08 09:55:37 +00:00
|
|
|
|
|
|
|
|
## Table of Contents
|
|
|
|
|
|
|
|
|
|
- [Overview](#overview)
|
|
|
|
|
- [Workflow 1: Coordinated Multi-Provider Deployment](#workflow-1-coordinated-multi-provider-deployment)
|
|
|
|
|
- [Workflow 2: Multi-Provider Disaster Recovery Failover](#workflow-2-multi-provider-disaster-recovery-failover)
|
|
|
|
|
- [Workflow 3: Cost Optimization Workload Migration](#workflow-3-cost-optimization-workload-migration)
|
|
|
|
|
- [Workflow 4: Multi-Region Database Replication](#workflow-4-multi-region-database-replication)
|
|
|
|
|
- [Best Practices](#best-practices)
|
|
|
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
|
|
The batch workflow system enables declarative orchestration of operations across multiple providers with:
|
|
|
|
|
|
|
|
|
|
- **Dependency Tracking**: Define what must complete before what
|
|
|
|
|
- **Error Handling**: Automatic rollback on failure
|
|
|
|
|
- **Idempotency**: Safe to re-run workflows
|
|
|
|
|
- **Status Tracking**: Real-time progress monitoring
|
|
|
|
|
- **Recovery Checkpoints**: Resume from failure points
|
|
|
|
|
|
|
|
|
|
## Workflow 1: Coordinated Multi-Provider Deployment
|
|
|
|
|
|
|
|
|
|
**Use Case**: Deploy web application across DigitalOcean, AWS, and Hetzner with proper sequencing and dependencies.
|
|
|
|
|
|
|
|
|
|
**Workflow Characteristics**:
|
|
|
|
|
- Database created first (dependencies)
|
|
|
|
|
- Backup storage ready before compute
|
|
|
|
|
- Web servers scale once database ready
|
|
|
|
|
- Health checks before considering complete
|
|
|
|
|
|
|
|
|
|
### Workflow Definition
|
|
|
|
|
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
# file: workflows/multi-provider-deployment.yml
|
|
|
|
|
|
|
|
|
|
name: multi-provider-app-deployment
|
|
|
|
|
version: "1.0"
|
|
|
|
|
description: "Deploy web app across three cloud providers"
|
|
|
|
|
|
|
|
|
|
parameters:
|
|
|
|
|
do_region: "nyc3"
|
|
|
|
|
aws_region: "us-east-1"
|
|
|
|
|
hetzner_location: "nbg1"
|
|
|
|
|
web_server_count: 3
|
|
|
|
|
|
|
|
|
|
phases:
|
|
|
|
|
# Phase 1: Create backup storage first (independent)
|
|
|
|
|
- name: "provision-backup-storage"
|
|
|
|
|
provider: "hetzner"
|
|
|
|
|
description: "Create backup storage volume in Hetzner"
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "create-backup-volume"
|
|
|
|
|
action: "create-volume"
|
|
|
|
|
config:
|
|
|
|
|
name: "webapp-backups"
|
|
|
|
|
size: 500
|
|
|
|
|
location: "{{ hetzner_location }}"
|
|
|
|
|
format: "ext4"
|
|
|
|
|
|
|
|
|
|
tags: ["storage", "backup"]
|
|
|
|
|
|
|
|
|
|
on_failure: "alert"
|
|
|
|
|
on_success: "proceed"
|
|
|
|
|
|
|
|
|
|
# Phase 2: Create database (independent, but must complete before app)
|
|
|
|
|
- name: "provision-database"
|
|
|
|
|
provider: "aws"
|
|
|
|
|
description: "Create managed PostgreSQL database"
|
|
|
|
|
depends_on: [] # Can run in parallel with Phase 1
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "create-rds-instance"
|
|
|
|
|
action: "create-db-instance"
|
|
|
|
|
config:
|
|
|
|
|
identifier: "webapp-db"
|
|
|
|
|
engine: "postgres"
|
|
|
|
|
engine_version: "14.6"
|
|
|
|
|
instance_class: "db.t3.medium"
|
|
|
|
|
allocated_storage: 100
|
|
|
|
|
multi_az: true
|
|
|
|
|
backup_retention_days: 30
|
|
|
|
|
|
|
|
|
|
tags: ["database", "primary"]
|
|
|
|
|
|
|
|
|
|
- id: "create-security-group"
|
|
|
|
|
action: "create-security-group"
|
|
|
|
|
config:
|
|
|
|
|
name: "webapp-db-sg"
|
|
|
|
|
description: "Security group for RDS"
|
|
|
|
|
|
|
|
|
|
depends_on: ["create-rds-instance"]
|
|
|
|
|
|
|
|
|
|
- id: "configure-db-access"
|
|
|
|
|
action: "authorize-security-group"
|
|
|
|
|
config:
|
|
|
|
|
group_id: "{{ create-security-group.id }}"
|
|
|
|
|
protocol: "tcp"
|
|
|
|
|
port: 5432
|
|
|
|
|
cidr: "10.0.0.0/8"
|
|
|
|
|
|
|
|
|
|
depends_on: ["create-security-group"]
|
|
|
|
|
|
|
|
|
|
timeout: 60
|
|
|
|
|
|
|
|
|
|
# Phase 3: Create web tier (depends on database being ready)
|
|
|
|
|
- name: "provision-web-tier"
|
|
|
|
|
provider: "digitalocean"
|
|
|
|
|
description: "Create web servers and load balancer"
|
|
|
|
|
depends_on: ["provision-database"] # Wait for database
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "create-droplets"
|
|
|
|
|
action: "create-droplet"
|
|
|
|
|
config:
|
|
|
|
|
name: "web-server"
|
|
|
|
|
size: "s-2vcpu-4gb"
|
|
|
|
|
region: "{{ do_region }}"
|
|
|
|
|
image: "ubuntu-22-04-x64"
|
|
|
|
|
count: "{{ web_server_count }}"
|
|
|
|
|
backups: true
|
|
|
|
|
monitoring: true
|
|
|
|
|
|
|
|
|
|
tags: ["web", "production"]
|
|
|
|
|
|
|
|
|
|
timeout: 300
|
|
|
|
|
retry:
|
|
|
|
|
max_attempts: 3
|
|
|
|
|
backoff: exponential
|
|
|
|
|
|
|
|
|
|
- id: "create-firewall"
|
|
|
|
|
action: "create-firewall"
|
|
|
|
|
config:
|
|
|
|
|
name: "web-firewall"
|
|
|
|
|
inbound_rules:
|
|
|
|
|
- protocol: "tcp"
|
|
|
|
|
ports: "22"
|
|
|
|
|
sources: ["0.0.0.0/0"]
|
|
|
|
|
- protocol: "tcp"
|
|
|
|
|
ports: "80"
|
|
|
|
|
sources: ["0.0.0.0/0"]
|
|
|
|
|
- protocol: "tcp"
|
|
|
|
|
ports: "443"
|
|
|
|
|
sources: ["0.0.0.0/0"]
|
|
|
|
|
|
|
|
|
|
depends_on: ["create-droplets"]
|
|
|
|
|
|
|
|
|
|
- id: "create-load-balancer"
|
|
|
|
|
action: "create-load-balancer"
|
|
|
|
|
config:
|
|
|
|
|
name: "web-lb"
|
|
|
|
|
algorithm: "round_robin"
|
|
|
|
|
region: "{{ do_region }}"
|
|
|
|
|
forwarding_rules:
|
|
|
|
|
- entry_protocol: "http"
|
|
|
|
|
entry_port: 80
|
|
|
|
|
target_protocol: "http"
|
|
|
|
|
target_port: 80
|
|
|
|
|
- entry_protocol: "https"
|
|
|
|
|
entry_port: 443
|
|
|
|
|
target_protocol: "http"
|
|
|
|
|
target_port: 80
|
|
|
|
|
health_check:
|
|
|
|
|
protocol: "http"
|
|
|
|
|
port: 80
|
|
|
|
|
path: "/health"
|
|
|
|
|
interval: 10
|
|
|
|
|
|
|
|
|
|
depends_on: ["create-droplets"]
|
|
|
|
|
|
|
|
|
|
# Phase 4: Network configuration (depends on all resources)
|
|
|
|
|
- name: "configure-networking"
|
|
|
|
|
description: "Setup VPN tunnels and security between providers"
|
|
|
|
|
depends_on: ["provision-web-tier"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "setup-vpn-tunnel-do-aws"
|
|
|
|
|
action: "create-vpn-tunnel"
|
|
|
|
|
config:
|
|
|
|
|
source_provider: "digitalocean"
|
|
|
|
|
destination_provider: "aws"
|
|
|
|
|
protocol: "ipsec"
|
|
|
|
|
encryption: "aes-256"
|
|
|
|
|
|
|
|
|
|
timeout: 120
|
|
|
|
|
|
|
|
|
|
- id: "setup-vpn-tunnel-aws-hetzner"
|
|
|
|
|
action: "create-vpn-tunnel"
|
|
|
|
|
config:
|
|
|
|
|
source_provider: "aws"
|
|
|
|
|
destination_provider: "hetzner"
|
|
|
|
|
protocol: "ipsec"
|
|
|
|
|
encryption: "aes-256"
|
|
|
|
|
|
|
|
|
|
# Phase 5: Validation and verification
|
|
|
|
|
- name: "verify-deployment"
|
|
|
|
|
description: "Verify all resources are operational"
|
|
|
|
|
depends_on: ["configure-networking"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "health-check-droplets"
|
|
|
|
|
action: "run-health-check"
|
|
|
|
|
config:
|
|
|
|
|
targets: "{{ create-droplets.ips }}"
|
|
|
|
|
endpoint: "/health"
|
|
|
|
|
expected_status: 200
|
|
|
|
|
timeout: 30
|
|
|
|
|
|
|
|
|
|
timeout: 300
|
|
|
|
|
|
|
|
|
|
- id: "health-check-database"
|
|
|
|
|
action: "verify-database"
|
|
|
|
|
config:
|
|
|
|
|
host: "{{ create-rds-instance.endpoint }}"
|
|
|
|
|
port: 5432
|
|
|
|
|
database: "postgres"
|
|
|
|
|
timeout: 30
|
|
|
|
|
|
|
|
|
|
- id: "health-check-backup"
|
|
|
|
|
action: "verify-volume"
|
|
|
|
|
config:
|
|
|
|
|
volume_id: "{{ create-backup-volume.id }}"
|
|
|
|
|
status: "available"
|
|
|
|
|
|
|
|
|
|
# Rollback strategy: if any phase fails
|
|
|
|
|
rollback:
|
|
|
|
|
strategy: "automatic"
|
|
|
|
|
on_phase_failure: "rollback-previous-phases"
|
|
|
|
|
preserve_data: true
|
|
|
|
|
|
|
|
|
|
# Notifications
|
|
|
|
|
notifications:
|
|
|
|
|
on_start: "slack:#deployments"
|
|
|
|
|
on_phase_complete: "slack:#deployments"
|
|
|
|
|
on_failure: "slack:#alerts"
|
|
|
|
|
on_success: "slack:#deployments"
|
|
|
|
|
|
|
|
|
|
# Validation checks
|
|
|
|
|
pre_flight:
|
|
|
|
|
- check: "credentials"
|
|
|
|
|
description: "Verify all provider credentials"
|
|
|
|
|
- check: "quotas"
|
|
|
|
|
description: "Verify sufficient quotas in each provider"
|
|
|
|
|
- check: "dependencies"
|
|
|
|
|
description: "Verify all dependencies are available"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Execution Flow
|
|
|
|
|
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
┌─────────────────────────────────────────────────────────┐
|
|
|
|
|
│ Start Deployment │
|
|
|
|
|
└──────────────────┬──────────────────────────────────────┘
|
|
|
|
|
│
|
|
|
|
|
┌──────────┴──────────┐
|
|
|
|
|
│ │
|
|
|
|
|
▼ ▼
|
|
|
|
|
┌─────────────┐ ┌──────────────────┐
|
|
|
|
|
│ Hetzner │ │ AWS │
|
|
|
|
|
│ Backup │ │ Database │
|
|
|
|
|
│ (Phase 1) │ │ (Phase 2) │
|
|
|
|
|
└──────┬──────┘ └────────┬─────────┘
|
|
|
|
|
│ │
|
|
|
|
|
│ Ready │ Ready
|
|
|
|
|
└────────┬───────────┘
|
|
|
|
|
│
|
|
|
|
|
▼
|
|
|
|
|
┌──────────────────┐
|
|
|
|
|
│ DigitalOcean │
|
|
|
|
|
│ Web Tier │
|
|
|
|
|
│ (Phase 3) │
|
|
|
|
|
│ - Droplets │
|
|
|
|
|
│ - Firewall │
|
|
|
|
|
│ - Load Balancer │
|
|
|
|
|
└────────┬─────────┘
|
|
|
|
|
│
|
|
|
|
|
▼
|
|
|
|
|
┌──────────────────┐
|
|
|
|
|
│ Network Setup │
|
|
|
|
|
│ (Phase 4) │
|
|
|
|
|
│ - VPN Tunnels │
|
|
|
|
|
└────────┬─────────┘
|
|
|
|
|
│
|
|
|
|
|
▼
|
|
|
|
|
┌──────────────────┐
|
|
|
|
|
│ Verification │
|
|
|
|
|
│ (Phase 5) │
|
|
|
|
|
│ - Health Checks │
|
|
|
|
|
└────────┬─────────┘
|
|
|
|
|
│
|
|
|
|
|
▼
|
|
|
|
|
┌──────────────────┐
|
|
|
|
|
│ Deployment OK │
|
|
|
|
|
│ (Ready to use) │
|
|
|
|
|
└──────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Workflow 2: Multi-Provider Disaster Recovery Failover
|
|
|
|
|
|
|
|
|
|
**Use Case**: Automated failover from primary provider (DigitalOcean) to backup provider (Hetzner) on detection of failure.
|
|
|
|
|
|
|
|
|
|
**Workflow Characteristics**:
|
|
|
|
|
- Continuous health monitoring
|
|
|
|
|
- Automatic failover trigger
|
|
|
|
|
- Database promotion
|
|
|
|
|
- DNS update
|
|
|
|
|
- Verification before considering complete
|
|
|
|
|
|
|
|
|
|
### Workflow Definition
|
|
|
|
|
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
# file: workflows/multi-provider-dr-failover.yml
|
|
|
|
|
|
|
|
|
|
name: multi-provider-dr-failover
|
|
|
|
|
version: "1.0"
|
|
|
|
|
description: "Automated failover from DigitalOcean to Hetzner"
|
|
|
|
|
|
|
|
|
|
parameters:
|
|
|
|
|
primary_provider: "digitalocean"
|
|
|
|
|
backup_provider: "hetzner"
|
|
|
|
|
dns_provider: "aws"
|
|
|
|
|
health_check_threshold: 3
|
|
|
|
|
|
|
|
|
|
phases:
|
|
|
|
|
# Phase 1: Monitor primary provider
|
|
|
|
|
- name: "monitor-primary"
|
|
|
|
|
description: "Continuous health monitoring of primary"
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "health-check-primary"
|
|
|
|
|
action: "run-health-check"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ primary_provider }}"
|
|
|
|
|
resources: ["web-servers", "load-balancer"]
|
|
|
|
|
checks:
|
|
|
|
|
- type: "http"
|
|
|
|
|
endpoint: "/health"
|
|
|
|
|
expected_status: 200
|
|
|
|
|
- type: "database"
|
|
|
|
|
host: "db.primary.example.com"
|
|
|
|
|
query: "SELECT 1"
|
|
|
|
|
- type: "connectivity"
|
|
|
|
|
test: "ping"
|
|
|
|
|
interval: 30 # Check every 30 seconds
|
|
|
|
|
|
|
|
|
|
timeout: 300
|
|
|
|
|
|
|
|
|
|
- id: "aggregate-health"
|
|
|
|
|
action: "aggregate-metrics"
|
|
|
|
|
config:
|
|
|
|
|
source: "{{ health-check-primary.results }}"
|
|
|
|
|
failure_threshold: 3 # 3 consecutive failures trigger failover
|
|
|
|
|
|
|
|
|
|
# Phase 2: Trigger failover (conditional on failure)
|
|
|
|
|
- name: "trigger-failover"
|
|
|
|
|
description: "Activate disaster recovery if primary fails"
|
|
|
|
|
depends_on: ["monitor-primary"]
|
|
|
|
|
condition: "{{ aggregate-health.status }} == 'FAILED'"
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "alert-on-failure"
|
|
|
|
|
action: "send-notification"
|
|
|
|
|
config:
|
|
|
|
|
type: "critical"
|
|
|
|
|
message: "Primary provider ({{ primary_provider }}) has failed. Initiating failover..."
|
|
|
|
|
recipients: ["ops-team@example.com", "slack:#alerts"]
|
|
|
|
|
|
|
|
|
|
- id: "enable-backup-infrastructure"
|
|
|
|
|
action: "scale-up"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ backup_provider }}"
|
|
|
|
|
target: "warm-standby-servers"
|
|
|
|
|
desired_count: 3
|
|
|
|
|
instance_type: "cx31"
|
|
|
|
|
|
|
|
|
|
timeout: 300
|
|
|
|
|
retry:
|
|
|
|
|
max_attempts: 3
|
|
|
|
|
|
|
|
|
|
- id: "promote-database-replica"
|
|
|
|
|
action: "promote-read-replica"
|
|
|
|
|
config:
|
|
|
|
|
provider: "aws"
|
|
|
|
|
replica_identifier: "backup-db-replica"
|
|
|
|
|
to_master: true
|
|
|
|
|
|
|
|
|
|
timeout: 600 # Allow time for promotion
|
|
|
|
|
|
|
|
|
|
# Phase 3: Network failover
|
|
|
|
|
- name: "network-failover"
|
|
|
|
|
description: "Switch traffic to backup provider"
|
|
|
|
|
depends_on: ["trigger-failover"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "update-load-balancer"
|
|
|
|
|
action: "reconfigure-load-balancer"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ dns_provider }}"
|
|
|
|
|
record: "api.example.com"
|
|
|
|
|
old_backend: "do-lb-{{ primary_provider }}"
|
|
|
|
|
new_backend: "hz-lb-{{ backup_provider }}"
|
|
|
|
|
|
|
|
|
|
- id: "update-dns"
|
|
|
|
|
action: "update-dns-record"
|
|
|
|
|
config:
|
|
|
|
|
provider: "route53"
|
|
|
|
|
record: "example.com"
|
|
|
|
|
old_value: "do-lb-ip"
|
|
|
|
|
new_value: "hz-lb-ip"
|
|
|
|
|
ttl: 60
|
|
|
|
|
|
|
|
|
|
- id: "update-cdn"
|
|
|
|
|
action: "update-cdn-origin"
|
|
|
|
|
config:
|
|
|
|
|
cdn_provider: "cloudfront"
|
|
|
|
|
distribution_id: "E123456789ABCDEF"
|
|
|
|
|
new_origin: "backup-lb.hetzner.com"
|
|
|
|
|
|
|
|
|
|
# Phase 4: Verify failover
|
|
|
|
|
- name: "verify-failover"
|
|
|
|
|
description: "Verify backup provider is operational"
|
|
|
|
|
depends_on: ["network-failover"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "health-check-backup"
|
|
|
|
|
action: "run-health-check"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ backup_provider }}"
|
|
|
|
|
resources: ["backup-servers"]
|
|
|
|
|
endpoint: "/health"
|
|
|
|
|
expected_status: 200
|
|
|
|
|
timeout: 30
|
|
|
|
|
|
|
|
|
|
timeout: 300
|
|
|
|
|
|
|
|
|
|
- id: "verify-database"
|
|
|
|
|
action: "verify-database"
|
|
|
|
|
config:
|
|
|
|
|
provider: "aws"
|
|
|
|
|
database: "backup-db-promoted"
|
|
|
|
|
query: "SELECT COUNT(*) FROM users"
|
|
|
|
|
expected_rows: "> 0"
|
|
|
|
|
|
|
|
|
|
- id: "verify-traffic"
|
|
|
|
|
action: "verify-traffic-flow"
|
|
|
|
|
config:
|
|
|
|
|
endpoint: "https://example.com"
|
|
|
|
|
expected_response_time: "< 500 ms"
|
|
|
|
|
expected_status: 200
|
|
|
|
|
|
|
|
|
|
# Phase 5: Activate backup fully
|
|
|
|
|
- name: "activate-backup"
|
|
|
|
|
description: "Run at full capacity on backup provider"
|
|
|
|
|
depends_on: ["verify-failover"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "scale-to-production"
|
|
|
|
|
action: "scale-up"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ backup_provider }}"
|
|
|
|
|
target: "all-backup-servers"
|
|
|
|
|
desired_count: 6
|
|
|
|
|
|
|
|
|
|
timeout: 600
|
|
|
|
|
|
|
|
|
|
- id: "configure-persistence"
|
|
|
|
|
action: "enable-persistence"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ backup_provider }}"
|
|
|
|
|
resources: ["backup-servers"]
|
|
|
|
|
persistence_type: "volume"
|
|
|
|
|
|
|
|
|
|
# Recovery strategy for primary restoration
|
|
|
|
|
recovery:
|
|
|
|
|
description: "Restore primary provider when recovered"
|
|
|
|
|
phases:
|
|
|
|
|
- name: "detect-primary-recovery"
|
|
|
|
|
operation: "health-check"
|
|
|
|
|
target: "primary-provider"
|
|
|
|
|
success_criteria: "3 consecutive successful checks"
|
|
|
|
|
|
|
|
|
|
- name: "resync-data"
|
|
|
|
|
operation: "database-resync"
|
|
|
|
|
direction: "backup-to-primary"
|
|
|
|
|
timeout: 3600
|
|
|
|
|
|
|
|
|
|
- name: "failback"
|
|
|
|
|
operation: "switch-traffic"
|
|
|
|
|
target: "primary-provider"
|
|
|
|
|
verification: "100% traffic restored"
|
|
|
|
|
|
|
|
|
|
# Notifications
|
|
|
|
|
notifications:
|
|
|
|
|
on_failover_start: "pagerduty:critical"
|
|
|
|
|
on_failover_complete: "slack:#ops"
|
|
|
|
|
on_failover_failed: ["pagerduty:critical", "email:cto@example.com"]
|
|
|
|
|
on_recovery_start: "slack:#ops"
|
|
|
|
|
on_recovery_complete: "slack:#ops"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Failover Timeline
|
|
|
|
|
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
Time Event
|
|
|
|
|
────────────────────────────────────────────────────
|
|
|
|
|
00:00 Health check detects failure (3 consecutive failures)
|
|
|
|
|
00:01 Alert sent to ops team
|
|
|
|
|
00:02 Backup infrastructure scaled to 3 servers
|
|
|
|
|
00:05 Database replica promoted to master
|
|
|
|
|
00:10 DNS updated (TTL=60s, propagation ~2 minutes)
|
|
|
|
|
00:12 Load balancer reconfigured
|
|
|
|
|
00:15 Traffic verified flowing through backup
|
|
|
|
|
00:20 Backup scaled to full production capacity (6 servers)
|
|
|
|
|
00:25 Fully operational on backup provider
|
|
|
|
|
|
|
|
|
|
Total RTO: 25 minutes (including DNS propagation)
|
|
|
|
|
Data loss (RPO): < 5 minutes (database replication lag)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Workflow 3: Cost Optimization Workload Migration
|
|
|
|
|
|
|
|
|
|
**Use Case**: Migrate running workloads to cheaper provider (DigitalOcean to Hetzner) for cost reduction.
|
|
|
|
|
|
|
|
|
|
**Workflow Characteristics**:
|
|
|
|
|
- Parallel deployment on target provider
|
|
|
|
|
- Gradual traffic migration
|
|
|
|
|
- Rollback capability
|
|
|
|
|
- Cost tracking
|
|
|
|
|
|
|
|
|
|
### Workflow Definition
|
|
|
|
|
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
# file: workflows/cost-optimization-migration.yml
|
|
|
|
|
|
|
|
|
|
name: cost-optimization-migration
|
|
|
|
|
version: "1.0"
|
|
|
|
|
description: "Migrate workload from DigitalOcean to Hetzner for cost savings"
|
|
|
|
|
|
|
|
|
|
parameters:
|
|
|
|
|
source_provider: "digitalocean"
|
|
|
|
|
target_provider: "hetzner"
|
|
|
|
|
migration_speed: "gradual" # or "aggressive"
|
|
|
|
|
traffic_split: [10, 25, 50, 75, 100] # Gradual percentages
|
|
|
|
|
|
|
|
|
|
phases:
|
|
|
|
|
# Phase 1: Create target infrastructure
|
|
|
|
|
- name: "create-target-infrastructure"
|
|
|
|
|
description: "Deploy identical workload on Hetzner"
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "provision-servers"
|
|
|
|
|
action: "create-server"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ target_provider }}"
|
|
|
|
|
name: "migration-app"
|
|
|
|
|
server_type: "cpx21" # Better price/performance than DO
|
|
|
|
|
count: 3
|
|
|
|
|
|
|
|
|
|
timeout: 300
|
|
|
|
|
|
|
|
|
|
# Phase 2: Verify target is ready
|
|
|
|
|
- name: "verify-target"
|
|
|
|
|
description: "Health checks on target infrastructure"
|
|
|
|
|
depends_on: ["create-target-infrastructure"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "health-check"
|
|
|
|
|
action: "run-health-check"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ target_provider }}"
|
|
|
|
|
endpoint: "/health"
|
|
|
|
|
|
|
|
|
|
timeout: 300
|
|
|
|
|
|
|
|
|
|
# Phase 3: Gradual traffic migration
|
|
|
|
|
- name: "migrate-traffic"
|
|
|
|
|
description: "Gradually shift traffic to target provider"
|
|
|
|
|
depends_on: ["verify-target"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "set-traffic-10"
|
|
|
|
|
action: "set-traffic-split"
|
|
|
|
|
config:
|
|
|
|
|
source: "{{ source_provider }}"
|
|
|
|
|
target: "{{ target_provider }}"
|
|
|
|
|
percentage: 10
|
|
|
|
|
duration: 300
|
|
|
|
|
|
|
|
|
|
- id: "verify-10"
|
|
|
|
|
action: "verify-traffic-flow"
|
|
|
|
|
config:
|
|
|
|
|
target_percentage: 10
|
|
|
|
|
error_rate_threshold: 0.1
|
|
|
|
|
|
|
|
|
|
- id: "set-traffic-25"
|
|
|
|
|
action: "set-traffic-split"
|
|
|
|
|
config:
|
|
|
|
|
percentage: 25
|
|
|
|
|
duration: 600
|
|
|
|
|
|
|
|
|
|
- id: "set-traffic-50"
|
|
|
|
|
action: "set-traffic-split"
|
|
|
|
|
config:
|
|
|
|
|
percentage: 50
|
|
|
|
|
duration: 900
|
|
|
|
|
|
|
|
|
|
- id: "set-traffic-75"
|
|
|
|
|
action: "set-traffic-split"
|
|
|
|
|
config:
|
|
|
|
|
percentage: 75
|
|
|
|
|
duration: 900
|
|
|
|
|
|
|
|
|
|
- id: "set-traffic-100"
|
|
|
|
|
action: "set-traffic-split"
|
|
|
|
|
config:
|
|
|
|
|
percentage: 100
|
|
|
|
|
duration: 600
|
|
|
|
|
|
|
|
|
|
# Phase 4: Cleanup source
|
|
|
|
|
- name: "cleanup-source"
|
|
|
|
|
description: "Remove old infrastructure from source provider"
|
|
|
|
|
depends_on: ["migrate-traffic"]
|
|
|
|
|
|
|
|
|
|
operations:
|
|
|
|
|
- id: "verify-final"
|
|
|
|
|
action: "run-health-check"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ target_provider }}"
|
|
|
|
|
duration: 3600 # Monitor for 1 hour
|
|
|
|
|
|
|
|
|
|
- id: "decommission-source"
|
|
|
|
|
action: "delete-resources"
|
|
|
|
|
config:
|
|
|
|
|
provider: "{{ source_provider }}"
|
|
|
|
|
resources: ["droplets", "load-balancer"]
|
|
|
|
|
preserve_backups: true
|
|
|
|
|
|
|
|
|
|
# Cost tracking
|
|
|
|
|
cost_tracking:
|
|
|
|
|
before:
|
|
|
|
|
provider: "{{ source_provider }}"
|
|
|
|
|
estimated_monthly: "$72"
|
|
|
|
|
|
|
|
|
|
after:
|
|
|
|
|
provider: "{{ target_provider }}"
|
|
|
|
|
estimated_monthly: "$42"
|
|
|
|
|
|
|
|
|
|
savings:
|
|
|
|
|
monthly: "$30"
|
|
|
|
|
annual: "$360"
|
|
|
|
|
percentage: "42%"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Workflow 4: Multi-Region Database Replication
|
|
|
|
|
|
|
|
|
|
**Use Case**: Setup database replication across multiple providers and regions for disaster recovery.
|
|
|
|
|
|
|
|
|
|
**Workflow Characteristics**:
|
|
|
|
|
- Create primary database
|
|
|
|
|
- Setup read replicas in other providers
|
|
|
|
|
- Configure replication
|
|
|
|
|
- Monitor lag
|
|
|
|
|
|
|
|
|
|
### Workflow Definition
|
|
|
|
|
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
# file: workflows/multi-region-replication.yml
|
|
|
|
|
|
|
|
|
|
name: multi-region-replication
|
|
|
|
|
version: "1.0"
|
|
|
|
|
description: "Setup database replication across providers"
|
|
|
|
|
|
|
|
|
|
phases:
|
|
|
|
|
# Primary database
|
|
|
|
|
- name: "create-primary"
|
|
|
|
|
provider: "aws"
|
|
|
|
|
operations:
|
|
|
|
|
- id: "create-rds"
|
|
|
|
|
action: "create-db-instance"
|
|
|
|
|
config:
|
|
|
|
|
identifier: "app-db-primary"
|
|
|
|
|
engine: "postgres"
|
|
|
|
|
instance_class: "db.t3.medium"
|
|
|
|
|
region: "us-east-1"
|
|
|
|
|
|
|
|
|
|
# Secondary replica
|
|
|
|
|
- name: "create-secondary-replica"
|
|
|
|
|
depends_on: ["create-primary"]
|
|
|
|
|
provider: "aws"
|
|
|
|
|
operations:
|
|
|
|
|
- id: "create-replica"
|
|
|
|
|
action: "create-read-replica"
|
|
|
|
|
config:
|
|
|
|
|
source: "app-db-primary"
|
|
|
|
|
region: "eu-west-1"
|
|
|
|
|
identifier: "app-db-secondary"
|
|
|
|
|
|
|
|
|
|
# Tertiary replica in different provider
|
|
|
|
|
- name: "create-tertiary-replica"
|
|
|
|
|
depends_on: ["create-primary"]
|
|
|
|
|
operations:
|
|
|
|
|
- id: "setup-replication"
|
|
|
|
|
action: "setup-external-replication"
|
|
|
|
|
config:
|
|
|
|
|
source_provider: "aws"
|
|
|
|
|
source_db: "app-db-primary"
|
|
|
|
|
target_provider: "hetzner"
|
|
|
|
|
replication_slot: "hetzner_replica"
|
|
|
|
|
replication_type: "logical"
|
|
|
|
|
|
|
|
|
|
# Monitor replication
|
|
|
|
|
- name: "monitor-replication"
|
|
|
|
|
depends_on: ["create-tertiary-replica"]
|
|
|
|
|
operations:
|
|
|
|
|
- id: "check-lag"
|
|
|
|
|
action: "monitor-replication-lag"
|
|
|
|
|
config:
|
|
|
|
|
replicas:
|
|
|
|
|
- name: "secondary"
|
|
|
|
|
warning_threshold: 300
|
|
|
|
|
critical_threshold: 600
|
|
|
|
|
- name: "tertiary"
|
|
|
|
|
warning_threshold: 1000
|
|
|
|
|
critical_threshold: 2000
|
|
|
|
|
interval: 60
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Best Practices
|
|
|
|
|
|
|
|
|
|
### 1. Workflow Design
|
|
|
|
|
|
|
|
|
|
- **Define Clear Dependencies**: Explicitly state what must happen before what
|
|
|
|
|
- **Use Idempotent Operations**: Workflows should be safe to re-run
|
|
|
|
|
- **Set Realistic Timeouts**: Account for cloud provider delays
|
|
|
|
|
- **Plan for Failures**: Define rollback strategies
|
|
|
|
|
- **Test Workflows**: Run in staging before production
|
|
|
|
|
|
|
|
|
|
### 2. Orchestration
|
|
|
|
|
|
|
|
|
|
- **Parallel Execution**: Run independent phases in parallel for speed
|
|
|
|
|
- **Checkpoints**: Add verification at each phase
|
|
|
|
|
- **Progressive Deployment**: Use gradual traffic shifting
|
|
|
|
|
- **Monitoring Integration**: Track metrics during workflow
|
|
|
|
|
- **Notifications**: Alert team at key points
|
|
|
|
|
|
|
|
|
|
### 3. Cost Management
|
|
|
|
|
|
|
|
|
|
- **Calculate ROI**: Track cost savings from optimizations
|
|
|
|
|
- **Monitor Resource Usage**: Watch for over-provisioning
|
|
|
|
|
- **Implement Cleanup**: Remove old resources after migration
|
|
|
|
|
- **Review Regularly**: Reassess provider choices
|
|
|
|
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
|
|
|
|
|
|
### Issue: Workflow Stuck in Phase
|
|
|
|
|
|
|
|
|
|
**Diagnosis**:
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
provisioning workflow status workflow-id --verbose
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Solution**:
|
|
|
|
|
- Increase timeout if legitimate long operation
|
|
|
|
|
- Check provider logs for actual status
|
|
|
|
|
- Manually intervene if necessary
|
|
|
|
|
- Use `--skip-phase` to skip problematic phase
|
|
|
|
|
|
|
|
|
|
### Issue: Rollback Failed
|
|
|
|
|
|
|
|
|
|
**Diagnosis**:
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
provisioning workflow rollback workflow-id --dry-run
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Solution**:
|
|
|
|
|
- Review what resources were created
|
|
|
|
|
- Manually delete resources if needed
|
|
|
|
|
- Fix root cause of failure
|
|
|
|
|
- Re-run workflow
|
|
|
|
|
|
|
|
|
|
### Issue: Data Inconsistency After Failover
|
|
|
|
|
|
|
|
|
|
**Diagnosis**:
|
2026-01-14 01:56:30 +00:00
|
|
|
```
|
2026-01-08 09:55:37 +00:00
|
|
|
provisioning database verify-consistency
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Solution**:
|
|
|
|
|
- Check replication lag before failover
|
|
|
|
|
- Manually resync if necessary
|
|
|
|
|
- Use backup to restore consistency
|
|
|
|
|
- Run validation queries
|
|
|
|
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
|
|
|
|
Batch workflows enable complex multi-provider orchestration with:
|
|
|
|
|
|
|
|
|
|
- Coordinated deployment across providers
|
|
|
|
|
- Automated failover and recovery
|
|
|
|
|
- Gradual workload migration
|
|
|
|
|
- Cost optimization
|
|
|
|
|
- Disaster recovery
|
|
|
|
|
|
|
|
|
|
Start with simple workflows and gradually add complexity as you gain confidence.
|