Files

321 lines
9.5 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 "$@"