Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Matrix Strategy Multi-Arch example #121

Open
kaovilai opened this issue May 2, 2023 · 4 comments
Open

[FEATURE] Matrix Strategy Multi-Arch example #121

kaovilai opened this issue May 2, 2023 · 4 comments
Labels
enhancement New feature or request

Comments

@kaovilai
Copy link

kaovilai commented May 2, 2023

Is your feature request related to a problem? Please describe.

Clone of docker/docs#16593

Describe the solution you'd like

similar to docker/docs#17180

Describe alternatives you've considered

Additional context

This should allow us to use more runner resources for longer multiarch builds.

graph TD;
    Workflow---|amd64|runner1;
    Workflow---|arm64|runner2;
    Workflow---|arm32v5|runner3;
    Workflow---|arm32v6|runner4;
    Workflow---|arm32v7|runner5;
    Workflow---|arm64v8|runner6;
    Workflow---|i386|runner7;
    Workflow---|ppc64le|runner8;
    Workflow---|s390x|runner9;
    runner1-->Z[podman push multi-arch-image]
    runner2-->Z
    runner3-->Z
    runner4-->Z
    runner5-->Z
    runner6-->Z
    runner7-->Z
    runner8-->Z
    runner9-->Z
@kaovilai kaovilai added the enhancement New feature or request label May 2, 2023
@Raboo
Copy link

Raboo commented Aug 16, 2023

I was also caught with the same problem.

I am able to build multiple architectures. But not publish, it simply overwrites the tag so only one architecture is available in ghcr.io.

name: Build container images
on: [push, workflow_dispatch]

concurrency:
  group: '${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
  cancel-in-progress: true

jobs:
  build-image:
    name: Build image
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    strategy:
      matrix:
        platform: [linux/amd64, linux/arm64]

    steps:
    - name: Checkout code
      uses: actions/checkout@v3
      with:
        fetch-depth: 0
        submodules: true

    - name: cache podman
      uses: actions/cache@v3
      with:
        path: ~/.local/share/containers
        key: podman

    - name: Set up QEMU
      uses: docker/setup-qemu-action@v2

    - name: set lower case owner name
      run: |
        echo "OWNER_LC=${OWNER,,}" >>${GITHUB_ENV}
      env:
        OWNER: '${{ github.repository_owner }}'

    - name: Container meta
      id: meta
      uses: docker/metadata-action@v4
      with:
        images: |
          ghcr.io/${{ env.OWNER_LC }}/rtfm
        tags: |
          type=semver,pattern={{version}},value=${{ inputs.version }}
          type=semver,pattern={{major}}.{{minor}},value=${{ inputs.version }}
          type=semver,pattern={{major}},value=${{ inputs.version }}
          type=ref,event=branch
          type=ref,event=pr
          # type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
        flavor: |
          latest=${{ github.ref_name == github.event.repository.default_branch }}

    - name: Build Image
      id: build-image
      uses: redhat-actions/buildah-build@v2
      with:
        image: rtfm
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        oci: true
        containerfiles: |
          ./Dockerfile
        platforms: ${{ matrix.platform }}


    # I guess this is the part that needs some type of resource sharing between the runners.
    - name: Push image to GHCR
      uses: redhat-actions/push-to-registry@v2
      id: push
      with:
        image: ${{ steps.build-image.outputs.image }}
        tags: ${{ steps.build-image.outputs.tags }}
        username: ${{ github.actor }}
        password: ${{ github.token }}
        registry: ghcr.io/${{ env.OWNER_LC }}

    - name: Print image url
      run: echo "Image pushed to ${{ steps.push.outputs.registry-paths }}"

Anyone has a good solution for this?

@kaovilai
Copy link
Author

kaovilai commented Aug 16, 2023

you would have to create manifests list containing different (already pushed single-arch, built by different runners) tags then push the manifest list in the final runner

@Raboo
Copy link

Raboo commented Aug 18, 2023

Ok, I cracked it!

Here is a working example using multiple runners to build for each architecture, that later upload their images to github temporary storage and a runner that downloads the different architecture images and merges them and publishes to GHCR.

name: Build container images
on: [push, workflow_dispatch]

concurrency:
  group: '${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'

jobs:
  build:
    name: Build image
    runs-on: ubuntu-latest
    strategy:
      matrix:
        platform: [linux/amd64, linux/arm64]

    steps:
    - name: Checkout code
      uses: actions/checkout@v3
      with:
        fetch-depth: 0
        submodules: true

    - name: cache podman storage
      uses: actions/cache@v3
      with:
        path: ~/.local/share/containers/storage
        key: podman-storage-${{ matrix.platform }}

    - name: Set up QEMU
      uses: docker/setup-qemu-action@v2

    - name: set lower case owner name
      run: |
        echo "OWNER_LC=${OWNER,,}" >>${GITHUB_ENV}
      env:
        OWNER: '${{ github.repository_owner }}'

    - name: export architecture name (removing os prefix)
      run: |
        echo "PODMAN_ARCH=${PLATFORM#*/}" >>${GITHUB_ENV}
      env:
        PLATFORM: ${{ matrix.platform }}

    - name: Container meta
      id: meta
      uses: docker/metadata-action@v4
      with:
        images: |
          ghcr.io/${{ env.OWNER_LC }}/rtfm
        tags: |
          type=raw,value=build
        flavor: |
          suffix=-${{ env.PODMAN_ARCH }}

    - name: Build Image
      id: build-image
      uses: redhat-actions/buildah-build@v2
      with:
        image: rtfm
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        oci: true
        containerfiles: |
          ./Dockerfile
        platforms: ${{ matrix.platform }}

    - run: skopeo copy containers-storage:ghcr.io/${{ env.OWNER_LC }}/rtfm:build-${{ env.PODMAN_ARCH }} oci-archive:/tmp/${{ env.PODMAN_ARCH }}-oci.tar

    - name: Upload digest
      uses: actions/upload-artifact@v3
      with:
        name: rtfm-build-${{ env.PODMAN_ARCH }}
        path: /tmp/${{ env.PODMAN_ARCH }}-oci.tar
        if-no-files-found: error
        retention-days: 1

  upload:
    name: Upload images
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    needs: build
    steps:

    - name: Download podman
      uses: actions/download-artifact@v3
      with:
        name: rtfm-build-amd64
        path: /tmp
    - name: Download podman
      uses: actions/download-artifact@v3
      with:
        name: rtfm-build-arm64
        path: /tmp

    - name: set lower case owner name
      run: |
        echo "OWNER_LC=${OWNER,,}" >>${GITHUB_ENV}
      env:
        OWNER: '${{ github.repository_owner }}'

    - run: podman manifest create ghcr.io/${{ env.OWNER_LC }}/rtfm:manifest
    - run: podman manifest add ghcr.io/${{ env.OWNER_LC }}/rtfm:manifest oci-archive:/tmp/arm64-oci.tar
    - run: podman manifest add ghcr.io/${{ env.OWNER_LC }}/rtfm:manifest oci-archive:/tmp/amd64-oci.tar
    # - run: podman manifest inspect ghcr.io/${{ env.OWNER_LC }}/rtfm:manifest

    - name: Container meta
      id: meta
      uses: docker/metadata-action@v4
      with:
        images: |
          ghcr.io/${{ env.OWNER_LC }}/rtfm
        tags: |
          type=semver,pattern={{version}},value=${{ inputs.version }}
          type=semver,pattern={{major}}.{{minor}},value=${{ inputs.version }}
          type=semver,pattern={{major}},value=${{ inputs.version }}
          type=ref,event=branch
          type=ref,event=pr
          # type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
        flavor: |
          latest=${{ github.ref_name == github.event.repository.default_branch }}

    - name: add tags
      run: |
        # fix multi-line issue from steps.meta.outputs.tags
        podman tag ghcr.io/${{ env.OWNER_LC }}/rtfm:manifest $(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' ')

    - name: Push image to GHCR
      uses: redhat-actions/push-to-registry@v2
      id: push
      with:
        image: rtfm
        tags: ${{ steps.meta.outputs.tags }}
        username: ${{ github.actor }}
        password: ${{ github.token }}
        registry: ghcr.io/${{ env.OWNER_LC }}

    - name: Print image url
      run: echo "Image pushed to ${{ steps.push.outputs.registry-paths }}"

I guess we now need to document this example somewhere.

@kaovilai
Copy link
Author

Awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants