#!/bin/bash # # 微服务容器化管理脚本 # 用法: ./docker.sh [command] # set -e cd "$(dirname "$0")" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color info() { echo -e "${GREEN}[INFO]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } check_docker() { if ! command -v docker &> /dev/null; then error "Docker 未安装,请先安装 Docker Desktop" fi if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then error "Docker Compose 未安装" fi } # 使用 docker compose 或 docker-compose(兼容旧版本) docker_compose() { if docker compose version &> /dev/null; then docker compose "$@" else docker-compose "$@" fi } cmd_build() { info "构建所有服务镜像..." docker_compose build --progress=plain "$@" info "构建完成" } cmd_up() { info "启动服务(后台运行)..." docker_compose up -d "$@" info "服务已启动" echo "" cmd_ps } cmd_down() { info "停止并移除容器..." docker_compose down "$@" info "服务已停止" } cmd_start() { info "启动已存在的容器..." docker_compose start "$@" } cmd_stop() { info "停止运行中的容器..." docker_compose stop "$@" } cmd_restart() { info "重启服务..." docker_compose restart "$@" } cmd_ps() { info "服务状态:" docker_compose ps } cmd_logs() { if [ -z "$1" ]; then docker_compose logs -f --tail=100 else docker_compose logs -f --tail=100 "$@" fi } cmd_shell() { local service=${1:-dnfs} info "进入 $service 容器 shell..." docker_compose exec "$service" /bin/bash || docker_compose exec "$service" /bin/sh } cmd_rebuild() { info "重新构建并启动服务..." docker_compose build --no-cache "$@" docker_compose up -d "$@" } cmd_clean() { warn "此操作将停止所有容器并删除数据卷(数据库和ChromaDB数据将丢失!)" read -p "确认继续? [y/N] " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then docker_compose down -v info "已清理所有容器和数据卷" else info "已取消" fi } cmd_pull() { info "拉取基础镜像..." docker pull python:3.11-slim } cmd_health() { info "检查服务健康状态..." local services=("db:8005" "ai_review:8007" "dnfs:8006") for svc in "${services[@]}"; do IFS=':' read -r name port <<< "$svc" if nc -z 127.0.0.1 "$port" 2>/dev/null; then echo -e " ${GREEN}✓${NC} $name :$port 健康" else echo -e " ${RED}✗${NC} $name :$port 不可达" fi done } # ── 数据库管理命令 ────────────────────────────────────────────── DB_CONTAINER="micro-db" _check_db_running() { if ! docker ps --format '{{.Names}}' | grep -q "^${DB_CONTAINER}$"; then error "db 容器未运行,请先启动服务: ./docker.sh up" fi } cmd_db_info() { _check_db_running info "数据库信息:" docker_compose exec db python db_tools.py info } cmd_db_backup() { _check_db_running local backup_file="${1:-}" if [ -z "$backup_file" ]; then backup_file="backup_$(date +%Y%m%d_%H%M%S).db" fi info "执行数据库备份..." # 先做 WAL checkpoint 确保数据完整 docker_compose exec db python db_tools.py wal-checkpoint # 备份到容器内的 /app/backups 目录(映射到宿主机 ./backups) docker_compose exec db python db_tools.py backup "/app/backups/$backup_file" echo "" info "备份文件: ./backups/$backup_file" ls -lh "./backups/$backup_file" 2>/dev/null || info "(文件已写入容器 /app/backups/) " } cmd_db_export() { _check_db_running local export_file="${1:-}" if [ -z "$export_file" ]; then export_file="export_$(date +%Y%m%d_%H%M%S).json" fi info "导出数据库为 JSON..." docker_compose exec db python db_tools.py export "/app/backups/$export_file" echo "" info "导出文件: ./backups/$export_file" } cmd_db_import() { local import_file="$1" if [ -z "$import_file" ]; then error "用法: ./docker.sh db-import " fi if [ ! -f "./backups/$import_file" ] && [ ! -f "$import_file" ]; then # 检查是否在backups目录 error "导入文件不存在: $import_file (请放在 ./backups/ 目录下或提供绝对路径)" fi _check_db_running local container_path="/app/backups/$(basename "$import_file")" # 如果是宿主机路径且不在backups下,先复制到backups if [ -f "$import_file" ] && [ ! -f "./backups/$(basename "$import_file")" ]; then warn "复制文件到 backups 目录..." cp "$import_file" "./backups/" fi warn "即将导入数据,现有数据将被覆盖!" read -p "确认继续? [y/N] " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then # 先备份当前数据 cmd_db_backup "before_import_$(date +%Y%m%d_%H%M%S).db" info "开始导入..." docker_compose exec db python db_tools.py import "$container_path" else info "已取消" fi } cmd_db_vacuum() { _check_db_running info "执行 VACUUM 压缩数据库..." docker_compose exec db python db_tools.py vacuum } cmd_db_restore() { local backup_file="$1" if [ -z "$backup_file" ]; then error "用法: ./docker.sh db-restore <备份文件路径> (从 ./backups/ 目录)" error "示例: ./docker.sh db-restore backup_20260730_120000.db" fi local backup_path="./backups/$backup_file" if [ ! -f "$backup_path" ]; then # 尝试绝对路径 if [ -f "$backup_file" ]; then backup_path="$backup_file" else error "备份文件不存在: $backup_file" fi fi _check_db_running warn "即将从备份恢复数据库,当前数据将被覆盖!" warn "备份文件: $backup_path" read -p "确认继续? [y/N] 输入 YES 确认: " -r echo if [[ "$REPLY" == "YES" ]]; then info "停止 dnfs 和 ai_review 服务..." docker_compose stop dnfs ai_review info "复制备份文件到容器..." docker cp "$backup_path" "$DB_CONTAINER:/app/data/database.db" info "重启 db 服务..." docker_compose restart db # 等待db健康 info "等待 db 服务恢复..." sleep 5 info "启动其他服务..." docker_compose start dnfs ai_review info "恢复完成!" else info "已取消" fi } cmd_db_list_backups() { info "备份文件列表 (./backups/):" if [ -d "./backups" ]; then ls -lhS ./backups/ 2>/dev/null | grep -v "total" | grep -v ".gitkeep" || echo " (无备份文件)" else echo " backups 目录不存在" fi } usage() { echo "微服务容器化管理脚本" echo "" echo "用法: ./docker.sh [options]" echo "" echo "── 服务管理 ──" echo " build 构建所有服务镜像" echo " up 启动所有服务(后台运行)" echo " down 停止并移除所有容器" echo " start 启动已停止的容器" echo " stop 停止运行中的容器" echo " restart 重启服务" echo " ps 查看服务状态" echo " logs [svc] 查看日志(默认所有服务,可指定服务名)" echo " shell [svc] 进入容器shell(默认dnfs)" echo " rebuild 重新构建(无缓存)并启动" echo " health 检查服务健康状态" echo " pull 拉取最新基础镜像" echo " clean 停止并删除所有容器+数据卷(危险!)" echo "" echo "── 数据库管理 (SQLite) ──" echo " db-info 查看数据库信息和表统计" echo " db-backup [file] 备份数据库(.db 二进制文件,输出到 ./backups/)" echo " db-export [file] 导出数据为 JSON(跨平台传输,输出到 ./backups/)" echo " db-import 从 JSON 导入数据(文件放在 ./backups/ 下)" echo " db-restore 从 .db 备份文件恢复" echo " db-vacuum VACUUM 压缩数据库" echo " db-ls 列出备份文件" echo "" echo "示例:" echo " ./docker.sh build dnfs # 仅构建dnfs服务" echo " ./docker.sh logs dnfs # 查看dnfs日志" echo " ./docker.sh shell db # 进入db容器" echo " ./docker.sh db-backup # 立即备份" echo " ./docker.sh db-export # 导出JSON用于迁移" echo " ./docker.sh db-ls # 查看现有备份" echo "" } # 主逻辑 check_docker case "${1:-}" in build) shift cmd_build "$@" ;; up) shift cmd_up "$@" ;; down) shift cmd_down "$@" ;; start) shift cmd_start "$@" ;; stop) shift cmd_stop "$@" ;; restart) shift cmd_restart "$@" ;; ps|status) cmd_ps ;; logs) shift cmd_logs "$@" ;; shell) shift cmd_shell "$@" ;; rebuild) shift cmd_rebuild "$@" ;; health) cmd_health ;; pull) cmd_pull ;; clean) cmd_clean ;; # ── 数据库管理命令 ── db-info|dbinfo) cmd_db_info ;; db-backup|dbbackup) shift cmd_db_backup "$@" ;; db-export|dbexport) shift cmd_db_export "$@" ;; db-import|dbimport) shift cmd_db_import "$@" ;; db-restore|dbrestore) shift cmd_db_restore "$@" ;; db-vacuum|dbvacuum) cmd_db_vacuum ;; db-ls|dbls) cmd_db_list_backups ;; *) usage exit 1 ;; esac