diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index 9d8ae12db1..ff15338ead 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -8,6 +8,7 @@ self-hosted-runner: - blacksmith-4vcpu-ubuntu-2404 - blacksmith-4vcpu-ubuntu-2404-arm - blacksmith-8vcpu-ubuntu-2404 + - blacksmith-16vcpu-ubuntu-2404 - blacksmith-32vcpu-ubuntu-2404 - large-linux-arm - large-linux-x86 diff --git a/.github/workflows/qemu-image-build.yml b/.github/workflows/qemu-image-build.yml index d5481ee614..3b17cf3ad7 100644 --- a/.github/workflows/qemu-image-build.yml +++ b/.github/workflows/qemu-image-build.yml @@ -4,11 +4,20 @@ on: push: paths: - .github/workflows/qemu-image-build.yml + - Dockerfile-kubernetes - ansible/vars.yml - ebssurrogate/scripts/* - nix/packages/build-qemu-image/* - qemu.pkr.hcl workflow_dispatch: + inputs: + arch: + description: 'Guest architecture (amd64 is experimental and publishes only to staging)' + type: choice + default: arm64 + options: + - arm64 + - amd64 permissions: contents: read @@ -34,7 +43,10 @@ jobs: strategy: matrix: postgres_version: ${{ fromJson(needs.prepare.outputs.postgres_versions) }} - runs-on: arm-native-runner + arch: ["${{ inputs.arch || 'arm64' }}"] + runs-on: ${{ matrix.arch == 'amd64' && 'blacksmith-16vcpu-ubuntu-2404' || 'arm-native-runner' }} + env: + TARGET_ARCH: ${{ matrix.arch }} timeout-minutes: 150 permissions: contents: write @@ -59,7 +71,7 @@ jobs: uses: ./.github/actions/nix-install-ephemeral - name: Run checks if triggered manually - if: ${{ github.event_name == 'workflow_dispatch' }} + if: ${{ github.event_name == 'workflow_dispatch' && matrix.arch == 'arm64' }} run: | SUFFIX=$(nix run nixpkgs#yq -- ".postgres_release[\"postgres${{ matrix.postgres_version }}\"]" ansible/vars.yml | sed -E 's/[0-9\.]+(.*)$/\1/') if [[ -z $SUFFIX ]] ; then @@ -67,6 +79,20 @@ jobs: exit 1 fi + - name: Verify QEMU can use KVM + shell: bash + run: | + case "$TARGET_ARCH" in + amd64) QEMU=qemu-system-x86_64; MACHINE=q35 ;; + arm64) QEMU=qemu-system-aarch64; MACHINE=virt,gic-version=max,highmem=on ;; + *) echo "Unsupported architecture: $TARGET_ARCH" >&2; exit 1 ;; + esac + printf '%s\n' '{"execute":"qmp_capabilities"}' '{"execute":"query-kvm"}' '{"execute":"quit"}' | + nix shell --inputs-from . nixpkgs#qemu --command \ + "$QEMU" -machine "$MACHINE,accel=kvm" -cpu host \ + -m 128M -nodefaults -display none -S -qmp stdio | + jq -se 'any(.[]; .return.enabled == true)' + - name: Resolve git sha id: resolve-git-sha uses: ./.github/actions/resolve-git-sha @@ -76,12 +102,15 @@ jobs: - name: Build QEMU artifact env: GIT_SHA: ${{ steps.resolve-git-sha.outputs.sha }} - run: BUILD_QEMU_IMAGE_HW_VIRT_ONLY=1 nix run .#build-qemu-image "${{ matrix.postgres_version }}" arm64 + run: BUILD_QEMU_IMAGE_HW_VIRT_ONLY=1 nix run .#build-qemu-image "${{ matrix.postgres_version }}" "$TARGET_ARCH" - name: Grab release version id: process_release_version run: | PG_VERSION=$(nix run nixpkgs#yq -- -r '.postgres_release["postgres'${{ matrix.postgres_version }}'"]' ansible/vars.yml) + if [[ "$TARGET_ARCH" == amd64 ]]; then + PG_VERSION="$PG_VERSION-amd64-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT" + fi echo "version=$PG_VERSION" >> $GITHUB_OUTPUT - name: configure aws credentials - staging @@ -100,7 +129,7 @@ jobs: env: IMAGE_TAG: ${{ steps.process_release_version.outputs.version }} run: | - docker build -f Dockerfile-kubernetes -t "postgres:$IMAGE_TAG" packer-work-qemu-* + docker build --platform "linux/$TARGET_ARCH" -f Dockerfile-kubernetes -t "postgres:$IMAGE_TAG" packer-work-qemu-* - name: Push docker image to Amazon ECR if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/') }} @@ -114,19 +143,19 @@ jobs: # TODO (darora): temporarily also push to prod account from here - add a guard to only publish proper tagged releases to prod? - name: configure aws credentials - prod - if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/') }} + if: ${{ matrix.arch == 'arm64' && (github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/')) }} uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 with: role-to-assume: ${{ secrets.CONTROL_PLANE_PROD_ROLE }} aws-region: "us-east-1" - name: Login to Amazon ECR Prod - if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/') }} + if: ${{ matrix.arch == 'arm64' && (github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/')) }} id: login-ecr-private-prod uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 - - name: Push docker image to Amazon ECR - if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/') }} + - name: Push docker image to Amazon ECR Prod + if: ${{ matrix.arch == 'arm64' && (github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/')) }} env: REGISTRY: 156470330064.dkr.ecr.us-east-1.amazonaws.com REPOSITORY: postgres-vm-image diff --git a/Dockerfile-kubernetes b/Dockerfile-kubernetes index 61eda4e1dd..8681c5cb7a 100644 --- a/Dockerfile-kubernetes +++ b/Dockerfile-kubernetes @@ -2,11 +2,23 @@ FROM alpine:3.23 ADD output-cloudimg/packer-cloudimg /disk/image.qcow2 -RUN apk add --no-cache qemu-system-aarch64 qemu-img openssh-client aavmf virtiofsd \ - && truncate -s 64M /root/varstore.img \ - && truncate -s 64M /root/efi.img \ - && dd if=/usr/share/AAVMF/QEMU_EFI.fd of=/root/efi.img conv=notrunc \ - && qemu-img create -f qcow2 /tmp/disk.qcow2 -b /disk/image.qcow2 -F qcow2 \ - && apk del --no-cache aavmf qemu-img +RUN set -eux; \ + case "$(apk --print-arch)" in \ + aarch64) \ + apk add --no-cache qemu-system-aarch64 aavmf; \ + truncate -s 64M /root/varstore.img; \ + truncate -s 64M /root/efi.img; \ + dd if=/usr/share/AAVMF/QEMU_EFI.fd of=/root/efi.img conv=notrunc; \ + apk del --no-cache aavmf ;; \ + x86_64) \ + apk add --no-cache qemu-system-x86_64 ovmf; \ + cp /usr/share/OVMF/OVMF_CODE.fd /root/efi.img; \ + cp /usr/share/OVMF/OVMF_VARS.fd /root/varstore.img; \ + apk del --no-cache ovmf ;; \ + *) echo "Unsupported architecture" >&2; exit 1 ;; \ + esac; \ + apk add --no-cache qemu-img openssh-client virtiofsd; \ + qemu-img create -f qcow2 /tmp/disk.qcow2 -b /disk/image.qcow2 -F qcow2; \ + apk del --no-cache qemu-img CMD exec /bin/sh -c "trap : TERM INT; sleep 9999999999d & wait"