-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathDockerfile
More file actions
233 lines (205 loc) · 8.85 KB
/
Copy pathDockerfile
File metadata and controls
233 lines (205 loc) · 8.85 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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# Build stage
FROM ubuntu:24.04 AS builder
# Use bash and enable pipefail
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
xz-utils \
git \
build-essential \
pkg-config \
libssl-dev \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install Zig 0.16.0 based on architecture
ARG TARGETARCH
RUN ZIG_VERSION="0.16.0" && \
case "$TARGETARCH" in \
amd64) ZIG_ARCH="x86_64" ;; \
arm64) ZIG_ARCH="aarch64" ;; \
arm) ZIG_ARCH="armv7a" ;; \
386) ZIG_ARCH="x86" ;; \
riscv64) ZIG_ARCH="riscv64" ;; \
*) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
esac && \
curl -L "https://ziglang.org/download/${ZIG_VERSION}/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}.tar.xz" | tar -xJ && \
mv "zig-${ZIG_ARCH}-linux-${ZIG_VERSION}" /opt/zig && \
ln -s /opt/zig/zig /usr/local/bin/zig
# Install Rust nightly (required by build.zig: rustup run nightly cargo …)
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly
ENV PATH="/root/.cargo/bin:${PATH}"
# Install RISC0 toolchain using rzup (only for linux/amd64)
ARG TARGETARCH
RUN if [ "$TARGETARCH" = "amd64" ]; then \
curl -L https://risczero.com/install | bash && \
export PATH="/root/.risc0/bin:${PATH}" && \
rzup install; \
fi
ENV PATH="/root/.risc0/bin:${PATH}"
# Configure Rust for ARM64 QEMU compatibility
# Use generic CPU target to avoid illegal instructions when running under QEMU emulation
ARG TARGETARCH
RUN if [ "$TARGETARCH" = "arm64" ]; then \
rustup target add aarch64-unknown-linux-gnu; \
fi
# Set RUSTFLAGS for ARM64 to use generic CPU (QEMU-compatible)
# This prevents illegal instruction errors when running ARM64 binaries under QEMU emulation
# We'll set this conditionally in the build step below
# Set working directory
WORKDIR /app
# Copy dependency files first for better caching
COPY build.zig.zon ./
COPY build.zig ./
# Fetch Zig dependencies using BuildKit cache mount
# This allows Docker to cache the dependency fetching step
RUN --mount=type=cache,target=/root/.cache/zig \
zig build --fetch
# Copy source code
COPY pkgs/ ./pkgs/
COPY build/ ./build/
COPY resources/ ./resources/
COPY rust/ ./rust/
COPY LICENSE ./
COPY README.md ./
# Copy git directory to get commit hash (exclude large objects)
COPY .git/HEAD .git/HEAD
COPY .git/refs .git/refs
# Get git commit hash and build the project with optimizations
# Use cache mounts for both the Zig and Cargo build caches.
#
# Without the three `/root/.cargo/{registry,git}` and `/app/rust/target`
# mounts, every Docker build (even for a 1-line Rust change) re-downloads
# the full Cargo crate graph and re-compiles the ~200 transitive deps
# (libp2p, gossipsub, quinn, tokio, ...) from scratch. On Mac/Docker
# Desktop that's 5-15 minutes of pure Cargo compile time burned per
# build. With these mounts: ~10s incremental rebuilds for typical changes.
#
# `rust/target` is where `cargo -C rust build` writes artifacts (see
# build.zig: `cargo_build = b.addSystemCommand({..., "-C", "rust", ...})`).
#
# CPU targeting per architecture:
# amd64: x86-64-v3 (AVX2, no AVX512) — portable across modern x86_64 servers
# arm64: generic CPU — QEMU-compatible when cross-building
ARG TARGETARCH
# Set to true to compile in the experimental ethp2p parallel RS-broadcast
# transport (`docker build --build-arg ETHP2P=true ...`). Default off: the
# adapter is comptime-excluded and its `zig_ethp2p` dependency is not fetched.
ARG ETHP2P=false
RUN --mount=type=cache,target=/root/.cache/zig \
--mount=type=cache,target=/root/.cargo/registry \
--mount=type=cache,target=/root/.cargo/git \
--mount=type=cache,target=/app/rust/target \
EXTRA_ZIG_FLAGS="-Dethp2p=${ETHP2P}" && \
if [ "$TARGETARCH" = "amd64" ]; then \
EXTRA_ZIG_FLAGS="$EXTRA_ZIG_FLAGS -Dcpu=x86_64_v3 -Drust-target-cpu=x86-64-v3"; \
elif [ "$TARGETARCH" = "arm64" ]; then \
export RUSTFLAGS="-C target-cpu=generic"; \
fi && \
GIT_VERSION=$(cat .git/HEAD | grep -o '[0-9a-f]\{40\}' || echo "unknown") && \
if [ -z "$GIT_VERSION" ] || [ "$GIT_VERSION" = "unknown" ]; then \
REF=$(cat .git/HEAD | sed 's/ref: //'); \
GIT_VERSION=$(cat ".git/$REF" 2>/dev/null | head -c 7 || echo "unknown"); \
else \
GIT_VERSION=$(echo "$GIT_VERSION" | head -c 7); \
fi && \
zig build --seed 0 -Doptimize=ReleaseSafe -Dgit_version="$GIT_VERSION" $EXTRA_ZIG_FLAGS && \
REC_AGG_DIR=$(find /root/.cargo/git/checkouts -path "*/rec_aggregation" -type d | head -1) && \
cp -r "$REC_AGG_DIR" /tmp/rec_aggregation_src && \
echo "$REC_AGG_DIR" > /tmp/rec_aggregation_path
# Note: rec_aggregation extraction is folded into the build RUN above
# (not a separate RUN) because we cache-mount /root/.cargo/git on that
# RUN — cache mounts are ephemeral per-RUN, so a subsequent layer cannot
# see /root/.cargo/git/checkouts at all. /tmp/rec_aggregation_src lands
# on the real image layer and is picked up by the runtime-prep stage.
# rec_aggregation's compilation.rs reads .py source files at runtime to
# verify a bytecode fingerprint (via env!("CARGO_MANIFEST_DIR") baked at
# compile time), so the crate source has to be present at the same path
# in the runtime image.
# Intermediate stage to prepare runtime libraries
FROM ubuntu:24.04 AS runtime-prep
ARG TARGETARCH
# Copy built binaries and resources from builder
COPY --from=builder /app/zig-out/ /app/zig-out/
COPY --from=builder /app/resources/ /app/resources/
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Recreate rec_aggregation .py files at the baked-in CARGO_MANIFEST_DIR path
COPY --from=builder /tmp/rec_aggregation_src /tmp/rec_aggregation_src
COPY --from=builder /tmp/rec_aggregation_path /tmp/rec_aggregation_path
RUN mkdir -p "$(cat /tmp/rec_aggregation_path)" \
&& cp -r /tmp/rec_aggregation_src/* "$(cat /tmp/rec_aggregation_path)/" \
&& rm -rf /tmp/rec_aggregation_src /tmp/rec_aggregation_path
# Create a script to copy the right libraries based on architecture
RUN mkdir -p /runtime-libs && \
case "$TARGETARCH" in \
amd64) \
LIBDIR="x86_64-linux-gnu" && \
LDSO="/lib64/ld-linux-x86-64.so.2" \
;; \
arm64) \
LIBDIR="aarch64-linux-gnu" && \
LDSO="/lib/ld-linux-aarch64.so.1" \
;; \
arm) \
LIBDIR="arm-linux-gnueabihf" && \
LDSO="/lib/ld-linux-armhf.so.3" \
;; \
386) \
LIBDIR="i386-linux-gnu" && \
LDSO="/lib/ld-linux.so.2" \
;; \
riscv64) \
LIBDIR="riscv64-linux-gnu" && \
LDSO="/lib/ld-linux-riscv64-lp64d.so.1" \
;; \
*) \
echo "Unsupported architecture: $TARGETARCH" && exit 1 \
;; \
esac && \
if [ -d "/lib/$LIBDIR" ]; then \
mkdir -p "/runtime-libs/lib/$LIBDIR" && \
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 librt.so.1 libgcc_s.so.1 libstdc++.so.6; do \
[ -f "/lib/$LIBDIR/$lib" ] && cp -L "/lib/$LIBDIR/$lib" "/runtime-libs/lib/$LIBDIR/" || true; \
done; \
fi && \
if [ -f "$LDSO" ]; then \
mkdir -p "/runtime-libs$(dirname $LDSO)" && \
cp -L "$LDSO" "/runtime-libs$LDSO"; \
fi
# Runtime stage - using scratch for absolute minimal size
FROM scratch AS runtime
ARG GIT_COMMIT=unknown
ARG GIT_BRANCH=unknown
LABEL org.opencontainers.image.revision=$GIT_COMMIT
LABEL org.opencontainers.image.ref.name=$GIT_BRANCH
# Copy the architecture-specific libraries and loader
COPY --from=runtime-prep /runtime-libs/ /
# Copy SSL certificates for HTTPS
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Copy built binaries
COPY --from=builder /app/zig-out/ /app/zig-out/
# Copy runtime resources
COPY --from=builder /app/resources/ /app/resources/
# Copy rec_aggregation .py files at baked-in CARGO_MANIFEST_DIR path
COPY --from=runtime-prep /root/.cargo/ /root/.cargo/
# Set the zeam binary as the entrypoint with beam parameter by default
ENTRYPOINT ["/app/zig-out/bin/zeam"]
# IMPORTANT NOTES:
#
#
# 1. The 'clock' and 'beam' commands use xev event loop which may have
# container compatibility issues. The PermissionDenied error occurs even
# with additional capabilities. This appears to be a limitation of running
# the xev-based event loop in a containerized environment.
#
# Hint: Use '--security-opt seccomp=unconfined' extra docker run arg to
# circumvent the issue.
#
# 2. The scratch image has no users, shells, or package managers - only
# the binary and required libraries.
#
# 3. For debugging, you'll need to copy the binary to another container
# with debugging tools.
#
# 4. The final image uses scratch base with manually copied libraries
# for the absolute minimal size possible.