diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 733b9c5..9cefcdd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -179,7 +179,7 @@ cache: policy: pull-push before_script: - apt-get update - - apt-get install -y --no-install-recommends ca-certificates curl just + - apt-get install -y --no-install-recommends ca-certificates curl just git - update-ca-certificates - curl -LsSf https://astral.sh/uv/install.sh | sh - export PATH="$HOME/.local/bin:$PATH" @@ -227,7 +227,7 @@ build-ml: <<: *build-config variables: IMAGE_NAME: $BASE_IMAGE_NAME/ml - CONTAINERFILE: Containerfile.ml + CONTAINERFILE: Containerfile BUILDTARGET: ml lint: @@ -252,6 +252,8 @@ test: --profile migrations --profile tests PODMAN_IGNORE_CGROUPSV1_WARNING: True + tags: + - self-hosted script: - dnf -y install podman podman-compose # - cp ./infrastructure/configs/podman/ci.conf /etc/containers/containers.conf @@ -348,6 +350,14 @@ sast-image-migrations: dependencies: - build-migrations +sast-image-ml: + <<: *trivy-image-scan + variables: + IMAGE_NAME: $BASE_IMAGE_NAME/ml + IMAGE_TYPE: ml + dependencies: + - build-ml + tag-runtime: <<: *tag-config variables: diff --git a/Containerfile b/Containerfile index 79e039f..74e6ca0 100644 --- a/Containerfile +++ b/Containerfile @@ -1,7 +1,14 @@ # syntax=docker/dockerfile:1.20 -# Stage 1: Builder -FROM docker.io/python:3.12-slim AS builder +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/* COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ @@ -14,18 +21,30 @@ 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 -# Stage 2: Runtime -FROM docker.io/python:3.12-alpine3.22 AS runtime +COPY ./src ./src + +RUN uv sync --frozen --no-dev --no-cache --group ml + + +# Stage 4: Backend Runtime +FROM ${PY_IMAGE} AS runtime + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl \ + && rm -rf /var/lib/apt/lists/* ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ @@ -35,28 +54,22 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ WORKDIR /app -RUN apk add --no-cache --virtual .runtime-deps \ - curl && \ - rm -rf /var/cache/apk/* +COPY --from=backend-builder /opt/venv /opt/venv -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/ ./ +COPY ./src/ ./ EXPOSE 8080 -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" ] +CMD [ "/opt/venv/bin/web_api_cli", "/app/config.toml" ] -# Stage 3: Testing -FROM builder AS tests +# 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/* ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ @@ -66,21 +79,39 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ WORKDIR /app -RUN uv sync --no-install-project --group tests --frozen --no-cache +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 COPY ./src ./src COPY ./tests ./tests -RUN uv pip install -e . +RUN uv sync --group tests --frozen --no-cache 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 4: Migrations -FROM builder AS migrations +# Stage 7: Migrations +FROM base-builder AS migrations ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ @@ -92,14 +123,12 @@ 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 pip install -e . +RUN uv sync --group migrations --frozen --no-cache CMD [ "alembic", "upgrade", "head" ] diff --git a/Containerfile.ml b/Containerfile.ml deleted file mode 100644 index 2aa78a6..0000000 --- a/Containerfile.ml +++ /dev/null @@ -1,50 +0,0 @@ -# syntax=docker/dockerfile:1.20 - -ARG PY_IMAGE=docker.io/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 8b160f9..3b4997a 100644 --- a/compose.yaml +++ b/compose.yaml @@ -78,6 +78,8 @@ services: retries: 5 networks: - default + profiles: + - ml ports: - name: web target: 8081