From 86a4c9985eca10ffb311f9701577bebbbb339906 Mon Sep 17 00:00:00 2001 From: Nayrosk <105997554+nayrosk@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:03:18 +0100 Subject: [PATCH 1/3] feat(docker): replace Ubuntu runtime with distroless image - Switch runtime base from ubuntu:22.04 to gcr.io/distroless/cc-debian13:nonroot - Run container as non-root user (UID 65532) - Use multi-stage build: config-fetcher (pre-fetches configs at build time), builder (compiles binary + extracts Wasmer libs), distroless runtime - Use --platform=$BUILDPLATFORM on config-fetcher stage to fix cross-arch builds - Auto-detect target arch for Wasmer .so extraction via dpkg --print-architecture - Add OCI labels (title, description, source, license) - Remove runtime git/curl dependency (configs embedded at build time) - Strip debug symbols (-ldflags="-s -w" -trimpath) for smaller binary --- Dockerfile | 76 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/Dockerfile b/Dockerfile index 14471980..ecd2d5a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,39 +1,73 @@ -FROM golang:1.23.6 AS builder +# --------------------------------------------------------------------------- +# Stage 1: Fetch configs +# --------------------------------------------------------------------------- +FROM --platform=$BUILDPLATFORM golang:1.23.6-bookworm AS config-fetcher - -WORKDIR /multiversx +WORKDIR /src COPY . . -RUN go mod tidy +WORKDIR /src/cmd/chainsimulator +RUN go build -o chainsimulator \ + && ./chainsimulator --fetch-configs-and-close -WORKDIR /multiversx/cmd/chainsimulator +# --------------------------------------------------------------------------- +# Stage 2: Build the binary + extract Wasmer libs +# --------------------------------------------------------------------------- +FROM golang:1.23.6-bookworm AS builder -RUN go build -o chainsimulator +WORKDIR /src +COPY . . -RUN mkdir -p /lib_amd64 /lib_arm64 +# Download all modules +RUN go mod download -RUN cp /go/pkg/mod/github.com/multiversx/$(cat /multiversx/go.sum | grep mx-chain-vm-v | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer/libwasmer_linux_amd64.so /lib_amd64/ -RUN cp /go/pkg/mod/github.com/multiversx/$(cat /multiversx/go.sum | grep mx-chain-vm-go | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer2/libvmexeccapi.so /lib_amd64/ +WORKDIR /src/cmd/chainsimulator -RUN cp /go/pkg/mod/github.com/multiversx/$(cat /multiversx/go.sum | grep mx-chain-vm-v | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer/libwasmer_linux_arm64_shim.so /lib_arm64/ -RUN cp /go/pkg/mod/github.com/multiversx/$(cat /multiversx/go.sum | grep mx-chain-vm-go | sort -n | tail -n -1 | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer2/libvmexeccapi_arm.so /lib_arm64/ +# Build with optimizations: strip debug symbols, smaller binary +# CGO_ENABLED=1 is implicit and required by Wasmer bindings. +RUN go build \ + -ldflags="-s -w" \ + -trimpath \ + -o /out/chainsimulator +# --------------------------------------------------------------------------- +# Extract architecture-specific Wasmer shared libraries +# --------------------------------------------------------------------------- +RUN mkdir -p /out/lib -FROM ubuntu:22.04 -ARG TARGETARCH -RUN apt-get update && apt-get install -y git curl +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /src/go.sum \ + | grep mx-chain-vm-v | sort -n | tail -n -1 \ + | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer/libwasmer_linux_$(dpkg --print-architecture | sed 's/arm64/arm64_shim/').so \ + /out/lib/ 2>/dev/null || true -COPY --from=builder /multiversx/cmd/chainsimulator /multiversx +RUN cp /go/pkg/mod/github.com/multiversx/$(cat /src/go.sum \ + | grep mx-chain-vm-go | sort -n | tail -n -1 \ + | awk -F '/' '{print$3}' | sed 's/ /@/g')/wasmer2/libvmexeccapi$(dpkg --print-architecture | sed 's/amd64//;s/arm64/_arm/').so \ + /out/lib/ 2>/dev/null || true -EXPOSE 8085 +# --------------------------------------------------------------------------- +# Stage 3: Minimal runtime image (distroless) +# --------------------------------------------------------------------------- +FROM gcr.io/distroless/cc-debian13:nonroot -WORKDIR /multiversx +LABEL org.opencontainers.image.title="mx-chain-simulator-go" \ + org.opencontainers.image.description="MultiversX Chain Simulator" \ + org.opencontainers.image.source="https://github.com/multiversx/mx-chain-simulator-go" \ + org.opencontainers.image.licenses="GPL-3.0" -# Copy architecture-specific files -COPY --from=builder "/lib_${TARGETARCH}/*" "/lib/" +# Copy binary +COPY --from=builder --chown=nonroot:nonroot /out/chainsimulator /app/chainsimulator -CMD ["/bin/bash"] +# Copy pre-fetched configs +COPY --from=config-fetcher --chown=nonroot:nonroot /src/cmd/chainsimulator/config /app/config -ENTRYPOINT ["./chainsimulator"] +# Copy Wasmer libs +COPY --from=builder /out/lib/ /lib/ +WORKDIR /app +EXPOSE 8085 + +# Run as non-root for security (UID 65532 is "nonroot" in distroless) +USER nonroot:nonroot +ENTRYPOINT ["./chainsimulator"] From 55160bb9737429fe8e44089d165691d9c27b275d Mon Sep 17 00:00:00 2001 From: Nayrosk <105997554+nayrosk@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:04:25 +0100 Subject: [PATCH 2/3] feat(makefile): add multi-arch build and security targets - Add docker-build-push target for multi-arch (amd64/arm64) with --push - Add qemu-setup target to register QEMU for cross-platform builds - Add docker-run target with --read-only and --tmpfs /tmp - Add docker-stop target for cleanup - Add docker-info and docker-scan (trivy) targets - Add configurable variables (IMAGE_NAME, REGISTRY, PLATFORMS) - Add help target listing all available commands - Add build target with -ldflags="-s -w" -trimpath - Add fetch-configs target --- Makefile | 116 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 9e3e34e3..633bb73e 100644 --- a/Makefile +++ b/Makefile @@ -1,41 +1,123 @@ -CHAIN_SIMULATOR_IMAGE_NAME=chainsimulator -CHAIN_SIMULATOR_IMAGE_TAG=latest -DOCKER_FILE=Dockerfile -IMAGE_NAME=simulator_image +IMAGE_NAME ?= chainsimulator +IMAGE_TAG ?= latest +REGISTRY ?= multiversx +DOCKER_FILE ?= Dockerfile +PLATFORMS ?= linux/amd64,linux/arm64 +CONTAINER_NAME ?= simulator_instance +FULL_IMAGE = $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG) +## Build the Go binary locally +.PHONY: build +build: + cd cmd/chainsimulator && go build -ldflags="-s -w" -trimpath -o chainsimulator + +## Fetch configs locally +.PHONY: fetch-configs +fetch-configs: build + cd cmd/chainsimulator && ./chainsimulator --fetch-configs-and-close + +## Build Docker image (single arch, current platform) +.PHONY: docker-build docker-build: - docker build \ - -t ${CHAIN_SIMULATOR_IMAGE_NAME}:${CHAIN_SIMULATOR_IMAGE_TAG} \ - -f ${DOCKER_FILE} \ - . + DOCKER_BUILDKIT=1 docker build \ + -t $(FULL_IMAGE) \ + -f $(DOCKER_FILE) \ + . -run-faucet-test: - $(MAKE) docker-build - docker run -d --name "${IMAGE_NAME}" -p 8085:8085 ${CHAIN_SIMULATOR_IMAGE_NAME}:${CHAIN_SIMULATOR_IMAGE_TAG} +## Build multi-arch image and push to registry (requires login) +## This is the correct way to produce a multi-platform manifest. +.PHONY: docker-build-push +docker-build-push: + docker buildx build \ + --platform $(PLATFORMS) \ + -t $(FULL_IMAGE) \ + -f $(DOCKER_FILE) \ + --push \ + . + +## Register QEMU for cross-platform builds (run once per boot) +.PHONY: qemu-setup +qemu-setup: + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + +## Run the simulator container +.PHONY: docker-run +docker-run: + docker run -d \ + --name "$(CONTAINER_NAME)" \ + --read-only \ + --tmpfs /tmp \ + -p 8085:8085 \ + $(FULL_IMAGE) + +## Stop and remove the simulator container +.PHONY: docker-stop +docker-stop: + docker stop "$(CONTAINER_NAME)" 2>/dev/null || true + docker rm "$(CONTAINER_NAME)" 2>/dev/null || true + +## Run faucet example test +.PHONY: run-faucet-test +run-faucet-test: docker-build + docker run -d --name "$(CONTAINER_NAME)" -p 8085:8085 $(FULL_IMAGE) sleep 2s cd examples/faucet && /bin/bash faucet.sh - docker stop "${IMAGE_NAME}" - docker rm ${IMAGE_NAME} 2> /dev/null + $(MAKE) docker-stop +## Run all examples +.PHONY: run-examples run-examples: printf '%s\n' '{ File = "enableEpochs.toml", Path = "EnableEpochs.StakeLimitsEnableEpoch", Value = 1000000 },' > temp.txt sed -i '4r temp.txt' cmd/chainsimulator/config/nodeOverrideDefault.toml rm temp.txt - $(MAKE) docker-build - docker run -d --name "${IMAGE_NAME}" -p 8085:8085 ${CHAIN_SIMULATOR_IMAGE_NAME}:${CHAIN_SIMULATOR_IMAGE_TAG} + docker run -d --name "$(CONTAINER_NAME)" -p 8085:8085 $(FULL_IMAGE) cd scripts/run-examples && /bin/bash install-python-deps.sh && /bin/bash script.sh - docker stop "${IMAGE_NAME}" - docker rm ${IMAGE_NAME} + $(MAKE) docker-stop +## Install golint if not already present +.PHONY: lint-install lint-install: ifeq (,$(wildcard test -f bin/golangci-lint)) @echo "Installing golint" curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s endif +## Run golint on the codebase +.PHONY: run-lint run-lint: @echo "Running golint" bin/golangci-lint run --max-issues-per-linter 0 --max-same-issues 0 --timeout=2m +## Run lint installation and then the linter +.PHONY: lint lint: lint-install run-lint + +## Show image details +.PHONY: docker-info +docker-info: + @echo "Image: $(FULL_IMAGE)" + @echo "Dockerfile: $(DOCKER_FILE)" + @echo "Platforms: $(PLATFORMS)" + @docker images $(FULL_IMAGE) --format "Size: {{.Size}}" + +## Run security scan with trivy (if installed) +.PHONY: docker-scan +docker-scan: + trivy image --severity HIGH,CRITICAL $(FULL_IMAGE) + +## Show available targets +.PHONY: help +help: + @echo "Available targets:" + @echo " build Build Go binary locally" + @echo " fetch-configs Fetch configs from GitHub repos" + @echo " docker-build Build Docker image (current platform, docker build)" + @echo " docker-build-push Build & push multi-arch image to registry" + @echo " qemu-setup Register QEMU for cross-arch builds" + @echo " docker-run Run the simulator container (read-only)" + @echo " docker-stop Stop and remove container" + @echo " docker-info Show image details" + @echo " docker-scan Trivy security scan" + @echo " lint Run linter" + @echo " help Show this help" From 6800e22e30a5982869db43d6fe33226fdc2895e3 Mon Sep 17 00:00:00 2001 From: Nayrosk <105997554+nayrosk@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:04:44 +0100 Subject: [PATCH 3/3] docs(readme): update Docker and build instructions - Update prerequisites to include Docker with BuildKit and Buildx - Add Makefile build alternative in Install section - Replace Docker section with new build/push/run instructions - Document distroless base image, non-root user, and read-only mode - Document QEMU setup for cross-platform builds - Reference make help for full list of targets --- README.md | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5d5eb73d..19e894bf 100644 --- a/README.md +++ b/README.md @@ -420,8 +420,10 @@ This endpoint resets (clears) an internal cache used by the `/validator/statisti Before proceeding, ensure you have the following prerequisites: -- Go programming environment set up. +- Go 1.23.x programming environment set up. - Git installed. +- Docker with BuildKit support (for container builds). +- Docker Buildx (for multi-arch builds). ## Install @@ -431,6 +433,11 @@ Using the `cmd/chainsimulator` package as root, execute the following commands: - install go dependencies: `go install` - build executable: `go build -o chainsimulator` +Alternatively, use the Makefile: +``` +make build +``` + Note: go version 1.23.* should be used to build the executable. @@ -513,14 +520,38 @@ INFO [2024-04-18 10:48:47.231] chain simulator's is accessible through the URL ``` -### Build docker image +### Docker + +The Docker image uses a multi-stage build with a [distroless](https://github.com/GoogleContainerTools/distroless) runtime image (`gcr.io/distroless/cc-debian13`). The container runs as a non-root user (UID 65532) for security. Configs are pre-fetched at build time so `git` and `curl` are not required at runtime. + +#### Build (single arch, current platform) +``` +make docker-build +``` + +#### Build & push multi-arch (amd64 + arm64) +``` +make docker-build-push +``` + +For cross-platform builds from an amd64 host, register QEMU first (once per boot): +``` +make qemu-setup +``` + +#### Run +``` +make docker-run +``` + +The container is started in read-only mode with a tmpfs on `/tmp`. To pass extra flags: ``` -DOCKER_BUILDKIT=1 docker build -t chainsimulator:latest . +docker run -p 8085:8085 multiversx/chainsimulator:latest --log-level *:DEBUG ``` -### Run with docker +#### Available Makefile targets ``` -docker run -p 8085:8085 chainsimulator:latest --log-level *:DEBUG +make help ``` ### Enable `HostDriver`