From 3cce4c93735bcfb3bae709d2d18f2c527978f726 Mon Sep 17 00:00:00 2001 From: RYDE-WORK Date: Mon, 26 Jan 2026 11:08:57 +0800 Subject: [PATCH] Dockerize --- .dockerignore | 75 ++++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 63 ++++++++++++++++++++++++++++++++++++++ Makefile | 42 ++++++++++++++++++++++++++ app/app.py | 7 ++++- docker-compose.yml | 60 +++++++++++++++++++++++++++++++++++++ requirements.txt | 20 +++++++++++++ 6 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 requirements.txt diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e9992af --- /dev/null +++ b/.dockerignore @@ -0,0 +1,75 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +*.egg-info/ +.eggs/ +dist/ +build/ +*.egg + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ +.pixi/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +.cursor/ + +# Git +.git/ +.gitignore + +# Data (不需要打包到镜像) +data/ +!data/.gitkeep + +# Notebooks +notebooks/ +*.ipynb + +# Documentation +docs/ + +# Reports +reports/ + +# References +references/ + +# Scripts (训练脚本不需要) +scripts/ + +# Lock files +pixi.lock + +# Tests +tests/ +.pytest_cache/ + +# Logs +*.log +logs/ + +# Temporary files +*.tmp +*.temp +.DS_Store + +# Models (will be mounted as volume or copied explicitly) +# Note: models/final/ is copied in Dockerfile +models/finetune_cv/ +models/pretrain_cv/ +models/mpnn/ +models/*.pt +models/*.json +!models/final/ + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..260ed56 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,63 @@ +# LNP-ML Docker Image +# 多阶段构建,支持 API 和 Streamlit 两种服务 + +FROM python:3.8-slim AS base + +# 设置环境变量 +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +WORKDIR /app + +# 安装系统依赖 +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + libxrender1 \ + libxext6 \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# 复制依赖文件 +COPY requirements.txt . + +# 安装 Python 依赖 +RUN pip install --upgrade pip && \ + pip install -r requirements.txt + +# 复制项目代码 +COPY pyproject.toml . +COPY README.md . +COPY LICENSE . +COPY lnp_ml/ ./lnp_ml/ +COPY app/ ./app/ + +# 安装项目包 +RUN pip install -e . + +# 复制模型文件 +COPY models/final/ ./models/final/ + +# ============ API 服务 ============ +FROM base AS api + +EXPOSE 8000 + +ENV MODEL_PATH=/app/models/final/model.pt + +CMD ["uvicorn", "app.api:app", "--host", "0.0.0.0", "--port", "8000"] + +# ============ Streamlit 服务 ============ +FROM base AS streamlit + +EXPOSE 8501 + +# Streamlit 配置 +ENV STREAMLIT_SERVER_PORT=8501 \ + STREAMLIT_SERVER_ADDRESS=0.0.0.0 \ + STREAMLIT_SERVER_HEADLESS=true \ + STREAMLIT_BROWSER_GATHER_USAGE_STATS=false + +CMD ["streamlit", "run", "app/app.py"] + diff --git a/Makefile b/Makefile index 8d7182b..53aade8 100644 --- a/Makefile +++ b/Makefile @@ -200,6 +200,48 @@ serve: @echo "然后访问: http://localhost:8501" +################################################################################# +# DOCKER COMMANDS # +################################################################################# + +## Build Docker images +.PHONY: docker-build +docker-build: + docker compose build + +## Start all services with Docker Compose +.PHONY: docker-up +docker-up: + docker compose up -d + +## Stop all Docker services +.PHONY: docker-down +docker-down: + docker compose down + +## View Docker logs +.PHONY: docker-logs +docker-logs: + docker compose logs -f + +## Build and start all services +.PHONY: docker-serve +docker-serve: docker-build docker-up + @echo "" + @echo "🚀 服务已启动!" + @echo " - API: http://localhost:8000" + @echo " - Web 应用: http://localhost:8501" + @echo "" + @echo "查看日志: make docker-logs" + @echo "停止服务: make docker-down" + +## Clean Docker resources (images, volumes, etc.) +.PHONY: docker-clean +docker-clean: + docker compose down -v --rmi local + docker system prune -f + + ################################################################################# # Self Documenting Commands # ################################################################################# diff --git a/app/app.py b/app/app.py index 4ecb698..ffccca8 100644 --- a/app/app.py +++ b/app/app.py @@ -3,9 +3,13 @@ Streamlit 配方优化交互界面 启动应用: streamlit run app/app.py + +Docker 环境变量: + API_URL: API 服务地址 (默认: http://localhost:8000) """ import io +import os from datetime import datetime import httpx @@ -14,7 +18,8 @@ import streamlit as st # ============ 配置 ============ -API_URL = "http://localhost:8000" +# 从环境变量读取 API 地址,支持 Docker 环境 +API_URL = os.environ.get("API_URL", "http://localhost:8000") AVAILABLE_ORGANS = [ "liver", diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..83f0ec5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,60 @@ +services: + # FastAPI 后端服务 + api: + build: + context: . + dockerfile: Dockerfile + target: api + container_name: lnp-api + # ports: + # - "8000:8000" + environment: + - MODEL_PATH=/app/models/final/model.pt + - DEVICE=cpu + volumes: + # 挂载模型目录以便更新模型 + - ./models/final:/app/models/final:ro + - ./models/mpnn:/app/models/mpnn:ro + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + # 如果有 NVIDIA GPU,取消下面的注释 + # deploy: + # resources: + # reservations: + # devices: + # - driver: nvidia + # count: 1 + # capabilities: [gpu] + + # Streamlit 前端服务 + streamlit: + build: + context: . + dockerfile: Dockerfile + target: streamlit + container_name: lnp-streamlit + ports: + - "8501:8501" + environment: + # 连接到 API 服务(使用 Docker 内部网络) + - API_URL=http://api:8000 + depends_on: + api: + condition: service_started + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8501/_stcore/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +networks: + default: + name: lnp-network + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..98176eb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,20 @@ +# 严格遵循 pixi.toml 的依赖版本 +# 注意: lnp_ml 本地包在 Dockerfile 中单独安装 + +# conda dependencies (in pixi.toml [dependencies]) +loguru +tqdm +typer + +# pypi dependencies (in pixi.toml [pypi-dependencies]) +chemprop==1.7.0 +setuptools +pandas>=2.0.3,<3 +openpyxl>=3.1.5,<4 +python-dotenv>=1.0.1,<2 +pyarrow>=17.0.0,<18 +fastparquet>=2024.2.0,<2025 +fastapi>=0.124.4,<0.125 +streamlit>=1.40.1,<2 +httpx>=0.28.1,<0.29 +uvicorn>=0.33.0,<0.34