-
Notifications
You must be signed in to change notification settings - Fork 95
Expand file tree
/
Copy pathDockerfile.web
More file actions
97 lines (94 loc) · 5.11 KB
/
Copy pathDockerfile.web
File metadata and controls
97 lines (94 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Duckle Web Editor (#75 phase 2) - the full Duckle studio as a self-hosted web
# app. Local-first: your data + pipelines stay in the mounted workspace; the
# engine runs in the container, nothing is sent to any Duckle-hosted service.
#
# The entrypoint binds 0.0.0.0. With no credential the console starts UNCLAIMED
# and anyone who reaches it can claim administrator for 15 minutes, so a token is
# not optional here. Serve plain HTTP behind a reverse proxy for TLS.
#
# docker build -f Dockerfile.web -t duckle-web .
# docker run -p 8080:8080 -v /path/to/workspace:/workspace \
# -e DUCKLE_CONSOLE_TOKEN=<secret> duckle-web
# open http://localhost:8080
#
# This is the EDITOR, and the editor does not schedule anything: the scheduler
# lives in `duckle-runner serve`. For a deployment that runs pipelines on a cron,
# override the entrypoint:
#
# docker run -p 8080:8080 -v /path/to/workspace:/workspace \
# -e DUCKLE_CONSOLE_TOKEN=<secret> --entrypoint duckle-runner duckle-web \
# serve --host 0.0.0.0 --port 8080 --workspace /workspace
#
# See website/deploy.html for AWS, Azure and Google Cloud recipes.
# 1) Build the browser edition of the frontend (DUCKLE_WEB aliases invoke() to
# the HTTP command bridge and outputs to dist-web).
FROM node:22-bookworm AS frontend
WORKDIR /app/frontend
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm ci || npm install
COPY frontend/ ./
RUN DUCKLE_WEB=1 npm run build
# 2) Build the headless runner that serves the editor + runs the engine.
# The engine shells out to the DuckDB CLI, so no libduckdb is needed here.
FROM rust:1-bookworm AS runner
# Be resilient to flaky/slow crates.io downloads: sparse protocol + retries, and
# disable curl's "too slow" abort (LOW_SPEED_LIMIT=0) so a slow-but-progressing
# download completes instead of failing the whole build.
ENV CARGO_NET_RETRY=10 \
CARGO_NET_GIT_FETCH_WITH_CLI=true \
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse \
CARGO_HTTP_MULTIPLEXING=false \
CARGO_HTTP_LOW_SPEED_LIMIT=0 \
CARGO_HTTP_TIMEOUT=600
WORKDIR /app
# Build deps for the LanceDB / Vortex sidecar (duckle-lance):
# - protoc: lance's proto codegen (pinned to match local builds).
# - clang + libclang: bindgen, pulled transitively (custom-labels) when the
# sidecar's crates join the build set.
# - unixodbc-dev: the runner links the engine with the teradata (ODBC) feature
# on Linux, so the linker needs libodbc (-lodbc) to resolve.
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates unzip clang libclang-dev unixodbc-dev \
&& curl -fsSL -o /tmp/protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v28.3/protoc-28.3-linux-x86_64.zip \
&& unzip -o /tmp/protoc.zip -d /usr/local \
&& rm /tmp/protoc.zip && rm -rf /var/lib/apt/lists/*
ENV PROTOC=/usr/local/bin/protoc
COPY . .
# Cache the crate registry + target dir across builds so a stalled download
# resumes on retry rather than restarting. Both binaries are copied out of the
# (ephemeral) target cache mount so the runtime stage can COPY them. duckle-lance
# is the sidecar that powers src/snk.lancedb + src/snk.vortex (feature parity
# with the desktop app).
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release -p duckle-runner -p duckle-lance \
&& cp /app/target/release/duckle-runner /usr/local/bin/duckle-runner \
&& cp /app/target/release/duckle-lance /usr/local/bin/duckle-lance
# 3) Runtime: runner + the built frontend + the DuckDB CLI.
FROM debian:bookworm-slim
ARG DUCKDB_VERSION=1.5.4
# python3 powers the code.python UDF (per-row Python transform); curl stays for
# the container healthcheck; libodbc2 is the unixODBC runtime the teradata
# (ODBC) engine feature dynamically links against.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl unzip python3 libodbc2 \
&& curl -fsSL -o /tmp/duckdb.zip "https://github.com/duckdb/duckdb/releases/download/v${DUCKDB_VERSION}/duckdb_cli-linux-amd64.zip" \
&& unzip /tmp/duckdb.zip -d /usr/local/bin \
&& rm /tmp/duckdb.zip \
&& apt-get purge -y unzip && apt-get autoremove -y && apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY --from=runner /usr/local/bin/duckle-runner /usr/local/bin/duckle-runner
COPY --from=runner /usr/local/bin/duckle-lance /usr/local/bin/duckle-lance
COPY --from=frontend /app/frontend/dist-web /app/dist-web
# Feature parity with the desktop app: DuckDB engine, the LanceDB/Vortex sidecar,
# and a Python interpreter for code.python are all wired up.
ENV DUCKLE_DUCKDB_BIN=/usr/local/bin/duckdb \
DUCKLE_LANCE_BIN=/usr/local/bin/duckle-lance \
DUCKLE_PYTHON_BIN=/usr/bin/python3
VOLUME ["/workspace"]
EXPOSE 8080
# /healthz needs no credential and says only "ok", so orchestrators can check
# liveness without holding a token and without learning anything.
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 CMD curl -fsS http://127.0.0.1:8080/healthz || exit 1
ENTRYPOINT ["duckle-runner", "web", "--host", "0.0.0.0", "--port", "8080", \
"--dist", "/app/dist-web", "--workspace", "/workspace"]