Its output is the same on every platform. Under emulation for an arm64 image this stage alone took 31 of 33 minutes, now about 2.
142 lines
6.5 KiB
Docker
142 lines
6.5 KiB
Docker
# The whole instance in one image: engine, API and dashboard on port 8000.
|
|
#
|
|
# docker build -f backend/Dockerfile -t fluksio .
|
|
# docker run -d -p 8080:8000 -v fluksio-data:/data fluksio
|
|
#
|
|
# The build context is the app repository root. EXTRAS picks the optional
|
|
# features (mqtt, influx, email, sentry, ml, or server for all of them); the
|
|
# default is the minimal engine.
|
|
|
|
# ── Dashboard ────────────────────────────────────────────────────
|
|
# Built on the build machine's own CPU: the output is the same for every
|
|
# platform, and under emulation for an arm64 image this stage alone takes
|
|
# half an hour.
|
|
FROM --platform=$BUILDPLATFORM oven/bun:1 AS frontend
|
|
|
|
WORKDIR /app
|
|
COPY package.json bun.lock /app/
|
|
COPY frontend/package.json /app/frontend/
|
|
WORKDIR /app/frontend
|
|
RUN bun install
|
|
|
|
COPY ./frontend /app/frontend
|
|
# Empty: the dashboard calls the API as a path on whichever origin served it,
|
|
# so one build works behind any port, hostname or proxy.
|
|
ENV VITE_API_URL=
|
|
# Set only for the copy the portal serves (the appshell stage below).
|
|
ARG VITE_BASE
|
|
RUN bun run build
|
|
|
|
# ── Python environment ───────────────────────────────────────────
|
|
FROM python:3.13-slim AS backend
|
|
|
|
# Ref: https://docs.astral.sh/uv/guides/integration/docker/
|
|
COPY --from=ghcr.io/astral-sh/uv:0.9.26 /uv /bin/
|
|
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
|
|
|
|
WORKDIR /app/
|
|
|
|
ARG EXTRAS=""
|
|
|
|
# Dependencies first, so a source change does not reinstall them.
|
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
uv sync --frozen --no-dev --no-install-workspace --package fluksio \
|
|
$(for e in $(echo "$EXTRAS" | tr , ' '); do printf -- '--extra %s ' "$e"; done)
|
|
|
|
COPY ./backend/scripts /app/backend/scripts
|
|
|
|
# README.md and LICENSE come along because pyproject.toml names them in
|
|
# `readme` and `license-files`; hatchling reads both when it builds the
|
|
# package metadata below, and fails the build if either is missing.
|
|
COPY ./backend/pyproject.toml ./backend/alembic.ini ./backend/README.md ./backend/LICENSE /app/backend/
|
|
|
|
COPY ./backend/fluksio /app/backend/fluksio
|
|
|
|
# The worker is a workspace member of its own, so the engine's sync needs it
|
|
# present to resolve the dependency on it. It is also what the engine runs
|
|
# node code with.
|
|
COPY ./worker /app/worker
|
|
|
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
uv sync --frozen --no-dev --package fluksio \
|
|
$(for e in $(echo "$EXTRAS" | tr , ' '); do printf -- '--extra %s ' "$e"; done)
|
|
|
|
# Connectors are ordinary installed packages found through the
|
|
# `fluksio.node_types` entry point. `make connectors` builds them into here;
|
|
# an image with none is the normal case.
|
|
COPY ./backend/connector-wheels /tmp/connector-wheels
|
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
sh -c 'ls /tmp/connector-wheels/*.whl >/dev/null 2>&1 \
|
|
&& uv pip install --python /app/.venv/bin/python /tmp/connector-wheels/*.whl \
|
|
|| echo "no connectors bundled"'
|
|
|
|
# ── The portal's copy of the dashboard ──────────────────────────
|
|
# The hub serves the dashboard for every instance it tunnels to, from
|
|
# /app-shell/. index/docker/compose.yml builds this target with
|
|
# VITE_BASE=/app-shell/ and copies /dist into the hub's volume.
|
|
FROM busybox:1.37 AS appshell
|
|
COPY --from=frontend /app/frontend/dist /dist
|
|
|
|
# ── git, without perl ────────────────────────────────────────────
|
|
# The flow store runs init, add, commit, gc and rev-parse, all built into the
|
|
# one binary, which needs nothing slim lacks. Debian's package would bring
|
|
# perl and the remote helpers along, about 100 MB nothing here calls.
|
|
FROM python:3.13-slim AS git
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ── Runtime ──────────────────────────────────────────────────────
|
|
FROM python:3.13-slim
|
|
|
|
COPY --from=git /usr/bin/git /usr/bin/git
|
|
|
|
# redis-server keeps message state when no external Redis is named. tini reaps
|
|
# the python node workers that outlive their pool, which would otherwise pile
|
|
# up as zombies under PID 1. procps is uptime, ps and free for Command nodes,
|
|
# the demo's among them. The git round trip fails the build if the binary
|
|
# above ever needs something this image does not have.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends procps redis-server tini \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& git init -q /tmp/g 2>/dev/null \
|
|
&& git -C /tmp/g -c user.name=x -c user.email=x@x commit -q --allow-empty -m x \
|
|
&& git -C /tmp/g gc --quiet && git -C /tmp/g rev-parse HEAD >/dev/null \
|
|
&& rm -rf /tmp/g
|
|
|
|
# /app/.venv holds the app and nothing of anybody else's, so there is nothing
|
|
# here to adopt: node code gets a venv of its own on the data volume, which is
|
|
# what the Modules screen installs into.
|
|
ENV PYTHONUNBUFFERED=1 \
|
|
PATH="/app/.venv/bin:$PATH" \
|
|
NODE_VENV=managed \
|
|
FRONTEND_DIST=/app/frontend/dist \
|
|
FRONTEND_HOST=http://localhost:8000
|
|
|
|
# The venv installs the app editable, so the source stays at the path it was
|
|
# synced from. uv comes with it: the Modules screen installs with it.
|
|
COPY --from=backend /app /app
|
|
COPY --from=frontend /app/frontend/dist /app/frontend/dist
|
|
COPY ./backend/docker-entrypoint.sh /usr/local/bin/
|
|
|
|
WORKDIR /app/backend/
|
|
VOLUME /data
|
|
EXPOSE 8000
|
|
|
|
# Deep health: fails when the event loop is wedged or Redis is gone, not just
|
|
# when the process is dead.
|
|
HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=5 \
|
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/utils/health/', timeout=4)"
|
|
|
|
ENTRYPOINT ["tini", "--", "docker-entrypoint.sh"]
|
|
|
|
# Single process on purpose: it hosts the flow engine, and a second worker
|
|
# would be a second engine — duplicated subscriptions, cron ticks and webhooks.
|
|
# `serve` makes the secret key and the first admin on first start, and prints
|
|
# the admin's password once.
|
|
CMD ["fluksio", "serve", "--plain", "--host", "0.0.0.0", "--port", "8000", "--data-dir", "/data"]
|