mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-17 01:30:33 +00:00
Compare commits
2 Commits
1.0.0-alph
...
1.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9c9a2d1f2 | ||
|
|
3ebab98d2d |
130
Dockerfile
130
Dockerfile
@@ -1,58 +1,56 @@
|
||||
# Multi-stage build for RustFS production image
|
||||
|
||||
# Build stage: Download and extract RustFS binary
|
||||
# -------------------
|
||||
# Build stage
|
||||
# -------------------
|
||||
FROM alpine:3.22 AS build
|
||||
|
||||
# Build arguments for platform and release
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE=latest
|
||||
|
||||
# Install minimal dependencies for downloading and extracting
|
||||
RUN apk add --no-cache ca-certificates curl unzip
|
||||
|
||||
# Create build directory
|
||||
WORKDIR /build
|
||||
|
||||
# Set architecture-specific variables
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
echo "x86_64-musl" > /tmp/arch; \
|
||||
elif [ "$TARGETARCH" = "arm64" ]; then \
|
||||
echo "aarch64-musl" > /tmp/arch; \
|
||||
# Download and extract release package matching current TARGETARCH
|
||||
# - If RELEASE=latest: take first tag_name from /releases (may include pre-releases)
|
||||
# - Otherwise use specified tag (e.g. v0.1.2)
|
||||
RUN set -eux; \
|
||||
case "$TARGETARCH" in \
|
||||
amd64) ARCH_SUBSTR="x86_64-musl" ;; \
|
||||
arm64) ARCH_SUBSTR="aarch64-musl" ;; \
|
||||
*) echo "Unsupported TARGETARCH=$TARGETARCH" >&2; exit 1 ;; \
|
||||
esac; \
|
||||
if [ "$RELEASE" = "latest" ]; then \
|
||||
TAG="$(curl -fsSL https://api.github.com/repos/rustfs/rustfs/releases \
|
||||
| grep -o '"tag_name": "[^"]*"' | cut -d'"' -f4 | head -n 1)"; \
|
||||
else \
|
||||
echo "unsupported" > /tmp/arch; \
|
||||
fi
|
||||
RUN ARCH=$(cat /tmp/arch) && \
|
||||
if [ "$ARCH" = "unsupported" ]; then \
|
||||
echo "Unsupported architecture: $TARGETARCH" && exit 1; \
|
||||
fi && \
|
||||
if [ "${RELEASE}" = "latest" ]; then \
|
||||
# For latest, download from GitHub releases using the -latest suffix
|
||||
PACKAGE_NAME="rustfs-linux-${ARCH}-latest.zip"; \
|
||||
# Use GitHub API to get the latest release URL
|
||||
LATEST_RELEASE_URL=$(curl -s https://api.github.com/repos/rustfs/rustfs/releases/latest | grep -o '"browser_download_url": "[^"]*'"${PACKAGE_NAME}"'"' | cut -d'"' -f4 | head -1); \
|
||||
if [ -z "$LATEST_RELEASE_URL" ]; then \
|
||||
echo "Failed to find latest release for ${PACKAGE_NAME}" >&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
DOWNLOAD_URL="$LATEST_RELEASE_URL"; \
|
||||
else \
|
||||
# For specific versions, construct the GitHub release URL directly
|
||||
# RELEASE is the GitHub release tag (e.g., "1.0.0-alpha.42")
|
||||
# VERSION is the version in filename (e.g., "v1.0.0-alpha.42")
|
||||
VERSION="v${RELEASE}"; \
|
||||
PACKAGE_NAME="rustfs-linux-${ARCH}-${VERSION}.zip"; \
|
||||
DOWNLOAD_URL="https://github.com/rustfs/rustfs/releases/download/${RELEASE}/${PACKAGE_NAME}"; \
|
||||
fi && \
|
||||
echo "Downloading ${PACKAGE_NAME} from ${DOWNLOAD_URL}" >&2 && \
|
||||
curl -f -L "${DOWNLOAD_URL}" -o rustfs.zip && \
|
||||
unzip rustfs.zip -d /build && \
|
||||
chmod +x /build/rustfs && \
|
||||
rm rustfs.zip || { echo "Failed to download or extract ${PACKAGE_NAME}" >&2; exit 1; }
|
||||
TAG="$RELEASE"; \
|
||||
fi; \
|
||||
echo "Using tag: $TAG (arch pattern: $ARCH_SUBSTR)"; \
|
||||
# Find download URL in assets list for this tag that contains arch substring and ends with .zip
|
||||
URL="$(curl -fsSL "https://api.github.com/repos/rustfs/rustfs/releases/tags/$TAG" \
|
||||
| grep -o "\"browser_download_url\": \"[^\"]*${ARCH_SUBSTR}[^\"]*\\.zip\"" \
|
||||
| cut -d'"' -f4 | head -n 1)"; \
|
||||
if [ -z "$URL" ]; then echo "Failed to locate release asset for $ARCH_SUBSTR at tag $TAG" >&2; exit 1; fi; \
|
||||
echo "Downloading: $URL"; \
|
||||
curl -fL "$URL" -o rustfs.zip; \
|
||||
unzip -q rustfs.zip -d /build; \
|
||||
# If binary is not in root directory, try to locate and move from zip to /build/rustfs
|
||||
if [ ! -x /build/rustfs ]; then \
|
||||
BIN_PATH="$(unzip -Z -1 rustfs.zip | grep -E '(^|/)rustfs$' | head -n 1 || true)"; \
|
||||
if [ -n "$BIN_PATH" ]; then \
|
||||
mkdir -p /build/.tmp && unzip -q rustfs.zip "$BIN_PATH" -d /build/.tmp && \
|
||||
mv "/build/.tmp/$BIN_PATH" /build/rustfs; \
|
||||
fi; \
|
||||
fi; \
|
||||
[ -x /build/rustfs ] || { echo "rustfs binary not found in asset" >&2; exit 1; }; \
|
||||
chmod +x /build/rustfs; \
|
||||
rm -rf rustfs.zip /build/.tmp || true
|
||||
|
||||
# Runtime stage: Configure runtime environment
|
||||
FROM alpine:3.22.1
|
||||
|
||||
# Build arguments and labels
|
||||
# -------------------
|
||||
# Runtime stage
|
||||
# -------------------
|
||||
FROM alpine:3.22
|
||||
|
||||
ARG RELEASE=latest
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
@@ -60,7 +58,7 @@ ARG VCS_REF
|
||||
LABEL name="RustFS" \
|
||||
vendor="RustFS Team" \
|
||||
maintainer="RustFS Team <dev@rustfs.com>" \
|
||||
version="${RELEASE}" \
|
||||
version="v${RELEASE#v}" \
|
||||
release="${RELEASE}" \
|
||||
build-date="${BUILD_DATE}" \
|
||||
vcs-ref="${VCS_REF}" \
|
||||
@@ -69,43 +67,37 @@ LABEL name="RustFS" \
|
||||
url="https://rustfs.com" \
|
||||
license="Apache-2.0"
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.20/community" >> /etc/apk/repositories && \
|
||||
apk update && \
|
||||
apk add --no-cache ca-certificates bash gosu coreutils shadow && \
|
||||
# Install only runtime requirements: certificates and coreutils (provides chroot --userspec)
|
||||
RUN apk add --no-cache ca-certificates coreutils && \
|
||||
addgroup -g 1000 rustfs && \
|
||||
adduser -u 1000 -G rustfs -s /bin/bash -D rustfs
|
||||
adduser -u 1000 -G rustfs -s /sbin/nologin -D rustfs
|
||||
|
||||
# Copy CA certificates and RustFS binary from build stage
|
||||
# Copy binary and entry script (ensure fixed entrypoint.sh exists in repository)
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /build/rustfs /usr/bin/rustfs
|
||||
|
||||
# Copy entry point script
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Set permissions
|
||||
RUN chmod +x /usr/bin/rustfs /entrypoint.sh && \
|
||||
mkdir -p /data /logs && \
|
||||
chown rustfs:rustfs /data /logs && \
|
||||
chmod 700 /data /logs
|
||||
chmod 0750 /data /logs
|
||||
|
||||
# Environment variables (credentials should be set via environment or secrets)
|
||||
ENV RUSTFS_ADDRESS=:9000 \
|
||||
RUSTFS_ACCESS_KEY=rustfsadmin \
|
||||
RUSTFS_SECRET_KEY=rustfsadmin \
|
||||
RUSTFS_CONSOLE_ENABLE=true \
|
||||
RUSTFS_VOLUMES=/data \
|
||||
RUST_LOG=warn \
|
||||
RUSTFS_OBS_LOG_DIRECTORY=/logs \
|
||||
RUSTFS_SINKS_FILE_PATH=/logs
|
||||
# Default environment (can be overridden in docker run/compose)
|
||||
ENV RUSTFS_ADDRESS=":9000" \
|
||||
RUSTFS_ACCESS_KEY="rustfsadmin" \
|
||||
RUSTFS_SECRET_KEY="rustfsadmin" \
|
||||
RUSTFS_CONSOLE_ENABLE="true" \
|
||||
RUSTFS_VOLUMES="/data" \
|
||||
RUST_LOG="warn" \
|
||||
RUSTFS_OBS_LOG_DIRECTORY="/logs" \
|
||||
RUSTFS_SINKS_FILE_PATH="/logs" \
|
||||
RUSTFS_USERNAME="rustfs" \
|
||||
RUSTFS_GROUPNAME="rustfs" \
|
||||
RUSTFS_UID="1000" \
|
||||
RUSTFS_GID="1000"
|
||||
|
||||
# Expose port
|
||||
EXPOSE 9000
|
||||
|
||||
# Volumes for data and logs
|
||||
VOLUME ["/data", "/logs"]
|
||||
|
||||
# Set entry point
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["/usr/bin/rustfs"]
|
||||
|
||||
|
||||
@@ -1,80 +1,88 @@
|
||||
# syntax=docker/dockerfile:1.6
|
||||
# Multi-stage Dockerfile for RustFS - LOCAL DEVELOPMENT ONLY
|
||||
#
|
||||
# ⚠️ IMPORTANT: This Dockerfile is for local development and testing only.
|
||||
# ⚠️ It builds RustFS from source code and is NOT used in CI/CD pipelines.
|
||||
# ⚠️ CI/CD pipeline uses pre-built binaries from Dockerfile instead.
|
||||
# IMPORTANT: This Dockerfile builds RustFS from source for local development and testing.
|
||||
# CI/CD uses the production Dockerfile with prebuilt binaries instead.
|
||||
#
|
||||
# Usage for local development:
|
||||
# Example:
|
||||
# docker build -f Dockerfile.source -t rustfs:dev-local .
|
||||
# docker run --rm -p 9000:9000 rustfs:dev-local
|
||||
#
|
||||
# Supports cross-compilation for amd64 and arm64 architectures
|
||||
# Supports cross-compilation for amd64 and arm64 via TARGETPLATFORM.
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILDPLATFORM
|
||||
|
||||
# -----------------------------
|
||||
# Build stage
|
||||
FROM --platform=$BUILDPLATFORM rust:1.88-bookworm AS builder
|
||||
# -----------------------------
|
||||
FROM rust:1.88-bookworm AS builder
|
||||
|
||||
# Re-declare build arguments after FROM (required for multi-stage builds)
|
||||
# Re-declare args after FROM
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILDPLATFORM
|
||||
|
||||
# Debug: Print platform information
|
||||
RUN echo "🐳 Build Info: BUILDPLATFORM=$BUILDPLATFORM, TARGETPLATFORM=$TARGETPLATFORM"
|
||||
# Debug: print platforms
|
||||
RUN echo "Build info -> BUILDPLATFORM=${BUILDPLATFORM}, TARGETPLATFORM=${TARGETPLATFORM}"
|
||||
|
||||
# Install required build dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
wget \
|
||||
git \
|
||||
# Install build toolchain and headers
|
||||
# Use distro packages for protoc/flatc to avoid host-arch mismatch
|
||||
RUN set -eux; \
|
||||
export DEBIAN_FRONTEND=noninteractive; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
curl \
|
||||
unzip \
|
||||
gcc \
|
||||
git \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
lld \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
protobuf-compiler \
|
||||
flatbuffers-compiler; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Note: sccache removed for simpler builds
|
||||
|
||||
# Install cross-compilation tools for ARM64
|
||||
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
|
||||
apt-get update && \
|
||||
apt-get install -y gcc-aarch64-linux-gnu && \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
# Optional: cross toolchain for aarch64 (only when targeting linux/arm64)
|
||||
RUN set -eux; \
|
||||
if [ "${TARGETPLATFORM:-linux/amd64}" = "linux/arm64" ]; then \
|
||||
export DEBIAN_FRONTEND=noninteractive; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Install protoc
|
||||
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-x86_64.zip \
|
||||
&& unzip protoc-31.1-linux-x86_64.zip -d protoc3 \
|
||||
&& mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \
|
||||
&& mv protoc3/include/* /usr/local/include/ && rm -rf protoc-31.1-linux-x86_64.zip protoc3
|
||||
|
||||
# Install flatc
|
||||
RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \
|
||||
&& unzip Linux.flatc.binary.g++-13.zip \
|
||||
&& mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip
|
||||
|
||||
# Set up Rust targets based on platform
|
||||
RUN set -e && \
|
||||
PLATFORM="${TARGETPLATFORM:-linux/amd64}" && \
|
||||
echo "🎯 Setting up Rust target for platform: $PLATFORM" && \
|
||||
case "$PLATFORM" in \
|
||||
"linux/amd64") rustup target add x86_64-unknown-linux-gnu ;; \
|
||||
"linux/arm64") rustup target add aarch64-unknown-linux-gnu ;; \
|
||||
*) echo "❌ Unsupported platform: $PLATFORM" && exit 1 ;; \
|
||||
# Add Rust targets based on TARGETPLATFORM
|
||||
RUN set -eux; \
|
||||
case "${TARGETPLATFORM:-linux/amd64}" in \
|
||||
linux/amd64) rustup target add x86_64-unknown-linux-gnu ;; \
|
||||
linux/arm64) rustup target add aarch64-unknown-linux-gnu ;; \
|
||||
*) echo "Unsupported TARGETPLATFORM=${TARGETPLATFORM}" >&2; exit 1 ;; \
|
||||
esac
|
||||
|
||||
# Set up environment for cross-compilation
|
||||
# Cross-compilation environment (used only when targeting aarch64)
|
||||
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
|
||||
ENV CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
|
||||
ENV CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
|
||||
|
||||
WORKDIR /usr/src/rustfs
|
||||
|
||||
# Copy all source code
|
||||
# Layered copy to maximize caching:
|
||||
# 1) top-level manifests
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
# 2) workspace member manifests (adjust if workspace layout changes)
|
||||
COPY rustfs/Cargo.toml rustfs/Cargo.toml
|
||||
COPY crates/*/Cargo.toml crates/
|
||||
COPY cli/rustfs-gui/Cargo.toml cli/rustfs-gui/Cargo.toml
|
||||
|
||||
# Pre-fetch dependencies for better caching
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
cargo fetch --locked || true
|
||||
|
||||
# 3) copy full sources (this is the main cache invalidation point)
|
||||
COPY . .
|
||||
|
||||
# Configure cargo for optimized builds
|
||||
# Cargo build configuration for lean release artifacts
|
||||
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true \
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse \
|
||||
CARGO_INCREMENTAL=0 \
|
||||
@@ -82,75 +90,92 @@ ENV CARGO_NET_GIT_FETCH_WITH_CLI=true \
|
||||
CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO=off \
|
||||
CARGO_PROFILE_RELEASE_STRIP=symbols
|
||||
|
||||
# Generate protobuf code
|
||||
RUN cargo run --bin gproto
|
||||
# Generate protobuf/flatbuffers code (uses protoc/flatc from distro)
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
--mount=type=cache,target=/usr/src/rustfs/target \
|
||||
cargo run --bin gproto
|
||||
|
||||
# Build the actual application with optimizations
|
||||
RUN case "$TARGETPLATFORM" in \
|
||||
"linux/amd64") \
|
||||
echo "🔨 Building for amd64..." && \
|
||||
rustup target add x86_64-unknown-linux-gnu && \
|
||||
cargo build --release --target x86_64-unknown-linux-gnu --bin rustfs -j $(nproc) && \
|
||||
cp target/x86_64-unknown-linux-gnu/release/rustfs /usr/local/bin/rustfs \
|
||||
;; \
|
||||
"linux/arm64") \
|
||||
echo "🔨 Building for arm64..." && \
|
||||
rustup target add aarch64-unknown-linux-gnu && \
|
||||
cargo build --release --target aarch64-unknown-linux-gnu --bin rustfs -j $(nproc) && \
|
||||
cp target/aarch64-unknown-linux-gnu/release/rustfs /usr/local/bin/rustfs \
|
||||
;; \
|
||||
*) \
|
||||
echo "❌ Unsupported platform: $TARGETPLATFORM" && exit 1 \
|
||||
;; \
|
||||
# Build RustFS (target depends on TARGETPLATFORM)
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/usr/local/cargo/git \
|
||||
--mount=type=cache,target=/usr/src/rustfs/target \
|
||||
set -eux; \
|
||||
case "${TARGETPLATFORM:-linux/amd64}" in \
|
||||
linux/amd64) \
|
||||
echo "Building for x86_64-unknown-linux-gnu"; \
|
||||
cargo build --release --locked --target x86_64-unknown-linux-gnu --bin rustfs -j "$(nproc)"; \
|
||||
install -m 0755 target/x86_64-unknown-linux-gnu/release/rustfs /usr/local/bin/rustfs \
|
||||
;; \
|
||||
linux/arm64) \
|
||||
echo "Building for aarch64-unknown-linux-gnu"; \
|
||||
cargo build --release --locked --target aarch64-unknown-linux-gnu --bin rustfs -j "$(nproc)"; \
|
||||
install -m 0755 target/aarch64-unknown-linux-gnu/release/rustfs /usr/local/bin/rustfs \
|
||||
;; \
|
||||
*) \
|
||||
echo "Unsupported TARGETPLATFORM=${TARGETPLATFORM}" >&2; exit 1 \
|
||||
;; \
|
||||
esac
|
||||
|
||||
# Runtime stage - Ubuntu minimal for better compatibility
|
||||
# -----------------------------
|
||||
# Runtime stage (Ubuntu minimal)
|
||||
# -----------------------------
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
|
||||
LABEL name="RustFS (dev-local)" \
|
||||
maintainer="RustFS Team" \
|
||||
build-date="${BUILD_DATE}" \
|
||||
vcs-ref="${VCS_REF}" \
|
||||
description="RustFS - local development image built from source (NOT for production)."
|
||||
|
||||
# Minimal runtime deps: certificates + tzdata + coreutils (for chroot --userspec)
|
||||
RUN set -eux; \
|
||||
export DEBIAN_FRONTEND=noninteractive; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
wget \
|
||||
coreutils \
|
||||
passwd \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
coreutils; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create rustfs user and group
|
||||
RUN groupadd -g 1000 rustfs && \
|
||||
useradd -d /app -g rustfs -u 1000 -s /bin/bash rustfs
|
||||
# Create a conventional runtime user/group (final switch happens in entrypoint via chroot --userspec)
|
||||
RUN set -eux; \
|
||||
groupadd -g 1000 rustfs; \
|
||||
useradd -u 1000 -g rustfs -M -s /usr/sbin/nologin rustfs
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Create data directories
|
||||
RUN mkdir -p /data/rustfs{0,1,2,3} && \
|
||||
chown -R rustfs:rustfs /data /app
|
||||
# Prepare data/log directories with sane defaults
|
||||
RUN set -eux; \
|
||||
mkdir -p /data /logs; \
|
||||
chown -R rustfs:rustfs /data /logs /app; \
|
||||
chmod 0750 /data /logs
|
||||
|
||||
# Copy binary from builder stage
|
||||
COPY --from=builder /usr/local/bin/rustfs /app/rustfs
|
||||
RUN chmod +x /app/rustfs && chown rustfs:rustfs /app/rustfs
|
||||
|
||||
# Copy entrypoint script
|
||||
# Copy the freshly built binary and the entrypoint
|
||||
COPY --from=builder /usr/local/bin/rustfs /usr/bin/rustfs
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
RUN chmod +x /usr/bin/rustfs /entrypoint.sh
|
||||
|
||||
# Switch to non-root user
|
||||
USER rustfs
|
||||
# Default environment (override in docker run/compose as needed)
|
||||
ENV RUSTFS_ADDRESS=":9000" \
|
||||
RUSTFS_ACCESS_KEY="rustfsadmin" \
|
||||
RUSTFS_SECRET_KEY="rustfsadmin" \
|
||||
RUSTFS_CONSOLE_ENABLE="true" \
|
||||
RUSTFS_VOLUMES="/data" \
|
||||
RUST_LOG="warn" \
|
||||
RUSTFS_OBS_LOG_DIRECTORY="/logs" \
|
||||
RUSTFS_SINKS_FILE_PATH="/logs" \
|
||||
RUSTFS_USERNAME="rustfs" \
|
||||
RUSTFS_GROUPNAME="rustfs" \
|
||||
RUSTFS_UID="1000" \
|
||||
RUSTFS_GID="1000"
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 9000
|
||||
VOLUME ["/data", "/logs"]
|
||||
|
||||
# Environment variables
|
||||
ENV RUSTFS_ACCESS_KEY=rustfsadmin \
|
||||
RUSTFS_SECRET_KEY=rustfsadmin \
|
||||
RUSTFS_ADDRESS=":9000" \
|
||||
RUSTFS_CONSOLE_ENABLE=true \
|
||||
RUSTFS_VOLUMES=/data \
|
||||
RUST_LOG=warn
|
||||
|
||||
# Volume for data
|
||||
VOLUME ["/data"]
|
||||
|
||||
# Set entrypoint and default command
|
||||
# Keep root here; entrypoint will drop privileges using chroot --userspec
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["/app/rustfs"]
|
||||
CMD ["/usr/bin/rustfs"]
|
||||
|
||||
6
Makefile
6
Makefile
@@ -23,7 +23,7 @@ fmt-check:
|
||||
.PHONY: clippy
|
||||
clippy:
|
||||
@echo "🔍 Running clippy checks..."
|
||||
cargo clippy --fix --allow-dirty
|
||||
cargo clippy --fix --allow-dirty
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
.PHONY: check
|
||||
@@ -210,7 +210,9 @@ docker-build-production:
|
||||
docker-build-source:
|
||||
@echo "🏗️ Building single-architecture source Docker image..."
|
||||
@echo "💡 Consider using 'make docker-dev-local' for multi-arch support"
|
||||
$(DOCKER_CLI) build -f $(DOCKERFILE_SOURCE) -t rustfs:source .
|
||||
DOCKER_BUILDKIT=1 $(DOCKER_CLI) build \
|
||||
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
||||
-f $(DOCKERFILE_SOURCE) -t rustfs:source .
|
||||
|
||||
# ========================================================================================
|
||||
# Development Environment
|
||||
|
||||
165
entrypoint.sh
Normal file → Executable file
165
entrypoint.sh
Normal file → Executable file
@@ -1,104 +1,81 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
APP_USER=rustfs
|
||||
APP_GROUP=rustfs
|
||||
APP_UID=${PUID:-1000}
|
||||
APP_GID=${PGID:-1000}
|
||||
|
||||
# Parse RUSTFS_VOLUMES into array (support space, comma, tab as separator)
|
||||
VOLUME_RAW="${RUSTFS_VOLUMES:-/data}"
|
||||
# Replace comma and tab with space, then split
|
||||
VOLUME_RAW=$(echo "$VOLUME_RAW" | tr ',\t' ' ')
|
||||
read -ra ALL_VOLUMES <<< "$VOLUME_RAW"
|
||||
|
||||
# Only keep local volumes (start with /, not http/https)
|
||||
LOCAL_VOLUMES=()
|
||||
for vol in "${ALL_VOLUMES[@]}"; do
|
||||
if [[ "$vol" =~ ^/ ]] && [[ ! "$vol" =~ ^https?:// ]]; then
|
||||
# Not a URL (http/https), just a local path
|
||||
LOCAL_VOLUMES+=("$vol")
|
||||
fi
|
||||
# If it's a URL (http/https), skip
|
||||
# If it's an empty string, skip
|
||||
# If it's a local path, keep
|
||||
# (We don't support other protocols here)
|
||||
done
|
||||
|
||||
# Always ensure /logs is included for permission fix
|
||||
include_logs=1
|
||||
for vol in "${LOCAL_VOLUMES[@]}"; do
|
||||
if [ "$vol" = "/logs" ]; then
|
||||
include_logs=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ $include_logs -eq 1 ]; then
|
||||
LOCAL_VOLUMES+=("/logs")
|
||||
# 1) Normalize command:
|
||||
# - No arguments: default to execute rustfs
|
||||
# - First argument starts with '-': treat as rustfs arguments, auto-prefix rustfs
|
||||
# - First argument is 'rustfs': replace with absolute path to avoid PATH interference
|
||||
if [ $# -eq 0 ] || [ "${1#-}" != "$1" ]; then
|
||||
set -- /usr/bin/rustfs "$@"
|
||||
elif [ "$1" = "rustfs" ]; then
|
||||
shift
|
||||
set -- /usr/bin/rustfs "$@"
|
||||
fi
|
||||
|
||||
# Try to update rustfs UID/GID if needed (requires root and shadow tools)
|
||||
update_user_group_ids() {
|
||||
local uid="$1"
|
||||
local gid="$2"
|
||||
local user="$3"
|
||||
local group="$4"
|
||||
local updated=0
|
||||
if [ "$(id -u "$user")" != "$uid" ]; then
|
||||
if command -v usermod >/dev/null 2>&1; then
|
||||
echo "🔧 Updating UID of $user to $uid"
|
||||
usermod -u "$uid" "$user"
|
||||
updated=1
|
||||
# 2) Parse and create local mount directories (ignore http/https), ensure /logs is included
|
||||
VOLUME_RAW="${RUSTFS_VOLUMES:-/data}"
|
||||
# Convert comma/tab to space
|
||||
VOLUME_LIST=$(echo "$VOLUME_RAW" | tr ',\t' ' ')
|
||||
LOCAL_VOLUMES=""
|
||||
for vol in $VOLUME_LIST; do
|
||||
case "$vol" in
|
||||
/*)
|
||||
case "$vol" in
|
||||
http://*|https://*) : ;;
|
||||
*) LOCAL_VOLUMES="$LOCAL_VOLUMES $vol" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
: # skip non-local paths
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# Ensure /logs is included
|
||||
case " $LOCAL_VOLUMES " in
|
||||
*" /logs "*) : ;;
|
||||
*) LOCAL_VOLUMES="$LOCAL_VOLUMES /logs" ;;
|
||||
esac
|
||||
|
||||
echo "Initializing mount directories:$LOCAL_VOLUMES"
|
||||
for vol in $LOCAL_VOLUMES; do
|
||||
if [ ! -d "$vol" ]; then
|
||||
echo " mkdir -p $vol"
|
||||
mkdir -p "$vol"
|
||||
# If target user is specified, try to set directory owner to that user (non-recursive to avoid large disk overhead)
|
||||
if [ -n "$RUSTFS_UID" ] && [ -n "$RUSTFS_GID" ]; then
|
||||
chown "$RUSTFS_UID:$RUSTFS_GID" "$vol" 2>/dev/null || true
|
||||
elif [ -n "$RUSTFS_USERNAME" ] && [ -n "$RUSTFS_GROUPNAME" ]; then
|
||||
chown "$RUSTFS_USERNAME:$RUSTFS_GROUPNAME" "$vol" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
if [ "$(id -g "$group")" != "$gid" ]; then
|
||||
if command -v groupmod >/dev/null 2>&1; then
|
||||
echo "🔧 Updating GID of $group to $gid"
|
||||
groupmod -g "$gid" "$group"
|
||||
updated=1
|
||||
done
|
||||
|
||||
# 3) Default credentials warning
|
||||
if [ "${RUSTFS_ACCESS_KEY}" = "rustfsadmin" ] || [ "${RUSTFS_SECRET_KEY}" = "rustfsadmin" ]; then
|
||||
echo "!!!WARNING: Using default RUSTFS_ACCESS_KEY or RUSTFS_SECRET_KEY. Override them in production!"
|
||||
fi
|
||||
|
||||
# 4) Start with specified user
|
||||
docker_switch_user() {
|
||||
if [ -n "${RUSTFS_USERNAME}" ] && [ -n "${RUSTFS_GROUPNAME}" ]; then
|
||||
if [ -n "${RUSTFS_UID}" ] && [ -n "${RUSTFS_GID}" ]; then
|
||||
# Execute with numeric UID:GID directly (doesn't depend on user existing in system)
|
||||
exec chroot --userspec="${RUSTFS_UID}:${RUSTFS_GID}" / "$@"
|
||||
else
|
||||
# When only names are provided, create minimal passwd/group entries with 1000:1000; deduplicate before writing
|
||||
if ! grep -q "^${RUSTFS_USERNAME}:" /etc/passwd 2>/dev/null; then
|
||||
echo "${RUSTFS_USERNAME}:x:1000:1000:${RUSTFS_USERNAME}:/nonexistent:/sbin/nologin" >> /etc/passwd
|
||||
fi
|
||||
if ! grep -q "^${RUSTFS_GROUPNAME}:" /etc/group 2>/dev/null; then
|
||||
echo "${RUSTFS_GROUPNAME}:x:1000:" >> /etc/group
|
||||
fi
|
||||
exec chroot --userspec="${RUSTFS_USERNAME}:${RUSTFS_GROUPNAME}" / "$@"
|
||||
fi
|
||||
else
|
||||
# If no user is specified, keep as root (container has minimal privilege practices that can be configured separately)
|
||||
exec "$@"
|
||||
fi
|
||||
return $updated
|
||||
}
|
||||
|
||||
echo "📦 Initializing mount directories: ${LOCAL_VOLUMES[*]}"
|
||||
|
||||
for vol in "${LOCAL_VOLUMES[@]}"; do
|
||||
if [ ! -d "$vol" ]; then
|
||||
echo "📁 Creating directory: $vol"
|
||||
mkdir -p "$vol"
|
||||
fi
|
||||
|
||||
# Alpine busybox stat does not support -c, coreutils is required
|
||||
dir_uid=$(stat -c '%u' "$vol")
|
||||
dir_gid=$(stat -c '%g' "$vol")
|
||||
|
||||
if [ "$dir_uid" != "$APP_UID" ] || [ "$dir_gid" != "$APP_GID" ]; then
|
||||
if [[ "$SKIP_CHOWN" != "true" ]]; then
|
||||
# Prefer to update rustfs user/group UID/GID
|
||||
update_user_group_ids "$dir_uid" "$dir_gid" "$APP_USER" "$APP_GROUP" || \
|
||||
{
|
||||
echo "🔧 Fixing ownership for: $vol → $APP_USER:$APP_GROUP"
|
||||
if [[ -n "$CHOWN_RECURSION_DEPTH" ]]; then
|
||||
echo "🔧 Applying ownership fix with recursion depth: $CHOWN_RECURSION_DEPTH"
|
||||
find "$vol" -mindepth 0 -maxdepth "$CHOWN_RECURSION_DEPTH" -exec chown "$APP_USER:$APP_GROUP" {} \;
|
||||
else
|
||||
echo "🔧 Applying ownership fix recursively (full depth)"
|
||||
chown -R "$APP_USER:$APP_GROUP" "$vol"
|
||||
fi
|
||||
}
|
||||
else
|
||||
echo "⚠️ SKIP_CHOWN is enabled. Skipping ownership fix for: $vol"
|
||||
fi
|
||||
fi
|
||||
chmod 700 "$vol"
|
||||
done
|
||||
|
||||
# Warn if default credentials are used
|
||||
if [[ "$RUSTFS_ACCESS_KEY" == "rustfsadmin" || "$RUSTFS_SECRET_KEY" == "rustfsadmin" ]]; then
|
||||
echo "⚠️ WARNING: Using default RUSTFS_ACCESS_KEY or RUSTFS_SECRET_KEY"
|
||||
echo "⚠️ It is strongly recommended to override these values in production!"
|
||||
fi
|
||||
|
||||
echo "🚀 Starting application: $*"
|
||||
exec gosu "$APP_USER" "$@"
|
||||
echo "Starting: $*"
|
||||
docker_switch_user "$@"
|
||||
|
||||
@@ -984,6 +984,7 @@ impl S3 for FS {
|
||||
accept_ranges: Some("bytes".to_string()),
|
||||
content_range,
|
||||
e_tag: info.etag,
|
||||
metadata: Some(info.user_defined),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user