Multi-Architecture Build Support
Overview
NoETL supports building container images for multiple CPU architectures (amd64, arm64) using Docker Buildx. This enables:
- Development flexibility: Mac M-series (arm64) and Intel/AMD (amd64) developers
- Deployment flexibility: Deploy to any Kubernetes cluster regardless of node architecture
- Cost optimization: Use cheaper arm64 cloud instances where appropriate
- CI/CD efficiency: Build once, deploy anywhere
Architecture Components
Rust CLI Binary (noetlctl)
The Rust CLI is compiled natively within the Docker build process:
- Dev image (
docker/noetl/dev/Dockerfile): Usesrust:1.83-slimbuilder - Standalone binary (
noetlctl/Dockerfile): Uses Alpine with musl static linking
Python Application
Platform-agnostic Python code with architecture-specific Rust binary embedded.
UI Assets
Built once with Node.js (platform-agnostic), copied into final image.
Enabling Multi-Architecture Builds
1. Setup Docker Buildx
# Create buildx builder (one-time setup)
docker buildx create --name noetl-builder --use --bootstrap
# Verify builder supports multiple platforms
docker buildx inspect noetl-builder
2. Update Build Scripts
docker/build-noetl-images.sh modifications:
# Add platform arguments
PLATFORMS="linux/amd64,linux/arm64"
BUILD_PLATFORMS=false
while [[ $# -gt 0 ]]; do
case $1 in
--platforms)
PLATFORMS="$2"
BUILD_PLATFORMS=true
shift 2
;;
# ... existing options
esac
done
# Use buildx for multi-arch builds
if $BUILD_PLATFORMS; then
docker buildx build \
--platform "${PLATFORMS}" \
${BUILD_PROGRESS_ARGS} \
-t "${REGISTRY}noetl-local-dev:${TAG}" \
-f "${DOCKER_DIR}/noetl/dev/Dockerfile" \
--push \
"${REPO_ROOT}"
else
# Single-arch build (existing behavior)
docker build ${BUILD_PROGRESS_ARGS} \
-t "${REGISTRY}noetl-local-dev:${TAG}" \
-f "${DOCKER_DIR}/noetl/dev/Dockerfile" \
"${REPO_ROOT}"
fi
3. Build Multi-Architecture Images
# Build for both amd64 and arm64
task docker-build-noetl --platforms linux/amd64,linux/arm64 --push --registry ghcr.io/noetl/
# Build for specific architecture
task docker-build-noetl --platforms linux/arm64 --push --registry ghcr.io/noetl/
# Local build (auto-detects native platform)
task docker-build-noetl
Local Development Without Docker
Issue: UI Assets Missing
When running python -m noetl.server locally (outside Docker), the UI assets directory doesn't exist because it's only built during Docker image creation.
Solution 1: Build UI Locally
cd ui-src
npm install
npm run build
mkdir -p ../noetl/core/ui
cp -r dist/* ../noetl/core/ui/
Solution 2: Disable UI
export NOETL_ENABLE_UI=false
python -m noetl.server --host 0.0.0.0 --port 8082
Solution 3: Create Placeholder Directory
mkdir -p noetl/core/ui/assets
# Server will detect empty directory and disable UI automatically
Issue: Rust Binary Architecture Mismatch
The Rust CLI binary (noetl) must match your local architecture:
Mac (arm64):
cd noetlctl
cargo build --release
cp target/release/noetl ../bin/noetl
Mac (Intel/amd64):
cd noetlctl
cargo build --release --target x86_64-apple-darwin
cp target/x86_64-apple-darwin/release/noetl ../bin/noetl
Linux (amd64):
cd noetlctl
cargo build --release --target x86_64-unknown-linux-gnu
cp target/x86_64-unknown-linux-gnu/release/noetl ../bin/noetl
CI/CD Integration
GitHub Actions Example
name: Build Multi-Arch Images
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push multi-arch images
run: |
./docker/build-noetl-images.sh \
--platforms linux/amd64,linux/arm64 \
--push \
--registry ghcr.io/${{ github.repository }}/ \
--tag ${{ github.sha }}
Testing Multi-Architecture Images
1. Verify Image Manifests
docker buildx imagetools inspect ghcr.io/noetl/noetl-local-dev:latest
Expected output shows both architectures:
Manifest List: yes
Manifest List Size: 2
Platforms:
linux/amd64
linux/arm64
2. Test on Different Architectures
# Pull and run amd64 image on any platform
docker run --platform linux/amd64 ghcr.io/noetl/noetl-local-dev:latest server start
# Pull and run arm64 image on any platform
docker run --platform linux/arm64 ghcr.io/noetl/noetl-local-dev:latest server start
3. Kubernetes Deployment
Kubernetes automatically selects the correct architecture:
apiVersion: apps/v1
kind: Deployment
metadata:
name: noetl-server
spec:
template:
spec:
containers:
- name: noetl
image: ghcr.io/noetl/noetl-local-dev:latest # Auto-selects matching arch
Performance Considerations
Build Time
- Multi-arch builds take ~2x longer (building twice)
- Use caching aggressively (
--cache-from,--cache-to) - Consider building platforms in parallel with separate jobs
Image Size
- Manifest list adds minimal overhead (~1KB)
- Each architecture image is stored separately
- Total registry storage ≈ 2x single-arch image
Rust Cross-Compilation
For faster builds, consider cross-compilation instead of emulation:
# Use cross-rs for faster arm64 builds on amd64 hosts
FROM rust:1.83-slim AS rust-builder
RUN cargo install cross
RUN cross build --release --target aarch64-unknown-linux-gnu
Troubleshooting
"exec format error"
Cause: Running binary built for different architecture
Solution: Rebuild for correct architecture or use multi-arch image
QEMU emulation slow
Cause: Emulating foreign architecture in Docker
Solution: Use native builders or cross-compilation
Kind cluster architecture mismatch
Cause: Kind cluster uses host architecture
Solution: Build for host architecture or use --platform in deployment
Best Practices
- Default to native builds for local development (faster)
- Use multi-arch for releases (flexibility)
- Cache aggressively to minimize rebuild time
- Test on both architectures before release
- Document architecture requirements in deployment guides
- Pin architecture in CI/CD for reproducibility