soleprint init commit
This commit is contained in:
6
station/tools/infra/gcp/Pulumi.yaml
Normal file
6
station/tools/infra/gcp/Pulumi.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
name: amar-gcp
|
||||
runtime:
|
||||
name: python
|
||||
options:
|
||||
virtualenv: venv
|
||||
description: Amar Mascotas infrastructure on Google Cloud Platform
|
||||
286
station/tools/infra/gcp/__main__.py
Normal file
286
station/tools/infra/gcp/__main__.py
Normal file
@@ -0,0 +1,286 @@
|
||||
"""
|
||||
Google Cloud Platform Infrastructure for Amar Mascotas
|
||||
|
||||
Deploys:
|
||||
- VPC with subnets
|
||||
- Compute Engine instance for Django app + Celery
|
||||
- Cloud SQL PostgreSQL (PostGIS via flags)
|
||||
- Memorystore Redis
|
||||
- Firewall rules
|
||||
- (Optional) Cloud Load Balancer, Cloud DNS
|
||||
|
||||
Estimated cost: ~$93/month
|
||||
|
||||
NOTE: GCP has good free tier credits and competitive pricing.
|
||||
PostGIS requires enabling the `cloudsql.enable_pgaudit` flag.
|
||||
"""
|
||||
|
||||
import pulumi
|
||||
import pulumi_gcp as gcp
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
from shared.config import get_config, APP_SERVER_INIT_SCRIPT
|
||||
|
||||
# Load configuration
|
||||
cfg = get_config()
|
||||
|
||||
# Get project
|
||||
project = gcp.organizations.get_project()
|
||||
|
||||
# =============================================================================
|
||||
# NETWORKING - VPC
|
||||
# =============================================================================
|
||||
|
||||
# VPC Network
|
||||
vpc = gcp.compute.Network(
|
||||
f"{cfg.resource_prefix}-vpc",
|
||||
name=f"{cfg.resource_prefix}-vpc",
|
||||
auto_create_subnetworks=False,
|
||||
description="VPC for Amar Mascotas",
|
||||
)
|
||||
|
||||
# Subnet for compute resources
|
||||
subnet = gcp.compute.Subnetwork(
|
||||
f"{cfg.resource_prefix}-subnet",
|
||||
name=f"{cfg.resource_prefix}-subnet",
|
||||
ip_cidr_range="10.0.1.0/24",
|
||||
region="us-east1",
|
||||
network=vpc.id,
|
||||
private_ip_google_access=True, # Access Google APIs without public IP
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# FIREWALL RULES
|
||||
# =============================================================================
|
||||
|
||||
# Allow SSH
|
||||
firewall_ssh = gcp.compute.Firewall(
|
||||
f"{cfg.resource_prefix}-allow-ssh",
|
||||
name=f"{cfg.resource_prefix}-allow-ssh",
|
||||
network=vpc.name,
|
||||
allows=[
|
||||
gcp.compute.FirewallAllowArgs(
|
||||
protocol="tcp",
|
||||
ports=["22"],
|
||||
),
|
||||
],
|
||||
source_ranges=cfg.allowed_ssh_ips or ["0.0.0.0/0"],
|
||||
target_tags=["app-server"],
|
||||
)
|
||||
|
||||
# Allow HTTP/HTTPS
|
||||
firewall_http = gcp.compute.Firewall(
|
||||
f"{cfg.resource_prefix}-allow-http",
|
||||
name=f"{cfg.resource_prefix}-allow-http",
|
||||
network=vpc.name,
|
||||
allows=[
|
||||
gcp.compute.FirewallAllowArgs(
|
||||
protocol="tcp",
|
||||
ports=["80", "443"],
|
||||
),
|
||||
],
|
||||
source_ranges=["0.0.0.0/0"],
|
||||
target_tags=["app-server"],
|
||||
)
|
||||
|
||||
# Allow internal traffic (for DB/Redis access)
|
||||
firewall_internal = gcp.compute.Firewall(
|
||||
f"{cfg.resource_prefix}-allow-internal",
|
||||
name=f"{cfg.resource_prefix}-allow-internal",
|
||||
network=vpc.name,
|
||||
allows=[
|
||||
gcp.compute.FirewallAllowArgs(
|
||||
protocol="tcp",
|
||||
ports=["0-65535"],
|
||||
),
|
||||
gcp.compute.FirewallAllowArgs(
|
||||
protocol="udp",
|
||||
ports=["0-65535"],
|
||||
),
|
||||
gcp.compute.FirewallAllowArgs(
|
||||
protocol="icmp",
|
||||
),
|
||||
],
|
||||
source_ranges=["10.0.0.0/8"],
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# DATABASE - Cloud SQL PostgreSQL
|
||||
# =============================================================================
|
||||
|
||||
# Cloud SQL instance
|
||||
# Note: PostGIS available via database flags
|
||||
db_instance = gcp.sql.DatabaseInstance(
|
||||
f"{cfg.resource_prefix}-db",
|
||||
name=f"{cfg.resource_prefix}-db",
|
||||
database_version="POSTGRES_15",
|
||||
region="us-east1",
|
||||
deletion_protection=False, # Set True for production!
|
||||
settings=gcp.sql.DatabaseInstanceSettingsArgs(
|
||||
tier="db-f1-micro", # $25/mo - smallest
|
||||
disk_size=10,
|
||||
disk_type="PD_SSD",
|
||||
ip_configuration=gcp.sql.DatabaseInstanceSettingsIpConfigurationArgs(
|
||||
ipv4_enabled=False,
|
||||
private_network=vpc.id,
|
||||
enable_private_path_for_google_cloud_services=True,
|
||||
),
|
||||
backup_configuration=gcp.sql.DatabaseInstanceSettingsBackupConfigurationArgs(
|
||||
enabled=True,
|
||||
start_time="03:00",
|
||||
),
|
||||
database_flags=[
|
||||
# Enable PostGIS extensions
|
||||
gcp.sql.DatabaseInstanceSettingsDatabaseFlagArgs(
|
||||
name="cloudsql.enable_pg_cron",
|
||||
value="on",
|
||||
),
|
||||
],
|
||||
user_labels=cfg.tags,
|
||||
),
|
||||
opts=pulumi.ResourceOptions(depends_on=[vpc]),
|
||||
)
|
||||
|
||||
# Database
|
||||
db = gcp.sql.Database(
|
||||
f"{cfg.resource_prefix}-database",
|
||||
name=cfg.db_name,
|
||||
instance=db_instance.name,
|
||||
)
|
||||
|
||||
# Database user
|
||||
db_user = gcp.sql.User(
|
||||
f"{cfg.resource_prefix}-db-user",
|
||||
name=cfg.db_user,
|
||||
instance=db_instance.name,
|
||||
password=pulumi.Config().require_secret("db_password"),
|
||||
)
|
||||
|
||||
# Private IP for Cloud SQL
|
||||
private_ip_address = gcp.compute.GlobalAddress(
|
||||
f"{cfg.resource_prefix}-db-private-ip",
|
||||
name=f"{cfg.resource_prefix}-db-private-ip",
|
||||
purpose="VPC_PEERING",
|
||||
address_type="INTERNAL",
|
||||
prefix_length=16,
|
||||
network=vpc.id,
|
||||
)
|
||||
|
||||
# VPC peering for Cloud SQL
|
||||
private_vpc_connection = gcp.servicenetworking.Connection(
|
||||
f"{cfg.resource_prefix}-private-vpc-connection",
|
||||
network=vpc.id,
|
||||
service="servicenetworking.googleapis.com",
|
||||
reserved_peering_ranges=[private_ip_address.name],
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# CACHE - Memorystore Redis
|
||||
# =============================================================================
|
||||
|
||||
redis_instance = gcp.redis.Instance(
|
||||
f"{cfg.resource_prefix}-redis",
|
||||
name=f"{cfg.resource_prefix}-redis",
|
||||
tier="BASIC", # $20/mo - no HA
|
||||
memory_size_gb=1,
|
||||
region="us-east1",
|
||||
redis_version="REDIS_7_0",
|
||||
authorized_network=vpc.id,
|
||||
connect_mode="PRIVATE_SERVICE_ACCESS",
|
||||
labels=cfg.tags,
|
||||
opts=pulumi.ResourceOptions(depends_on=[private_vpc_connection]),
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# COMPUTE - Compute Engine Instance
|
||||
# =============================================================================
|
||||
|
||||
# Service account for the instance
|
||||
service_account = gcp.serviceaccount.Account(
|
||||
f"{cfg.resource_prefix}-sa",
|
||||
account_id=f"{cfg.resource_prefix}-app-sa",
|
||||
display_name="Amar App Service Account",
|
||||
)
|
||||
|
||||
# Compute instance
|
||||
instance = gcp.compute.Instance(
|
||||
f"{cfg.resource_prefix}-app",
|
||||
name=f"{cfg.resource_prefix}-app",
|
||||
machine_type="e2-medium", # $30/mo - 4GB RAM, 2 vCPU
|
||||
zone="us-east1-b",
|
||||
tags=["app-server"],
|
||||
boot_disk=gcp.compute.InstanceBootDiskArgs(
|
||||
initialize_params=gcp.compute.InstanceBootDiskInitializeParamsArgs(
|
||||
image="ubuntu-os-cloud/ubuntu-2204-lts",
|
||||
size=30,
|
||||
type="pd-ssd",
|
||||
),
|
||||
),
|
||||
network_interfaces=[
|
||||
gcp.compute.InstanceNetworkInterfaceArgs(
|
||||
network=vpc.id,
|
||||
subnetwork=subnet.id,
|
||||
access_configs=[
|
||||
gcp.compute.InstanceNetworkInterfaceAccessConfigArgs(
|
||||
# Ephemeral public IP
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
service_account=gcp.compute.InstanceServiceAccountArgs(
|
||||
email=service_account.email,
|
||||
scopes=["cloud-platform"],
|
||||
),
|
||||
metadata_startup_script=APP_SERVER_INIT_SCRIPT,
|
||||
labels=cfg.tags,
|
||||
)
|
||||
|
||||
# Static external IP (optional, costs extra)
|
||||
static_ip = gcp.compute.Address(
|
||||
f"{cfg.resource_prefix}-static-ip",
|
||||
name=f"{cfg.resource_prefix}-static-ip",
|
||||
region="us-east1",
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# OPTIONAL: Cloud Load Balancer (uncomment if needed)
|
||||
# =============================================================================
|
||||
|
||||
# health_check = gcp.compute.HealthCheck(
|
||||
# f"{cfg.resource_prefix}-health-check",
|
||||
# name=f"{cfg.resource_prefix}-health-check",
|
||||
# http_health_check=gcp.compute.HealthCheckHttpHealthCheckArgs(
|
||||
# port=80,
|
||||
# request_path="/health/",
|
||||
# ),
|
||||
# )
|
||||
|
||||
# =============================================================================
|
||||
# OUTPUTS
|
||||
# =============================================================================
|
||||
|
||||
pulumi.export("instance_public_ip", instance.network_interfaces[0].access_configs[0].nat_ip)
|
||||
pulumi.export("instance_private_ip", instance.network_interfaces[0].network_ip)
|
||||
pulumi.export("static_ip", static_ip.address)
|
||||
pulumi.export("db_private_ip", db_instance.private_ip_address)
|
||||
pulumi.export("db_connection_name", db_instance.connection_name)
|
||||
pulumi.export("db_name", cfg.db_name)
|
||||
pulumi.export("db_user", cfg.db_user)
|
||||
pulumi.export("redis_host", redis_instance.host)
|
||||
pulumi.export("redis_port", redis_instance.port)
|
||||
|
||||
# Generate .env content
|
||||
pulumi.export("env_file", pulumi.Output.all(
|
||||
db_instance.private_ip_address,
|
||||
redis_instance.host,
|
||||
redis_instance.port,
|
||||
).apply(lambda args: f"""
|
||||
# Generated by Pulumi - GCP
|
||||
DB_HOST={args[0]}
|
||||
DB_PORT=5432
|
||||
DB_NAME={cfg.db_name}
|
||||
DB_USER={cfg.db_user}
|
||||
DB_PASSWORD=<set via pulumi config>
|
||||
CELERY_BROKER_URL=redis://{args[1]}:{args[2]}/0
|
||||
CELERY_RESULT_BACKEND=redis://{args[1]}:{args[2]}/0
|
||||
"""))
|
||||
2
station/tools/infra/gcp/requirements.txt
Normal file
2
station/tools/infra/gcp/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
pulumi>=3.0.0
|
||||
pulumi-gcp>=7.0.0
|
||||
Reference in New Issue
Block a user