feat: 优化 Docker 部署配置和健康检查
- 添加 WeasyPrint 依赖以支持 PDF 导出功能 - 新增 docker-entrypoint.sh 统一管理容器启动流程 - 添加容器健康检查机制(/health 端点) - 配置容器自动重启策略(unless-stopped) - 优化日志输出,仅使用 stdout 适配容器环境 - 改进 update-and-run.sh 添加健康状态检查 - 统一脚本中的 sudo 使用规范
This commit is contained in:
parent
a391357c32
commit
2a02a4030a
33
Dockerfile
33
Dockerfile
@ -25,10 +25,16 @@ ARG HOST_ARCH="amd64"
|
|||||||
|
|
||||||
# 1. Install System Dependencies & Node.js (for runtime)
|
# 1. Install System Dependencies & Node.js (for runtime)
|
||||||
# We need Node.js to run the Next.js production server (npm start)
|
# We need Node.js to run the Next.js production server (npm start)
|
||||||
|
# curl is needed for health check
|
||||||
|
# WeasyPrint dependencies for PDF export
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
curl \
|
curl \
|
||||||
nodejs \
|
nodejs \
|
||||||
npm \
|
npm \
|
||||||
|
libpango-1.0-0 \
|
||||||
|
libharfbuzz0b \
|
||||||
|
libpangoft2-1.0-0 \
|
||||||
|
libpangocairo-1.0-0 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@ -58,24 +64,29 @@ COPY --from=frontend-builder /app/frontend/node_modules ./frontend/node_modules/
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY backend/ ./backend/
|
COPY backend/ ./backend/
|
||||||
COPY *.py ./
|
COPY *.py ./
|
||||||
COPY *.sh ./
|
|
||||||
COPY entrypoint.sh /usr/local/bin/
|
# 6. Copy Scripts
|
||||||
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
# Make scripts executable
|
# Make scripts executable
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh ./start_app.sh
|
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
# Environment Variables Defaults
|
# Environment Variables Defaults
|
||||||
ENV PW_LOCAL_PORT=3001
|
ENV PW_LOCAL_PORT=3001
|
||||||
# Disable Next.js Telemetry
|
# Disable Next.js Telemetry
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
# Expose ports?
|
# Expose ports (for local debugging)
|
||||||
# Technically tunnel needs NO EXPOSE, but for local debugging we might want it.
|
EXPOSE 3001 8000
|
||||||
# EXPOSE 3000 8000
|
|
||||||
|
|
||||||
# Entrypoint & Command
|
# Health Check
|
||||||
|
# 检查后端健康状态,前端通过后端代理访问
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:8000/health || exit 1
|
||||||
|
|
||||||
|
# Entrypoint: Portwarden tunnel client
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
# 启动前端 (Next.js 生产模式) 和后端 (FastAPI)
|
|
||||||
# 前端监听 3001 端口,后端监听 8000 端口
|
# Command: Start backend and frontend with process management
|
||||||
# 使用虚拟环境中的 Python 运行后端
|
CMD ["/usr/local/bin/docker-entrypoint.sh"]
|
||||||
CMD ["bash", "-c", "cd /app/frontend && npm start & cd /app/backend && /app/.venv/bin/python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 && wait"]
|
|
||||||
|
|||||||
@ -9,12 +9,12 @@ import logging
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
# 配置日志系统 - 在最开始配置,确保所有模块都能使用
|
# 配置日志系统 - 在最开始配置,确保所有模块都能使用
|
||||||
|
# 仅输出到 stdout,容器环境下通过 docker logs 收集日志
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
handlers=[
|
handlers=[
|
||||||
logging.StreamHandler(sys.stdout),
|
logging.StreamHandler(sys.stdout)
|
||||||
logging.FileHandler("server.log", encoding='utf-8')
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
105
docker-entrypoint.sh
Normal file
105
docker-entrypoint.sh
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =============================================================================
|
||||||
|
# FA3 Datafetch 容器启动脚本
|
||||||
|
# 功能:启动后端 (FastAPI) 和前端 (Next.js),处理进程管理
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 信号处理函数
|
||||||
|
cleanup() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 收到停止信号,正在关闭服务..."
|
||||||
|
if [ -n "$BACKEND_PID" ]; then
|
||||||
|
kill "$BACKEND_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [ -n "$FRONTEND_PID" ]; then
|
||||||
|
kill "$FRONTEND_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# 等待进程结束
|
||||||
|
wait $BACKEND_PID $FRONTEND_PID 2>/dev/null || true
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 所有服务已停止"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup SIGTERM SIGINT
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 1. 启动后端服务
|
||||||
|
# =============================================================================
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 启动后端服务 (FastAPI on :8000)..."
|
||||||
|
|
||||||
|
cd /app/backend
|
||||||
|
export PYTHONPATH=/app:/app/backend
|
||||||
|
|
||||||
|
# 启动后端,日志输出到stdout
|
||||||
|
/app/.venv/bin/python -m uvicorn app.main:app \
|
||||||
|
--host 0.0.0.0 \
|
||||||
|
--port 8000 \
|
||||||
|
--access-log &
|
||||||
|
|
||||||
|
BACKEND_PID=$!
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 后端PID: $BACKEND_PID"
|
||||||
|
|
||||||
|
# 等待后端启动
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 检查后端是否启动成功
|
||||||
|
if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 错误:后端启动失败!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 2. 启动前端服务
|
||||||
|
# =============================================================================
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 启动前端服务 (Next.js on :3001)..."
|
||||||
|
|
||||||
|
cd /app/frontend
|
||||||
|
|
||||||
|
# 设置生产环境变量
|
||||||
|
export NODE_ENV=production
|
||||||
|
export PORT=3001
|
||||||
|
|
||||||
|
# 启动前端,日志输出到stdout
|
||||||
|
npm start &
|
||||||
|
|
||||||
|
FRONTEND_PID=$!
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 前端PID: $FRONTEND_PID"
|
||||||
|
|
||||||
|
# 等待前端启动
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 检查前端是否启动成功
|
||||||
|
if ! kill -0 "$FRONTEND_PID" 2>/dev/null; then
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 错误:前端启动失败!"
|
||||||
|
kill "$BACKEND_PID" 2>/dev/null || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 3. 监控服务状态
|
||||||
|
# =============================================================================
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ============================================"
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 所有服务启动成功!"
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] - 前端: http://localhost:3001 (通过隧道暴露)"
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] - 后端: http://localhost:8000 (仅容器内部)"
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ============================================"
|
||||||
|
|
||||||
|
# 持续监控进程状态
|
||||||
|
while true; do
|
||||||
|
# 检查后端
|
||||||
|
if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 错误:后端进程已停止!"
|
||||||
|
kill "$FRONTEND_PID" 2>/dev/null || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查前端
|
||||||
|
if ! kill -0 "$FRONTEND_PID" 2>/dev/null; then
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 错误:前端进程已停止!"
|
||||||
|
kill "$BACKEND_PID" 2>/dev/null || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
@ -1,6 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# FA3-Datafetch Docker 启动脚本
|
# FA3-Datafetch Docker 启动脚本
|
||||||
|
|
||||||
|
# 固定使用 sudo
|
||||||
|
DOCKER="sudo docker"
|
||||||
|
|
||||||
# 容器名称
|
# 容器名称
|
||||||
CONTAINER_NAME="fa3-app"
|
CONTAINER_NAME="fa3-app"
|
||||||
|
|
||||||
@ -33,15 +36,16 @@ CLIENT_KEY="$CERT_DIR/client.key"
|
|||||||
|
|
||||||
# 停止并删除旧容器
|
# 停止并删除旧容器
|
||||||
echo "停止旧容器..."
|
echo "停止旧容器..."
|
||||||
docker rm -f $CONTAINER_NAME 2>/dev/null || true
|
$DOCKER rm -f $CONTAINER_NAME 2>/dev/null || true
|
||||||
|
|
||||||
# 启动新容器
|
# 启动新容器
|
||||||
echo "启动新容器..."
|
echo "启动新容器..."
|
||||||
docker run -d \
|
$DOCKER run -d \
|
||||||
--name $CONTAINER_NAME \
|
--name $CONTAINER_NAME \
|
||||||
--dns 8.8.8.8 \
|
--dns 8.8.8.8 \
|
||||||
--dns 8.8.4.4 \
|
--dns 8.8.4.4 \
|
||||||
--dns 114.114.114.114 \
|
--dns 114.114.114.114 \
|
||||||
|
--restart unless-stopped \
|
||||||
-e PW_SERVICE_ID="$PW_SERVICE_ID" \
|
-e PW_SERVICE_ID="$PW_SERVICE_ID" \
|
||||||
-e PW_SERVER_ADDRS="$PW_SERVER_ADDRS" \
|
-e PW_SERVER_ADDRS="$PW_SERVER_ADDRS" \
|
||||||
-e PW_LOCAL_PORT="$PW_LOCAL_PORT" \
|
-e PW_LOCAL_PORT="$PW_LOCAL_PORT" \
|
||||||
@ -61,5 +65,5 @@ docker run -d \
|
|||||||
fa3-datafetch
|
fa3-datafetch
|
||||||
|
|
||||||
echo "容器已启动!"
|
echo "容器已启动!"
|
||||||
echo "查看日志: docker logs $CONTAINER_NAME"
|
echo "查看日志: $DOCKER logs -f $CONTAINER_NAME"
|
||||||
echo "进入容器: docker exec -it $CONTAINER_NAME sh"
|
echo "进入容器: $DOCKER exec -it $CONTAINER_NAME sh"
|
||||||
|
|||||||
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# 固定使用 sudo
|
||||||
|
DOCKER="sudo docker"
|
||||||
|
SUDO="sudo"
|
||||||
|
|
||||||
# 颜色定义
|
# 颜色定义
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
@ -18,40 +22,50 @@ echo -e "${GREEN}========================================${NC}"
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
# 1. 拉取最新代码
|
# 1. 拉取最新代码
|
||||||
echo -e "\n${YELLOW}[1/4] 拉取最新代码...${NC}"
|
echo -e "\n${YELLOW}[1/3] 拉取最新代码...${NC}"
|
||||||
git fetch origin
|
git fetch origin
|
||||||
git pull origin main
|
git pull origin main
|
||||||
echo -e "${GREEN}✓ 代码更新完成${NC}"
|
echo -e "${GREEN}✓ 代码更新完成${NC}"
|
||||||
|
|
||||||
# 2. 停止旧容器(如果存在)
|
# 2. 重新构建 Docker 镜像
|
||||||
CONTAINER_NAME="fa3-app"
|
echo -e "\n${YELLOW}[2/3] 重新构建 Docker 镜像...${NC}"
|
||||||
echo -e "\n${YELLOW}[2/4] 停止旧容器...${NC}"
|
|
||||||
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
||||||
docker stop $CONTAINER_NAME 2>/dev/null || true
|
|
||||||
docker rm $CONTAINER_NAME 2>/dev/null || true
|
|
||||||
echo -e "${GREEN}✓ 旧容器已停止并删除${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}✓ 没有运行中的旧容器${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 3. 重新构建 Docker 镜像
|
|
||||||
echo -e "\n${YELLOW}[3/4] 重新构建 Docker 镜像...${NC}"
|
|
||||||
echo -e " 这可能需要几分钟时间..."
|
echo -e " 这可能需要几分钟时间..."
|
||||||
docker build -t fa3-datafetch .
|
$DOCKER build --network=host -t fa3-datafetch .
|
||||||
echo -e "${GREEN}✓ Docker 镜像构建完成${NC}"
|
echo -e "${GREEN}✓ Docker 镜像构建完成${NC}"
|
||||||
|
|
||||||
# 4. 启动新容器
|
# 3. 启动新容器
|
||||||
echo -e "\n${YELLOW}[4/4] 启动新容器...${NC}"
|
echo -e "\n${YELLOW}[3/3] 启动新容器...${NC}"
|
||||||
./docker-run.sh
|
./docker-run.sh
|
||||||
echo -e "${GREEN}✓ 新容器已启动${NC}"
|
echo -e "${GREEN}✓ 新容器已启动${NC}"
|
||||||
|
|
||||||
# 清理未使用的镜像(可选)
|
# 等待容器启动并检查健康状态
|
||||||
|
echo -e "\n${YELLOW}等待容器启动...${NC}"
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# 检查容器状态
|
||||||
|
if $DOCKER ps --format '{{.Names}}' | grep -q "^fa3-app$"; then
|
||||||
|
echo -e "${GREEN}✓ 容器运行中${NC}"
|
||||||
|
|
||||||
|
# 显示容器日志(最后几行)
|
||||||
|
echo -e "\n${YELLOW}最新日志:${NC}"
|
||||||
|
$DOCKER logs fa3-app --tail 10
|
||||||
|
|
||||||
|
# 检查健康状态
|
||||||
|
HEALTH_STATUS=$($DOCKER inspect fa3-app --format='{{.State.Health.Status}}' 2>/dev/null || echo "checking")
|
||||||
|
echo -e "\n健康状态: ${GREEN}$HEALTH_STATUS${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ 容器启动失败${NC}"
|
||||||
|
echo -e "查看错误日志: $DOCKER logs fa3-app"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 清理未使用的镜像
|
||||||
echo -e "\n${YELLOW}清理旧镜像...${NC}"
|
echo -e "\n${YELLOW}清理旧镜像...${NC}"
|
||||||
docker image prune -f
|
$DOCKER image prune -f
|
||||||
echo -e "${GREEN}✓ 清理完成${NC}"
|
echo -e "${GREEN}✓ 清理完成${NC}"
|
||||||
|
|
||||||
echo -e "\n${GREEN}========================================${NC}"
|
echo -e "\n${GREEN}========================================${NC}"
|
||||||
echo -e "${GREEN} 更新完成!${NC}"
|
echo -e "${GREEN} 更新完成!${NC}"
|
||||||
echo -e "${GREEN}========================================${NC}"
|
echo -e "${GREEN}========================================${NC}"
|
||||||
echo -e "查看日志: docker logs -f $CONTAINER_NAME"
|
echo -e "查看日志: $DOCKER logs -f fa3-app"
|
||||||
echo -e "进入容器: docker exec -it $CONTAINER_NAME sh"
|
echo -e "进入容器: $DOCKER exec -it fa3-app sh"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user