Rust-only server, multi-arch Docker image, CI and docs #2
No files matched your search
@@ -55,6 +55,13 @@ jobs:
|
||||
echo "image=$image" >> "$GITHUB_OUTPUT"
|
||||
echo "Publishing $image:$tag as $REGISTRY_USER"
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
# Only the runtime stage runs emulated; Rust is cross-compiled natively.
|
||||
image: ${{ vars.DOCKERHUB_MIRROR || 'mirror.gcr.io' }}/tonistiigi/binfmt:latest
|
||||
platforms: arm64,arm
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
@@ -75,6 +82,7 @@ jobs:
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version_tagname }}
|
||||
|
||||
+26
-3
@@ -1,16 +1,39 @@
|
||||
FROM rust:1.90-bookworm AS build
|
||||
# Rust always runs natively on the build machine and cross-compiles for the
|
||||
# image platform; only the small runtime stage executes under emulation.
|
||||
FROM --platform=$BUILDPLATFORM rust:1.90-bookworm AS build
|
||||
ARG TARGETPLATFORM
|
||||
# Bundled SQLite is C, so a cross target also needs a C cross compiler and
|
||||
# bookworm's glibc for that architecture (matching the runtime image).
|
||||
RUN set -eu; \
|
||||
case "$TARGETPLATFORM" in \
|
||||
linux/amd64) target=x86_64-unknown-linux-gnu; gnu=x86_64-linux-gnu; arch=amd64 ;; \
|
||||
linux/arm64) target=aarch64-unknown-linux-gnu; gnu=aarch64-linux-gnu; arch=arm64 ;; \
|
||||
linux/arm/v7) target=armv7-unknown-linux-gnueabihf; gnu=arm-linux-gnueabihf; arch=armhf ;; \
|
||||
*) echo "Unsupported platform: $TARGETPLATFORM" >&2; exit 1 ;; \
|
||||
esac; \
|
||||
echo "export RUST_TARGET=$target" > /cross.env; \
|
||||
if [ "$target" != "$(rustc -vV | sed -n 's/^host: //p')" ]; then \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends "gcc-$(echo "$gnu" | tr _ -)" "libc6-dev-$arch-cross"; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
rustup target add "$target"; \
|
||||
env=$(echo "$target" | tr a-z- A-Z_); \
|
||||
echo "export CARGO_TARGET_${env}_LINKER=$gnu-gcc CC_$(echo "$target" | tr - _)=$gnu-gcc AR_$(echo "$target" | tr - _)=$gnu-ar" >> /cross.env; \
|
||||
fi
|
||||
WORKDIR /src
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY src ./src
|
||||
COPY internal/httpapi/web ./internal/httpapi/web
|
||||
RUN cargo build --locked --release
|
||||
RUN . /cross.env \
|
||||
&& cargo build --locked --release --target "$RUST_TARGET" \
|
||||
&& cp "target/$RUST_TARGET/release/balance-server" /balance-server
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
# Keep the original Alpine image's numeric identity so existing volumes remain writable.
|
||||
RUN groupadd --system --gid 101 balance \
|
||||
&& useradd --system --uid 100 --gid balance --home-dir /nonexistent --no-log-init balance \
|
||||
&& mkdir /data && chown balance:balance /data
|
||||
COPY --from=build /src/target/release/balance-server /usr/local/bin/balance-server
|
||||
COPY --from=build /balance-server /usr/local/bin/balance-server
|
||||
USER balance
|
||||
ENV BALANCE_ADDR=:8080 BALANCE_DB_PATH=/data/balance.db
|
||||
EXPOSE 8080
|
||||
|
||||
@@ -14,6 +14,13 @@ are compiled at image build time.
|
||||
The numeric identity remains UID 100 / GID 101, matching the original Alpine Go
|
||||
image, so existing volume permissions are preserved across the switch.
|
||||
|
||||
The image is multi-platform: `linux/amd64`, `linux/arm64` and `linux/arm/v7`
|
||||
(armhf) are published under the same tag, and `docker pull` selects the matching
|
||||
one. The builder stage runs on the build machine's platform and cross-compiles
|
||||
Rust (with GCC cross compilers for bundled SQLite), so only the runtime stage
|
||||
needs QEMU. Build all platforms locally with a `docker-container` buildx builder:
|
||||
`docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 .`
|
||||
|
||||
`docker compose up -d --build` publishes **http://localhost:8123** (host 8123,
|
||||
container 8080). `GET /health` is the liveness endpoint. Direct binary execution
|
||||
uses `:8080` by default. Place a TLS reverse proxy in front of the published port
|
||||
|
||||
Reference in new issue
Block a user