105 lines
4.4 KiB
Docker
105 lines
4.4 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|||
|
|
#
|
||
|
|
# Packet Station image. One image runs the app, the worker, migrations and doctor.
|
||
|
|
# Build: docker build --build-arg PS_BUILD=sha-<sha> -t gt.pescoinc.biz/spencerm/packetstation:sha-<sha> .
|
||
|
|
# No password is ever passed at build time. Secrets arrive at run time as compose file secrets.
|
||
|
|
|
||
|
|
ARG PYTHON_IMAGE=python:3.12-slim-bookworm
|
||
|
|
ARG NODE_IMAGE=node:22-bookworm-slim
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# Stage 1: frontend. Vite build into frontend/dist.
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
FROM ${NODE_IMAGE} AS frontend
|
||
|
|
WORKDIR /src/frontend
|
||
|
|
# Lockfile first so the npm ci layer is cached until dependencies change.
|
||
|
|
COPY frontend/package.json frontend/package-lock.json ./
|
||
|
|
RUN npm ci --no-audit --no-fund
|
||
|
|
COPY frontend/ ./
|
||
|
|
RUN npm run build
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# Stage 2: builder. Python venv in /opt/venv with every dependency and the package.
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
FROM ${PYTHON_IMAGE} AS builder
|
||
|
|
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
||
|
|
PIP_NO_CACHE_DIR=1 \
|
||
|
|
PYTHONDONTWRITEBYTECODE=1
|
||
|
|
# PESCO's private index on Gitea. It serves visualkit, mfileskit and their dependencies only.
|
||
|
|
ARG PESCO_INDEX_URL=https://gt.pescoinc.biz/api/packages/spencerm/pypi/simple/
|
||
|
|
RUN python -m venv /opt/venv
|
||
|
|
ENV PATH=/opt/venv/bin:$PATH
|
||
|
|
WORKDIR /src
|
||
|
|
|
||
|
|
# Public dependencies from PyPI.
|
||
|
|
COPY server/requirements.txt server/requirements.txt
|
||
|
|
RUN pip install -r server/requirements.txt
|
||
|
|
|
||
|
|
# PESCO libraries from the Gitea index only. Always --index-url, never --extra-index-url,
|
||
|
|
# so a public package with the same name can never be picked instead.
|
||
|
|
COPY server/requirements-pesco.txt server/requirements-pesco.txt
|
||
|
|
RUN pip install --index-url "${PESCO_INDEX_URL}" -r server/requirements-pesco.txt
|
||
|
|
|
||
|
|
# The package itself. Dependencies are already in the venv.
|
||
|
|
COPY pyproject.toml README* ./
|
||
|
|
COPY server/ server/
|
||
|
|
RUN pip install --no-deps . \
|
||
|
|
&& pip check
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# Stage 3: runtime.
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
FROM ${PYTHON_IMAGE} AS runtime
|
||
|
|
|
||
|
|
# ODBC Driver 18 from Microsoft's Debian 12 repository, plus tini and tzdata.
|
||
|
|
# curl and gnupg are only needed to fetch the signing key, so they are removed again.
|
||
|
|
RUN set -eux; \
|
||
|
|
apt-get update; \
|
||
|
|
apt-get install -y --no-install-recommends curl gnupg ca-certificates; \
|
||
|
|
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \
|
||
|
|
| gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg; \
|
||
|
|
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" \
|
||
|
|
> /etc/apt/sources.list.d/microsoft-prod.list; \
|
||
|
|
apt-get update; \
|
||
|
|
ACCEPT_EULA=Y apt-get install -y --no-install-recommends \
|
||
|
|
msodbcsql18 unixodbc tini tzdata ca-certificates; \
|
||
|
|
apt-get purge -y --auto-remove curl gnupg; \
|
||
|
|
rm -rf /var/lib/apt/lists/*; \
|
||
|
|
odbcinst -q -d -n "ODBC Driver 18 for SQL Server"
|
||
|
|
|
||
|
|
# Service user. Group 10001 matches the host secret files (root:10001 0440).
|
||
|
|
RUN groupadd -g 10001 ps \
|
||
|
|
&& useradd -u 10001 -g 10001 -r -M -d /app -s /usr/sbin/nologin ps
|
||
|
|
|
||
|
|
COPY --from=builder /opt/venv /opt/venv
|
||
|
|
COPY --from=frontend /src/frontend/dist /app/static
|
||
|
|
COPY --chmod=0755 docker/entrypoint.sh /app/entrypoint.sh
|
||
|
|
# Guard against CRLF line endings from a Windows checkout.
|
||
|
|
RUN sed -i 's/\r$//' /app/entrypoint.sh
|
||
|
|
|
||
|
|
# /data/assets is a named volume. Docker copies this ownership into a new volume on first use.
|
||
|
|
# /backups is mounted read-only into the worker; it only needs to exist.
|
||
|
|
RUN mkdir -p /data/assets /backups \
|
||
|
|
&& chown 10001:10001 /data/assets \
|
||
|
|
&& chmod 0750 /data/assets
|
||
|
|
|
||
|
|
ARG PS_BUILD=dev
|
||
|
|
ENV PATH=/opt/venv/bin:$PATH \
|
||
|
|
PYTHONUNBUFFERED=1 \
|
||
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
||
|
|
PS_BUILD=${PS_BUILD} \
|
||
|
|
STATIC_DIR=/app/static \
|
||
|
|
ASSETS_DIR=/data/assets \
|
||
|
|
BACKUP_DIR=/backups
|
||
|
|
LABEL org.opencontainers.image.title="packetstation" \
|
||
|
|
org.opencontainers.image.source="https://gt.pescoinc.biz/spencerm/packetstation" \
|
||
|
|
org.opencontainers.image.version="${PS_BUILD}" \
|
||
|
|
biz.pescoinc.packetstation.build="${PS_BUILD}"
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
USER ps
|
||
|
|
EXPOSE 8000
|
||
|
|
# No HEALTHCHECK here. Compose sets one per service (app and worker check different things).
|
||
|
|
ENTRYPOINT ["tini", "--", "/app/entrypoint.sh"]
|
||
|
|
CMD ["app"]
|