From 9c37d837c0f4b734e0959084893b2103256f290a Mon Sep 17 00:00:00 2001 From: Linzhi Zheng Date: Tue, 4 Aug 2026 01:49:43 +0000 Subject: [PATCH] feat: add ARM64 support for AgentENV --- .github/workflows/release.yml | 130 +++++++++++++++++++++---- Makefile | 3 +- config/deps_manifest.toml | 6 +- deploy/docker/Dockerfile.agentenv | 2 +- deploy/docker/Dockerfile.gateway | 2 +- deploy/docker/Dockerfile.scheduler | 4 +- docs/src/getting-started/quickstart.md | 2 +- scripts/install.sh | 10 +- src/setup/deps.rs | 15 ++- 9 files changed, 138 insertions(+), 36 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7c6f7b22..1dfeff8c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,9 +99,20 @@ jobs: path: dist/${{ matrix.asset_name }} build-server: - name: Build server (linux-x86_64) + name: Build server (linux-${{ matrix.arch }}) needs: validate-version - runs-on: ubuntu-24.04 + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-24.04 + arch: x86_64 + modes: kvm pvm + - os: ubuntu-24.04-arm + arch: aarch64 + modes: kvm + timeout-minutes: 30 steps: - uses: actions/checkout@v6 @@ -124,7 +135,7 @@ jobs: run: | set -euo pipefail - for MODE in kvm pvm; do + for MODE in ${{ matrix.modes }}; do BUNDLE="dist/bundle-$MODE" mkdir -p "$BUNDLE/ublk" cp target/release/server "$BUNDLE/server" @@ -163,18 +174,17 @@ jobs: mkdir -p "$BUNDLE/etc/overlaybd" cp "$STAGE_DEPS/overlaybd/etc/overlaybd/overlaybd.json" "$BUNDLE/etc/overlaybd/overlaybd.json" rm -rf "$STAGE_HOME" - if [[ "$MODE" == "kvm" ]]; then - ARCHIVE="dist/aenv-server-linux-x86_64.tar.gz" - else - ARCHIVE="dist/aenv-server-linux-x86_64-pvm.tar.gz" + ARCHIVE="dist/aenv-server-linux-${{ matrix.arch }}.tar.gz" + if [[ "$MODE" == "pvm" ]]; then + ARCHIVE="dist/aenv-server-linux-${{ matrix.arch }}-pvm.tar.gz" fi tar -czf "$ARCHIVE" -C "$BUNDLE" . done - uses: actions/upload-artifact@v7 with: - name: server-bundles - path: dist/aenv-server-linux-x86_64*.tar.gz + name: server-bundles-${{ matrix.arch }} + path: dist/aenv-server-linux-${{ matrix.arch }}*.tar.gz release: name: Create GitHub Release @@ -215,32 +225,36 @@ jobs: dist/aenv-darwin-aarch64 dist/aenv-server-linux-x86_64-pvm.tar.gz dist/aenv-server-linux-x86_64.tar.gz + dist/aenv-server-linux-aarch64.tar.gz - uses: actions/upload-artifact@v7 with: name: dist path: dist/ - docker-publish: - name: Build and push Docker image (${{ matrix.mode }}) + docker-publish-kvm-images: + name: Build KVM Docker image (${{ matrix.arch }}) needs: release - runs-on: ubuntu-24.04 + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - - mode: kvm - version_suffix: "" - - mode: pvm - version_suffix: "-pvm" + - os: ubuntu-24.04 + arch: amd64 + platform: linux/amd64 + - os: ubuntu-24.04-arm + arch: arm64 + platform: linux/arm64 permissions: contents: read packages: write + timeout-minutes: 30 steps: - uses: actions/checkout@v6 - name: Set lowercase owner - run: echo "OWNER=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_ENV + run: echo "OWNER=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_ENV" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -257,10 +271,84 @@ jobs: with: context: . file: deploy/docker/Dockerfile.agentenv + platforms: ${{ matrix.platform }} + push: true + build-args: | + AENV_VIRTUALIZATION_MODE=kvm + tags: | + ghcr.io/${{ env.OWNER }}/aenv-server:${{ github.ref_name }}-${{ matrix.arch }} + + docker-publish-kvm-manifest: + name: Publish multi-architecture KVM Docker image + needs: docker-publish-kvm-images + runs-on: ubuntu-24.04 + permissions: + packages: write + timeout-minutes: 30 + steps: + - name: Set lowercase owner + run: echo "OWNER=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_ENV" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create multi-architecture manifest + run: | + set -euo pipefail + image="ghcr.io/${OWNER}/aenv-server" + version="${GITHUB_REF_NAME}" + + docker buildx imagetools create \ + --tag "${image}:${version}" \ + --tag "${image}:latest" \ + "${image}:${version}-amd64" \ + "${image}:${version}-arm64" + + - name: Inspect published image + run: | + docker buildx imagetools inspect \ + "ghcr.io/${OWNER}/aenv-server:${GITHUB_REF_NAME}" + + docker-publish-pvm: + name: Build and push PVM Docker image (amd64) + needs: release + runs-on: ubuntu-24.04 + permissions: + contents: read + packages: write + timeout-minutes: 30 + steps: + - uses: actions/checkout@v6 + + - name: Set lowercase owner + run: echo "OWNER=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_ENV" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push PVM image + uses: docker/build-push-action@v7 + with: + context: . + file: deploy/docker/Dockerfile.agentenv + platforms: linux/amd64 push: true - cache-from: type=gha,scope=agentenv-runtime build-args: | - AENV_VIRTUALIZATION_MODE=${{ matrix.mode }} + AENV_VIRTUALIZATION_MODE=pvm tags: | - ghcr.io/${{ env.OWNER }}/aenv-server:${{ github.ref_name }}${{ matrix.version_suffix }} - ghcr.io/${{ env.OWNER }}/aenv-server:latest${{ matrix.version_suffix }} + ghcr.io/${{ env.OWNER }}/aenv-server:${{ github.ref_name }}-pvm + ghcr.io/${{ env.OWNER }}/aenv-server:latest-pvm diff --git a/Makefile b/Makefile index 116b30c0..8c994e96 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,8 @@ AENV_INSTALL_SUDO ?= sudo TEST_SCRIPTS_DIR := ./scripts/tests # Runner for tests that require AENV's network and namespace capabilities. -CAPABILITY_RUNNER := CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="$(CURDIR)/scripts/run-with-capabilities.sh" +CARGO_HOST_TARGET_ENV = $(shell $(CARGO) -vV | sed -n 's/^host: //p' | tr '[:lower:]-' '[:upper:]_') +CAPABILITY_RUNNER = CARGO_TARGET_$(CARGO_HOST_TARGET_ENV)_RUNNER="$(CURDIR)/scripts/run-with-capabilities.sh" AENV_TEST_STATE_ID ?= $(if $(GITHUB_RUN_ID),$(GITHUB_RUN_ID)-$(GITHUB_RUN_ATTEMPT),local-$$(id -u)) AENV_TEST_STATE_DIR ?= /tmp/aenv-test-$(AENV_TEST_STATE_ID) AENV_TEST_DEPS_PATH ?= $(if $(AENV_DEPS_PATH),$(AENV_DEPS_PATH),/var/lib/aenv/deps) diff --git a/config/deps_manifest.toml b/config/deps_manifest.toml index efe075e9..b6f94dbc 100644 --- a/config/deps_manifest.toml +++ b/config/deps_manifest.toml @@ -1,6 +1,6 @@ [firecracker.kvm] version = "1.15.1-patch-v1" -url = "https://pub-4ee15c400f554ab7a9eac3f5bc8f53de.r2.dev/firecracker-{version}-{arch}.tgz" +url = "https://github.com/kvcache-ai/firecracker/releases/download/aenv-deps/firecracker-{version}-{arch}.tgz" [firecracker.pvm] version = "v1.17.0-next.1" @@ -8,7 +8,7 @@ url = "https://github.com/kvcache-ai/firecracker-next/releases/download/{version [kernel.kvm] version = "vmlinux-6.1.175" -url = "https://pub-4ee15c400f554ab7a9eac3f5bc8f53de.r2.dev/{version}" +url = "https://github.com/kvcache-ai/firecracker/releases/download/aenv-deps/{version}-{arch}" [kernel.pvm] version = "6.12.33-pvm" @@ -16,7 +16,7 @@ url = "https://github.com/kvcache-ai/linux/releases/download/pvm-kernel-6.12.33/ [tools] version = "0.1.0" -url = "ghcr.io/zlzgithub-0801/agentenv-tools:{version}" +url = "ghcr.io/kvcache-ai/agentenv-tools:{version}" [overlaybd] version = "v1.0.18" diff --git a/deploy/docker/Dockerfile.agentenv b/deploy/docker/Dockerfile.agentenv index cb93dd7d..b8c320e9 100644 --- a/deploy/docker/Dockerfile.agentenv +++ b/deploy/docker/Dockerfile.agentenv @@ -52,7 +52,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \ FROM ubuntu:24.04 AS runtime-base ARG APT_MIRROR_BASE -ARG TARGETARCH=amd64 +ARG TARGETARCH # Optional extra runtime packages, specified as a whitespace- or comma-separated build arg. ARG EXTRA_RUNTIME_PACKAGES="" COPY deploy/docker/apply_apt_mirror.sh /usr/local/bin/apply_apt_mirror.sh diff --git a/deploy/docker/Dockerfile.gateway b/deploy/docker/Dockerfile.gateway index bd906930..0865ad30 100644 --- a/deploy/docker/Dockerfile.gateway +++ b/deploy/docker/Dockerfile.gateway @@ -1,6 +1,6 @@ FROM golang:1.25-bookworm AS build ARG TARGETOS=linux -ARG TARGETARCH=amd64 +ARG TARGETARCH WORKDIR /src COPY services/go.mod services/go.sum ./ RUN go mod download diff --git a/deploy/docker/Dockerfile.scheduler b/deploy/docker/Dockerfile.scheduler index 63dc4f80..eff588d0 100644 --- a/deploy/docker/Dockerfile.scheduler +++ b/deploy/docker/Dockerfile.scheduler @@ -1,6 +1,6 @@ FROM golang:1.25-bookworm AS build ARG TARGETOS=linux -ARG TARGETARCH=amd64 +ARG TARGETARCH WORKDIR /src COPY services/go.mod services/go.sum ./ RUN go mod download @@ -8,7 +8,7 @@ COPY services/ . RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /out/scheduler ./scheduler/cmd FROM alpine:3.21 AS grpc-health-probe -ARG TARGETARCH=amd64 +ARG TARGETARCH RUN apk add --no-cache curl \ && case "${TARGETARCH}" in \ amd64|arm64) ARCH="${TARGETARCH}" ;; \ diff --git a/docs/src/getting-started/quickstart.md b/docs/src/getting-started/quickstart.md index 58d4c59c..4689c816 100644 --- a/docs/src/getting-started/quickstart.md +++ b/docs/src/getting-started/quickstart.md @@ -20,7 +20,7 @@ uses a dedicated `aenv` system account with `CAP_NET_ADMIN` and ### 1. Install and start the server -**Option A — Install Script (Linux x86_64)** +**Option A — Install Script (Ubuntu 24.04)** The script installs both the server and the `aenv` CLI. Set `AENV_HOME_PATH` to choose the data directory; if it is not set, AENV stores runtime dependencies diff --git a/scripts/install.sh b/scripts/install.sh index 33de4e25..1cc31e2d 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -52,13 +52,19 @@ esac ARCH="$(uname -m)" case "$ARCH" in - x86_64) ARCH_TAG="x86_64" ;; + x86_64|amd64) ARCH_TAG="x86_64" ;; + aarch64|arm64) ARCH_TAG="aarch64" ;; *) - echo "error: unsupported architecture: $ARCH (server requires x86_64)" >&2 + echo "error: unsupported architecture: $ARCH (supported: x86_64/amd64, aarch64/arm64)" >&2 exit 1 ;; esac +if [[ "$VIRTUALIZATION_MODE" == "pvm" && "$ARCH_TAG" != "x86_64" ]]; then + echo "error: PVM virtualization mode is only supported on x86_64 hosts" >&2 + exit 1 +fi + OS="$(uname -s | tr '[:upper:]' '[:lower:]')" if [[ "$OS" != "linux" ]]; then echo "error: AgentENV server only supports Linux" >&2 diff --git a/src/setup/deps.rs b/src/setup/deps.rs index 3a757b16..b620f91b 100644 --- a/src/setup/deps.rs +++ b/src/setup/deps.rs @@ -90,7 +90,7 @@ pub async fn ensure(config: &AppConfig, deps_path: &Path) -> Result<()> { std::fs::create_dir_all(deps_path)?; ensure_firecracker(config, manifest, &arch).await?; - ensure_kernel(config, manifest).await?; + ensure_kernel(config, manifest, &arch).await?; // regctl is a runtime dependency for all registry access, not just tools // drive extraction, so it remains provisioned for explicit tools drives. @@ -149,7 +149,11 @@ async fn ensure_firecracker( Ok(()) } -async fn ensure_kernel(config: &AppConfig, manifest: &SetupDependencyManifest) -> Result<()> { +async fn ensure_kernel( + config: &AppConfig, + manifest: &SetupDependencyManifest, + arch: &str, +) -> Result<()> { if let Some(kernel_path) = config.kernel.image_path.as_deref() { return validate_explicit_file("kernel.image_path", kernel_path, false); } @@ -167,7 +171,10 @@ async fn ensure_kernel(config: &AppConfig, manifest: &SetupDependencyManifest) - .as_deref() .unwrap_or(&mode_manifest.version); let kernel_url_template = config.kernel.url.as_deref().unwrap_or(&mode_manifest.url); - let kernel_url = resolve_url(kernel_url_template, &[("version", kernel_version)]); + let kernel_url = resolve_url( + kernel_url_template, + &[("version", kernel_version), ("arch", arch)], + ); download_file(&kernel_url, &kernel_path).await } @@ -865,7 +872,7 @@ mod tests { ensure_firecracker(&config, bundled_manifest(), "x86_64") .await .expect("accept explicit firecracker"); - ensure_kernel(&config, bundled_manifest()) + ensure_kernel(&config, bundled_manifest(), "x86_64") .await .expect("accept explicit kernel"); ensure_tools(&config, &deps_path, bundled_manifest()).expect("import explicit tools");