check / check (push) Successful in 2m42s
entrypoint.sh now switches to the routewatch user (UID 1000) with setpriv instead of runuser. setpriv replaces itself with the daemon, so the daemon is the container's main process and receives docker stop's signal itself. runuser stayed in between, passed the signal on and killed the daemon 2 seconds later, so every stop ended with exit 143. The daemon now gets the whole wait the caller allows, up to its own 60-second limit, and a clean stop exits 0. Taking ownership of the state directory and the MALLOC_ARENA_MAX check still run as root first. Not fixed here: a stop while the feed is flowing can still panic (#34). Model: opus-5-5
104 lines
3.3 KiB
Docker
104 lines
3.3 KiB
Docker
# Lint stage — fast feedback on formatting and lint issues.
|
|
# The golangci-lint image bundles Go, gcc and make, so it can run go vet on
|
|
# the CGO sqlite package and golangci-lint without extra installs.
|
|
# golangci/golangci-lint:v2.7.2 (Go 1.25.5), 2026-09-21
|
|
FROM golangci/golangci-lint@sha256:5d6d5c70a61f1356adfd9dd6316ce286799fefc9d743421356ff1b00842368ba AS lint
|
|
|
|
WORKDIR /src
|
|
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
|
|
COPY . .
|
|
|
|
RUN make fmt-check
|
|
RUN make lint
|
|
|
|
# Build stage
|
|
# golang:1.24-bookworm, 2026-09-21
|
|
FROM golang@sha256:1a6d4452c65dea36aac2e2d606b01b4a029ec90cc1ae53890540ce6173ea77ac AS builder
|
|
|
|
# Install build dependencies (zstd for archive, gcc for CGO/sqlite3)
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
zstd \
|
|
gcc \
|
|
libc6-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /src
|
|
|
|
# Force BuildKit to run the lint stage before compiling or testing.
|
|
COPY --from=lint /src/go.sum /dev/null
|
|
|
|
# Copy everything
|
|
COPY . .
|
|
|
|
# Vendor dependencies (must be after copying source)
|
|
RUN go mod download && go mod vendor
|
|
|
|
# Run the test suite in the build stage: -race needs cgo and the C compiler
|
|
# installed above. The suite is offline (the live-feed test is opt-in).
|
|
RUN make test
|
|
|
|
# Build the binary with CGO enabled (required for sqlite3)
|
|
RUN CGO_ENABLED=1 GOOS=linux go build -o /routewatch ./cmd/routewatch
|
|
|
|
# Create source archive with vendored dependencies
|
|
RUN tar --zstd -cf /routewatch-source.tar.zst \
|
|
--exclude='.git' \
|
|
--exclude='*.tar.zst' \
|
|
.
|
|
|
|
# Runtime stage
|
|
# debian:bookworm-slim, 2026-09-21
|
|
FROM debian@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251
|
|
|
|
# Install runtime dependencies
|
|
# - ca-certificates: for HTTPS connections
|
|
# - curl: for health checks
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Create non-root user
|
|
RUN useradd -r -u 1000 -m routewatch
|
|
|
|
RUN mkdir -p /var/lib/berlin.sneak.app.routewatch && chown routewatch:routewatch /var/lib/berlin.sneak.app.routewatch
|
|
|
|
RUN mkdir /app
|
|
WORKDIR /app
|
|
|
|
# Copy binary and source archive from builder
|
|
COPY --from=builder /routewatch /app/routewatch
|
|
COPY --from=builder /routewatch-source.tar.zst /app/source/routewatch-source.tar.zst
|
|
|
|
# Set ownership
|
|
RUN chown -R routewatch:routewatch /app
|
|
|
|
ENV XDG_DATA_HOME=/var/lib
|
|
|
|
# Cap the Go heap at 1.5 GiB so the runtime collects harder before the
|
|
# container's memory limit is reached. setpriv in the entrypoint preserves this
|
|
# the way it does XDG_DATA_HOME above.
|
|
ENV GOMEMLIMIT=1536MiB
|
|
|
|
# Cap glibc's malloc arenas. The SQLite C library allocates and frees millions
|
|
# of small page-cache chunks from many threads; glibc otherwise creates up to
|
|
# eight arenas per core (hundreds on a large host) and keeps each arena's freed
|
|
# chunks resident, so process RSS climbs far above SQLite's live heap and never
|
|
# comes back down. Two arenas keep that retained memory bounded; database writes
|
|
# are already serialized, so the lost allocator concurrency costs nothing here.
|
|
ENV MALLOC_ARENA_MAX=2
|
|
|
|
# Expose HTTP port
|
|
EXPOSE 8080
|
|
|
|
COPY ./entrypoint.sh /entrypoint.sh
|
|
|
|
# Health check using the health endpoint, on the port PORT names
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD curl -sf "http://localhost:${PORT:-8080}/.well-known/healthcheck.json" || exit 1
|
|
|
|
ENTRYPOINT ["/bin/bash", "/entrypoint.sh" ]
|