#!/usr/bin/env bash set -euo pipefail # ============================================================================ # DevOps AI Stack - 一键启动脚本 # 功能:启动完整的开发栈,包含前置检查、健康验证和回滚机制 # ============================================================================ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" LOG_FILE="${PROJECT_ROOT}/logs/init-dev-$(date +%Y%m%d_%H%M%S).log" STARTED_SERVICES=() # 创建日志目录 mkdir -p "${PROJECT_ROOT}/logs" # ============================================================================ # 日志和颜色输出 # ============================================================================ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } log_info() { echo -e "\033[36ℹ️ INFO\033[0m: $*" | tee -a "$LOG_FILE" } log_success() { echo -e "\033[32✅ SUCCESS\033[0m: $*" | tee -a "$LOG_FILE" } log_warning() { echo -e "\033[33⚠️ WARNING\033[0m: $*" | tee -a "$LOG_FILE" } log_error() { echo -e "\033[31❌ ERROR\033[0m: $*" | tee -a "$LOG_FILE" } # ============================================================================ # 前置条件检查 # ============================================================================ check_requirements() { log_info "检查系统要求..." local missing_tools=() # 检查必要工具 for tool in docker docker-compose mkcert curl; do if ! command -v "$tool" &>/dev/null; then missing_tools+=("$tool") fi done if [[ ${#missing_tools[@]} -gt 0 ]]; then log_error "缺少必要工具: ${missing_tools[*]}" log_info "请安装: brew install ${missing_tools[*]}" return 1 fi # 检查Docker daemon if ! docker ps &>/dev/null; then log_error "Docker daemon 未运行" log_info "请启动 Docker Desktop" return 1 fi # 检查磁盘空间 (至少需要10GB) local free_space=$(df -BG "$PROJECT_ROOT" | awk 'NR==2 {print $4}' | sed 's/G//') if [[ $free_space -lt 10 ]]; then log_warning "可用磁盘空间: ${free_space}GB (建议 >=10GB)" fi log_success "系统要求检查通过" return 0 } # ============================================================================ # 环境变量验证 # ============================================================================ verify_environment() { log_info "验证环境变量配置..." # 检查环境文件 local env_files=( "${PROJECT_ROOT}/secrets/.env.core" "${PROJECT_ROOT}/secrets/.env.ai" ) for env_file in "${env_files[@]}"; do if [[ ! -f "$env_file" ]]; then log_warning "缺少配置文件: $env_file" local example_file="${env_file}.example" if [[ -f "$example_file" ]]; then log_info "使用模板文件: $example_file" cp "$example_file" "$env_file" log_warning "请编辑 $env_file 配置必要的环境变量" fi fi done # 验证关键变量 (可选,因为大部分变量有默认值) if [[ -f "${PROJECT_ROOT}/secrets/.env.core" ]]; then # shellcheck source=/dev/null source "${PROJECT_ROOT}/secrets/.env.core" || true fi log_success "环境变量配置检查完成" return 0 } # ============================================================================ # 证书初始化 # ============================================================================ setup_certificates() { log_info "设置 TLS 证书..." local cert_dir="${PROJECT_ROOT}/infra/traefik/certs" mkdir -p "$cert_dir" # 检查证书是否存在 if [[ -f "${cert_dir}/cert.pem" ]] && [[ -f "${cert_dir}/key.pem" ]]; then log_success "TLS 证书已存在" return 0 fi # 生成本地开发证书 if bash "${SCRIPT_DIR}/mkcert-init.sh" >> "$LOG_FILE" 2>&1; then log_success "TLS 证书生成成功" return 0 else log_error "TLS 证书生成失败" return 1 fi } # ============================================================================ # 启动服务 # ============================================================================ start_service() { local service_name=$1 local compose_file="${PROJECT_ROOT}/infra/compose/${service_name}.yml" if [[ ! -f "$compose_file" ]]; then log_error "找不到 Compose 文件: $compose_file" return 1 fi log_info "启动服务: $service_name" if docker compose -f "$compose_file" up -d >> "$LOG_FILE" 2>&1; then STARTED_SERVICES+=("$service_name") log_success "$service_name 启动成功" return 0 else log_error "$service_name 启动失败" return 1 fi } # ============================================================================ # 服务健康检查 # ============================================================================ wait_for_service() { local service_name=$1 local endpoint=$2 local max_retries=${3:-30} local retry_interval=${4:-2} log_info "等待 $service_name 就绪 (最多 $((max_retries * retry_interval)) 秒)..." local retries=0 while [[ $retries -lt $max_retries ]]; do if curl -sk "$endpoint" &>/dev/null; then log_success "$service_name 已就绪" return 0 fi retries=$((retries + 1)) if [[ $((retries % 5)) -eq 0 ]]; then log_info "仍在等待... ($retries/$max_retries)" fi sleep "$retry_interval" done log_warning "$service_name 启动缓慢,但继续启动其他服务" return 1 } verify_critical_services() { log_info "验证关键服务状态..." local services=( "Traefik:https://traefik.dev.lan/ping" "Gitea:https://git.dev.lan/api/healthz" "Grafana:https://grafana.dev.lan/api/health" ) local failed_services=() for service in "${services[@]}"; do IFS=':' read -r name endpoint <<< "$service" if ! wait_for_service "$name" "$endpoint" 20 3; then failed_services+=("$name") fi done if [[ ${#failed_services[@]} -gt 0 ]]; then log_warning "部分服务未就绪: ${failed_services[*]}" log_info "建议等待 1-2 分钟后手动检查服务状态" return 1 fi log_success "所有关键服务已就绪" return 0 } # ============================================================================ # 错误处理和回滚 # ============================================================================ cleanup_on_error() { local exit_code=$? if [[ $exit_code -ne 0 ]]; then log_error "启动过程失败 (退出码: $exit_code)" log_info "已启动的服务: ${STARTED_SERVICES[*]}" log_info "是否需要回滚? 运行: ./infra/scripts/cleanup-dev.sh" log_info "完整日志: $LOG_FILE" fi return "$exit_code" } trap cleanup_on_error EXIT # ============================================================================ # 主启动流程 # ============================================================================ main() { log "==========================================" log "🚀 DevOps AI Stack 启动开始" log "==========================================" # 1. 前置检查 if ! check_requirements; then return 1 fi # 2. 验证环境 if ! verify_environment; then return 1 fi # 3. 证书设置 if ! setup_certificates; then return 1 fi # 4. 启动服务 (按依赖顺序) local startup_order=( "core" # Traefik + Gitea + Registry "dev-cache" # 缓存服务 "observability" # 监控栈 "ai" # AI 网关和模型 "ai-visual" # AI 可视化 "taskflow" # 任务编排 ) for service in "${startup_order[@]}"; do if ! start_service "$service"; then log_warning "部分服务启动失败,继续尝试启动其他服务..." fi sleep 5 # 服务间隔 done # 5. 等待所有服务初始化 log_info "等待服务初始化 (30 秒)..." sleep 30 # 6. 验证关键服务 if ! verify_critical_services; then log_warning "部分服务未完全就绪,请查看日志: $LOG_FILE" fi # 7. 打印访问信息 log "" log "==========================================" log_success "✅ DevOps AI Stack 启动完成!" log "==========================================" log "" log "📍 服务访问地址 (使用 https):" log " • Traefik Dashboard: https://traefik.dev.lan/" log " • Gitea: https://git.dev.lan/" log " • Grafana: https://grafana.dev.lan/" log " • LiteLLM API: https://ai.dev.lan/v1" log " • Langfuse: https://langfuse.dev.lan/" log " • MLflow: https://mlflow.dev.lan/" log " • Prefect: https://prefect.dev.lan/" log " • MinIO: https://minio.dev.lan/" log "" log "📋 日志位置: $LOG_FILE" log "" log "💡 后续步骤:" log " 1. 查看 IMPLEMENTATION_GUIDE.md 了解详细配置" log " 2. 配置 Gitea Runner Token: ./infra/scripts/configure-gitea.sh" log " 3. 导入 Grafana 仪表板: ./infra/scripts/setup-grafana.sh" log "" log "🆘 遇到问题? 查看日志: tail -f logs/init-dev-*.log" log "==========================================" } main "$@"