Separate Python and Rust into python/ and rust/ with per-stack Dockerfiles

This commit is contained in:
2026-04-19 14:01:05 +08:00
parent be8e030940
commit c2ef37b84e
184 changed files with 96 additions and 85 deletions

6
.gitignore vendored
View File

@@ -27,11 +27,11 @@ dist/
.eggs/ .eggs/
# Rust / maturin build artifacts # Rust / maturin build artifacts
myfsio_core/target/ python/myfsio_core/target/
myfsio_core/Cargo.lock python/myfsio_core/Cargo.lock
# Rust engine build artifacts # Rust engine build artifacts
myfsio-engine/target/ rust/myfsio-engine/target/
# Local runtime artifacts # Local runtime artifacts
logs/ logs/

View File

@@ -1,6 +0,0 @@
#!/bin/sh
set -e
ENGINE="${ENGINE:-rust}"
exec python run.py --prod --engine "$ENGINE"

View File

@@ -11,5 +11,7 @@ htmlcov
logs logs
data data
tmp tmp
tests
myfsio_core/target myfsio_core/target
myfsio-engine/target Dockerfile
.dockerignore

View File

@@ -1,9 +1,9 @@
FROM python:3.14.3-slim FROM python:3.14.3-slim AS builder
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 PYTHONUNBUFFERED=1
WORKDIR /app WORKDIR /build
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential curl \ && apt-get install -y --no-install-recommends build-essential curl \
@@ -12,27 +12,34 @@ RUN apt-get update \
ENV PATH="/root/.cargo/bin:${PATH}" ENV PATH="/root/.cargo/bin:${PATH}"
COPY requirements.txt ./ RUN pip install --no-cache-dir maturin
COPY myfsio_core ./myfsio_core
RUN cd myfsio_core \
&& maturin build --release --out /wheels
FROM python:3.14.3-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
COPY . . COPY --from=builder /wheels/*.whl /tmp/
RUN pip install --no-cache-dir /tmp/*.whl && rm /tmp/*.whl
RUN pip install --no-cache-dir maturin \ COPY app ./app
&& cd myfsio_core \ COPY templates ./templates
&& maturin build --release \ COPY static ./static
&& pip install target/wheels/*.whl \ COPY run.py ./
&& cd ../myfsio-engine \ COPY docker-entrypoint.sh ./
&& cargo build --release \
&& cp target/release/myfsio-server /usr/local/bin/myfsio-server \
&& cd .. \
&& rm -rf myfsio_core/target myfsio-engine/target \
&& pip uninstall -y maturin \
&& rustup self uninstall -y
RUN chmod +x docker-entrypoint.sh RUN chmod +x docker-entrypoint.sh \
&& mkdir -p /app/data \
RUN mkdir -p /app/data \
&& useradd -m -u 1000 myfsio \ && useradd -m -u 1000 myfsio \
&& chown -R myfsio:myfsio /app && chown -R myfsio:myfsio /app
@@ -41,8 +48,7 @@ USER myfsio
EXPOSE 5000 5100 EXPOSE 5000 5100
ENV APP_HOST=0.0.0.0 \ ENV APP_HOST=0.0.0.0 \
FLASK_ENV=production \ FLASK_ENV=production \
FLASK_DEBUG=0 \ FLASK_DEBUG=0
ENGINE=rust
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:5000/myfsio/health', timeout=2)" CMD python -c "import requests; requests.get('http://localhost:5000/myfsio/health', timeout=2)"

View File

@@ -0,0 +1,4 @@
#!/bin/sh
set -e
exec python run.py --prod

View File

@@ -5,7 +5,6 @@ import argparse
import atexit import atexit
import os import os
import signal import signal
import subprocess
import sys import sys
import warnings import warnings
import multiprocessing import multiprocessing
@@ -75,49 +74,6 @@ def _serve_granian(target: str, port: int, config: Optional[AppConfig] = None) -
server.serve() server.serve()
def _find_rust_binary() -> Optional[Path]:
candidates = [
Path("/usr/local/bin/myfsio-server"),
Path(__file__).parent / "myfsio-engine" / "target" / "release" / "myfsio-server.exe",
Path(__file__).parent / "myfsio-engine" / "target" / "release" / "myfsio-server",
Path(__file__).parent / "myfsio-engine" / "target" / "debug" / "myfsio-server.exe",
Path(__file__).parent / "myfsio-engine" / "target" / "debug" / "myfsio-server",
]
for p in candidates:
if p.exists():
return p
return None
def serve_rust_api(port: int, config: Optional[AppConfig] = None) -> None:
binary = _find_rust_binary()
if binary is None:
print("ERROR: Rust engine binary not found. Build it first:")
print(" cd myfsio-engine && cargo build --release")
sys.exit(1)
env = os.environ.copy()
env["PORT"] = str(port)
env["HOST"] = _server_host()
if config:
env["STORAGE_ROOT"] = str(config.storage_root)
env["AWS_REGION"] = config.aws_region
if config.secret_key:
env["SECRET_KEY"] = config.secret_key
env.setdefault("ENCRYPTION_ENABLED", str(config.encryption_enabled).lower())
env.setdefault("KMS_ENABLED", str(config.kms_enabled).lower())
env.setdefault("LIFECYCLE_ENABLED", str(config.lifecycle_enabled).lower())
env.setdefault("RUST_LOG", "info")
print(f"Starting Rust S3 engine: {binary}")
proc = subprocess.Popen([str(binary)], env=env)
try:
proc.wait()
except KeyboardInterrupt:
proc.terminate()
proc.wait(timeout=5)
def serve_api(port: int, prod: bool = False, config: Optional[AppConfig] = None) -> None: def serve_api(port: int, prod: bool = False, config: Optional[AppConfig] = None) -> None:
if prod: if prod:
_serve_granian("app:create_api_app", port, config) _serve_granian("app:create_api_app", port, config)
@@ -271,7 +227,6 @@ if __name__ == "__main__":
parser.add_argument("--ui-port", type=int, default=5100) parser.add_argument("--ui-port", type=int, default=5100)
parser.add_argument("--prod", action="store_true", help="Run in production mode using Granian") parser.add_argument("--prod", action="store_true", help="Run in production mode using Granian")
parser.add_argument("--dev", action="store_true", help="Force development mode (Flask dev server)") parser.add_argument("--dev", action="store_true", help="Force development mode (Flask dev server)")
parser.add_argument("--engine", choices=["python", "rust"], default=os.getenv("ENGINE", "python"), help="API engine: python (Flask) or rust (myfsio-engine)")
parser.add_argument("--check-config", action="store_true", help="Validate configuration and exit") parser.add_argument("--check-config", action="store_true", help="Validate configuration and exit")
parser.add_argument("--show-config", action="store_true", help="Show configuration summary and exit") parser.add_argument("--show-config", action="store_true", help="Show configuration summary and exit")
parser.add_argument("--reset-cred", action="store_true", help="Reset admin credentials and exit") parser.add_argument("--reset-cred", action="store_true", help="Reset admin credentials and exit")
@@ -325,17 +280,9 @@ if __name__ == "__main__":
else: else:
print("Running in development mode (Flask dev server)") print("Running in development mode (Flask dev server)")
use_rust = args.engine == "rust"
if args.mode in {"api", "both"}: if args.mode in {"api", "both"}:
if use_rust: print(f"Starting API server on port {args.api_port}...")
print(f"Starting Rust API engine on port {args.api_port}...") api_proc = Process(target=serve_api, args=(args.api_port, prod_mode, config))
else:
print(f"Starting API server on port {args.api_port}...")
if use_rust:
api_proc = Process(target=serve_rust_api, args=(args.api_port, config))
else:
api_proc = Process(target=serve_api, args=(args.api_port, prod_mode, config))
api_proc.start() api_proc.start()
else: else:
api_proc = None api_proc = None

View File

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

View File

Before

Width:  |  Height:  |  Size: 872 KiB

After

Width:  |  Height:  |  Size: 872 KiB

Some files were not shown because too many files have changed in this diff Show More