|
|
|
|
|
""" |
|
|
Launch GCP spot instance using Python API (no gcloud CLI needed). |
|
|
|
|
|
Requirements: |
|
|
pip install google-cloud-compute google-auth |
|
|
|
|
|
Setup: |
|
|
1. Create service account: https://console.cloud.google.com/iam-admin/serviceaccounts |
|
|
2. Download JSON key |
|
|
3. Set environment variable: |
|
|
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/key.json" |
|
|
|
|
|
Or use: gcloud auth application-default login |
|
|
""" |
|
|
|
|
|
import os |
|
|
import sys |
|
|
import time |
|
|
from google.cloud import compute_v1 |
|
|
from google.oauth2 import service_account |
|
|
import logging |
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(message)s') |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
PROJECT_ID = os.getenv('GCP_PROJECT_ID', None) |
|
|
ZONE = 'us-central1-a' |
|
|
INSTANCE_NAME = f'ensemble-test-{int(time.time())}' |
|
|
MACHINE_TYPE = 'e2-medium' |
|
|
IMAGE_FAMILY = 'ubuntu-2204-lts' |
|
|
IMAGE_PROJECT = 'ubuntu-os-cloud' |
|
|
|
|
|
STARTUP_SCRIPT = """#!/bin/bash |
|
|
apt-get update |
|
|
apt-get install -y python3-pip git |
|
|
|
|
|
pip3 install --upgrade pip |
|
|
|
|
|
cd /home |
|
|
git clone https://huggingface.co/marcosremar2/ensemble-tts-annotation |
|
|
cd ensemble-tts-annotation |
|
|
|
|
|
pip3 install -q torch --index-url https://download.pytorch.org/whl/cpu |
|
|
pip3 install -q transformers datasets librosa soundfile numpy pandas tqdm scikit-learn |
|
|
|
|
|
python3 test_local.py > /tmp/test-results.log 2>&1 |
|
|
|
|
|
echo "✅ Test completed" >> /tmp/test-results.log |
|
|
""" |
|
|
|
|
|
|
|
|
def get_credentials(): |
|
|
"""Get GCP credentials.""" |
|
|
creds_path = os.getenv('GOOGLE_APPLICATION_CREDENTIALS') |
|
|
|
|
|
if creds_path and os.path.exists(creds_path): |
|
|
logger.info(f"Using credentials from: {creds_path}") |
|
|
return service_account.Credentials.from_service_account_file(creds_path) |
|
|
else: |
|
|
logger.info("Using default credentials (gcloud auth application-default login)") |
|
|
from google.auth import default |
|
|
credentials, project = default() |
|
|
return credentials |
|
|
|
|
|
|
|
|
def get_project_id(): |
|
|
"""Get GCP project ID.""" |
|
|
if PROJECT_ID: |
|
|
return PROJECT_ID |
|
|
|
|
|
|
|
|
try: |
|
|
import subprocess |
|
|
result = subprocess.run( |
|
|
['gcloud', 'config', 'get-value', 'project'], |
|
|
capture_output=True, |
|
|
text=True |
|
|
) |
|
|
if result.returncode == 0: |
|
|
project = result.stdout.strip() |
|
|
if project: |
|
|
return project |
|
|
except: |
|
|
pass |
|
|
|
|
|
|
|
|
project = input("Enter GCP Project ID: ").strip() |
|
|
return project |
|
|
|
|
|
|
|
|
def create_instance(project_id, zone, instance_name, machine_type): |
|
|
"""Create spot instance.""" |
|
|
logger.info("=" * 60) |
|
|
logger.info("Creating GCP Spot Instance") |
|
|
logger.info("=" * 60) |
|
|
logger.info(f"Project: {project_id}") |
|
|
logger.info(f"Zone: {zone}") |
|
|
logger.info(f"Instance: {instance_name}") |
|
|
logger.info(f"Machine Type: {machine_type}") |
|
|
logger.info(f"Estimated cost: ~$0.01/hr") |
|
|
logger.info("") |
|
|
|
|
|
|
|
|
credentials = get_credentials() |
|
|
|
|
|
|
|
|
instance_client = compute_v1.InstancesClient(credentials=credentials) |
|
|
|
|
|
|
|
|
logger.info("Getting Ubuntu image...") |
|
|
image_client = compute_v1.ImagesClient(credentials=credentials) |
|
|
image = image_client.get_from_family( |
|
|
project=IMAGE_PROJECT, |
|
|
family=IMAGE_FAMILY |
|
|
) |
|
|
|
|
|
|
|
|
logger.info("Configuring instance...") |
|
|
|
|
|
disk = compute_v1.AttachedDisk() |
|
|
disk.boot = True |
|
|
disk.auto_delete = True |
|
|
disk.initialize_params = compute_v1.AttachedDiskInitializeParams() |
|
|
disk.initialize_params.source_image = image.self_link |
|
|
disk.initialize_params.disk_size_gb = 20 |
|
|
|
|
|
network_interface = compute_v1.NetworkInterface() |
|
|
network_interface.name = "global/networks/default" |
|
|
access_config = compute_v1.AccessConfig() |
|
|
access_config.name = "External NAT" |
|
|
access_config.type_ = "ONE_TO_ONE_NAT" |
|
|
network_interface.access_configs = [access_config] |
|
|
|
|
|
metadata = compute_v1.Metadata() |
|
|
metadata.items = [ |
|
|
compute_v1.Items(key="startup-script", value=STARTUP_SCRIPT) |
|
|
] |
|
|
|
|
|
scheduling = compute_v1.Scheduling() |
|
|
scheduling.preemptible = True |
|
|
scheduling.automatic_restart = False |
|
|
scheduling.on_host_maintenance = "TERMINATE" |
|
|
|
|
|
instance = compute_v1.Instance() |
|
|
instance.name = instance_name |
|
|
instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}" |
|
|
instance.disks = [disk] |
|
|
instance.network_interfaces = [network_interface] |
|
|
instance.metadata = metadata |
|
|
instance.scheduling = scheduling |
|
|
|
|
|
|
|
|
logger.info("Launching instance...") |
|
|
operation = instance_client.insert( |
|
|
project=project_id, |
|
|
zone=zone, |
|
|
instance_resource=instance |
|
|
) |
|
|
|
|
|
|
|
|
logger.info("Waiting for instance to be created...") |
|
|
while operation.status != compute_v1.Operation.Status.DONE: |
|
|
time.sleep(2) |
|
|
operation = compute_v1.ZoneOperationsClient(credentials=credentials).get( |
|
|
project=project_id, |
|
|
zone=zone, |
|
|
operation=operation.name |
|
|
) |
|
|
|
|
|
if operation.error: |
|
|
logger.error(f"Error creating instance: {operation.error}") |
|
|
return None |
|
|
|
|
|
|
|
|
instance = instance_client.get( |
|
|
project=project_id, |
|
|
zone=zone, |
|
|
instance=instance_name |
|
|
) |
|
|
|
|
|
external_ip = instance.network_interfaces[0].access_configs[0].nat_i_p |
|
|
|
|
|
logger.info("") |
|
|
logger.info("=" * 60) |
|
|
logger.info("✅ Instance created successfully!") |
|
|
logger.info("=" * 60) |
|
|
logger.info(f"Instance Name: {instance_name}") |
|
|
logger.info(f"External IP: {external_ip}") |
|
|
logger.info(f"Zone: {zone}") |
|
|
logger.info("") |
|
|
logger.info("SSH Command:") |
|
|
logger.info(f" gcloud compute ssh {instance_name} --zone={zone} --project={project_id}") |
|
|
logger.info("") |
|
|
logger.info("Or from console:") |
|
|
logger.info(f" https://console.cloud.google.com/compute/instances?project={project_id}") |
|
|
logger.info("") |
|
|
logger.info("Check test results (wait ~2 min for setup):") |
|
|
logger.info(f" gcloud compute ssh {instance_name} --zone={zone} --command='cat /tmp/test-results.log'") |
|
|
logger.info("") |
|
|
logger.info("Delete instance:") |
|
|
logger.info(f" gcloud compute instances delete {instance_name} --zone={zone} --project={project_id} --quiet") |
|
|
logger.info("") |
|
|
logger.info("=" * 60) |
|
|
logger.info("⚠️ Remember to delete the instance to avoid charges!") |
|
|
logger.info("=" * 60) |
|
|
|
|
|
return instance_name |
|
|
|
|
|
|
|
|
def main(): |
|
|
"""Main function.""" |
|
|
logger.info("GCP Spot Instance Launcher (Python API)") |
|
|
logger.info("") |
|
|
|
|
|
|
|
|
project_id = get_project_id() |
|
|
if not project_id: |
|
|
logger.error("No project ID provided") |
|
|
return 1 |
|
|
|
|
|
|
|
|
try: |
|
|
instance_name = create_instance( |
|
|
project_id=project_id, |
|
|
zone=ZONE, |
|
|
instance_name=INSTANCE_NAME, |
|
|
machine_type=MACHINE_TYPE |
|
|
) |
|
|
|
|
|
if instance_name: |
|
|
logger.info("\n✅ Success! Instance is launching.") |
|
|
logger.info("\nNext steps:") |
|
|
logger.info("1. Wait ~2 minutes for setup to complete") |
|
|
logger.info("2. SSH to instance and check results") |
|
|
logger.info("3. Delete instance when done") |
|
|
return 0 |
|
|
else: |
|
|
logger.error("\n❌ Failed to create instance") |
|
|
return 1 |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"\n❌ Error: {e}") |
|
|
logger.error("\nMake sure you have:") |
|
|
logger.error("1. Installed: pip install google-cloud-compute") |
|
|
logger.error("2. Set credentials:") |
|
|
logger.error(" - Option A: export GOOGLE_APPLICATION_CREDENTIALS='/path/to/key.json'") |
|
|
logger.error(" - Option B: gcloud auth application-default login") |
|
|
logger.error("3. Enabled Compute Engine API in your project") |
|
|
return 1 |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
sys.exit(main()) |
|
|
|