From dc41f1895bd5f55e280a869639ec5e32f74a3027 Mon Sep 17 00:00:00 2001 From: ITQ Date: Sat, 22 Nov 2025 16:05:41 +0300 Subject: [PATCH] (scope): [body] [footer(s)] --- .gitlab-ci.yml | 15 +++- Containerfile | 91 ++++++++------------- Containerfile.ml | 50 +++++++++++ compose.yaml | 4 +- src/template_project/web_api/entry_point.py | 4 +- 5 files changed, 99 insertions(+), 65 deletions(-) create mode 100644 Containerfile.ml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c71820e..8ca7b94 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -223,6 +223,13 @@ build-migrations: CONTAINERFILE: Containerfile BUILDTARGET: migrations +build-ml: + <<: *build-config + variables: + IMAGE_NAME: $BASE_IMAGE_NAME/ml + CONTAINERFILE: Containerfile + BUILDTARGET: tests + lint: <<: *uv-job stage: test @@ -247,7 +254,7 @@ test: PODMAN_IGNORE_CGROUPSV1_WARNING: True script: - dnf -y install podman podman-compose - - cp ./infrastructure/configs/podman/ci.conf /etc/containers/containers.conf + # - cp ./infrastructure/configs/podman/ci.conf /etc/containers/containers.conf - cp "$TEST_STAGE_FIREBASE_CONF" ./infrastructure/configs/backend/firebase.json - podman info --format '{{.Host.EventLogger}}' - export PROFILES="$(printf '%s ' $COMPOSE_PROFILES)" @@ -256,7 +263,6 @@ test: ( while true; do podman-compose -f compose.yaml $PROFILES logs 2>&1 - echo aboba sleep 30 done ) | grep "Error: no container" -v | tee -a compose.log & @@ -357,6 +363,11 @@ tag-migrations: variables: IMAGE_NAME: $BASE_IMAGE_NAME/backend-migrations +tag-ml: + <<: *tag-config + variables: + IMAGE_NAME: $BASE_IMAGE_NAME/ml + webhook-migrations-deploy: <<: *webhook-config stage: deploy diff --git a/Containerfile b/Containerfile index ed431c3..9d4b9ce 100644 --- a/Containerfile +++ b/Containerfile @@ -2,13 +2,8 @@ ARG PY_IMAGE=python:3.12-slim -# Stage 1: Base Builder -FROM ${PY_IMAGE} AS base-builder - -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - curl git \ - && rm -rf /var/lib/apt/lists/* +# Stage 1: Builder +FROM ${PY_IMAGE} AS builder COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ @@ -21,30 +16,18 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ UV_LINK_MODE=copy \ UV_PROJECT_ENVIRONMENT=/opt/venv +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl git \ + && rm -rf /var/lib/apt/lists/* + COPY pyproject.toml uv.lock ./ -# Stage 2: Backend Builder -FROM base-builder AS backend-builder - -COPY ./src ./src - RUN uv sync --frozen --no-dev --no-cache --group backend -# Stage 3: ML Builder -FROM base-builder AS ml-builder -COPY ./src ./src - -RUN uv sync --frozen --no-dev --no-cache --group ml - - -# Stage 4: Backend Runtime -FROM ${PY_IMAGE} AS backend - -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - curl \ - && rm -rf /var/lib/apt/lists/* +# Stage 2: Runtime +FROM ${PY_IMAGE} AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ @@ -54,22 +37,28 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ WORKDIR /app -COPY --from=backend-builder /opt/venv /opt/venv +RUN apk add --no-cache --virtual .runtime-deps \ + curl && \ + rm -rf /var/cache/apk/* -COPY ./src/ ./ +RUN adduser -D -g '' app && chown -R app:app /app + +USER app + +COPY --from=builder --chown=app:app /opt/venv /opt/venv + +COPY --chown=app:app ./src/ ./ EXPOSE 8080 -CMD [ "/opt/venv/bin/web_api_cli", "/app/config.toml" ] +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD curl -fsS http://localhost:8080/healthcheck || exit 1 + +CMD [ "web_api_cli", "/app/config.toml" ] -# Stage 5: ML Runtime -FROM ${PY_IMAGE} AS ml - -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - curl \ - && rm -rf /var/lib/apt/lists/* +# Stage 3: Testing +FROM builder AS tests ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ @@ -79,39 +68,21 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ WORKDIR /app -COPY --from=ml-builder /opt/venv /opt/venv - -COPY ./src/ ./ - -EXPOSE 8081 - -CMD [ "/opt/venv/bin/ml_api_cli", "/app/config.toml" ] - - -# Stage 6: Testing -FROM base-builder AS tests - -ENV PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 \ - PYTHONOPTIMIZE=2 \ - PATH="/opt/venv/bin:$PATH" \ - PYTHONPATH="/app:$PYTHONPATH" - -WORKDIR /app +RUN uv sync --no-install-project --group tests --frozen --no-cache COPY ./src ./src COPY ./tests ./tests -RUN uv sync --group tests --frozen --no-cache +RUN uv pip install -e . RUN mkdir -p /app/cov && mkdir /app/cov/html CMD [ "sh", "-c", "coverage run --source=src -m pytest -v && coverage report > /app/cov/coverage.txt && coverage xml -o /app/cov/coverage.xml && coverage html -d /app/cov/html" ] -# Stage 7: Migrations -FROM base-builder AS migrations +# Stage 4: Migrations +FROM builder AS migrations ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ @@ -123,12 +94,14 @@ WORKDIR /app RUN mkdir -p ./src/template_project +RUN uv sync --no-install-project --group migrations --frozen --no-cache + COPY ./src ./src COPY ./tests ./tests COPY ./alembic.ini ./ -RUN uv sync --group migrations --frozen --no-cache +RUN uv pip install -e . CMD [ "alembic", "upgrade", "head" ] diff --git a/Containerfile.ml b/Containerfile.ml new file mode 100644 index 0000000..833c039 --- /dev/null +++ b/Containerfile.ml @@ -0,0 +1,50 @@ +# syntax=docker/dockerfile:1.20 + +ARG PY_IMAGE=python:3.12-slim + +# Stage 1: Builder +FROM ${PY_IMAGE} AS builder + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl git \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONOPTIMIZE=2 \ + UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy \ + UV_PROJECT_ENVIRONMENT=/opt/venv + +COPY pyproject.toml uv.lock ./ + +RUN uv sync --frozen --no-dev --no-cache --group ml + +# Stage 2: ML Runtime +FROM ${PY_IMAGE} AS ml + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl \ + && rm -rf /var/lib/apt/lists/* + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONOPTIMIZE=2 \ + PATH="/opt/venv/bin:$PATH" \ + PYTHONPATH="/app:$PYTHONPATH" + +WORKDIR /app + +COPY --from=builder /opt/venv /opt/venv + +COPY ./src/ ./ + +EXPOSE 8081 + +CMD [ "/opt/venv/bin/ml_api_cli", "/app/config.toml" ] diff --git a/compose.yaml b/compose.yaml index da850d5..8b160f9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,7 +5,7 @@ services: build: context: . dockerfile: Containerfile - target: backend + target: runtime tags: - template-project-backend:latest pull: true @@ -64,7 +64,7 @@ services: ml: build: context: . - dockerfile: Containerfile + dockerfile: Containerfile.ml target: ml tags: - template-project-ml:latest diff --git a/src/template_project/web_api/entry_point.py b/src/template_project/web_api/entry_point.py index 0ddb565..bef70e3 100644 --- a/src/template_project/web_api/entry_point.py +++ b/src/template_project/web_api/entry_point.py @@ -66,8 +66,8 @@ def make_asgi_application( app = FastAPI( lifespan=lifespan, docs_url="/docs", - title="Template project", - description="Template project API", + title="Recomenci Fluon", + description="Recomenci Fluon API", version="1.0.0", openapi_url="/openapi.json", )