start-services.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #!/bin/bash
  2. # Cloudpods 一键启动脚本 V2
  3. # 分阶段启动,先启动keystone,再启动其他服务
  4. set -e
  5. # 颜色定义
  6. RED='\033[0;31m'
  7. GREEN='\033[0;32m'
  8. YELLOW='\033[1;33m'
  9. NC='\033[0m'
  10. # 检测操作系统
  11. OS_TYPE="$(uname -s)"
  12. case "$OS_TYPE" in
  13. Darwin*) OS="macos" ;;
  14. Linux*) OS="linux" ;;
  15. CYGWIN*|MINGW*|MSYS*)
  16. echo -e "${RED}检测到 Windows 系统${NC}"
  17. echo -e "${YELLOW}建议使用 WSL (Windows Subsystem for Linux) 运行此项目${NC}"
  18. echo ""
  19. echo "如果您在 WSL 中运行,请忽略此消息"
  20. echo "如果您在原生 Windows 中运行,请:"
  21. echo " 1. 安装 WSL: https://docs.microsoft.com/zh-cn/windows/wsl/install"
  22. echo " 2. 在 WSL 中重新运行此脚本"
  23. exit 1
  24. ;;
  25. *) echo -e "${RED}不支持的操作系统: $OS_TYPE${NC}"; exit 1 ;;
  26. esac
  27. # 脚本目录
  28. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  29. BACKEND_DIR="$(dirname "$SCRIPT_DIR")"
  30. PROJECT_ROOT="$(dirname "$BACKEND_DIR")"
  31. BIN_DIR="$BACKEND_DIR/_output/bin"
  32. LOG_DIR="$BACKEND_DIR/logs"
  33. PID_DIR="$BACKEND_DIR/logs"
  34. ENV_FILE="$PROJECT_ROOT/.env.local"
  35. echo -e "${GREEN}=== Cloudpods 服务启动脚本 V2 ===${NC}"
  36. echo ""
  37. # 加载环境变量
  38. if [ ! -f "$ENV_FILE" ]; then
  39. echo -e "${RED}错误: 找不到环境变量文件 $ENV_FILE${NC}"
  40. exit 1
  41. fi
  42. source "$ENV_FILE"
  43. # 检查必要的环境变量
  44. if [ -z "$DB_HOST" ] || [ -z "$DB_PORT" ] || [ -z "$DB_USER" ]; then
  45. echo -e "${RED}错误: 数据库配置不完整${NC}"
  46. exit 1
  47. fi
  48. # 构建数据库连接字符串
  49. if [ -z "$DB_PASSWORD" ]; then
  50. SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/yunioncloud?charset=utf8"
  51. else
  52. SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/yunioncloud?charset=utf8"
  53. fi
  54. # Keystone认证URL
  55. AUTH_URL="http://localhost:35357/v3"
  56. echo -e "${GREEN}配置信息:${NC}"
  57. echo " 数据库: $DB_HOST:$DB_PORT"
  58. echo " 认证URL: $AUTH_URL"
  59. echo " 系统: $OS"
  60. echo ""
  61. # 创建必要的目录
  62. mkdir -p "$LOG_DIR" "$PID_DIR"
  63. # 检查服务是否运行
  64. is_service_running() {
  65. local service_name=$1
  66. local pid_file="$PID_DIR/${service_name}.pid"
  67. if [ -f "$pid_file" ]; then
  68. local pid=$(cat "$pid_file")
  69. if ps -p "$pid" > /dev/null 2>&1; then
  70. return 0
  71. fi
  72. fi
  73. return 1
  74. }
  75. # 启动keystone服务
  76. start_keystone() {
  77. local service_name="keystone"
  78. local port=35357
  79. local bin_path="$BIN_DIR/$service_name"
  80. local log_file="$LOG_DIR/${service_name}.log"
  81. local pid_file="$PID_DIR/${service_name}.pid"
  82. if is_service_running "$service_name"; then
  83. echo -e "${YELLOW}Keystone 已在运行${NC}"
  84. return 0
  85. fi
  86. echo -e "${GREEN}启动 Keystone (认证服务)...${NC}"
  87. > "$log_file"
  88. nohup "$bin_path" \
  89. --sql-connection "$SQL_CONNECTION" \
  90. --port $port \
  91. --admin-port 35358 \
  92. --auto-sync-table \
  93. --bootstrap-admin-user-password sysadmin \
  94. --region region0 \
  95. --enable-default-policy \
  96. --no-policy-violation-check \
  97. > "$log_file" 2>&1 &
  98. local pid=$!
  99. echo $pid > "$pid_file"
  100. # 等待keystone启动
  101. local max_wait=20
  102. local waited=0
  103. while [ $waited -lt $max_wait ]; do
  104. sleep 1
  105. waited=$((waited + 1))
  106. if ! ps -p "$pid" > /dev/null 2>&1; then
  107. echo -e "${RED}✗ Keystone 启动失败${NC}"
  108. tail -10 "$log_file"
  109. return 1
  110. fi
  111. if grep -q "Start listen on" "$log_file" 2>/dev/null; then
  112. echo -e "${GREEN}✓ Keystone 启动成功 (PID: $pid)${NC}"
  113. return 0
  114. fi
  115. if grep -q "\[fatal\]" "$log_file" 2>/dev/null; then
  116. echo -e "${RED}✗ Keystone 启动失败${NC}"
  117. grep "\[fatal\]" "$log_file" | tail -1
  118. return 1
  119. fi
  120. done
  121. echo -e "${YELLOW}⚠ Keystone 启动超时${NC}"
  122. return 1
  123. }
  124. # 启动其他服务
  125. start_service() {
  126. local service_name=$1
  127. local port=$2
  128. local need_auth=${3:-true}
  129. local need_db=${4:-true}
  130. local bin_path="$BIN_DIR/$service_name"
  131. local log_file="$LOG_DIR/${service_name}.log"
  132. local pid_file="$PID_DIR/${service_name}.pid"
  133. if is_service_running "$service_name"; then
  134. echo -e "${YELLOW} $service_name 已在运行${NC}"
  135. return 0
  136. fi
  137. echo -e "${GREEN}启动 $service_name (端口: $port)...${NC}"
  138. > "$log_file"
  139. # 构建启动命令
  140. local cmd="$bin_path --port $port"
  141. if [ "$need_db" = "true" ]; then
  142. cmd="$cmd --sql-connection \"$SQL_CONNECTION\" --auto-sync-table"
  143. fi
  144. if [ "$need_auth" = "true" ]; then
  145. cmd="$cmd --auth-url $AUTH_URL --admin-user sysadmin --admin-password sysadmin --admin-project system"
  146. fi
  147. # glance特殊配置
  148. if [ "$service_name" = "glance" ]; then
  149. mkdir -p "$BACKEND_DIR/data/glance"
  150. cmd="$cmd --filesystem-store-datadir $BACKEND_DIR/data/glance"
  151. fi
  152. # region特殊配置 (需要etcd)
  153. if [ "$service_name" = "region" ]; then
  154. cmd="$cmd --etcd-endpoints http://localhost:2379"
  155. fi
  156. nohup bash -c "$cmd" > "$log_file" 2>&1 &
  157. local pid=$!
  158. echo $pid > "$pid_file"
  159. # 等待服务启动
  160. local max_wait=15
  161. local waited=0
  162. while [ $waited -lt $max_wait ]; do
  163. sleep 1
  164. waited=$((waited + 1))
  165. if ! ps -p "$pid" > /dev/null 2>&1; then
  166. echo -e "${RED} ✗ $service_name 启动失败${NC}"
  167. echo -e "${YELLOW} 错误信息:${NC}"
  168. tail -3 "$log_file" | sed 's/^/ /'
  169. return 1
  170. fi
  171. if grep -q "Start listen on" "$log_file" 2>/dev/null; then
  172. echo -e "${GREEN} ✓ $service_name 启动成功 (PID: $pid)${NC}"
  173. return 0
  174. fi
  175. if grep -q "\[fatal\]" "$log_file" 2>/dev/null; then
  176. echo -e "${RED} ✗ $service_name 启动失败${NC}"
  177. grep "\[fatal\]" "$log_file" | tail -1 | sed 's/^/ /'
  178. return 1
  179. fi
  180. done
  181. echo -e "${YELLOW} ⚠ $service_name 可能正在启动 (PID: $pid)${NC}"
  182. return 0
  183. }
  184. # 停止服务
  185. stop_service() {
  186. local service_name=$1
  187. local pid_file="$PID_DIR/${service_name}.pid"
  188. if [ -f "$pid_file" ]; then
  189. local pid=$(cat "$pid_file")
  190. if ps -p "$pid" > /dev/null 2>&1; then
  191. echo -e "${YELLOW}停止 $service_name (PID: $pid)...${NC}"
  192. kill "$pid" 2>/dev/null || true
  193. sleep 1
  194. if ps -p "$pid" > /dev/null 2>&1; then
  195. kill -9 "$pid" 2>/dev/null || true
  196. fi
  197. rm -f "$pid_file"
  198. else
  199. rm -f "$pid_file"
  200. fi
  201. fi
  202. }
  203. # 启动所有服务
  204. start_all() {
  205. echo -e "${GREEN}=== 启动所有服务 ===${NC}"
  206. echo ""
  207. # 第0阶段:启动etcd
  208. echo -e "${YELLOW}阶段 0: 启动etcd${NC}"
  209. if [ -f "$SCRIPT_DIR/start-etcd.sh" ]; then
  210. bash "$SCRIPT_DIR/start-etcd.sh" || {
  211. echo -e "${RED}etcd 启动失败${NC}"
  212. exit 1
  213. }
  214. else
  215. echo -e "${YELLOW}未找到 start-etcd.sh,跳过etcd启动${NC}"
  216. fi
  217. echo ""
  218. # 第一阶段:启动keystone
  219. echo -e "${YELLOW}阶段 1: 启动认证服务${NC}"
  220. if ! start_keystone; then
  221. echo -e "${RED}Keystone 启动失败,无法继续${NC}"
  222. exit 1
  223. fi
  224. echo ""
  225. # 等待keystone完全就绪
  226. echo -e "${YELLOW}等待 Keystone 完全就绪...${NC}"
  227. sleep 3
  228. echo ""
  229. # 初始化RBAC策略
  230. echo -e "${YELLOW}初始化RBAC策略...${NC}"
  231. if [ -f "$SCRIPT_DIR/init-admin-policy.sh" ]; then
  232. bash "$SCRIPT_DIR/init-admin-policy.sh" 2>&1 | grep -v "Warning" || true
  233. fi
  234. echo ""
  235. # 注册服务到service catalog
  236. echo -e "${YELLOW}注册服务到Service Catalog...${NC}"
  237. if [ -f "$SCRIPT_DIR/register-services.sh" ]; then
  238. bash "$SCRIPT_DIR/register-services.sh" 2>&1 | grep -v "Warning" | grep -E "✓|已注册" || true
  239. fi
  240. echo ""
  241. # 第二阶段:启动业务服务
  242. echo -e "${YELLOW}阶段 2: 启动业务服务${NC}"
  243. # region: 计算服务 (需要认证、数据库和etcd)
  244. start_service "region" 30888 true true
  245. # glance: 镜像服务 (需要认证和数据库)
  246. start_service "glance" 9292 true true
  247. # yunionconf: 配置服务 (需要认证和数据库)
  248. start_service "yunionconf" 30889 true true
  249. # monitor: 监控服务 (需要认证和数据库) - 暂时跳过,需要influxdb
  250. # start_service "monitor" 30093 true true
  251. echo -e "${YELLOW} 跳过 monitor (需要 influxdb/victoria-metrics)${NC}"
  252. # scheduledtask: 定时任务服务 (需要认证和数据库)
  253. start_service "scheduledtask" 30891 true true
  254. echo ""
  255. # 第三阶段:启动API网关 (需要认证,不需要数据库)
  256. echo -e "${YELLOW}阶段 3: 启动API网关${NC}"
  257. start_service "apigateway" 30300 true false
  258. echo ""
  259. echo -e "${GREEN}=== 服务启动完成 ===${NC}"
  260. echo ""
  261. echo -e "${GREEN}访问地址:${NC}"
  262. echo " API Gateway: http://localhost:30300"
  263. echo " Keystone: http://localhost:35357"
  264. echo ""
  265. echo -e "${YELLOW}查看日志: tail -f $LOG_DIR/<service>.log${NC}"
  266. }
  267. # 停止所有服务
  268. stop_all() {
  269. echo -e "${YELLOW}=== 停止所有服务 ===${NC}"
  270. echo ""
  271. local services=("scheduledtask" "monitor" "yunionconf" "scheduler" "apigateway" "glance" "region" "keystone")
  272. for service in "${services[@]}"; do
  273. stop_service "$service"
  274. done
  275. echo ""
  276. echo -e "${GREEN}=== 所有服务已停止 ===${NC}"
  277. }
  278. # 查看服务状态
  279. status_all() {
  280. echo -e "${GREEN}=== 服务状态 ===${NC}"
  281. echo ""
  282. # 检查etcd
  283. if [ -f "$BACKEND_DIR/logs/etcd.pid" ]; then
  284. local pid=$(cat "$BACKEND_DIR/logs/etcd.pid")
  285. if ps -p "$pid" > /dev/null 2>&1; then
  286. echo -e "${GREEN} ✓ etcd${NC} (PID: $pid, Port: 2379)"
  287. else
  288. echo -e "${RED} ✗ etcd${NC} (未运行)"
  289. fi
  290. else
  291. echo -e "${RED} ✗ etcd${NC} (未运行)"
  292. fi
  293. local services=("keystone:35357" "region:30888" "glance:9292" "apigateway:30300" "scheduler:30888" "yunionconf:30889" "monitor:30093" "scheduledtask:30891")
  294. for service_info in "${services[@]}"; do
  295. local service_name="${service_info%%:*}"
  296. local port="${service_info##*:}"
  297. if is_service_running "$service_name"; then
  298. local pid=$(cat "$PID_DIR/${service_name}.pid")
  299. echo -e "${GREEN} ✓ $service_name${NC} (PID: $pid, Port: $port)"
  300. else
  301. echo -e "${RED} ✗ $service_name${NC} (未运行)"
  302. fi
  303. done
  304. echo ""
  305. }
  306. # 主函数
  307. case "${1:-}" in
  308. start)
  309. start_all
  310. ;;
  311. stop)
  312. stop_all
  313. ;;
  314. restart)
  315. stop_all
  316. sleep 2
  317. start_all
  318. ;;
  319. status)
  320. status_all
  321. ;;
  322. *)
  323. echo "用法: $0 {start|stop|restart|status}"
  324. echo ""
  325. echo "命令:"
  326. echo " start - 启动所有服务"
  327. echo " stop - 停止所有服务"
  328. echo " restart - 重启所有服务"
  329. echo " status - 查看服务状态"
  330. exit 1
  331. ;;
  332. esac