start-services.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. # 初始化 etcd 配置
  218. echo -e "${YELLOW}初始化 etcd 配置...${NC}"
  219. ETCDCTL="$BACKEND_DIR/bin/darwin/etcdctl"
  220. if [ ! -f "$ETCDCTL" ]; then
  221. # 尝试 Linux 版本
  222. ETCDCTL="$BACKEND_DIR/bin/linux/etcdctl"
  223. fi
  224. if [ -f "$ETCDCTL" ]; then
  225. $ETCDCTL --endpoints=http://localhost:2379 put unifiedmonitors '{"enabled":false}' > /dev/null 2>&1 || true
  226. echo -e "${GREEN}✓ etcd 配置初始化完成${NC}"
  227. fi
  228. echo ""
  229. # 第一阶段:启动keystone
  230. echo -e "${YELLOW}阶段 1: 启动认证服务${NC}"
  231. if ! start_keystone; then
  232. echo -e "${RED}Keystone 启动失败,无法继续${NC}"
  233. exit 1
  234. fi
  235. echo ""
  236. # 等待keystone完全就绪
  237. echo -e "${YELLOW}等待 Keystone 完全就绪...${NC}"
  238. sleep 3
  239. echo ""
  240. # 初始化RBAC策略
  241. echo -e "${YELLOW}初始化RBAC策略...${NC}"
  242. if [ -f "$SCRIPT_DIR/init-admin-policy.sh" ]; then
  243. bash "$SCRIPT_DIR/init-admin-policy.sh" 2>&1 | grep -v "Warning" || true
  244. fi
  245. echo ""
  246. # 注册服务到service catalog
  247. echo -e "${YELLOW}注册服务到Service Catalog...${NC}"
  248. if [ -f "$SCRIPT_DIR/register-services.sh" ]; then
  249. bash "$SCRIPT_DIR/register-services.sh" 2>&1 | grep -v "Warning" | grep -E "✓|已注册" || true
  250. fi
  251. echo ""
  252. # 第二阶段:启动业务服务
  253. echo -e "${YELLOW}阶段 2: 启动业务服务${NC}"
  254. # region: 计算服务 (需要认证、数据库和etcd)
  255. start_service "region" 30888 true true
  256. # glance: 镜像服务 (需要认证和数据库)
  257. start_service "glance" 9292 true true
  258. # yunionconf: 配置服务 (需要认证和数据库)
  259. start_service "yunionconf" 30889 true true
  260. # monitor: 监控服务 (需要认证和数据库)
  261. start_service "monitor" 30093 true true
  262. # scheduledtask: 定时任务服务 (需要认证和数据库)
  263. start_service "scheduledtask" 30891 true true
  264. echo ""
  265. # 第三阶段:启动API网关 (需要认证,不需要数据库)
  266. echo -e "${YELLOW}阶段 3: 启动API网关${NC}"
  267. start_service "apigateway" 30300 true false
  268. echo ""
  269. echo -e "${GREEN}=== 服务启动完成 ===${NC}"
  270. echo ""
  271. echo -e "${GREEN}访问地址:${NC}"
  272. echo " API Gateway: http://localhost:30300"
  273. echo " Keystone: http://localhost:35357"
  274. echo ""
  275. echo -e "${YELLOW}查看日志: tail -f $LOG_DIR/<service>.log${NC}"
  276. }
  277. # 停止所有服务
  278. stop_all() {
  279. echo -e "${YELLOW}=== 停止所有服务 ===${NC}"
  280. echo ""
  281. local services=("scheduledtask" "monitor" "yunionconf" "scheduler" "apigateway" "glance" "region" "keystone")
  282. for service in "${services[@]}"; do
  283. stop_service "$service"
  284. done
  285. echo ""
  286. echo -e "${GREEN}=== 所有服务已停止 ===${NC}"
  287. }
  288. # 查看服务状态
  289. status_all() {
  290. echo -e "${GREEN}=== 服务状态 ===${NC}"
  291. echo ""
  292. # 检查etcd
  293. if [ -f "$BACKEND_DIR/logs/etcd.pid" ]; then
  294. local pid=$(cat "$BACKEND_DIR/logs/etcd.pid")
  295. if ps -p "$pid" > /dev/null 2>&1; then
  296. echo -e "${GREEN} ✓ etcd${NC} (PID: $pid, Port: 2379)"
  297. else
  298. echo -e "${RED} ✗ etcd${NC} (未运行)"
  299. fi
  300. else
  301. echo -e "${RED} ✗ etcd${NC} (未运行)"
  302. fi
  303. local services=("keystone:35357" "region:30888" "glance:9292" "apigateway:30300" "scheduler:30888" "yunionconf:30889" "monitor:30093" "scheduledtask:30891")
  304. for service_info in "${services[@]}"; do
  305. local service_name="${service_info%%:*}"
  306. local port="${service_info##*:}"
  307. if is_service_running "$service_name"; then
  308. local pid=$(cat "$PID_DIR/${service_name}.pid")
  309. echo -e "${GREEN} ✓ $service_name${NC} (PID: $pid, Port: $port)"
  310. else
  311. echo -e "${RED} ✗ $service_name${NC} (未运行)"
  312. fi
  313. done
  314. echo ""
  315. }
  316. # 主函数
  317. case "${1:-}" in
  318. start)
  319. start_all
  320. ;;
  321. stop)
  322. stop_all
  323. ;;
  324. restart)
  325. stop_all
  326. sleep 2
  327. start_all
  328. ;;
  329. status)
  330. status_all
  331. ;;
  332. *)
  333. echo "用法: $0 {start|stop|restart|status}"
  334. echo ""
  335. echo "命令:"
  336. echo " start - 启动所有服务"
  337. echo " stop - 停止所有服务"
  338. echo " restart - 重启所有服务"
  339. echo " status - 查看服务状态"
  340. exit 1
  341. ;;
  342. esac