sdk-container-builds icon indicating copy to clipboard operation
sdk-container-builds copied to clipboard

Design multi-manifest (aka multi-architecture or multi-RID) publishing

Open baronfel opened this issue 3 years ago • 18 comments

It's possible for containers to be specified in a 'manifest list' - a set of container image manifests that represent the same application on different underlying OS/hardware configurations.

Fundamentally this would be something like a multitargeted build. For some selection of OS/OSVersions and Architectures we'd need to orchestrate

  • building the app for that os/version/architecture
  • publishing a container image for that os/version/architecture

then, once all of those were done, we'd need to

  • create a new 'manifest list',
  • add each of the created images to the list,
  • ensure each one was 'annotated' with the correct os/platform/etc annotations, and
  • push the manifest list to the registry

There are a couple hurdles we'd need to cover:

  • if we would like to perform this by default for projects that specify multiple RIDs, then we may need a concept of a cross-RID/cross-TFM publish. This doesn't exist right now, and in fact is explicitly stopped by the cross-targeting targets in the .NET SDK.
  • if we modeled this as a different, standlone target, then we wouldn't need to tie it to 'publish' necessarily, but we might lose the benefit of associating with 'publish' as a concept
  • I'm unsure how much users would use this - is it worth pioneering a potentially-new publishing concept?
    • This would bring us parity with Jib and Ko - maybe parity is enough of a motivator

Other requirements:

  • [ ] Need to ensure that tarball export works for multi-arch images as well.
  • [ ] This work might also be useful/necessary for users on the 8 LTS, so we should strongly consider keeping the NuGet package and making that package work seamlessly with 8 and 9 SDKs. We should do this work off of the .NET SDK 8.0.4xx branch and merge forward to 9.0 as a result.

Proposal

The gesture we want users to perform for multi-arch manifest publishing is

dotnet publish -t:PublishContainer

i.e. the same gesture they use today. To do this, we should change the implementation of the current PublishContainer Target from its current behavior of 'publish a single image for a single RID' to more of a decision-making target.

PublishContainer should

  • check if the project is currently in multi-TFM state. if so, error out. we require specifying a single specific TFM for now.
  • check if the project is in a 'multi RID' state - meaning the project does not have a RuntimeIdentifier specified and does have either ContainerRuntimeIdentifiers or RuntimeIdentifiers specified. If so, invoke a new "_BuildMultiImageManifest" target
  • otherwise, the project is in a single-RID, single-TFM state. In this state, invoke a new "_BuildSingleContainer" Target whose behavior is exactly the same as the single-image version of PublishContainer today.

An example of this per-scenario break-out is here.

Anticipated hurdles

Defaulted RIDs

The SDK does not have a concept of 'multi-RID' publish, and so today there are several places where it has assumed that the publish gesture implicates the desire for a single RID. The main way this negatively impacts us is the UseCurrentRuntimeIdentifier property, which is inferred as true here and ends up erroneously pinning us to a single RID. Setting it explicitly to false in the project files works around this.

PublishSingleFile

If PublishSingleFile is set and UseCurrentRuntimeIdentifier is not (as mentioned above), there is a mismatch in expectations. For now, for scenarios like our initial set, users may have to condition properties to only light up when the RID-specific build(s) are being done (for example, adding a Condition="'$(RuntimeIdentifier)' != ''" to several properties.

This is a symptom of the overall Publishing mechanisms of the .NET SDK not being designed for multi-RID publish today. In general, I think many SDK checks could be deferred to the 'inner RID' builds with no loss of intent, but we may have to push for this functionality in phases.

BuildMultiImageManifest

This target broadly should do two things

  • orchestrate the building of N RID-specific images, collecting output information about them
    • N in this case is either the ContainerRuntimeIdentfiers Property (invented for this feature), or the RuntimeIdentifiers property (existing)
  • combine those outputs into a single Manifest List structure
  • orchestrate the export of the RID-specific images and the Manifest List to a registry, local daemon, or tarball in the correct order

Ideally, it would also unify any shared work that may happen during the multiple single-RID publishes into one unit of work that is shared. A specific example of this is

  • determination of Base Image to and fetching of the various manifests for that Base Image
  • tracking and parallelization of image layers that for the base Image(s)

Characteristics of the Manifest List

  • It should use the OCI Image Index schema, not the Docker Manifest List structure, if at all possible
  • It should contain N manifests, one for each RID created. These should map to the existing 'PlatformSpecificManifest` structure we know today
  • It should contain annotations matching all of the conventionally-applied labels that we support for individual images

Visual Aids

flowchart TD
    A[Start Build] --> B[dotnet publish -t:PublishContainer]
    B --> D[Publish for linux-x64]
    D --> E[Package a linux-x64 container]
    B --> F[Publish for linux-arm64]
    F --> G[Package a linux-arm64 container]
    G --> I[Package both containers into an image index]
    E --> I
    I --> H[Push containers and image index to registry]

Work Stages/Milestones

We should have two phases of the work - initial MVP and then productizing.

Initial MVP

In this stage we implement the multi-RID aware publishing feature with external registries as the primary destination - so no pushing to local daemons or exporting to tarballs. This is the most well-known area of development. Once this is implemented, we can hand a preview nupkg over to the internal partner teams that want to test the feature so they can begin validation.

Productizing

In this stage we would implement tarball export and local-Daemon export of manifest lists, as well as full testing and error handling scenarios.

baronfel avatar Aug 09 '22 20:08 baronfel

I discussed this at MVP Summit this year, and feedback from folks was strong - we should do this so that we have parity with other ecosystems.

baronfel avatar Apr 26 '23 14:04 baronfel

There are two separate requirements here:

  • create images for each architecture/platform that a user specifies that has a match with the base image chosen
  • bundle those platform- and architecture-specific images into a single manifest list and publish it

The former is relatively straightforward today. We essentially want to 'multi-target' like you would with a TFM, but with RIDs:

A Directory.Build.targets file with a Containerize target that enables multi-rid container generation
<Project>
  <PropertyGroup>
    <!-- We have to build Publish AND PublishContainer because PublishContainer (and other
        PublishProfile-delivered targets) don't have an explicit Publish dependency. -->
    <_RequiredContainerPublishTargets>Publish;PublishContainer</_RequiredContainerPublishTargets>
  </PropertyGroup>

  <!-- Entrypoint, either from solution-level `/t:Containerize` or project-level `/t:Containerize` -->
  <Target Name="Containerize" Condition="'$(EnableSdkContainerSupport)' == 'true'">
    <!-- Strategy here is that we will figure out what proejct(s) to build the containerization targets(s) for
         based on project state. We use `AdditionalProperties` to customize the outputs of each of the builds. -->

    <!-- Properties set here:
      * TargetFramework - multitargeting - changes inference for base image based on TFM
      * VersionSuffix - without either explicitly setting `ContainerImageTag` or influencing the tag in some way
                        (I chose `VersionSuffix` because it lets folks still customize the 'base' of the version
                        and follows a Docker-ish convention of arch-specific info adding to the end of the tag)
                        we'll get the same 'tag' for each image, which would cause the images to override when
                        pushed to a registry or local daemon. NOTE: this is currently the RID, but it _should_
                        be a golang-style platform string (e.g linux-amd64 instead of linux-x64).
      * ContainerRuntimeIdentifier - if we're building for a specific RID, we need to set this so that the
                                     containerization targets know what RID to build for
      * RuntimeIdentifier - if we're building for a specific RID, we need to set this so that we get optimized 
                            RID-specific assets in the publish output

      NOTE: we could get away with setting `RuntimeIdentfier` here to control `ContainerRuntimeIdentifier` inference
            but this is also nice and explicit.
     -->

    <!-- TFMs but no TF -> multitarget, making image for each TFM -->
    <ItemGroup Condition="'$(TargetFrameworks)' != ''
                          and '$(TargetFramework)' == ''" >
      <_TFMItems Include="$(TargetFrameworks)" />
      <_SingleContainerPublish Include="$(MSBuildProjectFullPath)"
        AdditionalProperties="TargetFramework=%(_TFMItems.Identity);
                              VersionSuffix=$([MSBuild]::GetTargetFrameworkVersion('%(_TFMItems.Identity)', 2))" />
    </ItemGroup>
    
    <!-- TF but no TFMs -> single image (aka the default pathway) up until now -->
    <ItemGroup Condition="'$(TargetFramework)' != ''
                          and '$(RuntimeIdentifiers)' == ''">
        <_SingleContainerPublish Include="$(MSBuildProjectFullPath)" />
    </ItemGroup>

    <!-- TF with RIDs -> multi-arch, single image per arch -->
    <ItemGroup Condition="'$(TargetFramework)' != ''
                          and '$(RuntimeIdentifiers)' != ''">
      <_RIDItems Include="$(RuntimeIdentifiers)" />
      <_SingleContainerPublish Include="$(MSBuildProjectFullPath)"
        AdditionalProperties="ContainerRuntimeIdentifier=%(_RIDItems.Identity);
                              RuntimeIdentifier=%(_RIDItems.Identity);
                              VersionSuffix=%(_RIDItems.Identity);" />
    </ItemGroup>
    
    <MSBuild Projects="@(_SingleContainerPublish)" Targets="$(_RequiredContainerPublishTargets)" BuildInParallel="true" />
  </Target>
</Project>

Adding this target to a project lets you run dotnet build /t:Containerize and generate architecture- and platform-specific images. We should look at including something like this in the official build targets for the 7.0.400 time frame if at all possible. Adding such a target also enables a related use case: containerizing every project in a solution that can be containerized. This enables workflows like dotnet build /t:Containerize myapp.sln && docker-compose up, where there is a compose.yaml that specifies the relationships between the services in the usual way, just using image: instead of build: stanzas for the project.

A worked example of this can be seen with this diff of the eshoponcontainers project. The docker compose YAML specifically is a useful example.

baronfel avatar Jun 10 '23 14:06 baronfel

I would love seeing this feature, as it's literally the last missing piece from throwing away my Dockerfiles and replacing them with the SDK Container Building Tools.

mu88 avatar Nov 12 '23 16:11 mu88

Making multi-architecture images is pretty straightforward, as shown above. The next step is creating image manifests using those images. There's an example of this in my sdk-container-demo repository here that builds upon the snippet above by:

  • building architecture-specific images with a certain labeling scheme
  • pushing those images to the destination registry
  • creating a container manifest using the docker CLI with each of those images
  • pushing the generated manifest to the destination registry

Our tooling doesn't yet speak these manifests, but it could learn to.

baronfel avatar May 02 '24 16:05 baronfel

That looks very promising @baronfel !

This of course sparks hope 😉 what's missing from adding it to the Container Building Tools?

mu88 avatar May 07 '24 07:05 mu88

So I gave @baronfel's prototype a try yesterday and it works nicely. The icing on the cake (despite being integrated into the SDK Container Building Tools) would be if pushing the arch-specific images to the container registry wouldn't be necessary - I prefer my build process not to rely on external things like a foreign container registry. Instead, it would be cool to build the multi-arch image completely within one's local environment.

mu88 avatar May 21 '24 08:05 mu88

No, not as of yet. This probably won't make it for 8.0.400, but it is our highest-rated request so we do want to get to it!

baronfel avatar Jun 25 '24 15:06 baronfel

We are looking at taking this work on in the near term.

baronfel avatar Aug 01 '24 22:08 baronfel

check if the project is in a 'multi RID' state - meaning the project does not have a RuntimeIdentifier specified and does have either ContainerRuntimeIdentifiers or RuntimeIdentifiers specified.

I'm not a fan of RuntimeIdentifiers since it (at least in theory) affects build. We should be focusing new functionality on publish time properties. This is similar to our old friend SelfContained, but I think we created PublishSelfContained for that.

I think many SDK checks could be deferred to the 'inner RID' builds with no loss of intent,

I like the idea of an inner-RID build, where one RID is set as a simplifying approach.

I know that MAUI had this same desire at one point, but perhaps it was satisfied via the TFMs that were created for them.

richlander avatar Aug 04 '24 14:08 richlander

To not c&p the same MSBuild draft logic (kudos again to @baronfel 🥳) into several of my .NET apps, I added it to my NuGet package mu88.Shared (see here for the sources).
I've added it to several of my .NET apps targeting both x64 and arm64 and it works nicely 🤓

mu88 avatar Aug 04 '24 14:08 mu88

I'm not a fan of RuntimeIdentifiers since it (at least in theory) affects build. We should be focusing new functionality on publish time properties. This is similar to our old friend SelfContained, but I think we created PublishSelfContained for that.

Generally agree - that's why for this iteration the first-checked property would be ContainerRuntimeIdenfiers. We could easily drop consideration of RuntimeIdentifiers, but I'd like to encourage people to at least have that property set since that's the property that Restore actually keys off of, and as much as possible I'd like to avoid breaking some of the implicit assumptions that dotnet build && dotnet publish --no-restore --no-build -r <something> promises.

I like the idea of an inner-RID build, where one RID is set as a simplifying approach.

I know that MAUI had this same desire at one point, but perhaps it was satisfied via the TFMs that were created for them.

cc @jonathanpeppers for comment, but from my digging I think MAUI still broadly use RIDs in their publishing workflows. Examples here for calculating which then turns into a set of MSBuild Projects which are then reused in at least AOT publishing but possibly elsewhere as well.

In addition @jonathanpeppers has requested better SDK-level support for managing RIDs in https://github.com/dotnet/sdk/issues/37830.

baronfel avatar Aug 04 '24 15:08 baronfel

I forgot about 'restore'. That said, I think it is still problematic. Let's talk this one through. I think we last discussed this one at length about 5 years ago.

I would also like to think this through to a broader set of scenarios. The key one I have in mind is native AOT, which requires an additional toolset and is easiest with build containers. The buildx behavior enables it quite well.

I don't think we need to build the perfect solution from the get-go, but we should ensure we know where we are heading.

richlander avatar Aug 04 '24 20:08 richlander

Android apps unfortunately have four RIDs (arm, arm64, x86, x64), and Mac apps have two (x64, arm64). iOS debug builds could have two if you build for simulator and device.

What we do currently is use $(RuntimeIdentifiers) with an s and do an "inner build" in a similar fashion as $(TargetFrameworks) with an s and gather the outputs into the app package. This runs the trimmer per architecture, and the AOT compiler per architecture. Right now, we have this logic in each platform's workload, as there wasn't anything built into the .NET SDK for this. I think this could be improved, but what we have has been working ok for customers.

jonathanpeppers avatar Aug 05 '24 13:08 jonathanpeppers

Thanks @jonathanpeppers - that matches with the plan here. I do agree that we need some better concept built into the SDK (and maybe even NuGet for per-TFM-per-RID targets?!). How did you all deal with some of the hurdles for the publish properties that assume a default RID when publishing at a RID-less level? From the issue description I mean things like:

Defaulted RIDs

The SDK does not have a concept of 'multi-RID' publish, and so today there are several places where it has assumed that the publish gesture implicates the desire for a single RID. The main way this negatively impacts us is the UseCurrentRuntimeIdentifier property, which is inferred as true here and ends up erroneously pinning us to a single RID. Setting it explicitly to false in the project files works around this.

PublishSingleFile

If PublishSingleFile is set and UseCurrentRuntimeIdentifier is not (as mentioned above), there is a mismatch in expectations. For now, for scenarios like our initial set, users may have to condition properties to only light up when the RID-specific build(s) are being done (for example, adding a Condition="'$(RuntimeIdentifier)' != ''" to several properties.

This is a symptom of the overall Publishing mechanisms of the .NET SDK not being designed for multi-RID publish today. In general, I think many SDK checks could be deferred to the 'inner RID' builds with no loss of intent, but we may have to push for this functionality in phases.

when doing dotnet publish ... with no specific RID requested.

baronfel avatar Aug 05 '24 13:08 baronfel

Some of the behavior mentioned above, we had to turn off. Android opts out of $(UseCurrentRuntimeIdentifier), for example:

  • https://github.com/dotnet/android/blob/95bf32a84e72aabcd3f074b6a47b0e7ca8930783/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets#L79

The approach we took for Android, was to default to set $(RuntimeIdentifiers) to all 4 by default when $(RuntimeIdentifier) is omitted. A customer might not ever set a RID or have to know about them. To make Debug builds reasonable, we detect the RID based on the attached device (or selected device in VS/C# Dev Kit). This way Debug-mode can just build one instead of four.

Since Mac is the only other platform with multiple RIDs (2) and they are not cross-compiling, $(UseCurrentRuntimeIdentifier) works for that case. They also made Release builds default to two architectures by default.

jonathanpeppers avatar Aug 05 '24 14:08 jonathanpeppers

@surayya-MS / @baronfel so great that this feature made it into the SDK 🚀 how does the release process work, when will it become available for customers?

mu88 avatar Dec 03 '24 09:12 mu88

This will first ship in January's patch releases of 8.0.4xx and 9.0.1xx SDKs (alongside standalone packages if you're using it that way). I'll be working on a blog post showcasing the feature that same month, and some additional docs should be appearing in this repo as well.

baronfel avatar Dec 03 '24 16:12 baronfel

@baronfel: as mentioned earlier, I've wrapped your MSBuild snippet into a small NuGet package (see this comment). This worked fine for several weeks.
After upgrading one of my apps to v9.0.102, the approach now no longer seems to work - the build is failing:

Logs
  Determining projects to restore...
  Restored /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/ScreenshotCreator.Logic.csproj (in 298 ms).
  Restored /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/ScreenshotCreator.Api.csproj (in 330 ms).
  Sonar: (ScreenshotCreator.Logic.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Logic -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/bin/Release/net9.0/ScreenshotCreator.Logic.dll
  Sonar: (ScreenshotCreator.Api.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/ScreenshotCreator.Api.dll
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/publish/
  Sonar: (ScreenshotCreator.Logic.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Logic -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/bin/Release/net9.0/ScreenshotCreator.Logic.dll
  Sonar: (ScreenshotCreator.Logic.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Logic -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/bin/Release/net9.0/ScreenshotCreator.Logic.dll
  Sonar: (ScreenshotCreator.Api.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-arm64/ScreenshotCreator.Api.dll
  Sonar: (ScreenshotCreator.Api.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-x64/ScreenshotCreator.Api.dll
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-arm64/publish/
  Sonar: (ScreenshotCreator.Logic.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Logic -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/bin/Release/net9.0/ScreenshotCreator.Logic.dll
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-x64/publish/
  Sonar: (ScreenshotCreator.Logic.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  Sonar: (ScreenshotCreator.Api.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Logic -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/bin/Release/net9.0/ScreenshotCreator.Logic.dll
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-x64/ScreenshotCreator.Api.dll
  Sonar: (ScreenshotCreator.Logic.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  Sonar: (ScreenshotCreator.Api.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Logic -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/bin/Release/net9.0/ScreenshotCreator.Logic.dll
  Sonar: (ScreenshotCreator.Api.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-x64/publish/
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-arm64/ScreenshotCreator.Api.dll
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-x64/ScreenshotCreator.Api.dll
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-arm64/publish/
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-x64/publish/
  Building image '***/screenshotcreator' with tags 'dev-arm64-linux-x64' on top of base image 'docker.io/***/screenshotcreator-playwright:9.0.16'.
  Sonar: (ScreenshotCreator.Logic.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Logic -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Logic/bin/Release/net9.0/ScreenshotCreator.Logic.dll
  Sonar: (ScreenshotCreator.Api.csproj) The analysis targets file not found: /home/runner/work/ScreenshotCreator/ScreenshotCreator/.sonarqube/bin/targets/SonarQube.Integration.targets
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-arm64/ScreenshotCreator.Api.dll
  Building image '***/screenshotcreator' with tags 'dev-amd64-linux-x64' on top of base image 'docker.io/***/screenshotcreator-playwright:9.0.16'.
  Building image '***/screenshotcreator' with tags 'dev-arm64-linux-arm64' on top of base image 'docker.io/***/screenshotcreator-playwright:9.0.16'.
  ScreenshotCreator.Api -> /home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/bin/Release/net9.0/linux-arm64/publish/
  Building image '***/screenshotcreator' with tags 'dev-amd64-linux-arm64' on top of base image 'docker.io/***/screenshotcreator-playwright:9.0.16'.
  Uploading layer 'sha256:bc0965b23a04fe7f2d9fb20f597008fcf89891de1c705ffc1c80483a1f098e4f' to 'registry.hub.docker.com'.
  Uploading layer 'sha256:10a0cac16c18dc91869aa83b6fe269ab878344e544d3a5ab7e292ec9bdb11b07' to 'registry.hub.docker.com'.
  Uploading layer 'sha256:505bf3781970140729f9a78ccd409ced917d9f1d8c230cbb27e652f4c3a0707b' to 'registry.hub.docker.com'.
  Layer 'sha256:fcb3c8ccc80837edbfcef4b2afedb9299bb9026cd2410fd61db159b8305b0c80' already exists.
  Layer 'sha256:1320006b6943abd35b8fa5e531b0efa6cef916bebb1b650f34987da8f7438f52' already exists.
  Layer 'sha256:bb3f2b52e6af242cee1bc6c19ce79e05544f8a1d13f5a6c1e828d98d2dbdc94e' already exists.
  Finished uploading layer 'sha256:9b3575c33d26ca7ee5285e622bc35ade2722a187c66a7d301357c25a2727793d' to 'registry.hub.docker.com'.
  Uploading config to registry at blob 'sha256:5a9246358336165fe5d7e6def9a4a54f0a122722417b653a59b96a3c265bae25',
  Uploaded config to registry.
  Uploading tag 'dev-arm64-linux-x64' to 'registry.hub.docker.com'.
  Finished uploading layer 'sha256:9ce9e66b77bf07b281814b31b2effd4fe2ace040095673670aa0d91ba542ee8c' to 'registry.hub.docker.com'.
  Uploading config to registry at blob 'sha256:db524745eb8dbd11e2f344710f735421cd2e0400e2cf6791ffc71c84e7421b02',
  Uploaded tag 'dev-arm64-linux-x64' to 'registry.hub.docker.com'.
  Pushed image '***/screenshotcreator:dev-arm64-linux-x64' to registry 'registry.hub.docker.com'.
  Finished uploading layer 'sha256:19008c09065d938894dbf1e850d538e073cefc3033e2f88018af83d01721d201' to 'registry.hub.docker.com'.
  Uploading config to registry at blob 'sha256:1ec59b250a553c653418370eb2d0d427f4c9ca0f88054ea6853a4640a11d5a26',
  Uploaded config to registry.
  Uploading tag 'dev-amd64-linux-arm64' to 'registry.hub.docker.com'.
  Uploaded config to registry.
  Uploading tag 'dev-arm64-linux-arm64' to 'registry.hub.docker.com'.
  Uploaded tag 'dev-amd64-linux-arm64' to 'registry.hub.docker.com'.
  Pushed image '***/screenshotcreator:dev-amd64-linux-arm64' to registry 'registry.hub.docker.com'.
  Uploaded tag 'dev-arm64-linux-arm64' to 'registry.hub.docker.com'.
  Pushed image '***/screenshotcreator:dev-arm64-linux-arm64' to registry 'registry.hub.docker.com'.
  Building image index '***/screenshotcreator:dev-arm64' on top of manifests sha256:cdc35f3a7c45918138d30ebc7d3a6723bdce00a7b70d629c21f5bc15005af020, sha256:b0626f527e35b3f019131bde6e3618eff104a00508bd82b8b9e544b6e7e462b2.
  Uploading tag 'dev-arm64' to 'registry.hub.docker.com'.
  Uploaded tag 'dev-arm64' to 'registry.hub.docker.com'.
  Pushed image index '***/screenshotcreator:dev-arm64' to registry 'registry.hub.docker.com'.
  Finished uploading layer 'sha256:989de153d90ec39acac7be66d2509a7beeafc552a9b9156e0b6807c2a4f7b614' to 'registry.hub.docker.com'.
  Uploading config to registry at blob 'sha256:f95e896bd5df1cc1ce73e9f998af59363d80b977a5b03fd79a5e0454fb386115',
  Uploaded config to registry.
  Uploading tag 'dev-amd64-linux-x64' to 'registry.hub.docker.com'.
  Uploaded tag 'dev-amd64-linux-x64' to 'registry.hub.docker.com'.
  Pushed image '***/screenshotcreator:dev-amd64-linux-x64' to registry 'registry.hub.docker.com'.
  Building image index '***/screenshotcreator:dev-amd64' on top of manifests sha256:929e212cce4c48702e5c5aed73ff02dc701aa596a9b7a7ec34149246dcec479b, sha256:dcd6129c8b821b6251a827d03fd4b52b8636f3eb3a1fa1ef0b2744c5d40a150a.
  Uploading tag 'dev-amd64' to 'registry.hub.docker.com'.
  Uploaded tag 'dev-amd64' to 'registry.hub.docker.com'.
  Pushed image index '***/screenshotcreator:dev-amd64' to registry 'registry.hub.docker.com'.
  Created architecture-specific images, now creating manifest list
  registry.hub.docker.com/***/screenshotcreator:dev-amd64 is a manifest list
/home/runner/.nuget/packages/***.shared/1.1.1/build/***.Shared.targets(81,5): error MSB3073: The command "docker manifest create registry.hub.docker.com/***/screenshotcreator:dev registry.hub.docker.com/***/screenshotcreator:dev-amd64 registry.hub.docker.com/***/screenshotcreator:dev-arm64 --amend" exited with code 1. [/home/runner/work/ScreenshotCreator/ScreenshotCreator/src/ScreenshotCreator.Api/ScreenshotCreator.Api.csproj]

Is it related to the changes in this issue? If yes, it would be great if you would share how one uses it so that I can reinvigorate my builds.

mu88 avatar Jan 21 '25 07:01 mu88

@baronfel: is there any news about this issue? It is still failing for me 🫤

mu88 avatar Feb 03 '25 12:02 mu88

@mu88 you're probably hitting the bug where in some cases we publish for all RIDs ourselves now, which is ruining your local multi-arch orchestration. This should be fixed with next Tuesday's SDK release.

baronfel avatar Feb 03 '25 13:02 baronfel

@baronfel: so it should work after 04.02.2025?

mu88 avatar Feb 03 '25 13:02 mu88

@baronfel: so it should work after 04.02.2025?

No, the following Tuesday - Patch Tuesday is the second Tuesday of the month.

baronfel avatar Feb 03 '25 13:02 baronfel

I tested it and it works. 👍 Although it pushed multiple manifests, the desired multi-platform manifest with my tag, but also a single image for each RID I used with the tag mytag-RID. Not sure if I did something wrong, I don't believe that is how docker does it, but I'm happy that there is a multi-platform manifest now.

alexaka1 avatar Feb 12 '25 07:02 alexaka1