#!/bin/bash # Cloudpods 一键启动脚本 V2 # 分阶段启动,先启动keystone,再启动其他服务 set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # 检测操作系统 OS_TYPE="$(uname -s)" case "$OS_TYPE" in Darwin*) OS="macos" ;; Linux*) OS="linux" ;; CYGWIN*|MINGW*|MSYS*) echo -e "${RED}检测到 Windows 系统${NC}" echo -e "${YELLOW}建议使用 WSL (Windows Subsystem for Linux) 运行此项目${NC}" echo "" echo "如果您在 WSL 中运行,请忽略此消息" echo "如果您在原生 Windows 中运行,请:" echo " 1. 安装 WSL: https://docs.microsoft.com/zh-cn/windows/wsl/install" echo " 2. 在 WSL 中重新运行此脚本" exit 1 ;; *) echo -e "${RED}不支持的操作系统: $OS_TYPE${NC}"; exit 1 ;; esac # 脚本目录 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BACKEND_DIR="$(dirname "$SCRIPT_DIR")" PROJECT_ROOT="$(dirname "$BACKEND_DIR")" BIN_DIR="$BACKEND_DIR/_output/bin" LOG_DIR="$BACKEND_DIR/logs" PID_DIR="$BACKEND_DIR/logs" ENV_FILE="$PROJECT_ROOT/.env.local" echo -e "${GREEN}=== Cloudpods 服务启动脚本 V2 ===${NC}" echo "" # 加载环境变量 if [ ! -f "$ENV_FILE" ]; then echo -e "${RED}错误: 找不到环境变量文件 $ENV_FILE${NC}" exit 1 fi source "$ENV_FILE" # 检查必要的环境变量 if [ -z "$DB_HOST" ] || [ -z "$DB_PORT" ] || [ -z "$DB_USER" ]; then echo -e "${RED}错误: 数据库配置不完整${NC}" exit 1 fi # 构建数据库连接字符串 if [ -z "$DB_PASSWORD" ]; then SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/yunioncloud?charset=utf8" else SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/yunioncloud?charset=utf8" fi # Keystone认证URL AUTH_URL="http://localhost:35357/v3" echo -e "${GREEN}配置信息:${NC}" echo " 数据库: $DB_HOST:$DB_PORT" echo " 认证URL: $AUTH_URL" echo " 系统: $OS" echo "" # 创建必要的目录 mkdir -p "$LOG_DIR" "$PID_DIR" # 检查服务是否运行 is_service_running() { local service_name=$1 local pid_file="$PID_DIR/${service_name}.pid" if [ -f "$pid_file" ]; then local pid=$(cat "$pid_file") if ps -p "$pid" > /dev/null 2>&1; then return 0 fi fi return 1 } # 启动keystone服务 start_keystone() { local service_name="keystone" local port=35357 local bin_path="$BIN_DIR/$service_name" local log_file="$LOG_DIR/${service_name}.log" local pid_file="$PID_DIR/${service_name}.pid" if is_service_running "$service_name"; then echo -e "${YELLOW}Keystone 已在运行${NC}" return 0 fi echo -e "${GREEN}启动 Keystone (认证服务)...${NC}" > "$log_file" nohup "$bin_path" \ --sql-connection "$SQL_CONNECTION" \ --port $port \ --admin-port 35358 \ --auto-sync-table \ --bootstrap-admin-user-password sysadmin \ --region region0 \ --enable-default-policy \ --no-policy-violation-check \ > "$log_file" 2>&1 & local pid=$! echo $pid > "$pid_file" # 等待keystone启动 local max_wait=20 local waited=0 while [ $waited -lt $max_wait ]; do sleep 1 waited=$((waited + 1)) if ! ps -p "$pid" > /dev/null 2>&1; then echo -e "${RED}✗ Keystone 启动失败${NC}" tail -10 "$log_file" return 1 fi if grep -q "Start listen on" "$log_file" 2>/dev/null; then echo -e "${GREEN}✓ Keystone 启动成功 (PID: $pid)${NC}" return 0 fi if grep -q "\[fatal\]" "$log_file" 2>/dev/null; then echo -e "${RED}✗ Keystone 启动失败${NC}" grep "\[fatal\]" "$log_file" | tail -1 return 1 fi done echo -e "${YELLOW}⚠ Keystone 启动超时${NC}" return 1 } # 启动其他服务 start_service() { local service_name=$1 local port=$2 local need_auth=${3:-true} local need_db=${4:-true} local bin_path="$BIN_DIR/$service_name" local log_file="$LOG_DIR/${service_name}.log" local pid_file="$PID_DIR/${service_name}.pid" if is_service_running "$service_name"; then echo -e "${YELLOW} $service_name 已在运行${NC}" return 0 fi echo -e "${GREEN}启动 $service_name (端口: $port)...${NC}" > "$log_file" # 构建启动命令 local cmd="$bin_path --port $port" if [ "$need_db" = "true" ]; then cmd="$cmd --sql-connection \"$SQL_CONNECTION\" --auto-sync-table" fi if [ "$need_auth" = "true" ]; then cmd="$cmd --auth-url $AUTH_URL --admin-user sysadmin --admin-password sysadmin --admin-project system" fi # glance特殊配置 if [ "$service_name" = "glance" ]; then mkdir -p "$BACKEND_DIR/data/glance" cmd="$cmd --filesystem-store-datadir $BACKEND_DIR/data/glance" fi # region特殊配置 (需要etcd) if [ "$service_name" = "region" ]; then cmd="$cmd --etcd-endpoints http://localhost:2379" fi nohup bash -c "$cmd" > "$log_file" 2>&1 & local pid=$! echo $pid > "$pid_file" # 等待服务启动 local max_wait=15 local waited=0 while [ $waited -lt $max_wait ]; do sleep 1 waited=$((waited + 1)) if ! ps -p "$pid" > /dev/null 2>&1; then echo -e "${RED} ✗ $service_name 启动失败${NC}" echo -e "${YELLOW} 错误信息:${NC}" tail -3 "$log_file" | sed 's/^/ /' return 1 fi if grep -q "Start listen on" "$log_file" 2>/dev/null; then echo -e "${GREEN} ✓ $service_name 启动成功 (PID: $pid)${NC}" return 0 fi if grep -q "\[fatal\]" "$log_file" 2>/dev/null; then echo -e "${RED} ✗ $service_name 启动失败${NC}" grep "\[fatal\]" "$log_file" | tail -1 | sed 's/^/ /' return 1 fi done echo -e "${YELLOW} ⚠ $service_name 可能正在启动 (PID: $pid)${NC}" return 0 } # 停止服务 stop_service() { local service_name=$1 local pid_file="$PID_DIR/${service_name}.pid" if [ -f "$pid_file" ]; then local pid=$(cat "$pid_file") if ps -p "$pid" > /dev/null 2>&1; then echo -e "${YELLOW}停止 $service_name (PID: $pid)...${NC}" kill "$pid" 2>/dev/null || true sleep 1 if ps -p "$pid" > /dev/null 2>&1; then kill -9 "$pid" 2>/dev/null || true fi rm -f "$pid_file" else rm -f "$pid_file" fi fi } # 启动所有服务 start_all() { echo -e "${GREEN}=== 启动所有服务 ===${NC}" echo "" # 第0阶段:启动etcd echo -e "${YELLOW}阶段 0: 启动etcd${NC}" if [ -f "$SCRIPT_DIR/start-etcd.sh" ]; then bash "$SCRIPT_DIR/start-etcd.sh" || { echo -e "${RED}etcd 启动失败${NC}" exit 1 } else echo -e "${YELLOW}未找到 start-etcd.sh,跳过etcd启动${NC}" fi # 初始化 etcd 配置 echo -e "${YELLOW}初始化 etcd 配置...${NC}" ETCDCTL="$BACKEND_DIR/bin/darwin/etcdctl" if [ ! -f "$ETCDCTL" ]; then # 尝试 Linux 版本 ETCDCTL="$BACKEND_DIR/bin/linux/etcdctl" fi if [ -f "$ETCDCTL" ]; then $ETCDCTL --endpoints=http://localhost:2379 put unifiedmonitors '{"enabled":false}' > /dev/null 2>&1 || true echo -e "${GREEN}✓ etcd 配置初始化完成${NC}" fi echo "" # 第一阶段:启动keystone echo -e "${YELLOW}阶段 1: 启动认证服务${NC}" if ! start_keystone; then echo -e "${RED}Keystone 启动失败,无法继续${NC}" exit 1 fi echo "" # 等待keystone完全就绪 echo -e "${YELLOW}等待 Keystone 完全就绪...${NC}" sleep 3 echo "" # 初始化RBAC策略 echo -e "${YELLOW}初始化RBAC策略...${NC}" if [ -f "$SCRIPT_DIR/init-admin-policy.sh" ]; then bash "$SCRIPT_DIR/init-admin-policy.sh" 2>&1 | grep -v "Warning" || true fi echo "" # 注册服务到service catalog echo -e "${YELLOW}注册服务到Service Catalog...${NC}" if [ -f "$SCRIPT_DIR/register-services.sh" ]; then bash "$SCRIPT_DIR/register-services.sh" 2>&1 | grep -v "Warning" | grep -E "✓|已注册" || true fi echo "" # 第二阶段:启动业务服务 echo -e "${YELLOW}阶段 2: 启动业务服务${NC}" # region: 计算服务 (需要认证、数据库和etcd) start_service "region" 30888 true true # glance: 镜像服务 (需要认证和数据库) start_service "glance" 9292 true true # yunionconf: 配置服务 (需要认证和数据库) start_service "yunionconf" 30889 true true # monitor: 监控服务 (需要认证和数据库) start_service "monitor" 30093 true true # scheduledtask: 定时任务服务 (需要认证和数据库) start_service "scheduledtask" 30891 true true echo "" # 第三阶段:启动API网关 (需要认证,不需要数据库) echo -e "${YELLOW}阶段 3: 启动API网关${NC}" start_service "apigateway" 30300 true false echo "" echo -e "${GREEN}=== 服务启动完成 ===${NC}" echo "" echo -e "${GREEN}访问地址:${NC}" echo " API Gateway: http://localhost:30300" echo " Keystone: http://localhost:35357" echo "" echo -e "${YELLOW}查看日志: tail -f $LOG_DIR/.log${NC}" } # 停止所有服务 stop_all() { echo -e "${YELLOW}=== 停止所有服务 ===${NC}" echo "" local services=("scheduledtask" "monitor" "yunionconf" "scheduler" "apigateway" "glance" "region" "keystone") for service in "${services[@]}"; do stop_service "$service" done echo "" echo -e "${GREEN}=== 所有服务已停止 ===${NC}" } # 查看服务状态 status_all() { echo -e "${GREEN}=== 服务状态 ===${NC}" echo "" # 检查etcd if [ -f "$BACKEND_DIR/logs/etcd.pid" ]; then local pid=$(cat "$BACKEND_DIR/logs/etcd.pid") if ps -p "$pid" > /dev/null 2>&1; then echo -e "${GREEN} ✓ etcd${NC} (PID: $pid, Port: 2379)" else echo -e "${RED} ✗ etcd${NC} (未运行)" fi else echo -e "${RED} ✗ etcd${NC} (未运行)" fi local services=("keystone:35357" "region:30888" "glance:9292" "apigateway:30300" "scheduler:30888" "yunionconf:30889" "monitor:30093" "scheduledtask:30891") for service_info in "${services[@]}"; do local service_name="${service_info%%:*}" local port="${service_info##*:}" if is_service_running "$service_name"; then local pid=$(cat "$PID_DIR/${service_name}.pid") echo -e "${GREEN} ✓ $service_name${NC} (PID: $pid, Port: $port)" else echo -e "${RED} ✗ $service_name${NC} (未运行)" fi done echo "" } # 主函数 case "${1:-}" in start) start_all ;; stop) stop_all ;; restart) stop_all sleep 2 start_all ;; status) status_all ;; *) echo "用法: $0 {start|stop|restart|status}" echo "" echo "命令:" echo " start - 启动所有服务" echo " stop - 停止所有服务" echo " restart - 重启所有服务" echo " status - 查看服务状态" exit 1 ;; esac