Skip to content

Commit 5de2576

Browse files
committed
refactor(docker): Implement optimized multi-stage Go/Rust build
Introduces a robust, cached, multi-stage Docker build for Juno. - Uses cargo-chef in rust-builder stage for optimal Rust dependency caching. - Builds Rust components as static libraries (.a) with C headers (.h). - Copies Rust artifacts to relative paths expected by in-source #cgo directives in go-builder stage (avoids modifying Go source). - Adds `ranlib` step to ensure static library symbol tables are valid. - Configures CGO to link necessary system libraries (libm, jemalloc, etc.). - Creates minimal debian:bookworm-slim final image with runtime deps. - Runs final image as root for backward compatibility with volume permissions. - Adds Cargo.lock files (for reproducible Rust builds) and .dockerignore (for optimized build context). - Parameterizes Go, Rust, and Juno versions via ARGs. - Updated pipeline to cache Docker layers.
1 parent b468df7 commit 5de2576

File tree

13 files changed

+6689
-31
lines changed

13 files changed

+6689
-31
lines changed

.dockerignore

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
11
# do not copy local database files
22
p2p-dbs/
3-
juno/
3+
4+
.github/
5+
.git/
6+
.vscode/
7+
8+
# Build artifacts
9+
build/
10+
coverage/
11+
12+
# Local data/config
13+
.DS_Store
14+
docker-compose.yml
15+
juno/
16+
17+
# Documentation
18+
docs/
19+
README.md
20+
LICENSE
21+
SECURITY.md
22+
CODE_OF_CONDUCT.md
23+
CONTRIBUTING.md
24+
25+
# Scripts/Other
26+
scripts/

.github/workflows/build-image.yaml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ on:
1313
required: true
1414

1515
permissions:
16-
contents: read
16+
contents: read
17+
actions: write
1718

1819
concurrency:
1920
group: ${{ github.workflow }}-${{ github.ref }}-image-build
@@ -37,22 +38,22 @@ jobs:
3738
- name: Define image tag
3839
id: set_tag
3940
run: |
40-
export DOCKER_IMAGE_TAG=$(git describe --tags)
41-
echo "DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG" >> $GITHUB_ENV
42-
echo "DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG" >> $GITHUB_OUTPUT
41+
TAG=$(git describe --tags)
42+
echo "DOCKER_IMAGE_TAG=$TAG" | tee -a $GITHUB_ENV >> $GITHUB_OUTPUT
4343
4444
- name: Setup Docker Buildx
4545
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2
4646

4747
- name: Login to registry
48-
run: |
49-
docker login ${{ env.DOCKER_REGISTRY }} -u ${{ secrets.ARTIFACTORY_NUBIA_USERNAME }} -p ${{ secrets.ARTIFACTORY_NUBIA_TOKEN_DEVELOPER }}
48+
run: docker login ${{ env.DOCKER_REGISTRY }} -u ${{ secrets.ARTIFACTORY_NUBIA_USERNAME }} -p ${{ secrets.ARTIFACTORY_NUBIA_TOKEN_DEVELOPER }}
5049

51-
- name: Build Docker Image
50+
- name: Build & push Docker images
5251
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4
5352
with:
5453
context: .
55-
platforms: "linux/amd64"
5654
push: true
57-
tags: |
58-
${{ env.DOCKER_REGISTRY }}/${{ env.REPO_DEV }}/juno:${{ env.DOCKER_IMAGE_TAG }}
55+
platforms: linux/amd64
56+
tags: ${{ env.DOCKER_REGISTRY }}/${{ env.REPO_DEV }}/juno:${{ steps.set_tag.outputs.DOCKER_IMAGE_TAG }}
57+
build-args: JUNO_VERSION=${{ steps.set_tag.outputs.DOCKER_IMAGE_TAG }}
58+
cache-from: type=gha,scope=${{ github.workflow }}
59+
cache-to: type=gha,mode=min,scope=${{ github.workflow }}

.github/workflows/deploy-dev-and-test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717
build:
1818
# Skip for PRs from forks as they don't have access to secrets
1919
if: github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork
20+
permissions:
21+
contents: read
22+
actions: write
2023
uses: ./.github/workflows/build-image.yaml
2124
secrets:
2225
ARTIFACTORY_NUBIA_USERNAME: ${{ secrets.ARTIFACTORY_NUBIA_USERNAME }}

.github/workflows/docker-image-build-push.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ jobs:
6666
with:
6767
context: .
6868
platforms: linux/amd64
69+
build-args: JUNO_VERSION=${{ needs.setup.outputs.tag }}
6970
push: true
7071
tags: nethermindeth/juno:${{ needs.setup.outputs.tag }}
7172

@@ -93,6 +94,7 @@ jobs:
9394
with:
9495
context: .
9596
platforms: linux/arm64
97+
build-args: JUNO_VERSION=${{ needs.setup.outputs.tag }}
9698
push: true
9799
tags: nethermindeth/juno:${{ needs.setup.outputs.tag }}-arm64
98100

Dockerfile

Lines changed: 133 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,147 @@
1-
# Stage 1: Build golang dependencies and binaries
2-
FROM ubuntu:25.04 AS build
1+
# Multi-stage Dockerfile for building Juno
32

4-
ARG VM_DEBUG
3+
ARG GO_VERSION=1.24.1
4+
ARG RUST_VERSION=1.85.1
5+
ARG JUNO_VERSION=unknown
56

7+
# ==================================================================
8+
# Stage 1: Build Rust libs with cargo-chef
9+
# ==================================================================
10+
FROM lukemathwalker/cargo-chef:0.1.71-rust-${RUST_VERSION}-slim-bookworm AS rust-builder
611

7-
RUN apt-get -qq update && \
8-
apt-get -qq install curl build-essential git golang upx-ucl libjemalloc-dev libjemalloc2 libbz2-dev
9-
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -q -y
12+
# Install Rust build tools (cbindgen) & C toolchain
13+
RUN apt-get update && \
14+
apt-get install -y --no-install-recommends build-essential && \
15+
cargo install cbindgen --version 0.26.0 && \
16+
rm -rf /var/lib/apt/lists/*
17+
18+
WORKDIR /build
19+
20+
# --- Prepare shared dependencies using cargo-chef ---
21+
22+
# Virtual workspace for cargo-chef
23+
RUN echo '[workspace]\nmembers = ["starknet_compiler", "core_rust", "vm_rust"]' > Cargo.toml
24+
25+
# Copy manifests for cache
26+
COPY starknet/compiler/rust/Cargo.toml starknet/compiler/rust/Cargo.lock ./starknet_compiler/
27+
COPY core/rust/Cargo.toml core/rust/Cargo.lock ./core_rust/
28+
COPY vm/rust/Cargo.toml vm/rust/Cargo.lock ./vm_rust/
29+
30+
# Prepare cargo-chef recipe (requires dummy lib.rs for libs)
31+
RUN mkdir -p ./starknet_compiler/src ./core_rust/src ./vm_rust/src && \
32+
touch ./starknet_compiler/src/lib.rs ./core_rust/src/lib.rs ./vm_rust/src/lib.rs && \
33+
cargo chef prepare --recipe-path recipe.json
34+
35+
# Cook dependencies (cached layer)
36+
RUN cargo chef cook --release --recipe-path recipe.json
37+
38+
# --- Build the actual projects ---
39+
40+
# Copy source
41+
COPY starknet/compiler/rust/ ./starknet_compiler/
42+
COPY core/rust/ ./core_rust/
43+
COPY vm/rust/ ./vm_rust/
44+
45+
# Args for package/library names
46+
ARG STARKNET_COMPILER_PKG_NAME=juno-starknet-compiler-rs
47+
ARG CORE_RUST_PKG_NAME=juno-starknet-core-rs
48+
ARG VM_RUST_PKG_NAME=juno-starknet-rs
49+
ARG STARKNET_COMPILER_LIB_NAME=juno_starknet_compiler_rs
50+
ARG CORE_RUST_LIB_NAME=juno_starknet_core_rs
51+
ARG VM_RUST_LIB_NAME=juno_starknet_rs
52+
53+
# Build Rust libs & generate C headers
54+
RUN cargo build --release --package ${STARKNET_COMPILER_PKG_NAME} && \
55+
cbindgen --crate ${STARKNET_COMPILER_PKG_NAME} --output target/release/${STARKNET_COMPILER_PKG_NAME}.h
56+
57+
RUN cargo build --release --package ${CORE_RUST_PKG_NAME} && \
58+
cbindgen --crate ${CORE_RUST_PKG_NAME} --output target/release/${CORE_RUST_PKG_NAME}.h
59+
60+
RUN cargo build --release --package ${VM_RUST_PKG_NAME} && \
61+
cbindgen --crate ${VM_RUST_PKG_NAME} --output target/release/${VM_RUST_PKG_NAME}.h
62+
63+
# --- Collect build artifacts ---
64+
RUN mkdir /artifacts && \
65+
cp target/release/lib${STARKNET_COMPILER_LIB_NAME}.a /artifacts/ && \
66+
cp target/release/${STARKNET_COMPILER_PKG_NAME}.h /artifacts/ && \
67+
\
68+
cp target/release/lib${CORE_RUST_LIB_NAME}.a /artifacts/ && \
69+
cp target/release/${CORE_RUST_PKG_NAME}.h /artifacts/ && \
70+
\
71+
cp target/release/lib${VM_RUST_LIB_NAME}.a /artifacts/ && \
72+
cp target/release/${VM_RUST_PKG_NAME}.h /artifacts/
73+
74+
75+
# ==================================================================
76+
# Stage 2: Build Go application
77+
# ==================================================================
78+
FROM golang:${GO_VERSION}-bookworm AS go-builder
79+
80+
ARG JUNO_VERSION
81+
82+
# Install CGO dependencies
83+
RUN apt-get update && \
84+
apt-get install -y --no-install-recommends \
85+
build-essential \
86+
libjemalloc-dev \
87+
libbz2-dev \
88+
&& rm -rf /var/lib/apt/lists/*
1089

1190
WORKDIR /app
1291

13-
# Copy source code
92+
# Cache Go dependencies
93+
COPY go.mod go.sum ./
94+
RUN go mod download && go mod verify
95+
96+
# Copy source
1497
COPY . .
1598

16-
# Build the project
17-
RUN bash -c 'source ~/.cargo/env && VM_DEBUG=${VM_DEBUG} make juno'
99+
# Args needed for artifact paths
100+
ARG STARKNET_COMPILER_PKG_NAME=juno-starknet-compiler-rs
101+
ARG CORE_RUST_PKG_NAME=juno-starknet-core-rs
102+
ARG VM_RUST_PKG_NAME=juno-starknet-rs
103+
ARG STARKNET_COMPILER_LIB_NAME=juno_starknet_compiler_rs
104+
ARG CORE_RUST_LIB_NAME=juno_starknet_core_rs
105+
ARG VM_RUST_LIB_NAME=juno_starknet_rs
106+
107+
# Copy Rust libs (.a)
108+
COPY --from=rust-builder /artifacts/lib${STARKNET_COMPILER_LIB_NAME}.a /app/starknet/compiler/rust/target/release/
109+
COPY --from=rust-builder /artifacts/lib${CORE_RUST_LIB_NAME}.a /app/core/rust/target/release/
110+
COPY --from=rust-builder /artifacts/lib${VM_RUST_LIB_NAME}.a /app/vm/rust/target/release/
111+
112+
# Copy Rust headers (.h)
113+
COPY --from=rust-builder /artifacts/${STARKNET_COMPILER_PKG_NAME}.h /app/starknet/compiler/
114+
COPY --from=rust-builder /artifacts/${CORE_RUST_PKG_NAME}.h /app/core/
115+
COPY --from=rust-builder /artifacts/${VM_RUST_PKG_NAME}.h /app/vm/
116+
117+
# Index static libs (for linker)
118+
RUN ranlib /app/starknet/compiler/rust/target/release/lib${STARKNET_COMPILER_LIB_NAME}.a && \
119+
ranlib /app/core/rust/target/release/lib${CORE_RUST_LIB_NAME}.a && \
120+
ranlib /app/vm/rust/target/release/lib${VM_RUST_LIB_NAME}.a
121+
122+
# Configure CGO
123+
ENV CGO_ENABLED=1
124+
ENV CGO_LDFLAGS="-ljemalloc -lm -lpthread -ldl"
125+
126+
# Build Go binary (passing version, stripping symbols)
127+
RUN go build -ldflags="-X main.Version=${JUNO_VERSION} -s -w" -o /app/juno ./cmd/juno/
18128

19-
# Compress the executable with UPX
20-
RUN upx-ucl /app/build/juno
21129

22-
# Stage 2: Build Docker image
23-
FROM ubuntu:25.04 AS runtime
130+
# ==================================================================
131+
# Stage 3: Final runtime image
132+
# ==================================================================
133+
FROM debian:bookworm-slim AS final
24134

25-
RUN apt-get update && apt-get install -y ca-certificates curl gawk grep libjemalloc-dev libjemalloc2
135+
# Install minimal runtime dependencies
136+
RUN apt-get update && \
137+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
138+
ca-certificates \
139+
libjemalloc2 \
140+
libbz2-1.0 \
141+
tzdata \
142+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
26143

27-
COPY --from=build /app/build/juno /usr/local/bin/
144+
# Copy final binary
145+
COPY --from=go-builder /app/juno /usr/local/bin/
28146

29147
ENTRYPOINT ["juno"]

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ juno-cached: ## Cached Juno compilation
4444
@go build $(GO_TAGS) -ldflags="-X main.Version=$(shell git describe --tags)" -o build/juno ./cmd/juno/
4545

4646

47-
MINIMUM_RUST_VERSION = 1.85.0
47+
MINIMUM_RUST_VERSION = 1.85.1
4848
CURR_RUST_VERSION = $(shell rustc --version | grep -o '[0-9.]\+' | head -n1)
4949
check-rust: ## Ensure rust version is greater than minimum
5050
@echo "Checking if current rust version >= $(MINIMUM_RUST_VERSION)"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
- Golang 1.24 or higher is required to build and run the project. You can find the installer on
4949
the official Golang [download](https://go.dev/doc/install) page.
50-
- [Rust](https://www.rust-lang.org/tools/install) 1.85.0 or higher.
50+
- [Rust](https://www.rust-lang.org/tools/install) 1.85.1 or higher.
5151
- A C compiler: `gcc`.
5252
- Install some dependencies on your system:
5353

core/rust/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@
66
# already existing elements were commented out
77

88
#/target
9-
/Cargo.lock

0 commit comments

Comments
 (0)