-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathdeploy-multi.sh.example
More file actions
229 lines (206 loc) · 11 KB
/
Copy pathdeploy-multi.sh.example
File metadata and controls
229 lines (206 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/usr/bin/env bash
# =============================================================================
# dradis deploy-multi.sh TEMPLATE
# Deploys DRADIS trading containers + Control Tower UI
#
# SETUP:
# 1. Copy this file to deploy-multi.sh and fill in HOST, USER, KEY
# 2. Ensure Docker is installed on the remote server
# 3. Run: chmod +x deploy-multi.sh && ./deploy-multi.sh
#
# Phase 3f-6: Two deployment models are now supported:
#
# MODEL A — Single container, multi-asset (recommended):
# One dradis container with ASSETS=btc,eth,sol runs all three market loops
# concurrently in a single process. Simpler operations, lower memory.
# DB covers the primary asset (btc); all assets write CSV metrics.
#
# MODEL B — Separate containers per asset (legacy, still works):
# dradis-btc, dradis-eth, dradis-sol each run with CRYPTO_FILTER=<asset>.
# Full DB persistence per asset, easier per-asset restart.
# Uncomment the individual docker run blocks below to use this model.
#
# Vipers (seven total): Arbitrage, Momentum, Maker, Basis, TimeDecay, GBoost,
# TrendCapture. All are enabled when using config.balanced.rs.example or
# config.aggressive.rs.example. Disable individually via Control Tower UI.
#
# Containers started (Model A):
# dradis — Rust trading engine (all assets) + axum API on :9000
# control-tower — Next.js dashboard on :3002
#
# All containers share the 'dradis-net' Docker network so the UI can reach
# the API at http://dradis:9000 without exposing it on the host.
# =============================================================================
set -euo pipefail
# Load .env so CT_USERNAME / CT_PASSWORD are available for the control-tower container
if [ -f ".env" ]; then
set -o allexport
# shellcheck disable=SC1091
source .env
set +o allexport
fi
# ⚙️ CUSTOMIZE THESE VALUES FOR YOUR SERVER
HOST="<YOUR_SERVER_IP>" # e.g. "54.12.34.56" or "myserver.example.com"
USER="<YOUR_USERNAME>" # e.g. "ubuntu", "ec2-user", "root"
KEY="<PATH_TO_SSH_KEY>" # e.g. "~/.ssh/id_rsa" or "~/.ssh/my-key.pem"
REMOTE_DIR="~/dradis" # Remote directory to deploy into
# Optional: SSH port (default 22)
SSH_PORT="22"
echo "🚀 Deploying DRADIS + Control Tower to $HOST..."
# ── 1. Create remote directory structure ──────────────────────────────────────
echo "📁 Creating remote directory structure..."
ssh -i "$KEY" "$USER@$HOST" \
"mkdir -p $REMOTE_DIR/src \
$REMOTE_DIR/tests \
$REMOTE_DIR/control-tower"
# ── 2. Copy Rust source ───────────────────────────────────────────────────────
# Use scp -r so every module file is picked up automatically — no manual list
# to maintain. New files added during refactoring are included without any
# change to this script.
echo "📤 Copying Rust source (src/ → remote)..."
scp -r -P "$SSH_PORT" -i "$KEY" src "$USER@$HOST:$REMOTE_DIR/"
# Test modules
scp -P "$SSH_PORT" -i "$KEY" tests/toctou.rs "$USER@$HOST:$REMOTE_DIR/tests/toctou.rs"
# Root files
scp -P "$SSH_PORT" -i "$KEY" .env "$USER@$HOST:$REMOTE_DIR/.env"
scp -P "$SSH_PORT" -i "$KEY" Cargo.toml "$USER@$HOST:$REMOTE_DIR/Cargo.toml"
scp -P "$SSH_PORT" -i "$KEY" Cargo.lock "$USER@$HOST:$REMOTE_DIR/Cargo.lock"
scp -P "$SSH_PORT" -i "$KEY" Dockerfile "$USER@$HOST:$REMOTE_DIR/Dockerfile"
# ── 3. Copy Control Tower UI source ──────────────────────────────────────────
echo "📤 Copying Control Tower source..."
scp -P "$SSH_PORT" -i "$KEY" control-tower/Dockerfile "$USER@$HOST:$REMOTE_DIR/control-tower/Dockerfile"
scp -P "$SSH_PORT" -i "$KEY" control-tower/.dockerignore "$USER@$HOST:$REMOTE_DIR/control-tower/.dockerignore"
scp -P "$SSH_PORT" -i "$KEY" control-tower/package.json "$USER@$HOST:$REMOTE_DIR/control-tower/package.json"
scp -P "$SSH_PORT" -i "$KEY" control-tower/package-lock.json "$USER@$HOST:$REMOTE_DIR/control-tower/package-lock.json"
scp -P "$SSH_PORT" -i "$KEY" control-tower/next.config.ts "$USER@$HOST:$REMOTE_DIR/control-tower/next.config.ts"
scp -P "$SSH_PORT" -i "$KEY" control-tower/tailwind.config.ts "$USER@$HOST:$REMOTE_DIR/control-tower/tailwind.config.ts"
scp -P "$SSH_PORT" -i "$KEY" control-tower/postcss.config.mjs "$USER@$HOST:$REMOTE_DIR/control-tower/postcss.config.mjs"
scp -P "$SSH_PORT" -i "$KEY" control-tower/tsconfig.json "$USER@$HOST:$REMOTE_DIR/control-tower/tsconfig.json"
# Copy entire src tree (app, components, lib) — includes api/[...path]/route.ts
scp -r -P "$SSH_PORT" -i "$KEY" control-tower/src "$USER@$HOST:$REMOTE_DIR/control-tower/"
# public/ dir (may be empty but must exist for Next.js standalone)
scp -r -P "$SSH_PORT" -i "$KEY" control-tower/public "$USER@$HOST:$REMOTE_DIR/control-tower/"
# ── 4. Build + deploy all containers in one SSH session ───────────────────────
ssh -p "$SSH_PORT" -i "$KEY" "$USER@$HOST" bash -s -- "$REMOTE_DIR" "${CT_USERNAME:-}" "${CT_PASSWORD:-}" << 'REMOTE'
set -euo pipefail
REMOTE_DIR="$1"
CT_USERNAME="$2"
CT_PASSWORD="$3"
cd "$REMOTE_DIR"
echo "🧹 Stopping old containers..."
docker stop dradis-btc dradis-eth dradis-sol control-tower ollama 2>/dev/null || true
docker rm dradis-btc dradis-eth dradis-sol control-tower ollama 2>/dev/null || true
# Note: do NOT prune volumes — ollama-models volume holds the downloaded LLM (~4 GB).
# Pruning it forces a full re-download on every deploy.
docker system prune -af 2>/dev/null || true
# Create isolated Docker network so containers talk internally
docker network create dradis-net 2>/dev/null || true
# ── Ollama (LLM Advisor backend) ─────────────────────────────────────────
# Serves the Ollama API at http://ollama:11434 on the dradis-net network.
# Model files are stored in a named volume so they survive container restarts
# and re-deploys without a costly re-download.
#
# Recommended model: qwen2.5:3b-instruct-q4_K_M (~2 GB, fast CPU inference)
# Completes in ~30-60s on t3.large CPU — well within the 360s reqwest timeout.
# Alternative: phi3:mini (~2.3 GB, very fast) or mistral:7b-instruct-q4_0 (~4.1 GB, slower).
# ⚠️ mistral:7b-instruct-q4_K_M (~4.1 GB) regularly exceeds the 360s timeout on t3.large;
# use only on t3.xlarge (4 vCPU / 16 GB RAM) or better.
# Disable by commenting out this block AND removing OLLAMA_URL from .env.
echo "🤖 Starting Ollama (LLM Advisor)..."
docker run -d \
--restart unless-stopped \
--name ollama \
--network dradis-net \
-v ollama-models:/root/.ollama \
-p 11434:11434 \
ollama/ollama
# Wait for Ollama HTTP API to be ready before issuing pull.
# docker run -d returns immediately but the server needs a few seconds to init.
echo "⏳ Waiting for Ollama to be ready..."
for i in $(seq 1 30); do
if docker exec ollama curl -sf http://localhost:11434/api/tags > /dev/null 2>&1; then
echo "✅ Ollama is ready."
break
fi
sleep 2
done
# Pull the LLM model only if it is not already present in the ollama-models volume.
# `ollama list` is a local operation (no registry contact) — if the model shows up, skip.
# This saves ~2 GB of bandwidth and 2-4 minutes on every deploy after the first.
# To force a re-pull (e.g. to upgrade to a newer quantization), delete the named volume
# with: docker volume rm ollama-models
echo "📥 Checking for qwen2.5:3b-instruct-q4_K_M in local model cache..."
if docker exec ollama ollama list 2>/dev/null | grep -q "qwen2.5:3b-instruct-q4_K_M"; then
echo "✅ Model already cached — skipping pull."
else
echo "📥 Model not found — pulling qwen2.5:3b-instruct-q4_K_M (~2 GB)..."
docker exec ollama ollama pull qwen2.5:3b-instruct-q4_K_M || true
fi
echo "🔨 Building DRADIS image..."
docker build -t dradis .
echo "🔨 Building Control Tower image..."
docker build -t dradis-ui control-tower/
# ── MODEL A: Single container, multi-asset (Phase 3f-6 — recommended) ───────
# ASSETS=btc,eth,sol runs all three market loops concurrently in one process.
# DB persists the primary asset (btc); secondary assets use CSV-only metrics.
#
# Worker threads: the runtime defaults to the host's core count (floor 2).
# For 3 concurrent asset loops, provision at least 4 vCPUs and set
# TOKIO_WORKER_THREADS to the core count (or a bit higher) so the patrol
# loops don't oversubscribe — e.g. `-e TOKIO_WORKER_THREADS=4` on a t3.xlarge.
echo "🚀 Starting dradis (BTC+ETH+SOL concurrent, API on :9000)..."
docker run -d \
--restart unless-stopped \
--name dradis \
--network dradis-net \
--env-file .env \
-e ASSETS=btc,eth,sol \
-e API_PORT=9000 \
-e TOKIO_WORKER_THREADS=4 \
-e OLLAMA_URL=http://ollama:11434 \
-e OLLAMA_MODEL=qwen2.5:3b-instruct-q4_K_M \
-p 9000:9000 \
-v $(pwd)/logs:/app/logs \
dradis
# # ── MODEL B: Separate containers per asset (legacy — full DB per asset) ────
# echo "🚀 Starting dradis-btc (BTC filter, API on :9000)..."
# docker run -d \
# --restart unless-stopped \
# --name dradis-btc \
# --network dradis-net \
# --env-file .env \
# -e CRYPTO_FILTER=btc \
# -e API_PORT=9000 \
# -e OLLAMA_URL=http://ollama:11434 \
# -e OLLAMA_MODEL=qwen2.5:3b-instruct-q4_K_M \
# -p 9000:9000 \
# -v $(pwd)/logs:/app/logs \
# dradis
#
echo "🌐 Starting Control Tower UI (port :3002)..."
docker run -d \
--restart unless-stopped \
--name control-tower \
--network dradis-net \
-e DRADIS_API_URL=http://dradis-btc:9000 \
-e CT_USERNAME="$CT_USERNAME" \
-e CT_PASSWORD="$CT_PASSWORD" \
-p 3002:3002 \
dradis-ui
echo ""
echo "✅ Deployment complete!"
echo "📜 Running containers:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "(dradis|control|ollama)"
echo ""
echo "🌐 Control Tower: http://$(hostname -I | awk '{print $1}'):3002"
echo "📡 DRADIS API: http://$(hostname -I | awk '{print $1}'):9000/api/health"
echo "🤖 Ollama API: http://$(hostname -I | awk '{print $1}'):11434"
REMOTE
echo ""
echo "✅ Deployment complete on $HOST!"
echo "🌐 Control Tower: http://$HOST:3002"
echo "📡 DRADIS API: http://$HOST:9000/api/health"
echo "🤖 Ollama API: http://$HOST:11434 (internal-only; not exposed to public)"
echo ""
echo "📜 Tailing BTC logs (Ctrl+C to stop):"
ssh -p "$SSH_PORT" -i "$KEY" "$USER@$HOST" "docker logs -f dradis-btc --tail 30"