-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-manager.sh
More file actions
executable file
·445 lines (372 loc) · 12.7 KB
/
Copy pathdocker-manager.sh
File metadata and controls
executable file
·445 lines (372 loc) · 12.7 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
#!/bin/bash
# =============================================================================
# Docker Version Management Script for wav_parse Project
# =============================================================================
# Features:
# - Automatic version management (keeps latest 3 versions)
# - Build, deploy, rollback operations
# - Container lifecycle management
# - Cleanup of old images and containers
# =============================================================================
set -e # Exit on any error
# Configuration
PROJECT_NAME="wav_parse"
IMAGE_NAME="zh-asr"
CONTAINER_NAME="asr"
PORT="8000"
MAX_VERSIONS=3
VERSION_FILE=".docker_version"
LOG_FILE="docker-manager.log"
# Model cache configuration
HOST_MODEL_CACHE="$HOME/.cache/modelscope/hub/models/iic"
CONTAINER_MODEL_CACHE="/root/.cache/modelscope/hub/models/iic"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging function
log() {
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
info() {
log "${BLUE}INFO${NC}: $1"
}
success() {
log "${GREEN}SUCCESS${NC}: $1"
}
warning() {
log "${YELLOW}WARNING${NC}: $1"
}
error() {
log "${RED}ERROR${NC}: $1"
}
# Get next version number (timestamp format: yyyyMMddHHmm)
get_next_version() {
date +"%Y%m%d%H%M"
}
# Update version file
update_version() {
echo "$1" > "$VERSION_FILE"
}
# Get current version
get_current_version() {
if [[ -f "$VERSION_FILE" ]]; then
cat "$VERSION_FILE"
else
echo "none"
fi
}
# Get previous version from docker images
get_previous_version() {
# Get the second latest version from docker images
docker images "$IMAGE_NAME" --format "{{.Tag}}" | grep -E '^[0-9]{12}$' | sort -r | sed -n '2p'
}
# Stop and remove container
stop_container() {
info "Stopping container $CONTAINER_NAME..."
if docker ps -q -f name="$CONTAINER_NAME" | grep -q .; then
docker stop "$CONTAINER_NAME" || true
success "Container stopped"
else
info "Container $CONTAINER_NAME is not running"
fi
info "Removing container $CONTAINER_NAME..."
if docker ps -a -q -f name="$CONTAINER_NAME" | grep -q .; then
docker rm "$CONTAINER_NAME" || true
success "Container removed"
else
info "Container $CONTAINER_NAME does not exist"
fi
}
# Clean up old images (keep only latest N versions)
cleanup_old_images() {
info "Cleaning up old images (keeping latest $MAX_VERSIONS versions)..."
# Get all image versions with timestamp format, sorted by creation time (newest first)
local images=($(docker images "$IMAGE_NAME" --format "{{.Tag}}" | grep -E '^[0-9]{12}$' | sort -r))
if [[ ${#images[@]} -gt $MAX_VERSIONS ]]; then
local to_remove=("${images[@]:$MAX_VERSIONS}")
for tag in "${to_remove[@]}"; do
info "Removing old image: $IMAGE_NAME:$tag"
docker rmi "$IMAGE_NAME:$tag" || warning "Failed to remove $IMAGE_NAME:$tag"
done
success "Cleaned up ${#to_remove[@]} old images"
else
info "No old images to clean up (current count: ${#images[@]})"
fi
}
# Build new image
build_image() {
local version=$1
local tag="$version" # 直接使用时间戳作为tag
info "Building new image: $IMAGE_NAME:$tag ($(format_version_display $version))"
# Build the image
docker build -t "$IMAGE_NAME:$tag" .
# Tag as latest
docker tag "$IMAGE_NAME:$tag" "$IMAGE_NAME:latest"
success "Image built successfully: $IMAGE_NAME:$tag"
}
# Format version for display
format_version_display() {
local version=$1
if [[ ${#version} -eq 12 ]]; then
# Format: 202412151430 -> 2024-12-15 14:30
echo "${version:0:4}-${version:4:2}-${version:6:2} ${version:8:2}:${version:10:2}"
else
echo "$version"
fi
}
# Start container
start_container() {
local version=${1:-"latest"}
local tag="$version"
if [[ "$version" == "latest" ]]; then
tag="latest"
fi
info "Starting container with image: $IMAGE_NAME:$tag"
# Check if model cache directory exists
if [[ ! -d "$HOST_MODEL_CACHE" ]]; then
warning "Model cache directory not found: $HOST_MODEL_CACHE"
warning "Container will download models on first use"
else
info "Using model cache from: $HOST_MODEL_CACHE"
fi
# Start container with health check, restart policy, and model cache volume
docker run -d \
--name="$CONTAINER_NAME" \
--restart=unless-stopped \
-p "$PORT:8000" \
-v "$HOST_MODEL_CACHE:$CONTAINER_MODEL_CACHE" \
-e LAZY_LOAD_MODEL=true \
-e PYTHONUNBUFFERED=1 \
-e PYTHONDONTWRITEBYTECODE=1 \
--health-cmd="python /app/health_check.py" \
--health-interval=20s \
--health-timeout=15s \
--health-retries=5 \
--health-start-period=120s \
"$IMAGE_NAME:$tag"
success "Container started successfully"
# Wait for container to be healthy
info "Waiting for container to be healthy..."
local max_wait=300 # 5 minutes for model loading
local wait_time=0
while [[ $wait_time -lt $max_wait ]]; do
local health_status=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "unknown")
if [[ "$health_status" == "healthy" ]]; then
success "Container is healthy and ready!"
break
elif [[ "$health_status" == "unhealthy" ]]; then
error "Container is unhealthy"
docker logs "$CONTAINER_NAME"
exit 1
else
info "Container health status: $health_status (waiting...)"
sleep 10
wait_time=$((wait_time + 10))
fi
done
if [[ $wait_time -ge $max_wait ]]; then
warning "Timeout waiting for container to be healthy"
docker logs "$CONTAINER_NAME"
fi
}
# Deploy (build and start)
deploy() {
local version=$(get_next_version)
info "Starting deployment of version $version"
# Stop current container
stop_container
# Build new image
build_image "$version"
# Clean up old images
cleanup_old_images
# Start new container
start_container "$version"
# Update version file
update_version "$version"
success "Deployment completed successfully! Version: $version"
info "Application is running on http://localhost:$PORT"
info "Health check: http://localhost:$PORT/system/health"
info "System status: http://localhost:$PORT/system/status"
}
# Rollback to previous version
rollback() {
local target_version=${1:-""}
local current_version=$(get_current_version)
if [[ -z "$target_version" ]]; then
# Get the previous version automatically
target_version=$(get_previous_version)
if [[ -z "$target_version" ]]; then
error "No previous version found"
list_versions
exit 1
fi
fi
# Check if target image exists
if ! docker images -q "$IMAGE_NAME:$target_version" | grep -q .; then
error "Image $IMAGE_NAME:$target_version not found"
list_versions
exit 1
fi
info "Rolling back from version $current_version to version $target_version"
info "Target version: $(format_version_display $target_version)"
# Stop current container
stop_container
# Start container with target version
start_container "$target_version"
# Update version file
update_version "$target_version"
success "Rollback completed successfully! Now running version: $target_version"
}
# List available versions
list_versions() {
info "Available versions:"
echo "TAG CREATED SIZE FORMATTED TIME"
echo "-------------------------------------------------------------------"
# Show latest tag
docker images "$IMAGE_NAME:latest" --format "{{.Tag}}\t\t{{.CreatedAt}}\t{{.Size}}" 2>/dev/null | head -1
# Show timestamped versions with formatted display
docker images "$IMAGE_NAME" --format "{{.Tag}}\t{{.CreatedAt}}\t{{.Size}}" | \
grep -E '^[0-9]{12}' | \
sort -r | \
while IFS=$'\t' read -r tag created size; do
formatted_time=$(format_version_display "$tag")
printf "%-20s%-20s%-12s%s\n" "$tag" "$created" "$size" "$formatted_time"
done
}
# Show container status
status() {
info "Container status:"
docker ps -a -f name="$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}\t{{.Image}}"
if docker ps -q -f name="$CONTAINER_NAME" | grep -q .; then
echo ""
info "Container logs (last 10 lines):"
docker logs --tail=10 "$CONTAINER_NAME"
echo ""
info "Health check status:"
local health_status=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "unknown")
echo "Health: $health_status"
if command -v curl >/dev/null 2>&1; then
echo ""
info "Quick health check:"
curl -s "http://localhost:$PORT/system/health" | python3 -m json.tool 2>/dev/null || echo "Health check endpoint not responding"
fi
fi
}
# Show logs
logs() {
local lines=${1:-50}
if docker ps -q -f name="$CONTAINER_NAME" | grep -q .; then
docker logs -f --tail="$lines" "$CONTAINER_NAME"
else
error "Container $CONTAINER_NAME is not running"
exit 1
fi
}
# Cleanup everything (dangerous!)
cleanup_all() {
warning "This will remove ALL containers and images for this project!"
read -p "Are you sure? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
stop_container
docker rmi $(docker images -q "$IMAGE_NAME") || true
rm -f "$VERSION_FILE"
success "Cleanup completed"
else
info "Cleanup cancelled"
fi
}
# Show help
show_help() {
cat << EOF
Docker Manager for $PROJECT_NAME
Usage: $0 [COMMAND] [OPTIONS]
Commands:
deploy Build new version and deploy
rollback [TIMESTAMP] Rollback to previous version (or specified timestamp)
start [TIMESTAMP] Start container with specified version (default: latest)
stop Stop and remove container
restart Restart current container
status Show container status and logs
logs [LINES] Show container logs (default: 50 lines)
versions List available image versions
cleanup Remove all images and containers (DANGEROUS!)
help Show this help message
Examples:
$0 deploy # Deploy new version with timestamp
$0 rollback # Rollback to previous version
$0 rollback 202412151430 # Rollback to specific timestamp
$0 start 202412151430 # Start container with specific version
$0 logs 100 # Show last 100 log lines
$0 status # Show current status
Configuration:
Project: $PROJECT_NAME
Image: $IMAGE_NAME
Container: $CONTAINER_NAME
Port: $PORT
Max versions: $MAX_VERSIONS
Host model cache: $HOST_MODEL_CACHE
Container model cache: $CONTAINER_MODEL_CACHE
Version Format:
Versions use timestamp format: yyyyMMddHHmm (e.g., 202412151430 = 2024-12-15 14:30)
This provides better traceability and automatic chronological ordering.
Model Cache:
The script automatically mounts your local ModelScope cache directory
to the container, avoiding re-downloading models on each deployment.
EOF
}
# Main script logic
main() {
local command=${1:-"help"}
case $command in
"deploy")
deploy
;;
"rollback")
rollback "$2"
;;
"start")
stop_container
start_container "$2"
;;
"stop")
stop_container
;;
"restart")
stop_container
start_container
;;
"status")
status
;;
"logs")
logs "$2"
;;
"versions"|"list")
list_versions
;;
"cleanup")
cleanup_all
;;
"help"|"-h"|"--help")
show_help
;;
*)
error "Unknown command: $command"
show_help
exit 1
;;
esac
}
# Check if Docker is available
if ! command -v docker >/dev/null 2>&1; then
error "Docker is not installed or not in PATH"
exit 1
fi
# Create log file if it doesn't exist
touch "$LOG_FILE"
# Run main function
main "$@"