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)
|
||||
# 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 \
|
||||
curl \
|
||||
nodejs \
|
||||
npm \
|
||||
libpango-1.0-0 \
|
||||
libharfbuzz0b \
|
||||
libpangoft2-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
@ -58,24 +64,29 @@ COPY --from=frontend-builder /app/frontend/node_modules ./frontend/node_modules/
|
||||
WORKDIR /app
|
||||
COPY backend/ ./backend/
|
||||
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
|
||||
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
|
||||
ENV PW_LOCAL_PORT=3001
|
||||
# Disable Next.js Telemetry
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# Expose ports?
|
||||
# Technically tunnel needs NO EXPOSE, but for local debugging we might want it.
|
||||
# EXPOSE 3000 8000
|
||||
# Expose ports (for local debugging)
|
||||
EXPOSE 3001 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"]
|
||||
# 启动前端 (Next.js 生产模式) 和后端 (FastAPI)
|
||||
# 前端监听 3001 端口,后端监听 8000 端口
|
||||
# 使用虚拟环境中的 Python 运行后端
|
||||
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"]
|
||||
|
||||
# Command: Start backend and frontend with process management
|
||||
CMD ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
|
||||
@ -9,12 +9,12 @@ import logging
|
||||
from typing import List, Optional
|
||||
|
||||
# 配置日志系统 - 在最开始配置,确保所有模块都能使用
|
||||
# 仅输出到 stdout,容器环境下通过 docker logs 收集日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout),
|
||||
logging.FileHandler("server.log", encoding='utf-8')
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
# FA3-Datafetch Docker 启动脚本
|
||||
|
||||
# 固定使用 sudo
|
||||
DOCKER="sudo docker"
|
||||
|
||||
# 容器名称
|
||||
CONTAINER_NAME="fa3-app"
|
||||
|
||||
@ -33,15 +36,16 @@ CLIENT_KEY="$CERT_DIR/client.key"
|
||||
|
||||
# 停止并删除旧容器
|
||||
echo "停止旧容器..."
|
||||
docker rm -f $CONTAINER_NAME 2>/dev/null || true
|
||||
$DOCKER rm -f $CONTAINER_NAME 2>/dev/null || true
|
||||
|
||||
# 启动新容器
|
||||
echo "启动新容器..."
|
||||
docker run -d \
|
||||
$DOCKER run -d \
|
||||
--name $CONTAINER_NAME \
|
||||
--dns 8.8.8.8 \
|
||||
--dns 8.8.4.4 \
|
||||
--dns 114.114.114.114 \
|
||||
--restart unless-stopped \
|
||||
-e PW_SERVICE_ID="$PW_SERVICE_ID" \
|
||||
-e PW_SERVER_ADDRS="$PW_SERVER_ADDRS" \
|
||||
-e PW_LOCAL_PORT="$PW_LOCAL_PORT" \
|
||||
@ -61,5 +65,5 @@ docker run -d \
|
||||
fa3-datafetch
|
||||
|
||||
echo "容器已启动!"
|
||||
echo "查看日志: docker logs $CONTAINER_NAME"
|
||||
echo "进入容器: docker exec -it $CONTAINER_NAME sh"
|
||||
echo "查看日志: $DOCKER logs -f $CONTAINER_NAME"
|
||||
echo "进入容器: $DOCKER exec -it $CONTAINER_NAME sh"
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
|
||||
set -e
|
||||
|
||||
# 固定使用 sudo
|
||||
DOCKER="sudo docker"
|
||||
SUDO="sudo"
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
@ -18,40 +22,50 @@ echo -e "${GREEN}========================================${NC}"
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 1. 拉取最新代码
|
||||
echo -e "\n${YELLOW}[1/4] 拉取最新代码...${NC}"
|
||||
echo -e "\n${YELLOW}[1/3] 拉取最新代码...${NC}"
|
||||
git fetch origin
|
||||
git pull origin main
|
||||
echo -e "${GREEN}✓ 代码更新完成${NC}"
|
||||
|
||||
# 2. 停止旧容器(如果存在)
|
||||
CONTAINER_NAME="fa3-app"
|
||||
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}"
|
||||
# 2. 重新构建 Docker 镜像
|
||||
echo -e "\n${YELLOW}[2/3] 重新构建 Docker 镜像...${NC}"
|
||||
echo -e " 这可能需要几分钟时间..."
|
||||
docker build -t fa3-datafetch .
|
||||
$DOCKER build --network=host -t fa3-datafetch .
|
||||
echo -e "${GREEN}✓ Docker 镜像构建完成${NC}"
|
||||
|
||||
# 4. 启动新容器
|
||||
echo -e "\n${YELLOW}[4/4] 启动新容器...${NC}"
|
||||
# 3. 启动新容器
|
||||
echo -e "\n${YELLOW}[3/3] 启动新容器...${NC}"
|
||||
./docker-run.sh
|
||||
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}"
|
||||
docker image prune -f
|
||||
$DOCKER image prune -f
|
||||
echo -e "${GREEN}✓ 清理完成${NC}"
|
||||
|
||||
echo -e "\n${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} 更新完成!${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "查看日志: docker logs -f $CONTAINER_NAME"
|
||||
echo -e "进入容器: docker exec -it $CONTAINER_NAME sh"
|
||||
echo -e "查看日志: $DOCKER logs -f fa3-app"
|
||||
echo -e "进入容器: $DOCKER exec -it fa3-app sh"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user