From d2caee171e59e60b8ac35e891650e7f8d588183e Mon Sep 17 00:00:00 2001 From: Hans Bakker Date: Fri, 23 Oct 2026 13:38:45 +0000 Subject: [PATCH] Multi-arch container publishing --- .dockerignore | 7 +++ .github/workflows/container-build.yml | 74 +++++++++++++++++++++++++++ Dockerfile | 6 ++- README.md | 2 + docker/README.md | 67 ++++++++++++++++++++++++ scripts/publish-container.sh | 26 ++++++++++ 6 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/container-build.yml create mode 100644 docker/README.md create mode 100755 scripts/publish-container.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..b98a796ef7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.git +**/bin +**/obj +**/out +**/TestResults +.env +.env.* diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml new file mode 100644 index 0000000000..ff3ac9df4c --- /dev/null +++ b/.github/workflows/container-build.yml @@ -0,0 +1,74 @@ +name: Container build + +on: + pull_request: + paths: + - Dockerfile + - .dockerignore + - .github/workflows/container-build.yml + - scripts/publish-container.sh + - src/** + - schemas/** + - Nuget.config + - global.json + push: + branches: [main] + paths: + - Dockerfile + - .dockerignore + - .github/workflows/container-build.yml + - scripts/publish-container.sh + - src/** + - schemas/** + - Nuget.config + - global.json + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + strategy: + fail-fast: false + matrix: + target: [runtime, runtime-nonroot] + runner: [ubuntu-24.04, ubuntu-24.04-arm] + include: + - runner: ubuntu-24.04 + platform: linux/amd64 + arch: x64 + - runner: ubuntu-24.04-arm + platform: linux/arm64 + arch: arm64 + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - name: Check publishing script syntax + run: bash -n scripts/publish-container.sh + - name: Build image + uses: docker/build-push-action@v6 + with: + context: . + target: ${{ matrix.target }} + platforms: ${{ matrix.platform }} + load: true + tags: dab:container-test + - name: Check runtime and application architecture + env: + ARCH: ${{ matrix.arch }} + TARGET: ${{ matrix.target }} + run: | + docker run --rm --entrypoint sh dab:container-test -ec ' + dotnet --info + test -s /App/dab-config.json + grep -F "\"name\": \".NETCoreApp,Version=v10.0/linux-$1\"" \ + /App/Azure.DataApiBuilder.Service.deps.json + if [ "$2" = runtime-nonroot ]; then + test "$(id -u)" = "$APP_UID" + touch /App/logs/container-test + else + test "$(id -u)" = 0 + fi + ' sh "$ARCH" "$TARGET" diff --git a/Dockerfile b/Dockerfile index 70bd3a0c58..e71d48b412 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,13 @@ # Version values referenced from https://hub.docker.com/_/microsoft-dotnet-aspnet -FROM mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0 AS build +ARG TARGETARCH WORKDIR /src COPY [".", "./"] -RUN dotnet build "./src/Service/Azure.DataApiBuilder.Service.csproj" -c Docker -o /out -r linux-x64 +RUN dotnet publish "./src/Service/Azure.DataApiBuilder.Service.csproj" \ + -c Docker -o /out -a "$TARGETARCH" --self-contained false -p:UseAppHost=false # --------------------------------------------------------------------------- # Common runtime base. diff --git a/README.md b/README.md index 4ad3a6d58d..206f24f4ab 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ Data API builder (DAB) is an open-source, no-code tool that creates secure, full Use the [Getting Started](https://learn.microsoft.com/azure/data-api-builder/get-started/get-started-with-data-api-builder) tutorial to quickly explore the core tools and concepts. +For source builds, see [building and publishing multi-architecture container images](docker/README.md). + ### 1. Install the `dotnet` [command line](https://get.dot.net) https://get.dot.net diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000..3e17cf9b89 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,67 @@ +# Building and publishing container images + +The Dockerfile supports `linux/amd64` and `linux/arm64`. The SDK runs on the +builder's native architecture and cross-compiles for the requested platform; +the ASP.NET runtime base image is selected for that platform. Both the default +`runtime` target and the opt-in `runtime-nonroot` target support these platforms. + +## Local build + +Build and load a single platform into the local Docker image store: + +```sh +docker buildx build --platform linux/arm64 --target runtime --load -t dab:local . +``` + +Omit `--platform` to build for the local builder's default platform. Running an +image for another architecture requires emulation. Cross-building +`runtime-nonroot` also requires emulation (or native builder nodes for both +architectures) because its permissions setup executes in the target image. + +## Release publishing + +Use a Buildx builder with multi-platform support, for example: + +```sh +docker buildx create --name dab-multiarch --driver docker-container --use +docker buildx inspect --bootstrap +``` + +Authenticate to the release pipeline's **writable registry**, then publish all +tags for a variant in one build. Replace the example registry and version with +the release pipeline's existing destination and version: + +```sh +docker login +bash scripts/publish-container.sh runtime \ + /data-api-builder: \ + /data-api-builder:latest +bash scripts/publish-container.sh runtime-nonroot \ + /data-api-builder:-nonroot \ + /data-api-builder:latest-nonroot +``` + +The script pushes both platform images and their shared image index for each +supplied tag. Do not push the architectures separately to the same tag: the +last push would replace the earlier architecture instead of publishing an +index. A multi-platform build uses `--push`, not `--load`. + +The official MCR release pipeline is not defined in this repository. Its +maintainers must replace the existing single-platform build/push with this +script (or the equivalent `docker buildx build --platform +linux/amd64,linux/arm64 --push` command), preserving their existing registry +authentication, tag selection, and MCR promotion process. Every promoted tag +must retain the multi-platform index and both referenced platform images. +Changing the Dockerfile alone does not make existing MCR tags multi-platform. + +Verify the resulting tag in the writable registry and again after MCR promotion: + +```sh +docker buildx imagetools inspect /data-api-builder: +docker buildx imagetools inspect mcr.microsoft.com/azure-databases/data-api-builder: +``` + +Both must list `linux/amd64` and `linux/arm64`. Provenance attestations may also +appear as `unknown/unknown`; these do not replace the required platform images. +The `Container build` workflow builds both variants on native x64 and ARM64 +runners and checks the application's runtime identifier and non-root log access. diff --git a/scripts/publish-container.sh b/scripts/publish-container.sh new file mode 100755 index 0000000000..edd5cb403d --- /dev/null +++ b/scripts/publish-container.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +set -euo pipefail + +if [[ $# -lt 2 || ( "$1" != runtime && "$1" != runtime-nonroot ) ]]; then + echo "Usage: $0 [ ...]" >&2 + exit 1 +fi + +target=$1 +shift +tags=() +for tag in "$@"; do + tags+=(--tag "$tag") +done + +repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +docker buildx build \ + --file "$repo_root/Dockerfile" \ + --platform linux/amd64,linux/arm64 \ + --target "$target" \ + "${tags[@]}" \ + --push \ + "$repo_root"