Fundamental_Analysis/scripts/deploy_to_harbor.sh
Lv, Qi 15cdfb12e0 feat: fix provider test endpoints and update deployment scripts
- Finnhub: Add missing /test endpoint
- AlphaVantage: Fix test endpoint deserialization (handle null api_url)
- Mock Provider: Add /test endpoint and fix Zodios validation error by adding Mock enum
- Deployment: Remove Mock Provider from production deployment script
- Infrastructure: Add production Dockerfiles and compose configs
2025-12-01 02:05:00 +08:00

404 lines
12 KiB
Bash
Executable File
Raw 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.

#!/bin/bash
# 遇到错误立即退出
set -e
# 配置变量
REGISTRY="harbor.3prism.ai"
PROJECT="fundamental_analysis"
VERSION="latest"
NAMESPACE="$REGISTRY/$PROJECT"
# 颜色输出
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# 清理工作目录函数
function cleanup {
echo -e "\n${YELLOW}>>> 清理临时文件...${NC}"
rm -rf ./dist_bin
rm -rf ./temp_build_context
# 尝试删除构建容器(如果存在)
docker rm -f fundamental-builder-extract 2>/dev/null || true
}
trap cleanup EXIT
echo -e "${GREEN}=== 开始优化的构建部署流程 ===${NC}"
echo -e "目标仓库: $NAMESPACE"
# ==========================================
# 阶段 1: 全局构建 (Build Once)
# ==========================================
echo -e "\n${YELLOW}>>> [阶段 1/3] 全局构建: 编译所有 Rust 服务...${NC}"
echo "使用 Dockerfile: docker/Dockerfile.builder"
# 检查是否需要重新构建 (这一步可以进一步优化但为了简单起见我们总是构建依赖Docker层缓存)
docker build -t fundamental-workspace-builder -f docker/Dockerfile.builder .
# 提取二进制文件
echo -e "${YELLOW}>>> 正在提取二进制文件...${NC}"
mkdir -p ./dist_bin
# 创建临时容器
docker create --name fundamental-builder-extract fundamental-workspace-builder
# 从容器中复制 target/release 目录下的二进制文件
# 注意: 这里我们复制整个 release 目录可能会太大,我们只复制二进制文件
# 但是 docker cp 不支持通配符复制特定文件列表,所以我们先全部复制出来,或者我们知道名字
# 定义二进制文件映射 (服务目录 -> 二进制名称)
# 如果二进制名称与目录名一致,则只需列出目录名
declare -A SERVICE_BIN_MAP
SERVICE_BIN_MAP=(
["data-persistence-service"]="data-persistence-service-server"
["api-gateway"]="api-gateway"
["alphavantage-provider-service"]="alphavantage-provider-service"
["tushare-provider-service"]="tushare-provider-service"
["finnhub-provider-service"]="finnhub-provider-service"
["yfinance-provider-service"]="yfinance-provider-service"
["report-generator-service"]="report-generator-service"
["workflow-orchestrator-service"]="workflow-orchestrator-service"
# ["mock-provider-service"]="mock-provider-service" # Skipped for Prod
)
for SERVICE_DIR in "${!SERVICE_BIN_MAP[@]}"; do
BINARY_NAME="${SERVICE_BIN_MAP[$SERVICE_DIR]}"
echo "提取: $BINARY_NAME"
docker cp "fundamental-builder-extract:/usr/src/app/target/release/$BINARY_NAME" "./dist_bin/$BINARY_NAME"
done
# 删除临时容器
docker rm -f fundamental-builder-extract
echo -e "${GREEN}√ 二进制提取完成${NC}"
# ==========================================
# 阶段 2: 前端构建 (Frontend)
# ==========================================
echo -e "\n${YELLOW}>>> [阶段 2/3] 构建前端服务...${NC}"
FRONTEND_IMAGE="$NAMESPACE/frontend:$VERSION"
docker build -t "$FRONTEND_IMAGE" -f docker/Dockerfile.frontend.prod .
echo -e "${YELLOW}>>> 推送前端镜像...${NC}"
docker push "$FRONTEND_IMAGE"
echo -e "${GREEN}√ 前端处理完成${NC}"
# ==========================================
# 阶段 3: 打包与分发 (Package Many)
# ==========================================
echo -e "\n${YELLOW}>>> [阶段 3/3] 打包并推送后端微服务...${NC}"
TOTAL_SIZE=0
for SERVICE_DIR in "${!SERVICE_BIN_MAP[@]}"; do
BINARY_NAME="${SERVICE_BIN_MAP[$SERVICE_DIR]}"
IMAGE_NAME="$NAMESPACE/$SERVICE_DIR:$VERSION"
echo -e "\n------------------------------------------------"
echo -e "${YELLOW}处理服务: $SERVICE_DIR${NC}"
# 准备构建上下文
CONTEXT_DIR="./temp_build_context/$SERVICE_DIR"
rm -rf "$CONTEXT_DIR"
mkdir -p "$CONTEXT_DIR"
mkdir -p "$CONTEXT_DIR/assets"
# 1. 复制二进制文件并重命名为 app
cp "./dist_bin/$BINARY_NAME" "$CONTEXT_DIR/app"
# 2. 复制配置目录 (如果需要)
# data-persistence-service 等服务需要根目录的 config
cp -r config "$CONTEXT_DIR/config"
# 3. 复制服务特定的资产 (Assets)
# 3.1 Migrations
if [ -d "services/$SERVICE_DIR/migrations" ]; then
echo " - 包含 migrations"
mkdir -p "$CONTEXT_DIR/assets/migrations"
cp -r "services/$SERVICE_DIR/migrations/"* "$CONTEXT_DIR/assets/migrations/"
fi
# 3.2 Templates
if [ -d "services/$SERVICE_DIR/templates" ]; then
echo " - 包含 templates"
mkdir -p "$CONTEXT_DIR/assets/templates"
cp -r "services/$SERVICE_DIR/templates/"* "$CONTEXT_DIR/assets/templates/"
fi
# 3.3 Cookies
if [ -f "services/$SERVICE_DIR/cookies.txt" ]; then
echo " - 包含 cookies.txt"
cp "services/$SERVICE_DIR/cookies.txt" "$CONTEXT_DIR/assets/cookies.txt"
fi
# 3.4 Web Assets (e.g. data-persistence-service assets folder if exists)
if [ -d "services/$SERVICE_DIR/assets" ]; then
echo " - 包含 web assets"
cp -r "services/$SERVICE_DIR/assets/"* "$CONTEXT_DIR/assets/"
fi
# 4. 构建极简镜像
# 不需要传递构建参数,因为文件已经准备好了
docker build -t "$IMAGE_NAME" -f docker/Dockerfile.dist "$CONTEXT_DIR"
# 5. 推送
echo -e "${YELLOW} 推送 $SERVICE_DIR 到 Harbor ...${NC}"
docker push "$IMAGE_NAME"
# 统计大小
SIZE_BYTES=$(docker inspect "$IMAGE_NAME" --format='{{.Size}}')
TOTAL_SIZE=$(echo "$TOTAL_SIZE + $SIZE_BYTES" | bc)
done
TOTAL_SIZE_MB=$(echo "scale=2; $TOTAL_SIZE / 1024 / 1024" | bc)
echo -e "\n${GREEN}=== 所有镜像处理完成 ===${NC}"
echo -e "${GREEN}后端总大小: ${TOTAL_SIZE_MB} MB${NC}"
# ==========================================
# 阶段 4: 生成部署文件
# ==========================================
echo -e "\n${YELLOW}>>> 正在生成服务器部署文件 docker-compose.server.yml ...${NC}"
cat > docker-compose.server.yml <<YAML
services:
postgres-db:
image: timescale/timescaledb:2.15.2-pg16
container_name: fundamental-postgres
command: -c shared_preload_libraries=timescaledb
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: fundamental
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d fundamental"]
interval: 5s
timeout: 5s
retries: 10
networks:
- app-network
restart: always
nats:
image: nats:2.9
container_name: fundamental-nats
volumes:
- nats_data:/data
networks:
- app-network
restart: always
data-persistence-service:
image: $NAMESPACE/data-persistence-service:$VERSION
container_name: data-persistence-service
environment:
HOST: 0.0.0.0
PORT: 3000
DATABASE_URL: postgresql://postgres:postgres@postgres-db:5432/fundamental
RUST_LOG: info
RUST_BACKTRACE: "1"
SKIP_MIGRATIONS_ON_MISMATCH: "1"
depends_on:
postgres-db:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:3000/health >/dev/null || exit 1"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
restart: always
api-gateway:
image: $NAMESPACE/api-gateway:$VERSION
container_name: api-gateway
environment:
SERVER_PORT: 4000
NATS_ADDR: nats://nats:4222
DATA_PERSISTENCE_SERVICE_URL: http://data-persistence-service:3000
REPORT_GENERATOR_SERVICE_URL: http://report-generator-service:8004
RUST_LOG: info,axum=info
RUST_BACKTRACE: "1"
depends_on:
nats:
condition: service_started
data-persistence-service:
condition: service_healthy
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:4000/health >/dev/null || exit 1"]
interval: 10s
timeout: 5s
retries: 5
restart: always
alphavantage-provider-service:
image: $NAMESPACE/alphavantage-provider-service:$VERSION
container_name: alphavantage-provider-service
volumes:
- workflow_data:/mnt/workflow_data
environment:
SERVER_PORT: 8000
NATS_ADDR: nats://nats:4222
DATA_PERSISTENCE_SERVICE_URL: http://data-persistence-service:3000
API_GATEWAY_URL: http://api-gateway:4000
WORKFLOW_DATA_PATH: /mnt/workflow_data
SERVICE_HOST: alphavantage-provider-service
RUST_LOG: info
RUST_BACKTRACE: "1"
depends_on:
- nats
- data-persistence-service
networks:
- app-network
restart: always
tushare-provider-service:
image: $NAMESPACE/tushare-provider-service:$VERSION
container_name: tushare-provider-service
volumes:
- workflow_data:/mnt/workflow_data
environment:
SERVER_PORT: 8001
NATS_ADDR: nats://nats:4222
DATA_PERSISTENCE_SERVICE_URL: http://data-persistence-service:3000
TUSHARE_API_URL: http://api.waditu.com
API_GATEWAY_URL: http://api-gateway:4000
WORKFLOW_DATA_PATH: /mnt/workflow_data
SERVICE_HOST: tushare-provider-service
RUST_LOG: info
RUST_BACKTRACE: "1"
depends_on:
- nats
- data-persistence-service
networks:
- app-network
restart: always
finnhub-provider-service:
image: $NAMESPACE/finnhub-provider-service:$VERSION
container_name: finnhub-provider-service
volumes:
- workflow_data:/mnt/workflow_data
environment:
SERVER_PORT: 8002
NATS_ADDR: nats://nats:4222
DATA_PERSISTENCE_SERVICE_URL: http://data-persistence-service:3000
FINNHUB_API_URL: https://finnhub.io/api/v1
API_GATEWAY_URL: http://api-gateway:4000
WORKFLOW_DATA_PATH: /mnt/workflow_data
SERVICE_HOST: finnhub-provider-service
RUST_LOG: info
RUST_BACKTRACE: "1"
depends_on:
- nats
- data-persistence-service
networks:
- app-network
restart: always
yfinance-provider-service:
image: $NAMESPACE/yfinance-provider-service:$VERSION
container_name: yfinance-provider-service
volumes:
- workflow_data:/mnt/workflow_data
environment:
SERVER_PORT: 8003
NATS_ADDR: nats://nats:4222
DATA_PERSISTENCE_SERVICE_URL: http://data-persistence-service:3000
API_GATEWAY_URL: http://api-gateway:4000
WORKFLOW_DATA_PATH: /mnt/workflow_data
SERVICE_HOST: yfinance-provider-service
RUST_LOG: info
RUST_BACKTRACE: "1"
depends_on:
- nats
- data-persistence-service
networks:
- app-network
dns:
- 8.8.8.8
- 8.8.4.4
restart: always
report-generator-service:
image: $NAMESPACE/report-generator-service:$VERSION
container_name: report-generator-service
volumes:
- workflow_data:/mnt/workflow_data
environment:
SERVER_PORT: 8004
NATS_ADDR: nats://nats:4222
DATA_PERSISTENCE_SERVICE_URL: http://data-persistence-service:3000
GOTENBERG_URL: http://gotenberg:3000
WORKFLOW_DATA_PATH: /mnt/workflow_data
RUST_LOG: info
RUST_BACKTRACE: "1"
depends_on:
- nats
- data-persistence-service
- gotenberg
networks:
- app-network
restart: always
workflow-orchestrator-service:
image: $NAMESPACE/workflow-orchestrator-service:$VERSION
container_name: workflow-orchestrator-service
volumes:
- workflow_data:/mnt/workflow_data
environment:
SERVER_PORT: 8005
NATS_ADDR: nats://nats:4222
DATA_PERSISTENCE_SERVICE_URL: http://data-persistence-service:3000
WORKFLOW_DATA_PATH: /mnt/workflow_data
RUST_LOG: info
RUST_BACKTRACE: "1"
depends_on:
- nats
- data-persistence-service
networks:
- app-network
restart: always
gotenberg:
image: gotenberg/gotenberg:8
container_name: gotenberg
networks:
- app-network
restart: always
frontend:
image: $NAMESPACE/frontend:$VERSION
container_name: fundamental-frontend
ports:
- "8080:80" # Map host 8080 to container 80 (Nginx)
depends_on:
api-gateway:
condition: service_healthy
networks:
- app-network
restart: always
volumes:
workflow_data:
pgdata:
nats_data:
networks:
app-network:
YAML
echo -e "${GREEN}生成完成: docker-compose.server.yml${NC}"
echo -e "请执行以下步骤更新远端服务器:"
echo -e "1. 将 docker-compose.server.yml 复制到服务器"
echo -e "2. 在服务器执行: docker-compose -f docker-compose.server.yml pull (拉取最新镜像)"
echo -e "3. 在服务器执行: docker-compose -f docker-compose.server.yml up -d (重启服务)"
echo -e " 或者一键命令: docker-compose -f docker-compose.server.yml up -d --pull always"