cleanup and prefect service setup

This commit is contained in:
Deeman
2026-02-04 22:24:55 +01:00
parent fc27d5f887
commit 6d4377ccf9
41 changed files with 15888 additions and 2591 deletions

View File

@@ -1,116 +0,0 @@
"""
BeanFlows.coffee Infrastructure
Hetzner compute stack for ephemeral worker orchestration
Note: R2 buckets are managed manually in Cloudflare dashboard
- beanflows-artifacts: Stores CLI and pipeline artifacts
- beanflows-data-prod: Iceberg data lakehouse
"""
import pulumi
import pulumi_hcloud as hcloud
# Load configuration
config = pulumi.Config()
hetzner_location = config.get("hetzner_location") or "nbg1" # Nuremberg datacenter
# ============================================================
# R2 Bucket Names (managed manually in Cloudflare R2 UI)
# ============================================================
# R2 buckets cannot be managed via Pulumi as they require R2-specific tokens
# that don't work with the Cloudflare Pulumi provider.
# These are defined here for documentation purposes only.
ARTIFACTS_BUCKET = "beanflows-artifacts" # CLI + extract/transform packages
LAKEHOUSE_BUCKET = "beanflows-data-prod" # Iceberg tables (EEUR region)
# ============================================================
# Hetzner Cloud Infrastructure
# ============================================================
# SSH key for server access (imported from existing key)
ssh_key = hcloud.SshKey(
"materia-ssh-key",
name="deeman@DeemanPC",
public_key=config.require_secret("ssh_public_key"),
opts=pulumi.ResourceOptions(protect=True),
)
# Small CPX instance for supervisor (runs materia CLI to orchestrate pipelines)
# This is an always-on instance that creates/destroys ephemeral workers on-demand
supervisor_server = hcloud.Server(
"materia-supervisor",
name="materia-supervisor",
server_type="cpx11", # 2 vCPU (shared), 2GB RAM, ~€4.49/mo (cheapest option)
image="ubuntu-24.04",
location=hetzner_location,
ssh_keys=[ssh_key.id],
labels={
"role": "supervisor",
"project": "materia",
},
user_data="""#!/bin/bash
set -e
# Basic server setup
apt-get update
apt-get install -y python3.13 python3-pip curl unzip
# Install Pulumi ESC CLI
curl -fsSL https://get.pulumi.com/esc/install.sh | sh
export PATH="$HOME/.pulumi/bin:$PATH"
echo 'export PATH="$HOME/.pulumi/bin:$PATH"' >> /root/.bashrc
# Create deployment directory
mkdir -p /opt/materia
# Configure environment
echo 'Setup complete. Materia CLI will be deployed via CI/CD.' > /opt/materia/README.txt
""",
)
# Note: Workers are created on-demand by the materia CLI
# No always-on worker instances in this architecture
# Firewall for servers (restrict to SSH + outbound only)
firewall = hcloud.Firewall(
"materia-firewall",
name="materia-firewall",
rules=[
# Allow SSH from anywhere (consider restricting to your IP)
hcloud.FirewallRuleArgs(
direction="in",
protocol="tcp",
port="22",
source_ips=["0.0.0.0/0", "::/0"],
),
# Allow all outbound traffic
hcloud.FirewallRuleArgs(
direction="out",
protocol="tcp",
port="any",
destination_ips=["0.0.0.0/0", "::/0"],
),
hcloud.FirewallRuleArgs(
direction="out",
protocol="udp",
port="any",
destination_ips=["0.0.0.0/0", "::/0"],
),
],
)
# Apply firewall to supervisor
supervisor_firewall = hcloud.FirewallAttachment(
"supervisor-firewall",
firewall_id=firewall.id,
server_ids=[supervisor_server.id],
)
# ============================================================
# Outputs
# ============================================================
pulumi.export("supervisor_ip", supervisor_server.ipv4_address)
pulumi.export("artifacts_bucket_name", ARTIFACTS_BUCKET)
pulumi.export("lakehouse_bucket_name", LAKEHOUSE_BUCKET)

View File

@@ -0,0 +1,80 @@
services:
postgres:
image: postgres:14
environment:
POSTGRES_USER: prefect
POSTGRES_PASSWORD: prefect
POSTGRES_DB: prefect
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U prefect"]
interval: 5s
timeout: 5s
retries: 5
dragonfly:
image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
ulimits:
memlock: -1
volumes:
- dragonflydata:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli ping"]
interval: 5s
timeout: 5s
retries: 5
prefect-server:
image: prefecthq/prefect:3-latest
depends_on:
postgres:
condition: service_healthy
dragonfly:
condition: service_healthy
environment:
PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://prefect:prefect@postgres:5432/prefect
PREFECT_SERVER_API_HOST: 0.0.0.0
PREFECT_UI_API_URL: http://localhost:4200/api
PREFECT_MESSAGING_BROKER: prefect_redis.messaging
PREFECT_MESSAGING_CACHE: prefect_redis.messaging
PREFECT_REDIS_MESSAGING_HOST: dragonfly
PREFECT_REDIS_MESSAGING_PORT: 6379
PREFECT_REDIS_MESSAGING_DB: 0
command: prefect server start --no-services
ports:
- "4200:4200"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request as u; u.urlopen('http://localhost:4200/api/health', timeout=1)"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
prefect-services:
image: prefecthq/prefect:3-latest
depends_on:
prefect-server:
condition: service_healthy
environment:
PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://prefect:prefect@postgres:5432/prefect
PREFECT_MESSAGING_BROKER: prefect_redis.messaging
PREFECT_MESSAGING_CACHE: prefect_redis.messaging
PREFECT_REDIS_MESSAGING_HOST: dragonfly
PREFECT_REDIS_MESSAGING_PORT: 6379
PREFECT_REDIS_MESSAGING_DB: 0
command: prefect server services start
prefect-worker:
image: prefecthq/prefect:3-latest
depends_on:
prefect-server:
condition: service_healthy
environment:
PREFECT_API_URL: http://prefect-server:4200/api
command: prefect worker start --pool local-pool
restart: on-failure
volumes:
postgres_data:
dragonflydata:

View File

@@ -25,10 +25,8 @@ do
uv sync
# Run pipelines (SQLMesh handles scheduling)
uv run materia pipeline run extract
uv run materia pipeline run transform
#uv run materia pipeline run extract
#uv run materia pipeline run transform
) || sleep 600 # Sleep 10 min on failure to avoid busy-loop retries
sleep 3600 # Run pipelines every hour
done