-
Notifications
You must be signed in to change notification settings - Fork 39
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
Design multi-manifest (aka multi-architecture or multi-RID) publishing #87
Comments
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. |
There are two separate requirements here:
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 A worked example of this can be seen with this diff of the eshoponcontainers project. The docker compose YAML specifically is a useful example. |
I would love seeing this feature, as it's literally the last missing piece from throwing away my |
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:
Our tooling doesn't yet speak these manifests, but it could learn to. |
That looks very promising @baronfel ! This of course sparks hope 😉 what's missing from adding it to the Container Building Tools? |
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. |
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! |
We are looking at taking this work on in the near term. |
I'm not a fan of
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. |
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 |
Generally agree - that's why for this iteration the first-checked property would be
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 dotnet/sdk#37830. |
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. |
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 |
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:
when doing |
Some of the behavior mentioned above, we had to turn off. Android opts out of The approach we took for Android, was to default to set Since Mac is the only other platform with multiple RIDs (2) and they are not cross-compiling, |
@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? |
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. |
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
then, once all of those were done, we'd need to
There are a couple hurdles we'd need to cover:
Other requirements:
Proposal
The gesture we want users to perform for multi-arch manifest publishing is
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
shouldRuntimeIdentifier
specified and does have eitherContainerRuntimeIdentifiers
orRuntimeIdentifiers
specified. If so, invoke a new"_BuildMultiImageManifest"
target"_BuildSingleContainer"
Target whose behavior is exactly the same as the single-image version ofPublishContainer
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 theUseCurrentRuntimeIdentifier
property, which is inferred astrue
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 aCondition="'$(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
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
Characteristics of the Manifest List
Visual Aids
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.
The text was updated successfully, but these errors were encountered: