Compare commits
1
Commits
release/0.1.0
...
rust
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed90a2a5a6 |
+3
-2
@@ -82,8 +82,9 @@ TURN_TIMEOUT_SECONDS=30
|
||||
# OpenTelemetry (compiled in by default). Off unless OTEL_ENABLED is
|
||||
# truthy; exports traces (HTTP/WebSocket) and process metrics, plus custom
|
||||
# tavolo.active_users / tavolo.ongoing_games gauges and logs over OTLP/HTTP.
|
||||
# The endpoint defaults to the standard OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
# (localhost:4318) when unset.
|
||||
# The endpoint defaults to http://localhost:4318 when unset and must use
|
||||
# http:// (the OTLP client is built without a TLS backend; terminate TLS in
|
||||
# front of a remote collector).
|
||||
#OTEL_ENABLED=true
|
||||
#OTEL_SERVICE_NAME=tavolo
|
||||
#OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
|
||||
|
||||
@@ -43,3 +43,4 @@ uuid = { version = "1", features = ["v4", "serde"] }
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
strip = "symbols"
|
||||
|
||||
+29
-30
@@ -3,13 +3,22 @@
|
||||
# Sycamore/WASM frontend). The Docker build context is the REPOSITORY ROOT
|
||||
# (see docker-compose.yml) so this single image assembles both parts:
|
||||
#
|
||||
# web-builder rust + trunk -> compiles web/ into web/dist
|
||||
# rust-builder rust -> compiles server/ into the tavolo binary
|
||||
# runtime debian slim -> the binary + the compiled frontend
|
||||
# web-builder rust + trunk -> compiles web/ into web/dist
|
||||
# rust-builder rust (alpine) -> compiles server/ into a static musl binary
|
||||
# runtime scratch -> static binary + compiled frontend
|
||||
#
|
||||
# The backend embeds its sqlx migrations, serves /static and /assets itself
|
||||
# and hosts the SPA shell, so the runtime image needs no Python, Granian or
|
||||
# migration files.
|
||||
# The runtime is a scratch image: the backend is a fully static musl binary,
|
||||
# embeds its sqlx migrations and serves /static, /assets and the SPA shell
|
||||
# itself, so no shell, package manager or libc is needed.
|
||||
#
|
||||
# TLS trust model: the OIDC client (reqwest 0.12) and Postgres (sqlx) validate
|
||||
# against Mozilla's roots embedded in the binary by the webpki-roots crate, so
|
||||
# no certificate files exist in the image. The OTLP exporter is built without
|
||||
# a TLS backend, so OTEL_EXPORTER_OTLP_ENDPOINT must be http:// (terminate TLS
|
||||
# in front of the collector if needed).
|
||||
#
|
||||
# There is deliberately no HEALTHCHECK (no curl in scratch): Kubernetes
|
||||
# probes /api/health over HTTP, and Compose gates on its dependency checks.
|
||||
#
|
||||
# Build and run it with (from the repository root):
|
||||
# docker compose up --build
|
||||
@@ -47,8 +56,11 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/web/target \
|
||||
trunk build --release --public-url /static/
|
||||
|
||||
# --- Rust builder ------------------------------------------------------------
|
||||
FROM rust:1-slim-bookworm AS rust-builder
|
||||
# --- Rust builder (static musl) ----------------------------------------------
|
||||
FROM rust:1-alpine AS rust-builder
|
||||
|
||||
# ring (the only C dependency) compiles with the musl toolchain from build-base.
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
@@ -59,37 +71,24 @@ COPY server/crates ./crates
|
||||
# and the binary is copied out of the mount into the image layer.
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/build/target \
|
||||
cargo build --release --locked \
|
||||
&& cp target/release/tavolo /tavolo
|
||||
cargo build --release --locked --target x86_64-unknown-linux-musl \
|
||||
&& cp target/x86_64-unknown-linux-musl/release/tavolo /tavolo
|
||||
|
||||
# --- Runtime -----------------------------------------------------------------
|
||||
FROM debian:bookworm-slim
|
||||
# --- Runtime (scratch) --------------------------------------------------------
|
||||
FROM scratch
|
||||
|
||||
# The slim base ships no CA store; seed it from the builder so the HTTPS apt
|
||||
# mirrors (and later, outbound TLS) can be verified.
|
||||
COPY --from=web-builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
{ sed -i 's|http://deb.debian.org|https://deb.debian.org|g' /etc/apt/sources.list 2>/dev/null || true; } \
|
||||
&& { sed -i 's|http://deb.debian.org|https://deb.debian.org|g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || true; } \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates curl tzdata \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& addgroup --system app && adduser --system --ingroup app app
|
||||
|
||||
COPY --from=rust-builder /tavolo /usr/local/bin/tavolo
|
||||
# Fully static musl binary; no libc, shell or package manager in the image.
|
||||
# Installed on PATH so `tavolo migrate` / `tavolo serve` work as commands.
|
||||
COPY --from=rust-builder --chmod=755 /tavolo /usr/local/bin/tavolo
|
||||
# The compiled single-page application: hashed js/wasm/css under /static and
|
||||
# the card images under /assets (mounted by the server from STATIC_DIR).
|
||||
COPY --from=web-builder /web/dist /app/web/dist
|
||||
|
||||
ENV STATIC_DIR=/app/web/dist
|
||||
|
||||
USER app
|
||||
# Numeric user: scratch has no /etc/passwd and Kubernetes verifies runAsNonRoot.
|
||||
USER 10001:10001
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
|
||||
CMD curl -fsS http://127.0.0.1:8080/api/health || exit 1
|
||||
|
||||
CMD ["tavolo", "serve"]
|
||||
|
||||
+17
-2
@@ -50,6 +50,14 @@ WebSocket endpoint.
|
||||
|
||||
OpenAPI documentation is served at `/api/docs` (`/api/openapi.json`).
|
||||
|
||||
The release image is a **scratch image** containing a fully static musl
|
||||
binary and the compiled frontend: no shell, package manager or libc. It runs
|
||||
as the numeric user `10001:10001` and has no Docker `HEALTHCHECK` (scratch has
|
||||
no `curl`); Kubernetes probes `/api/health` over HTTP, and Compose gates on
|
||||
the Postgres/Redis/OIDC dependency checks. To debug a running container use
|
||||
`docker debug` (Docker Desktop) or `kubectl debug` with an ephemeral
|
||||
container.
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration comes from environment variables (see `.env.example`):
|
||||
@@ -87,7 +95,7 @@ All configuration comes from environment variables (see `.env.example`):
|
||||
| `CORS_MAX_AGE` | `600` | Seconds browsers may cache the preflight response |
|
||||
| `OTEL_ENABLED` | `false` | `1`/`true`/`yes`/`on` enable OpenTelemetry traces, metrics and logs (compiled in by default) |
|
||||
| `OTEL_SERVICE_NAME` | `tavolo` | `service.name` resource attribute of the exported telemetry |
|
||||
| `OTEL_EXPORTER_OTLP_ENDPOINT` | unset | Base URL of an OTLP/HTTP collector (e.g. `http://localhost:4318`); unset uses the exporter default |
|
||||
| `OTEL_EXPORTER_OTLP_ENDPOINT` | unset | Base URL of an OTLP/HTTP collector (e.g. `http://localhost:4318`; unset uses that default). Must be `http://`: the OTLP client is built without a TLS backend |
|
||||
| `OTEL_EXPORTER_OTLP_HEADERS` | unset | Comma-separated `key=value` headers sent to the collector (e.g. authentication) |
|
||||
| `OTEL_EXCLUDED_PATHS` | `/api/health` | Comma-separated paths excluded from tracing and metrics (exact matches) |
|
||||
| `OTEL_METRIC_EXPORT_INTERVAL_MILLIS` | `60000` | How often the OTLP metric reader exports |
|
||||
@@ -161,6 +169,13 @@ cluster-wide counts, in-memory stores the process's own). With multiple
|
||||
replicas each process reports the same global counts: aggregate with `max`,
|
||||
not `sum`.
|
||||
|
||||
The OTLP client is built without a TLS backend, so
|
||||
`OTEL_EXPORTER_OTLP_ENDPOINT` must use `http://`; terminate TLS in front of
|
||||
the collector (or run a local sidecar) if the collector is remote. The
|
||||
OIDC/Postgres clients do not use this client and validate HTTPS against
|
||||
Mozilla's roots embedded in the binary (no certificate files are needed, and
|
||||
private CAs are not trusted by them).
|
||||
|
||||
## Data model
|
||||
|
||||
### Redis (live games)
|
||||
@@ -411,7 +426,7 @@ crates/tavolo-app/ # deployable app: composition root (own crate)
|
||||
src/{main,app,config,cors,logging,static_files,telemetry}.rs
|
||||
migrations/ # embedded sqlx migrations
|
||||
dev/mockoauth/ # dev OIDC provider image (compose)
|
||||
Dockerfile # web-builder + cargo + slim runtime
|
||||
Dockerfile # web-builder + musl cargo + scratch runtime
|
||||
.env.example
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user