dotnet / sdk-container-builds

Libraries and build tooling to create container images from .NET projects using MSBuild
https://learn.microsoft.com/en-us/dotnet/core/docker/publish-as-container
MIT License
179 stars 38 forks source link

ContainerRegistry doesn't support registry name (path-based) #381

Open hrimhari opened 1 year ago

hrimhari commented 1 year ago

Steps to reproduce:

  1. Setup a registry in a Docker registry provider (i.e. JFrog Cloud). The registry will be identified by <provider-host>/<registry-name>. Example: registryprovider.example.com/registryname
  2. Specify the fully qualified registry name to -p:ContainerRegistry.

Expected behavior:

Actual behavior: Could not recognize registry

baronfel commented 1 year ago

Hi @hrimhari - this should work, you'll just need to use the JFrog-specific convention of the 'artifact server name' as part of the name of your generated container image. This is JFRog kinda going off-spec here, because container registries by the spec are just supposed to be identifiable by domain/port - the name of the container 'repository' (what most people think of as the image name) is completely separate.

I tested this with the following settings and had some success at least authenticating to a JFrog Cloud registry trial that I set up:

<ContainerRegistry>dotnetsdkcontainersdemo.jfrog.io</ContainerRegistry>
<ContainerImageName>docker/sdk-container-demo</ContainerImageName>

So I think to get this to work, you need to include registryname as part of your ContainerImageName, not as part of the ContainerRegistry.

There are other issues with JFrog that @Danielku15 has fixes for in #383 that we'll also need to take in order to successfully push, but once that merges you should be able to grab the prerelease version of the package from the GitHub package feed for this repository and verify that.

Danielku15 commented 1 year ago

Lucky timing. I can also confirm that we are prefixing the Image Name with the "Repository Key" (as Artifactory calls it). We do this centrally in our Directory.Build.targets via.

    <PropertyGroup Condition=" '$(ContainerImageName)' != '' AND '$(ContainerNamePrefix)' != '' ">
        <ContainerImageName>$(ContainerNamePrefix)$(ContainerImageName)</ContainerImageName>
    </PropertyGroup>

ContainerNamePrefix is a property provided from the CI system (depending on where we want to deploy it to) but you could also define it as fixed prefix there. This way in each .csproj of the solution we can add a simple <ContainerImageName>sdk-container-demo</ContainerImageName> matching the name of the image we want for this project, and during publish it will be automatically prefixed.

I think also on Docker hub this principle exists as user separation. https://docs.docker.com/docker-hub/repos/ Here they have examples like namespace1/docker101tutorial, new_org/docker101tutorial and <hub-user>/<repo-name>.

It might be a bit off topic but worth mentioning here:

I thought of proposing as part of https://github.com/dotnet/sdk-container-builds/issues/376 proposing that we maybe use URL fragment (aka. hash tags) to have for each registry a prefix. e.g. artifactory.mycompany.com:1234#my-docker-repo would push to artifactory.mycompany.com:1234 and when constructing the push url the my-docker-repo/ will be used as additional prefix: /v2/my-docker-repo/my-image-name/blobs/uploads/

Then you could do a multi push like: <OutputRegistries>hub.docker.com#my-company;artifactory.mycompany.com#department-docker</OutputRegistries> and get a respective push with the correct prefixes while keeping the same image.

hrimhari commented 1 year ago

To tell the truth, I'm a bit confused by the extra hoops to deal with repositories as paths in the registry URL and the image name. The docker command seems to take all that as 1 argument. I.e. https://docs.docker.com/engine/reference/commandline/tag/#tag-an-image-for-a-private-repository

baronfel commented 1 year ago

That's valid and good feedback - we've opted for more 'granular' splits initially because we expected folks to want to push to multiple tags, or different registries, etc. while keeping certain parts the same. Other tools like Jib do allow for providing a single, complete destination image name (with or without tags) and the tooling will handle that. I would be ok with that.

WeetA34 commented 1 year ago

Hi @hrimhari - this should work, you'll just need to use the JFrog-specific convention of the 'artifact server name' as part of the name of your generated container image. This is JFRog kinda going off-spec here, because container registries by the spec are just supposed to be identifiable by domain/port - the name of the container 'repository' (what most people think of as the image name) is completely separate.

I tested this with the following settings and had some success at least authenticating to a JFrog Cloud registry trial that I set up:

<ContainerRegistry>dotnetsdkcontainersdemo.jfrog.io</ContainerRegistry>
<ContainerImageName>docker/sdk-container-demo</ContainerImageName>

So I think to get this to work, you need to include registryname as part of your ContainerImageName, not as part of the ContainerRegistry.

There are other issues with JFrog that @Danielku15 has fixes for in #383 that we'll also need to take in order to successfully push, but once that merges you should be able to grab the prerelease version of the package from the GitHub package feed for this repository and verify that.

Hello it doesn't work as expected for me with gitlab container registry ContainerRegistry: gitlab.xxx.xxx ContainerImageName: path/to/the/project

The generated url for blobs uploads is: gitlab.xxx.xxx/v2/path/to/the/project/blobs/uploads/ instead of: giltab.xxx.xxx/path/to/the/project/v2/blobs/uploads/

note the position of /v2

baronfel commented 1 year ago

Hi @WeetA34 - the generated url is correct per the OPI distribution spec. I was just able to push myself - the spec outlinse the url as <base domain>/v2/<repository name>/blobs/uploads/<uploadid>, where repository name can have multiple segments. Here's a publish I triggered just now.

However, if you are getting errors during publish around 504 gateway timeouts, you may need to enable chunked uploads for the registry. You can do this by setting the SDK_CONTAINER_REGISTRY_CHUNKED_UPLOAD environment variable to true. This will send smaller payloads so that the registry doesn't get slammed. I've found this to be required for registry.gitlab.com, but may not be required if you're using a self-hosted GitLab instance.

WeetA34 commented 1 year ago

Hello @baronfel My bad. In fact, it was just a project access token permission issue :( Thank you

baronfel commented 1 year ago

Glad to hear it @WeetA34 - always happy to see someone having success with the tools :)