#!/bin/bash # Cloudpods 统一初始化脚本 # 用于全新部署时的完整初始化 set -e RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # 初始化配置 INIT_CONFIG_FILE="${INIT_CONFIG_FILE:-init-config.env}" # 加载配置文件 load_config() { if [ -f "$INIT_CONFIG_FILE" ]; then echo -e "${BLUE}加载配置文件: $INIT_CONFIG_FILE${NC}" set -a source "$INIT_CONFIG_FILE" set +a fi # 加载 .env.local if [ -f "../.env.local" ]; then set -a source ../.env.local set +a fi } # 设置默认值 setup_defaults() { # 数据库配置 DB_HOST=${DB_HOST:-127.0.0.1} DB_PORT=${DB_PORT:-3306} DB_USER=${DB_USER:-root} DB_PASSWORD=${DB_PASSWORD:-} DB_NAME=${DB_NAME:-yunioncloud} # Keystone 配置 KEYSTONE_HOST=${KEYSTONE_HOST:-127.0.0.1} KEYSTONE_PORT=${KEYSTONE_PORT:-30500} ADMIN_USER=${ADMIN_USER:-sysadmin} ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin@123} ADMIN_PROJECT=${ADMIN_PROJECT:-system} ADMIN_DOMAIN=${ADMIN_DOMAIN:-Default} REGION_NAME=${REGION_NAME:-region0} # 服务端口配置 REGION_PORT=${REGION_PORT:-30889} GLANCE_PORT=${GLANCE_PORT:-9292} YUNIONCONF_PORT=${YUNIONCONF_PORT:-30888} MONITOR_PORT=${MONITOR_PORT:-30900} SCHEDULEDTASK_PORT=${SCHEDULEDTASK_PORT:-30950} APIGATEWAY_PORT=${APIGATEWAY_PORT:-3000} } # 打印配置信息 print_config() { echo -e "${GREEN}=== Cloudpods 初始化配置 ===${NC}" echo "" echo -e "${BLUE}数据库配置:${NC}" echo " 主机: ${DB_HOST}:${DB_PORT}" echo " 数据库: ${DB_NAME}" echo " 用户: ${DB_USER}" echo "" echo -e "${BLUE}Keystone 配置:${NC}" echo " 地址: http://${KEYSTONE_HOST}:${KEYSTONE_PORT}/v3" echo " 管理员: ${ADMIN_USER}@${ADMIN_DOMAIN}" echo " 项目: ${ADMIN_PROJECT}" echo " Region: ${REGION_NAME}" echo "" echo -e "${BLUE}服务端口配置:${NC}" echo " Region: ${REGION_PORT}" echo " Glance: ${GLANCE_PORT}" echo " YunionConf: ${YUNIONCONF_PORT}" echo " Monitor: ${MONITOR_PORT}" echo " ScheduledTask: ${SCHEDULEDTASK_PORT}" echo " APIGateway: ${APIGATEWAY_PORT}" echo "" } # 初始化阶段 1: 数据库 init_database() { echo -e "${GREEN}=== 阶段 1: 初始化数据库 ===${NC}" # 构建 mysql 命令 if [ -z "$DB_PASSWORD" ]; then MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER}" else MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD}" fi # 检查 MySQL 连接 echo -e "${YELLOW}检查 MySQL 连接...${NC}" if ! $MYSQL_CMD -e "SELECT 1" > /dev/null 2>&1; then echo -e "${RED}错误: 无法连接到 MySQL${NC}" exit 1 fi echo -e "${GREEN}✓ MySQL 连接正常${NC}" # 删除已存在的数据库 echo -e "${YELLOW}删除已存在的数据库 ${DB_NAME}...${NC}" $MYSQL_CMD -e "DROP DATABASE IF EXISTS ${DB_NAME};" 2>/dev/null || true # 创建新数据库 echo -e "${YELLOW}创建数据库 ${DB_NAME}...${NC}" $MYSQL_CMD -e "CREATE DATABASE ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" echo -e "${GREEN}✓ 数据库创建成功${NC}" # 创建用户(如果不存在) if [ -n "$DB_PASSWORD" ]; then echo -e "${YELLOW}创建数据库用户...${NC}" $MYSQL_CMD -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}';" 2>/dev/null || true $MYSQL_CMD -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%';" 2>/dev/null || true $MYSQL_CMD -e "FLUSH PRIVILEGES;" 2>/dev/null || true echo -e "${GREEN}✓ 数据库用户权限设置完成${NC}" fi } # 初始化阶段 2: 启动基础服务 init_services() { echo -e "${GREEN}=== 阶段 2: 启动基础服务 ===${NC}" # 构建数据库连接字符串 if [ -z "$DB_PASSWORD" ]; then SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4" else SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4" fi # 创建日志目录 mkdir -p logs # 启动 Keystone echo -e "${YELLOW}启动 Keystone...${NC}" if [ ! -f "_output/bin/keystone" ]; then echo -e "${YELLOW}编译 Keystone...${NC}" GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/keystone yunion.io/x/onecloud/cmd/keystone fi ./_output/bin/keystone \ --config /dev/null \ --sql-connection "$SQL_CONNECTION" \ --auto-sync-table \ --port ${KEYSTONE_PORT} \ --admin-port 35357 \ --bootstrap-admin-user-password ${ADMIN_PASSWORD} \ > logs/keystone.log 2>&1 & echo $! > logs/keystone.pid echo -e "${GREEN}✓ Keystone 已启动${NC}" # 等待 Keystone 就绪 echo -e "${YELLOW}等待 Keystone 就绪...${NC}" for i in {1..30}; do if curl -s http://127.0.0.1:${KEYSTONE_PORT}/v3 > /dev/null 2>&1; then echo -e "${GREEN}✓ Keystone 已就绪${NC}" break fi sleep 1 if [ $i -eq 30 ]; then echo -e "${RED}错误: Keystone 启动超时${NC}" exit 1 fi done # 设置环境变量 export OS_AUTH_URL=http://127.0.0.1:${KEYSTONE_PORT}/v3 export OS_USERNAME=${ADMIN_USER} export OS_PASSWORD=${ADMIN_PASSWORD} export OS_DOMAIN_NAME=${ADMIN_DOMAIN} export OS_PROJECT_NAME=${ADMIN_PROJECT} export OS_REGION_NAME=${REGION_NAME} } # 初始化阶段 3: 注册服务和 Endpoint init_keystone() { echo -e "${GREEN}=== 阶段 3: 注册服务和 Endpoint ===${NC}" # 编译 climc if [ ! -f "_output/bin/climc" ]; then echo -e "${YELLOW}编译 climc...${NC}" GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/climc yunion.io/x/onecloud/cmd/climc fi # 创建 Region echo -e "${YELLOW}创建 Region...${NC}" if ! ./_output/bin/climc region-list 2>/dev/null | grep -q "| ${REGION_NAME} "; then ./_output/bin/climc region-create ${REGION_NAME} 2>/dev/null echo -e "${GREEN}✓ Region ${REGION_NAME} 创建成功${NC}" else echo -e "${GREEN}✓ Region ${REGION_NAME} 已存在${NC}" fi # 创建 Zone echo -e "${YELLOW}创建 Zone...${NC}" if ! ./_output/bin/climc zone-list 2>/dev/null | grep -q "| zone0 "; then ./_output/bin/climc zone-create zone0 2>/dev/null echo -e "${GREEN}✓ Zone zone0 创建成功${NC}" else echo -e "${GREEN}✓ Zone zone0 已存在${NC}" fi # 定义服务和端口 declare -A SERVICES=( ["region"]=${REGION_PORT} ["glance"]=${GLANCE_PORT} ["yunionconf"]=${YUNIONCONF_PORT} ["monitor"]=${MONITOR_PORT} ["scheduledtask"]=${SCHEDULEDTASK_PORT} ["compute_v2"]=${REGION_PORT} ["common"]=${REGION_PORT} ["yunionapi"]=${REGION_PORT} ["meter"]=${REGION_PORT} ) # 注册服务和 Endpoint for service_name in "${!SERVICES[@]}"; do port=${SERVICES[$service_name]} service_type=$service_name echo -e "${YELLOW}注册服务: ${service_name}${NC}" # 检查服务是否存在 if ! ./_output/bin/climc service-list 2>/dev/null | grep -q "| ${service_name} "; then ./_output/bin/climc service-create ${service_type} ${service_name} 2>/dev/null echo -e "${GREEN} ✓ 服务 ${service_name} 创建成功${NC}" else echo -e "${GREEN} ✓ 服务 ${service_name} 已存在${NC}" fi # 获取服务 ID service_id=$(./_output/bin/climc service-show ${service_name} 2>/dev/null | grep "^| id" | awk '{print $4}') # 创建 Endpoints for interface in public internal admin; do endpoint_name="${service_name}-${interface}" if ! ./_output/bin/climc endpoint-list --service ${service_id} 2>/dev/null | grep -q "| ${endpoint_name} "; then ./_output/bin/climc endpoint-create ${service_id} ${REGION_NAME} ${interface} "http://127.0.0.1:${port}" 2>/dev/null echo -e "${GREEN} ✓ Endpoint ${interface} 创建成功${NC}" fi done done # 为 Keystone 创建 slave endpoint echo -e "${YELLOW}注册 Keystone slave endpoint...${NC}" keystone_id=$(./_output/bin/climc service-show keystone 2>/dev/null | grep "^| id" | awk '{print $4}') if [ -n "$keystone_id" ]; then if ! ./_output/bin/climc endpoint-list --service ${keystone_id} 2>/dev/null | grep -q "slave"; then ./_output/bin/climc endpoint-create ${keystone_id} ${REGION_NAME} slave "http://127.0.0.1:35357" 2>/dev/null echo -e "${GREEN}✓ Keystone slave endpoint 创建成功${NC}" fi fi } # 初始化阶段 4: 启动其他服务 start_other_services() { echo -e "${GREEN}=== 阶段 4: 启动其他服务 ===${NC}" # 构建数据库连接字符串 if [ -z "$DB_PASSWORD" ]; then SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4" else SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4" fi # 通用服务启动参数 COMMON_ARGS="--auth-url http://127.0.0.1:${KEYSTONE_PORT}/v3 --admin-user ${ADMIN_USER} --admin-password ${ADMIN_PASSWORD} --admin-project ${ADMIN_PROJECT} --admin-domain ${ADMIN_DOMAIN}" # 启动 Region echo -e "${YELLOW}启动 Region...${NC}" if [ ! -f "_output/bin/region" ]; then GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/region yunion.io/x/onecloud/cmd/region fi ./_output/bin/region \ --config /dev/null \ --sql-connection "$SQL_CONNECTION" \ --auto-sync-table \ --port ${REGION_PORT} \ ${COMMON_ARGS} \ > logs/region.log 2>&1 & echo $! > logs/region.pid echo -e "${GREEN}✓ Region 已启动${NC}" # 创建 glance 数据目录 mkdir -p /tmp/glance_data # 启动 Glance echo -e "${YELLOW}启动 Glance...${NC}" if [ ! -f "_output/bin/glance" ]; then GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/glance yunion.io/x/onecloud/cmd/glance fi ./_output/bin/glance \ --config /dev/null \ --sql-connection "$SQL_CONNECTION" \ --auto-sync-table \ --port ${GLANCE_PORT} \ --filesystem-store-datadir /tmp/glance_data \ ${COMMON_ARGS} \ > logs/glance.log 2>&1 & echo $! > logs/glance.pid echo -e "${GREEN}✓ Glance 已启动${NC}" # 启动 YunionConf echo -e "${YELLOW}启动 YunionConf...${NC}" if [ ! -f "_output/bin/yunionconf" ]; then GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/yunionconf yunion.io/x/onecloud/cmd/yunionconf fi ./_output/bin/yunionconf \ --config /dev/null \ --sql-connection "$SQL_CONNECTION" \ --auto-sync-table \ --port ${YUNIONCONF_PORT} \ ${COMMON_ARGS} \ > logs/yunionconf.log 2>&1 & echo $! > logs/yunionconf.pid echo -e "${GREEN}✓ YunionConf 已启动${NC}" # 启动 ScheduledTask echo -e "${YELLOW}启动 ScheduledTask...${NC}" if [ ! -f "_output/bin/scheduledtask" ]; then GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/scheduledtask yunion.io/x/onecloud/cmd/scheduledtask fi ./_output/bin/scheduledtask \ --config /dev/null \ --sql-connection "$SQL_CONNECTION" \ --auto-sync-table \ --port ${SCHEDULEDTASK_PORT} \ ${COMMON_ARGS} \ > logs/scheduledtask.log 2>&1 & echo $! > logs/scheduledtask.pid echo -e "${GREEN}✓ ScheduledTask 已启动${NC}" # 启动 APIGateway echo -e "${YELLOW}启动 APIGateway...${NC}" if [ ! -f "_output/bin/apigateway" ]; then GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/apigateway yunion.io/x/onecloud/cmd/apigateway fi ./_output/bin/apigateway \ --config /dev/null \ --port ${APIGATEWAY_PORT} \ ${COMMON_ARGS} \ --default-region ${REGION_NAME} \ > logs/apigateway.log 2>&1 & echo $! > logs/apigateway.pid echo -e "${GREEN}✓ APIGateway 已启动${NC}" # 等待所有服务就绪 echo -e "${YELLOW}等待所有服务就绪...${NC}" sleep 5 } # 初始化阶段 5: 初始化数据 init_data() { echo -e "${GREEN}=== 阶段 5: 初始化基础数据 ===${NC}" # 等待服务完全就绪 echo -e "${YELLOW}等待服务就绪...${NC}" for i in {1..30}; do if ./_output/bin/climc region-list > /dev/null 2>&1; then echo -e "${GREEN}✓ 服务已就绪${NC}" break fi sleep 2 if [ $i -eq 30 ]; then echo -e "${RED}警告: 服务未完全就绪,继续初始化...${NC}" fi done # 创建参数 echo -e "${YELLOW}创建系统参数...${NC}" # dashboard-system-default if ! ./_output/bin/climc parameter-show dashboard-system-default > /dev/null 2>&1; then ./_output/bin/climc parameter-create dashboard-system-default '[{"layout":{"component":"NumberCard","h":6,"w":20,"x":0,"y":0},"params":{"name":"虚拟机数量","regionAccountType":"region","usage_key":"all.servers"}},{"layout":{"component":"NumberCard","h":6,"w":20,"x":20,"y":0},"params":{"name":"宿主机数量","regionAccountType":"region","usage_key":"hosts"}},{"layout":{"component":"NumberCard","h":6,"w":20,"x":40,"y":0},"params":{"name":"物理机数量","regionAccountType":"region","usage_key":"baremetals"}},{"layout":{"component":"NumberCard","h":6,"w":20,"x":60,"y":0},"params":{"name":"存储桶数量","regionAccountType":"region","usage_key":"buckets"}}]' 2>/dev/null echo -e "${GREEN}✓ dashboard-system-default 创建成功${NC}" fi # global-settings if ! ./_output/bin/climc parameter-show global-settings > /dev/null 2>&1; then ./_output/bin/climc parameter-create global-settings '{"setupKeys":["zh-CN","en"]}' 2>/dev/null echo -e "${GREEN}✓ global-settings 创建成功${NC}" fi # widget-settings if ! ./_output/bin/climc parameter-show widget-settings > /dev/null 2>&1; then ./_output/bin/climc parameter-create widget-settings '{}' 2>/dev/null echo -e "${GREEN}✓ widget-settings 创建成功${NC}" fi # profile if ! ./_output/bin/climc parameter-show profile > /dev/null 2>&1; then ./_output/bin/climc parameter-create profile '{}' 2>/dev/null echo -e "${GREEN}✓ profile 创建成功${NC}" fi # 更新 sysadmin 用户 echo -e "${YELLOW}更新 sysadmin 用户配置...${NC}" user_id=$(./_output/bin/climc user-show ${ADMIN_USER} 2>/dev/null | grep "^| id" | awk '{print $4}') if [ -n "$user_id" ]; then # 允许 Web 控制台登录 ./_output/bin/climc user-update --allow-web-console ${ADMIN_USER} 2>/dev/null || true # 分配 admin 角色 project_id=$(./_output/bin/climc project-show ${ADMIN_PROJECT} 2>/dev/null | grep "^| id" | awk '{print $4}') if [ -n "$project_id" ]; then ./_output/bin/climc project-add-user ${ADMIN_PROJECT} ${ADMIN_USER} admin 2>/dev/null || true fi echo -e "${GREEN}✓ sysadmin 用户配置更新成功${NC}" fi } # 初始化阶段 6: 初始化 RBAC 策略 init_policies() { echo -e "${GREEN}=== 阶段 6: 初始化 RBAC 策略 ===${NC}" # 检查 policy 表是否为空 policy_count=$(./_output/bin/climc policy-list 2>/dev/null | grep -c "^| " || echo "0") if [ "$policy_count" -gt "2" ]; then echo -e "${GREEN}✓ 策略已存在,跳过初始化${NC}" return fi echo -e "${YELLOW}创建默认策略...${NC}" # 创建系统管理员策略 sysadmin_policy='{"policy":{"compute":{"servers":"allow","hosts":"allow","zones":"allow","cloudregions":"allow"},"identity":{"users":"allow","projects":"allow","roles":"allow","policies":"allow","domains":"allow"},"image":{"images":"allow"},"yunionconf":{"parameters":"allow"}}}' if ! ./_output/bin/climc policy-show sysadmin-system-policy > /dev/null 2>&1; then ./_output/bin/climc policy-create --type admin --scope system sysadmin-system-policy "${sysadmin_policy}" 2>/dev/null echo -e "${GREEN}✓ sysadmin-system-policy 创建成功${NC}" fi # 绑定策略到角色 admin_role_id=$(./_output/bin/climc role-show admin 2>/dev/null | grep "^| id" | awk '{print $4}') if [ -n "$admin_role_id" ]; then # 检查是否已绑定 if ! ./_output/bin/climc policy-binding-list --role ${admin_role_id} 2>/dev/null | grep -q "sysadmin-system-policy"; then ./_output/bin/climc policy-binding-create --role ${admin_role_id} sysadmin-system-policy 2>/dev/null || true echo -e "${GREEN}✓ 策略绑定成功${NC}" fi fi } # 打印完成信息 print_completion() { echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} Cloudpods 初始化完成!${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo -e "${BLUE}服务地址:${NC}" echo " Keystone: http://127.0.0.1:${KEYSTONE_PORT}" echo " Region: http://127.0.0.1:${REGION_PORT}" echo " Glance: http://127.0.0.1:${GLANCE_PORT}" echo " YunionConf: http://127.0.0.1:${YUNIONCONF_PORT}" echo " ScheduledTask:http://127.0.0.1:${SCHEDULEDTASK_PORT}" echo " APIGateway: http://127.0.0.1:${APIGATEWAY_PORT}" echo "" echo -e "${BLUE}管理信息:${NC}" echo " 管理员账号: ${ADMIN_USER}" echo " 管理员密码: ${ADMIN_PASSWORD}" echo " 项目: ${ADMIN_PROJECT}" echo " Domain: ${ADMIN_DOMAIN}" echo "" echo -e "${BLUE}前端访问:${NC}" echo " http://localhost:8080" echo "" echo -e "${BLUE}常用命令:${NC}" echo " 查看日志: tail -f backend/logs/*.log" echo " 停止服务: ./backend/stop-backend.sh" echo " 服务状态: ps aux | grep -E 'keystone|region|glance|yunionconf|scheduledtask|apigateway'" echo "" } # 主流程 main() { echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} Cloudpods 统一初始化脚本${NC}" echo -e "${GREEN}========================================${NC}" echo "" # 加载配置 load_config setup_defaults print_config # 确认初始化 if [ "$SKIP_CONFIRM" != "true" ]; then echo -e "${YELLOW}警告: 这将删除现有的数据库并重新初始化所有数据!${NC}" read -p "是否继续? (y/N): " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then echo -e "${YELLOW}已取消初始化${NC}" exit 0 fi fi # 执行初始化阶段 init_database init_services init_keystone start_other_services init_data init_policies # 打印完成信息 print_completion } # 处理命令行参数 while [[ $# -gt 0 ]]; do case $1 in --config) INIT_CONFIG_FILE="$2" shift 2 ;; --skip-confirm) SKIP_CONFIRM="true" shift ;; --help) echo "Cloudpods 统一初始化脚本" echo "" echo "用法: $0 [选项]" echo "" echo "选项:" echo " --config <文件> 指定配置文件 (默认: init-config.env)" echo " --skip-confirm 跳过确认提示" echo " --help 显示帮助信息" echo "" echo "环境变量:" echo " DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME" echo " ADMIN_USER, ADMIN_PASSWORD, ADMIN_PROJECT, ADMIN_DOMAIN" echo " REGION_NAME, KEYSTONE_PORT, etc." echo "" exit 0 ;; *) echo "未知选项: $1" echo "使用 --help 查看帮助" exit 1 ;; esac done # 运行主流程 main