-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathdeploy.sh
More file actions
executable file
·559 lines (463 loc) · 16.2 KB
/
deploy.sh
File metadata and controls
executable file
·559 lines (463 loc) · 16.2 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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
#!/bin/bash
# Waverless unified deployment script
# Supports multi-environment, image building, K8s deployment, etc.
set -e
# Default configuration
NAMESPACE="${NAMESPACE:-wavespeed}"
ENVIRONMENT="${ENVIRONMENT:-dev}"
API_KEY="${RUNPOD_AI_API_KEY:-waverless-api-key-2025}"
IMAGE_REGISTRY="${IMAGE_REGISTRY:-docker.io/wavespeed}"
IMAGE_TAG="${IMAGE_TAG:-latest}"
REDIS_PASSWORD="${REDIS_PASSWORD:-}"
API_BACKEND_URL="${API_BACKEND_URL:-http://waverless-svc:80}"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_blue() {
echo -e "${BLUE}$1${NC}"
}
# Print configuration information
print_config() {
log_blue "╔═══════════════════════════════════════════════════════╗"
log_blue "║ Waverless Deployment Tool ║"
log_blue "╚═══════════════════════════════════════════════════════╝"
echo ""
log_info "Configuration:"
echo " Namespace: ${NAMESPACE}"
echo " Environment: ${ENVIRONMENT}"
echo " Image: ${IMAGE_REGISTRY}/waverless:${IMAGE_TAG}"
echo " Registry: ${IMAGE_REGISTRY}"
echo ""
}
# Create namespace
create_namespace() {
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
log_info "Creating namespace: ${NAMESPACE}"
kubectl create namespace "$NAMESPACE"
kubectl label namespace "$NAMESPACE" environment="${ENVIRONMENT}"
else
log_info "Namespace ${NAMESPACE} already exists"
fi
}
# Apply YAML with namespace replacement
apply_yaml() {
local file="$1"
local temp_file="/tmp/$(basename "$file")"
# Replace namespace, image tag, and backend address
sed -e "s/namespace: wavespeed/namespace: ${NAMESPACE}/g" \
-e "s|docker.io/wavespeed/waverless:latest|${IMAGE_REGISTRY}/waverless:${IMAGE_TAG}|g" \
-e "s|docker.io/wavespeed/waverless-web:latest|${IMAGE_REGISTRY}/waverless-web:${IMAGE_TAG}|g" \
-e "s|value: \"http://waverless-svc:80\"|value: \"${API_BACKEND_URL}\"|g" \
"$file" > "$temp_file"
kubectl apply -f "$temp_file"
rm "$temp_file"
}
# ============================================================
# Build-related commands
# ============================================================
# Build and push API Server image
build_image() {
log_info "Building API Server Docker image..."
print_config
# Call build-and-push.sh script
IMAGE_REGISTRY=${IMAGE_REGISTRY} IMAGE_TAG=${IMAGE_TAG} ./build-and-push.sh ${IMAGE_TAG} waverless
}
# Build and push Web UI image
build_web_image() {
log_info "Building Web UI Docker image..."
print_config
# Call build-and-push.sh script
IMAGE_REGISTRY=${IMAGE_REGISTRY} IMAGE_TAG=${IMAGE_TAG} ./build-and-push.sh ${IMAGE_TAG} web-ui
}
# ============================================================
# Deployment-related commands
# ============================================================
# Deploy complete environment
install() {
log_info "Installing Waverless..."
print_config
# Create namespace
create_namespace
# Deploy RBAC
log_info "Deploying RBAC..."
apply_yaml "k8s/waverless-rbac.yaml"
# Create ConfigMap
log_info "Creating ConfigMap..."
kubectl create configmap waverless-config \
--from-file=config.yaml="config/config.yaml" \
--from-file=specs.yaml="config/specs.yaml" \
--from-file=deployment.yaml="config/templates/deployment.yaml" \
--namespace="${NAMESPACE}" \
--dry-run=client -o yaml | kubectl apply -f -
# Deploy Redis
log_info "Deploying Redis..."
apply_yaml "k8s/redis-deployment.yaml"
# Wait for Redis to be ready
log_info "Waiting for Redis..."
kubectl wait --for=condition=ready pod -l app=waverless-redis -n "${NAMESPACE}" --timeout=120s || true
# Deploy MySQL
log_info "Deploying MySQL..."
apply_yaml "k8s/mysql-deployment.yaml"
# Wait for MySQL to be ready
log_info "Waiting for MySQL..."
kubectl wait --for=condition=ready pod -l app=waverless-mysql -n "${NAMESPACE}" --timeout=180s || true
# Deploy Waverless
log_info "Deploying Waverless..."
apply_yaml "k8s/waverless-deployment.yaml"
# Wait for Waverless to be ready
log_info "Waiting for Waverless..."
kubectl wait --for=condition=available --timeout=120s \
deployment/waverless -n "${NAMESPACE}" || true
# Deploy Web UI (if YAML file exists)
if [ -f k8s/waverless-web-deployment.yaml ]; then
log_info "Deploying Web UI..."
apply_yaml "k8s/waverless-web-deployment.yaml"
log_info "Waiting for Web UI..."
kubectl wait --for=condition=available --timeout=120s \
deployment/waverless-web -n "${NAMESPACE}" || true
fi
echo ""
log_info "✓ Installation complete!"
echo ""
log_blue "Next steps:"
echo " 1. Check status: $0 status -n ${NAMESPACE}"
echo " 2. Port forward: kubectl port-forward -n ${NAMESPACE} svc/waverless-svc 8080:80"
echo " 3. Test API: curl http://localhost:8080/health"
echo ""
}
# Deploy Web UI separately
install_web() {
log_info "Installing Web UI..."
print_config
# Check if namespace exists
if ! kubectl get namespace "${NAMESPACE}" &> /dev/null; then
log_error "Namespace ${NAMESPACE} does not exist. Please run 'install' first."
exit 1
fi
# Check if waverless-svc exists
if ! kubectl get service waverless-svc -n "${NAMESPACE}" &> /dev/null; then
log_warn "API Server service (waverless-svc) not found in namespace ${NAMESPACE}"
log_warn "Web UI may not be able to connect to the backend"
fi
# Deploy Web UI
if [ -f k8s/waverless-web-deployment.yaml ]; then
log_info "Deploying Web UI..."
apply_yaml "k8s/waverless-web-deployment.yaml"
log_info "Waiting for Web UI..."
kubectl wait --for=condition=available --timeout=120s \
deployment/waverless-web -n "${NAMESPACE}" || true
echo ""
log_info "✓ Web UI installation complete!"
echo ""
log_blue "Next steps:"
echo " 1. Port forward: kubectl port-forward -n ${NAMESPACE} svc/waverless-web-svc 3000:80"
echo " 2. Open browser: http://localhost:3000"
echo ""
else
log_error "Web UI deployment file not found: k8s/waverless-web-deployment.yaml"
exit 1
fi
}
# Upgrade deployment
upgrade() {
log_info "Upgrading Waverless..."
print_config
# Update deployment
log_info "Updating deployment..."
apply_yaml "k8s/waverless-deployment.yaml"
# Wait for rollout
log_info "Waiting for rollout..."
kubectl rollout status deployment/waverless -n "${NAMESPACE}"
log_info "✓ Upgrade complete!"
}
# Uninstall
uninstall() {
log_warn "This will remove all Waverless resources from namespace ${NAMESPACE}"
read -p "Are you sure? (yes/no): " -r
echo
if [[ ! $REPLY =~ ^[Yy]es$ ]]; then
echo "Cancelled."
exit 0
fi
log_info "Removing Waverless..."
kubectl delete deployment waverless -n "${NAMESPACE}" --ignore-not-found=true
kubectl delete service waverless-svc -n "${NAMESPACE}" --ignore-not-found=true
kubectl delete configmap waverless-config -n "${NAMESPACE}" --ignore-not-found=true
log_info "Removing Web UI..."
kubectl delete deployment waverless-web -n "${NAMESPACE}" --ignore-not-found=true
kubectl delete service waverless-web-svc -n "${NAMESPACE}" --ignore-not-found=true
log_info "Removing Redis..."
kubectl delete deployment waverless-redis -n "${NAMESPACE}" --ignore-not-found=true
kubectl delete service waverless-redis-svc -n "${NAMESPACE}" --ignore-not-found=true
log_info "Removing Workers..."
kubectl delete deployment -n "${NAMESPACE}" -l managed-by=waverless --ignore-not-found=true
kubectl delete service -n "${NAMESPACE}" -l managed-by=waverless --ignore-not-found=true
log_info "Removing RBAC..."
kubectl delete rolebinding waverless-manager-binding -n "${NAMESPACE}" --ignore-not-found=true
kubectl delete role waverless-manager -n "${NAMESPACE}" --ignore-not-found=true
kubectl delete serviceaccount waverless -n "${NAMESPACE}" --ignore-not-found=true
log_info "✓ Uninstall complete!"
echo ""
log_warn "Note: Namespace ${NAMESPACE} was not deleted"
echo "To delete the namespace: kubectl delete namespace ${NAMESPACE}"
echo ""
}
# Deploy Worker
deploy_worker() {
local worker_name=$1
if [ -z "$worker_name" ]; then
log_error "Please specify worker name"
log_info "Available workers:"
ls k8s/*-waverless.yaml k8s/flux-*.yaml 2>/dev/null | xargs -n1 basename | sed 's/.yaml$//'
exit 1
fi
local worker_file="k8s/${worker_name}.yaml"
if [ ! -f "$worker_file" ]; then
log_error "Worker config file not found: $worker_file"
exit 1
fi
log_info "Deploying Worker: $worker_name"
apply_yaml "$worker_file"
log_info "Waiting for Worker..."
kubectl wait --for=condition=ready pod -l app=${worker_name} -n ${NAMESPACE} --timeout=300s || true
log_info "✓ Worker deployed!"
}
# ============================================================
# Management-related commands
# ============================================================
# Restart service
restart_service() {
log_info "Restarting Waverless..."
kubectl rollout restart deployment/waverless -n ${NAMESPACE}
kubectl rollout status deployment/waverless -n ${NAMESPACE}
log_info "✓ Restart complete!"
}
# Show status
show_status() {
print_config
log_blue "Deployment Status:"
echo ""
# Check namespace
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
log_error "Namespace ${NAMESPACE} does not exist"
exit 1
fi
# Deployments
log_warn "Deployments:"
kubectl get deployments -n "${NAMESPACE}" -o wide
echo ""
# Services
log_warn "Services:"
kubectl get services -n "${NAMESPACE}"
echo ""
# Pods
log_warn "Pods:"
kubectl get pods -n "${NAMESPACE}" -o wide
echo ""
# ConfigMaps
log_warn "ConfigMaps:"
kubectl get configmaps -n "${NAMESPACE}"
echo ""
}
# View logs
view_logs() {
local component=${1:-waverless}
local lines=${2:-100}
log_info "Viewing ${component} logs (last ${lines} lines)..."
kubectl logs -n ${NAMESPACE} deployment/${component} --tail=${lines} -f
}
# Test service
test_service() {
log_info "Testing Waverless service..."
# Port forward
log_info "Starting port-forward..."
kubectl port-forward -n ${NAMESPACE} svc/waverless-svc 8080:80 > /dev/null 2>&1 &
PF_PID=$!
sleep 3
# Health check
log_info "Health check..."
if curl -s http://localhost:8080/health | grep -q "ok"; then
log_info "✓ Health check passed"
else
log_error "✗ Health check failed"
fi
# List workers
log_info "Listing workers..."
curl -s http://localhost:8080/v1/workers | jq . || echo "No workers online"
# Cleanup
kill $PF_PID 2>/dev/null || true
log_info "✓ Test complete!"
}
# ============================================================
# Help information
# ============================================================
show_help() {
cat << EOF
Waverless Deployment Tool
Usage: $0 [OPTIONS] COMMAND [ARGS]
Commands:
build Build and push Waverless API Docker image
build-web Build and push Web UI Docker image
install Install Waverless (Redis + API + Web UI)
install-web Install Web UI only (requires existing API)
upgrade Upgrade existing deployment
uninstall Remove Waverless deployment
deploy-worker <name> Deploy a worker
restart Restart Waverless deployment
status Show deployment status
logs [component] [lines] View logs (default: waverless, 100 lines)
test Test service health
help Show this help message
Options:
-n, --namespace NAMESPACE Kubernetes namespace (default: wavespeed)
-e, --environment ENV Environment: dev, test, prod (default: dev)
-t, --tag TAG Image tag (default: latest)
-r, --registry REGISTRY Image registry (default: docker.io/wavespeed)
-k, --api-key KEY API key for worker authentication
-b, --backend-url URL Backend API URL for Web UI (default: http://waverless-svc:80)
--redis-password PASSWORD Redis password (optional)
Environment Variables:
NAMESPACE - Same as --namespace
ENVIRONMENT - Same as --environment
IMAGE_TAG - Same as --tag
IMAGE_REGISTRY - Same as --registry
RUNPOD_AI_API_KEY - Same as --api-key
API_BACKEND_URL - Same as --backend-url
REDIS_PASSWORD - Same as --redis-password
Examples:
# Build images
$0 build # Build Waverless API image
$0 build-web # Build Web UI image
# Deploy to development
$0 install
# Deploy to test environment
$0 -n wavespeed-test -e test -k "test-api-key" install
# Deploy to production with specific version
$0 -n wavespeed-prod -e prod -t v1.0.0 -k "prod-key" install
# Deploy with custom backend URL
$0 -n wavespeed-prod -b https://api.example.com install
# Deploy Web UI only (if API already exists)
$0 -n wavespeed-test -b http://waverless-svc:80 install-web
# Deploy a worker
$0 -n wavespeed-test deploy-worker flux-dev-lora-trainer
# Check status
$0 -n wavespeed-test status
# View logs
$0 logs waverless 50
# Upgrade production
$0 -n wavespeed-prod -t v1.0.1 upgrade
# Clean up
$0 -n wavespeed-test uninstall
More information: README.md and k8s/README.md
EOF
}
# ============================================================
# Main function
# ============================================================
main() {
local command=""
# Parse parameters
while [[ $# -gt 0 ]]; do
case $1 in
-n|--namespace)
NAMESPACE="$2"
shift 2
;;
-e|--environment)
ENVIRONMENT="$2"
shift 2
;;
-t|--tag)
IMAGE_TAG="$2"
shift 2
;;
-r|--registry)
IMAGE_REGISTRY="$2"
shift 2
;;
-k|--api-key)
API_KEY="$2"
shift 2
;;
--redis-password)
REDIS_PASSWORD="$2"
shift 2
;;
-b|--backend-url)
API_BACKEND_URL="$2"
shift 2
;;
build|build-web|install|install-web|upgrade|uninstall|deploy-worker|restart|status|logs|test|help)
command="$1"
shift
break
;;
-h|--help)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Execute command
case "$command" in
build)
build_image
;;
build-web)
build_web_image
;;
install)
install
;;
install-web)
install_web
;;
upgrade)
upgrade
;;
uninstall)
uninstall
;;
deploy-worker)
deploy_worker "$@"
;;
restart)
restart_service
;;
status)
show_status
;;
logs)
view_logs "$@"
;;
test)
test_service
;;
help|"")
show_help
;;
*)
log_error "Unknown command: $command"
show_help
exit 1
;;
esac
}
main "$@"