Files
stonks-oracle/deploy-docker.sh
T
Celes Renata f151747d56
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
feat: add deploy-docker.sh with auto-detect Ollama, configurable model/URL
2026-04-29 03:03:57 +00:00

327 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# deploy-docker.sh — Deploy Stonks Oracle to a Docker host via SSH
#
# Usage: bash deploy-docker.sh [OPTIONS]
#
# Options:
# --host USER@HOST SSH target (default: celes@192.168.42.254)
# --ollama-url URL Ollama API URL (default: auto-detect or install)
# --ollama-model MODEL Ollama model name (default: qwen3.5:9b-fast)
# --dir PATH Remote install directory (default: ~/stonks-oracle)
#
# Examples:
# bash deploy-docker.sh
# bash deploy-docker.sh --ollama-url http://10.1.1.12:2701 --ollama-model qwen3.6
# bash deploy-docker.sh --host user@myserver --dir /opt/stonks
# -------------------------------------------------------
# Configuration (override via flags or environment)
# -------------------------------------------------------
REMOTE_HOST="${DEPLOY_HOST:-celes@192.168.42.254}"
REMOTE_DIR="${DEPLOY_DIR:-/home/celes/stonks-oracle}"
OLLAMA_URL="${DEPLOY_OLLAMA_URL:-}"
OLLAMA_MODEL="${DEPLOY_OLLAMA_MODEL:-qwen3.5:9b-fast}"
REPO_URL="http://admin:St0nks0racl3!@10.1.1.12:30300/admin/stonks-oracle.git"
# Parse command-line flags
while [[ $# -gt 0 ]]; do
case $1 in
--host) REMOTE_HOST="$2"; shift 2 ;;
--ollama-url) OLLAMA_URL="$2"; shift 2 ;;
--ollama-model) OLLAMA_MODEL="$2"; shift 2 ;;
--dir) REMOTE_DIR="$2"; shift 2 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
echo "=== Stonks Oracle Docker Deployment ==="
echo " Target: ${REMOTE_HOST}:${REMOTE_DIR}"
echo " Model: ${OLLAMA_MODEL}"
echo " Ollama: ${OLLAMA_URL:-auto-detect}"
echo ""
# -------------------------------------------------------
# Step 1: Clone or update the repo on the remote host
# -------------------------------------------------------
echo "--- Step 1: Syncing repository ---"
ssh "$REMOTE_HOST" bash -s -- "$REMOTE_DIR" "$REPO_URL" <<'REMOTE_SCRIPT'
set -euo pipefail
REMOTE_DIR="$1"
REPO_URL="$2"
if [ -d "$REMOTE_DIR/.git" ]; then
echo " Updating existing repo..."
cd "$REMOTE_DIR"
git fetch origin
git reset --hard origin/main
else
echo " Cloning fresh..."
git clone "$REPO_URL" "$REMOTE_DIR"
cd "$REMOTE_DIR"
fi
echo " ✓ Repo synced at $(git log --oneline -1)"
REMOTE_SCRIPT
echo ""
# -------------------------------------------------------
# Step 2: Detect or configure Ollama
# -------------------------------------------------------
echo "--- Step 2: Configuring Ollama ---"
if [ -z "$OLLAMA_URL" ]; then
# Auto-detect: check if Ollama is running on the remote host
OLLAMA_URL=$(ssh "$REMOTE_HOST" bash -s <<'DETECT_SCRIPT'
# Check common Ollama ports
for port in 11434 2701; do
if curl -sf --connect-timeout 2 "http://localhost:$port/api/tags" > /dev/null 2>&1; then
echo "http://localhost:$port"
exit 0
fi
done
echo ""
DETECT_SCRIPT
)
if [ -n "$OLLAMA_URL" ]; then
echo " ✓ Found existing Ollama at: $OLLAMA_URL"
else
echo " No Ollama detected — will use Docker container"
OLLAMA_URL="http://ollama:11434"
fi
else
echo " Using provided Ollama URL: $OLLAMA_URL"
fi
# Determine if we need the Docker Ollama container
USE_DOCKER_OLLAMA=false
if [ "$OLLAMA_URL" = "http://ollama:11434" ]; then
USE_DOCKER_OLLAMA=true
fi
echo ""
# -------------------------------------------------------
# Step 3: Create .env and compose override
# -------------------------------------------------------
echo "--- Step 3: Configuring environment ---"
ssh "$REMOTE_HOST" bash -s -- "$REMOTE_DIR" "$OLLAMA_URL" "$OLLAMA_MODEL" "$USE_DOCKER_OLLAMA" <<'REMOTE_SCRIPT'
set -euo pipefail
REMOTE_DIR="$1"
OLLAMA_URL="$2"
OLLAMA_MODEL="$3"
USE_DOCKER_OLLAMA="$4"
cd "$REMOTE_DIR"
# Read API keys from local files if they exist
POLYGON_KEY=""
ALPACA_KEY=""
ALPACA_SECRET=""
ALPACA_URL="https://paper-api.alpaca.markets"
[ -f polygon.io.key ] && POLYGON_KEY=$(cat polygon.io.key)
[ -f alpaca.key ] && ALPACA_KEY=$(cat alpaca.key)
[ -f alpaca.secret ] && ALPACA_SECRET=$(cat alpaca.secret)
[ -f alpaca.url ] && ALPACA_URL=$(cat alpaca.url)
cat > .env <<EOF
# Stonks Oracle — Docker Deployment Environment
MARKET_DATA_API_KEY=${POLYGON_KEY}
BROKER_API_KEY=${ALPACA_KEY}
BROKER_API_SECRET=${ALPACA_SECRET}
BROKER_BASE_URL=${ALPACA_URL}
TRADING_ENABLED=true
TRADING_RISK_TIER=moderate
TRADING_MAX_OPEN_POSITIONS=15
OLLAMA_MODEL=${OLLAMA_MODEL}
MACRO_ENABLED=true
COMPETITIVE_ENABLED=true
EOF
# Create compose override based on Ollama configuration
if [ "$USE_DOCKER_OLLAMA" = "true" ]; then
# Using Docker Ollama — no override needed, default compose handles it
rm -f docker-compose.override.yml
echo " ✓ Using Docker Ollama container"
else
# Using external Ollama — disable the container and point services to it
# Determine if URL is localhost (needs host-gateway) or remote
if echo "$OLLAMA_URL" | grep -qE "localhost|127\.0\.0\.1"; then
DOCKER_OLLAMA_URL="http://host.docker.internal:$(echo "$OLLAMA_URL" | grep -oP ':\K[0-9]+')"
cat > docker-compose.override.yml <<EOF
services:
ollama:
entrypoint: ["true"]
restart: "no"
ports: []
extractor:
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
OLLAMA_BASE_URL: "${DOCKER_OLLAMA_URL}"
extra_hosts:
- "host.docker.internal:host-gateway"
recommendation:
environment:
OLLAMA_BASE_URL: "${DOCKER_OLLAMA_URL}"
extra_hosts:
- "host.docker.internal:host-gateway"
EOF
else
# Remote Ollama — containers can reach it directly
cat > docker-compose.override.yml <<EOF
services:
ollama:
entrypoint: ["true"]
restart: "no"
extractor:
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
OLLAMA_BASE_URL: "${OLLAMA_URL}"
recommendation:
environment:
OLLAMA_BASE_URL: "${OLLAMA_URL}"
EOF
fi
echo " ✓ Override created — services will use external Ollama at ${OLLAMA_URL}"
fi
echo " ✓ .env configured (polygon=$([ -n "$POLYGON_KEY" ] && echo 'set' || echo 'empty'), alpaca=$([ -n "$ALPACA_KEY" ] && echo 'set' || echo 'empty'))"
REMOTE_SCRIPT
echo ""
# -------------------------------------------------------
# Step 4: Build and start all services
# -------------------------------------------------------
echo "--- Step 4: Building and starting services ---"
ssh "$REMOTE_HOST" bash -s -- "$REMOTE_DIR" "$USE_DOCKER_OLLAMA" <<'REMOTE_SCRIPT'
set -euo pipefail
REMOTE_DIR="$1"
USE_DOCKER_OLLAMA="$2"
cd "$REMOTE_DIR"
# Stop any existing deployment
docker compose down 2>/dev/null || true
# Build all images
echo " Building images (this may take a few minutes)..."
docker compose build --quiet 2>&1 | tail -5
# Start infrastructure
echo " Starting infrastructure..."
if [ "$USE_DOCKER_OLLAMA" = "true" ]; then
docker compose up -d postgres redis minio minio-init ollama
else
docker compose up -d postgres redis minio minio-init
fi
# Wait for infrastructure to be healthy
echo " Waiting for infrastructure health checks..."
for svc in postgres redis minio; do
for i in $(seq 1 30); do
if docker compose ps "$svc" 2>/dev/null | grep -q healthy; then
break
fi
sleep 2
done
done
echo " ✓ Infrastructure healthy"
# Start all application services
echo " Starting application services..."
docker compose up -d
echo " Waiting for services to stabilize..."
sleep 20
# Show status
echo ""
echo " Service Status:"
docker compose ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null | head -25 || docker compose ps
REMOTE_SCRIPT
echo ""
# -------------------------------------------------------
# Step 5: Seed the database
# -------------------------------------------------------
echo "--- Step 5: Seeding database ---"
ssh "$REMOTE_HOST" bash -s -- "$REMOTE_DIR" <<'REMOTE_SCRIPT'
set -euo pipefail
cd "$1"
# Wait for query-api to be healthy
for i in $(seq 1 30); do
if docker compose ps query-api 2>/dev/null | grep -q healthy; then
break
fi
sleep 3
done
# Run the symbol registry seed
echo " Seeding symbol registry..."
docker compose exec -T scheduler python -m services.symbol_registry.seed 2>/dev/null && echo " ✓ Database seeded" || echo " ⚠ Seed skipped (may already be seeded or service not ready)"
REMOTE_SCRIPT
echo ""
# -------------------------------------------------------
# Step 6: Ensure Ollama model is available
# -------------------------------------------------------
echo "--- Step 6: Checking Ollama model ---"
ssh "$REMOTE_HOST" bash -s -- "$OLLAMA_URL" "$OLLAMA_MODEL" "$USE_DOCKER_OLLAMA" "$REMOTE_DIR" <<'REMOTE_SCRIPT'
set -euo pipefail
OLLAMA_URL="$1"
OLLAMA_MODEL="$2"
USE_DOCKER_OLLAMA="$3"
REMOTE_DIR="$4"
if [ "$USE_DOCKER_OLLAMA" = "true" ]; then
# Pull via Docker container
cd "$REMOTE_DIR"
if docker compose exec -T ollama ollama list 2>/dev/null | grep -q "$OLLAMA_MODEL"; then
echo " ✓ Model $OLLAMA_MODEL already available"
else
echo " Pulling $OLLAMA_MODEL via Docker Ollama..."
docker compose exec -T ollama ollama pull "$OLLAMA_MODEL"
echo " ✓ Model pulled"
fi
else
# Check via API
if curl -sf "$OLLAMA_URL/api/tags" 2>/dev/null | grep -q "$OLLAMA_MODEL"; then
echo " ✓ Model $OLLAMA_MODEL already available at $OLLAMA_URL"
else
echo " Pulling $OLLAMA_MODEL via $OLLAMA_URL..."
curl -sf "$OLLAMA_URL/api/pull" -d "{\"name\":\"$OLLAMA_MODEL\"}" | tail -1
echo " ✓ Model pulled"
fi
fi
REMOTE_SCRIPT
echo ""
# -------------------------------------------------------
# Done
# -------------------------------------------------------
REMOTE_IP=$(echo "$REMOTE_HOST" | cut -d@ -f2)
echo "=== Deployment Complete ==="
echo ""
echo "Endpoints:"
echo " Dashboard: http://${REMOTE_IP}:3000"
echo " Query API: http://${REMOTE_IP}:8004"
echo " Symbol Registry: http://${REMOTE_IP}:8001"
echo " Trading Engine: http://${REMOTE_IP}:8002"
echo " Risk Engine: http://${REMOTE_IP}:8003"
echo " MinIO Console: http://${REMOTE_IP}:9001"
echo " Superset: http://${REMOTE_IP}:8088"
echo " Ollama: ${OLLAMA_URL}"
echo ""
echo "Commands:"
echo " ssh $REMOTE_HOST 'cd $REMOTE_DIR && docker compose logs -f'"
echo " ssh $REMOTE_HOST 'cd $REMOTE_DIR && docker compose ps'"
echo " ssh $REMOTE_HOST 'cd $REMOTE_DIR && docker compose down'"