- 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
404 lines
12 KiB
Bash
Executable File
404 lines
12 KiB
Bash
Executable File
#!/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"
|