build: add the container image and developer targets
Browse filesThe artifact is baked into the image rather than mounted so that the
image tag is a complete description of what the service predicts and a
rollback is deploying the previous tag.
The build runs the golden row check so that an image unable to reproduce
its own recorded prediction never reaches a registry.
- Dockerfile +48 -0
- Makefile +41 -0
Dockerfile
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# The model is baked into the image rather than mounted, so the image tag is a complete
|
| 2 |
+
# description of what the service will predict and rolling back means deploying the
|
| 3 |
+
# previous tag. The build fails if the artifact does not reproduce its own golden row.
|
| 4 |
+
|
| 5 |
+
FROM python:3.13-slim AS builder
|
| 6 |
+
|
| 7 |
+
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
| 8 |
+
PIP_NO_CACHE_DIR=1
|
| 9 |
+
|
| 10 |
+
WORKDIR /app
|
| 11 |
+
COPY requirements.txt .
|
| 12 |
+
RUN python -m venv /opt/venv \
|
| 13 |
+
&& /opt/venv/bin/pip install --upgrade pip \
|
| 14 |
+
&& /opt/venv/bin/pip install -r requirements.txt
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
FROM python:3.13-slim AS runtime
|
| 18 |
+
|
| 19 |
+
ENV PATH="/opt/venv/bin:$PATH" \
|
| 20 |
+
PYTHONPATH=/app \
|
| 21 |
+
PYTHONDONTWRITEBYTECODE=1 \
|
| 22 |
+
PYTHONUNBUFFERED=1 \
|
| 23 |
+
OMP_NUM_THREADS=2 \
|
| 24 |
+
OPENBLAS_NUM_THREADS=2 \
|
| 25 |
+
MKL_NUM_THREADS=2
|
| 26 |
+
|
| 27 |
+
COPY --from=builder /opt/venv /opt/venv
|
| 28 |
+
|
| 29 |
+
WORKDIR /app
|
| 30 |
+
COPY phiusiil/ ./phiusiil/
|
| 31 |
+
COPY server/ ./server/
|
| 32 |
+
COPY model/ ./model/
|
| 33 |
+
COPY pyproject.toml README.md ./
|
| 34 |
+
|
| 35 |
+
RUN useradd --uid 10001 --no-create-home --shell /usr/sbin/nologin serve \
|
| 36 |
+
&& chown -R serve:serve /app
|
| 37 |
+
USER serve
|
| 38 |
+
|
| 39 |
+
# Boot-time honesty check, at build time: an image whose artifact produces different
|
| 40 |
+
# numbers from the training run must not be publishable.
|
| 41 |
+
RUN python -m server.selftest --golden
|
| 42 |
+
|
| 43 |
+
EXPOSE 8000
|
| 44 |
+
|
| 45 |
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
| 46 |
+
CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=4).status==200 else 1)"
|
| 47 |
+
|
| 48 |
+
CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "8000"]
|
Makefile
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
VENV := .venv
|
| 2 |
+
PY := $(VENV)/bin/python
|
| 3 |
+
PIP := $(VENV)/bin/pip
|
| 4 |
+
PORT ?= 8000
|
| 5 |
+
IMAGE ?= fetiai-v1-phiusiil-binclf-knn-scratch-500k:local
|
| 6 |
+
|
| 7 |
+
.DEFAULT_GOAL := help
|
| 8 |
+
|
| 9 |
+
help: ## Show this help
|
| 10 |
+
@grep -hE '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'
|
| 11 |
+
|
| 12 |
+
$(VENV): ## Create the virtualenv
|
| 13 |
+
python3 -m venv $(VENV)
|
| 14 |
+
|
| 15 |
+
install: $(VENV) ## Install pinned dependencies
|
| 16 |
+
$(PIP) install --upgrade pip
|
| 17 |
+
$(PIP) install -r requirements.txt -r requirements-dev.txt
|
| 18 |
+
|
| 19 |
+
selftest: ## Verify the artifact reproduces its recorded golden row
|
| 20 |
+
$(PY) -m server.selftest --golden
|
| 21 |
+
|
| 22 |
+
namecheck: ## Fail if the parent project's internal library name appears anywhere
|
| 23 |
+
$(PY) -m pytest tests/test_naming.py -q
|
| 24 |
+
|
| 25 |
+
test: ## Run the full offline suite
|
| 26 |
+
$(PY) -m pytest -q
|
| 27 |
+
|
| 28 |
+
serve: ## Run the API on http://127.0.0.1:$(PORT)
|
| 29 |
+
$(VENV)/bin/uvicorn server.app:app --host 127.0.0.1 --port $(PORT)
|
| 30 |
+
|
| 31 |
+
docker-build: ## Build the container image
|
| 32 |
+
docker build -t $(IMAGE) .
|
| 33 |
+
|
| 34 |
+
docker-test: ## Run the golden-row self-test inside the built image
|
| 35 |
+
docker run --rm $(IMAGE) python -m server.selftest --golden
|
| 36 |
+
|
| 37 |
+
clean: ## Remove caches
|
| 38 |
+
rm -rf .pytest_cache .ruff_cache .mypy_cache
|
| 39 |
+
find . -name __pycache__ -type d -prune -exec rm -rf {} +
|
| 40 |
+
|
| 41 |
+
.PHONY: help install selftest namecheck test serve docker-build docker-test clean
|