Skip to content

ImmrBhattarai/cloud-migration-spot

Repository files navigation

Cross-Cloud Migration Project: GCP to Azure (Spot Instances)

This project demonstrates a production-style migration of a stateless, interruptible workload from Google Cloud Platform (GCP) to Microsoft Azure. The goal is to migrate a containerized application running on GCP Preemptible VMs to Azure Spot VMs, including migrating the object storage data, while maintaining minimal cost and using industry-standard practices. This project is referenced from the client work type I performed at my last job.

Medium Article

For detailed step breakdown, follow this Medium Article I wrote about this project: Medium Article

Screenshot From 2025-12-05 21-45-10 Screenshot From 2025-12-05 21-53-55

🏗 Architecture & Migration Story

The workload consists of an API service (FastAPI) that accepts image processing jobs and a Worker service that processes images (grayscale conversion) in the background.

  • Design Pattern: Stateless, "Spot-friendly" architecture. State is decoupled from compute and stored in object storage.
  • Source Cloud (GCP): 2x Preemptible VMs (API & Worker) + Google Cloud Storage (GCS).
  • Target Cloud (Azure): 2x Spot VMs (API & Worker) + Azure Blob Storage.
  • Migration Strategy: "Re-platform / Re-deploy". We deploy the same Docker containers to the target infrastructure and perform a data sync for the storage layer.

Architecture Diagram

flowchart TB
    subgraph GCP ["Google Cloud Platform (Source)"]
        direction TB
        GCP_LB[("Google Cloud Storage")]
        subgraph GCP_Compute ["Preemptible VMs"]
            GCP_API["API Container"]
            GCP_Worker["Worker Container"]
        end
        GCP_API --> GCP_LB
        GCP_Worker --> GCP_LB
    end

    subgraph Azure ["Microsoft Azure (Target)"]
        direction TB
        AZ_Blob[("Azure Blob Storage")]
        subgraph AZ_Compute ["Azure Spot VMs"]
            AZ_API["API Container"]
            AZ_Worker["Worker Container"]
        end
        AZ_API --> AZ_Blob
        AZ_Worker --> AZ_Blob
    end

    %% Data Flow / Migration
    User((User)) --> GCP_API
    User -.->|Cutover| AZ_API
    
    %% Migration Script
    Script[["Migration Script<br/>(Python)"]]
    GCP_LB -.->|Copy Objects| Script
    Script -.->|Write Objects| AZ_Blob
    
    style GCP fill:#e8f0fe,stroke:#4285f4,stroke-width:2px
    style Azure fill:#f0f7ff,stroke:#0078d4,stroke-width:2px
    style Script fill:#fff3e0,stroke:#ff9800,stroke-dasharray: 5 5
Loading

☁️ Cloud Migration Demo: GCP Preemptible to Azure Spot

This project demonstrates a real-world multi-cloud migration runbook, moving a containerized, stateless image-processing application from Google Cloud Platform (GCP) Preemptible VMs to Azure Spot VMs.

The core goal is to showcase the portability of containerized workloads and implement a migration strategy (ETL + Cutover) while leveraging high cost-saving measures in both cloud environments.

📂 Project Structure

cloud-migration-spot/
├── api/                    # FastAPI Application (Handles uploads and status)
│   ├── main.py             
│   └── templates/          
├── worker/                 # Background Worker (Handles image resizing)
│   └── worker.py           
├── common/                 # Shared Library (Crucial for multi-cloud abstraction)
│   ├── config.py           # Configuration loader
│   ├── storage.py          # Storage Abstraction (Local/GCS/Azure)
│   └── job_schema.py       # Pydantic Models for job queue
├── infra/docker/           # Infrastructure
│   ├── Dockerfile.api      # API Container Definition
│   └── Dockerfile.worker   # Worker Container Definition
├── tools/                  # Migration Tools
│   └── gcs_to_azure_copy.py # ETL Script for data synchronization
├── requirements.txt        # Python Dependencies
└── README.md               

⚡ Getting Started (Local Development)

To test the API and Worker components locally without any cloud dependencies:

Clone the repository:

git clone https://github.com/ImmrBhattarai/cloud-migration-spot.git
cd cloud-migration-spot

Build Docker Images:

docker build -f infra/docker/Dockerfile.api -t demo-api:local .
docker build -f infra/docker/Dockerfile.worker -t demo-worker:local .

Run Locally (with shared volume):

Screenshot From 2025-12-05 22-08-53
mkdir -p data

# Run API on port 8000
docker run -d -p 8000:8000 -v "$(pwd)/data:/app/data" --name local-api demo-api:local

# Run Worker
docker run -d -v "$(pwd)/data:/app/data" --name local-worker demo-worker:local
Screenshot From 2025-12-05 22-08-27

Access: Open http://localhost:8000 in your browser to upload an image.

☁️ Cloud Deployment & Migration Runbook

The application is deployed with the help of environment variables (STORAGE_BACKEND) to abstract the underlying storage technology (GCS or Azure Blob).

Environment Variables Reference

Variable Description Context
STORAGE_BACKEND gcp or azure All Containers
GCS_BUCKET Source Bucket Name GCP Only
GOOGLE_APPLICATION_CREDENTIALS Path to Service Account JSON GCP Only
AZURE_STORAGE_CONNECTION_STRING Storage Account Connection String Azure Only
AZURE_CONTAINER Target Blob Container Name Azure Only

Phase 1: Deploy to GCP (Source)

Deploy the services to GCP Preemptible VMs. These services will use GCS for job queuing and image storage. Screenshot From 2025-12-05 23-06-33 Screenshot From 2025-12-08 20-27-33

# 1. Start API Service (GCP)
docker run -d --name cm-api -p 80:8000 \
  -e STORAGE_BACKEND=gcp \
  -e GCS_BUCKET=$GCS_BUCKET \
  -e GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json \
  <your-dockerhub-user>/cloud-migration-demo-api:v2
Screenshot From 2025-12-08 20-28-31
# 2. Start Worker Service (GCP)
docker run -d --name cm-worker \
  -e STORAGE_BACKEND=gcp \
  -e GCS_BUCKET=$GCS_BUCKET \
  -e GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json \
  <your-dockerhub-user>/cloud-migration-demo-worker:v2
Screenshot From 2025-12-08 21-23-44

Phase 2: Deploy to Azure (Target)

Deploy the services to Azure Spot VMs. These services are configured to use Azure Blob Storage for images and job queue.

# 1. Start API Service (Azure)
docker run -d --name cm-api -p 80:8000 \
  -e STORAGE_BACKEND=azure \
  -e AZURE_STORAGE_CONNECTION_STRING="<your-conn-string>" \
  -e AZURE_CONTAINER="cm-demo-images-az" \
  <your-dockerhub-user>/cloud-migration-demo-api:v2
Screenshot From 2025-12-11 23-52-18
# 2. Start Worker Service (Azure)
docker run -d --name cm-worker \
  -e STORAGE_BACKEND=azure \
  -e AZURE_STORAGE_CONNECTION_STRING="<your-conn-string>" \
  -e AZURE_CONTAINER="cm-demo-images-az" \
  <your-dockerhub-user>/cloud-migration-demo-worker:v2
Screenshot From 2025-12-11 23-55-19

Phase 3: Data Migration (ETL)

Use the included tool to copy all existing data from the GCP bucket to the Azure container.

# Export credentials for both clouds
export GCS_BUCKET=...
export GOOGLE_APPLICATION_CREDENTIALS=...
export AZURE_STORAGE_CONNECTION_STRING=...
export AZURE_CONTAINER=...

# Run the migration script
python tools/gcs_to_azure_copy.py
Screenshot From 2025-12-12 00-01-07 Screenshot From 2025-12-12 00-10-26

Phase 4: Cutover Strategy (Cold Migration)

  • Freeze: Stop the GCP API container to prevent new data writes.
  • Sync: Run python tools/gcs_to_azure_copy.py one last time to ensure consistency.
  • Switch: Update DNS or client configuration to point traffic to the Azure API's public IP address.
  • Verify: Test image upload and processing on Azure.
  • Decommission: Delete GCP resources.

💸 Cost Analysis & Cleanup

Why Spot/Preemptible?

  • Cost Savings: GCP Preemptible and Azure Spot instances offer discounts of up to 90% compared to on-demand pricing.
  • Trade-off: Instances can be reclaimed by the cloud provider with little warning.
  • Architectural Solution: The application is built to be stateless. If a worker VM dies, the job remains safely stored in the cloud storage queue (GCS or Azure Blob) and is picked up by a new, available worker instance.

Cleanup (Critical)

Run these commands to avoid incurring charges after the demo is complete:

Cloud Action Command
GCP Delete VM instances and GCS Buckets. gcloud compute instances delete cm-api-vm cm-worker-vm --zone=us-central1-a gsutil rm -r gs://$GCS_BUCKET
Azure Delete the entire Resource Group (removes VMs, Storage, IPs). az group delete --name cm-demo-rg --yes --no-wait

⚖️ Production Considerations vs. Demo

image

✍️ Author

ImmrBhattarai (Suraj Bhattarai)

About

The requirement for a company is to migrate it's spot instances (and batch processing jobs) running over Google Cloud Platform into Azure (spot instances). Migration Strategy is "Redeploy"

Topics

Resources

License

Stars

Watchers

Forks

Contributors