Inedo Community Forums Forums
    • Recent
    • Tags
    • Popular
    • Login

    Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.

    If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!

    Proget 6.0.4: can't remove docker image blob via API

    Scheduled Pinned Locked Moved Support
    13 Posts 5 Posters 48 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • araxnid_6067A Offline
      araxnid_6067 @atripp
      last edited by araxnid_6067

      @atripp thanks. yes, it works and clean up some blobs from disk.

      but I found on the disk a very old blobs that were created more than a year ago. Is it possible to see in the database or through the API where this blobs is used?

      1 Reply Last reply Reply Quote 0
      • Dan_WoolfD Offline
        Dan_Woolf inedo-engineer
        last edited by

        Hi @araxnid_6067,

        You can see where those blobs are used by querying the [DockerImageLayers_Extended] view in SQL server. The file name should match the [Blob_Digest] column and that will show you which repository that uses. If you need to see tags too, you will need to join that with [DockerRepositoryTags_Extended] view on the [DockerRepository_Id] column.

        Hope this helps!

        Thanks,
        Dan

        araxnid_6067A 1 Reply Last reply Reply Quote 0
        • araxnid_6067A Offline
          araxnid_6067 @Dan_Woolf
          last edited by

          @Dan_Woolf thank you.

          so if I don't find a blob in DockerImageLayers_Extended it can be removed from disk manually ?

          atrippA 1 Reply Last reply Reply Quote 0
          • atrippA Offline
            atripp inedo-engineer @araxnid_6067
            last edited by

            @araxnid_6067 is it in the [DockerBlobs] table? If not, then ProGet doesn't know about it, and then it's safe to delete

            Otherwise, it might still be referenced by a manifest, but ProGet doesn't have that relation in the database.

            You'd have to parse [ManifestJson_Bytes] to find out. If you're comfortable with SQL, you could do a "hack" query to convert that column a VARCHAR, then use OPENJSON or a LIKE query to search all manifests for that digest.

            Howver, that's what ProGet does during feed cleanup.

            1 Reply Last reply Reply Quote 0
            • araxnid_6067A Offline
              araxnid_6067
              last edited by

              @atripp @Dan_Woolf thank you both so much!

              1 Reply Last reply Reply Quote 0
              • Dan_WoolfD Offline
                Dan_Woolf inedo-engineer
                last edited by

                Hi @araxnid_6067,

                We are always happy to help. Please let us know if you have any other questions for us!

                Thanks,
                Dan

                1 Reply Last reply Reply Quote 0
                • P Offline
                  pariv_0352
                  last edited by pariv_0352

                  I face the same problem (probably).
                  Even if I delete all images that use blob, it won't delete blob from disk during feed cleanup.
                  I checked digest in [DockerBlobs_Usage] - no entries, parsed [DockerImages].[ManifestJson_Bytes] for that digest - no entries.
                  It exists only in [DockerBlobs] and if i try to look for it in any other tables which I can join via [DockerBlobs].[DockerBlob_Id] - failed to find any records.

                  And that's not only one blob, looks like all unused blobs never deleted from storage.
                  I use windows images, they are huge, so I want to reclaim space from deleted images.

                  I use proget 6.0.10

                  atrippA 1 Reply Last reply Reply Quote 0
                  • atrippA Offline
                    atripp inedo-engineer @pariv_0352
                    last edited by

                    Hi @pariv_0352 ,

                    Are you able to see the results of the "DockerGarbageCollection" job?

                    This is actually what's responsible for deleting those images, and it runs nightly by default.

                    Let me share the code to it; if you can understand the database structure already, then hopefully it will help you to identify why it's not workikng, and what you might be able to look at in the logs to help troubleshoot:

                    [ScheduledTaskProperties(
                    ScheduledTaskTypes.DockerGarbageCollection,
                    "Deletes unreferenced Docker blobs.")]
                    public sealed class DockerGarbageCollectionTask : ScheduledTaskBase
                    {
                        public override Task ExecuteAsync(ScheduledTaskContext context) => this.GarbageCollectAsync();
                    
                        private async Task GarbageCollectAsync()
                        {
                            using var db = new DB.Context();
                    
                            this.PercentComplete = 0;
                    
                            var usedBlobs = new HashSet<DockerDigest>();
                    
                            this.LogDebug("Gathering list of all rooted blobs...");
                    
                            foreach (var image in db.DockerImages_GetImages(Feed_Id: null))
                            {
                                if (image.ContainerConfigBlob_Id.HasValue)
                                    usedBlobs.Add(image.ContainerConfigBlobDigest);
                    
                                DockerManifest manifest;
                                try
                                {
                                    manifest = new DockerManifest(image.ManifestJson_Bytes);
                                }
                                catch (Exception ex)
                                {
                                    this.LogError($"Image {image.Image_Digest} has invalid manifest: {ex.Message}");
                                    continue;
                                }
                    
                                // "Fat images" do not have blobs as layers.
                                if (manifest.Layers == null)
                                    continue;
                    
                                foreach (var l in manifest.Layers)
                                    usedBlobs.Add(l.Digest);
                            }
                    
                            this.LogDebug($"Found total of {usedBlobs.Count} rooted blobs; finding unreferenced blobs...");
                    
                            var allBlobs = (await db.DockerBlobs_GetBlobsAsync(Feed_Id: null))
                                .Where(b => b.Feed_Id == null)
                                .Select(b => DockerDigest.Parse(b.Blob_Digest));
                    
                            var unreferencedBlobs = allBlobs
                                .Where(d => !usedBlobs.Contains(d))
                                .ToList();
                    
                            this.LogDebug($"Found {unreferencedBlobs.Count} unreferenced blobs.");
                    
                            using var fileSystem = new DirectoryFileSystem(ProGetConfig.Storage.DockerBlobStorageLibrary);
                    
                            for (int i = 0; i < unreferencedBlobs.Count; i++)
                            {
                                this.PercentComplete = (i + 1) * 100 / unreferencedBlobs.Count;
                    
                                var digest = unreferencedBlobs[i];
                                this.LogInformation($"Deleting blob {digest}...");
                                if (!ProGetConfig.Feeds.RetentionDryRun)
                                {
                                    await db.DockerBlobs_DeleteBlobAsync(Feed_Id: null, Blob_Digest: digest.ToString());
                                    await fileSystem.DeleteDockerBlobAsync(digest);
                                }
                            }
                        }
                    }
                    
                    P 1 Reply Last reply Reply Quote 0
                    • P Offline
                      pariv_0352 @atripp
                      last edited by

                      Hi @atripp.

                      Looks like I misundestood logic of "FeedCleanup" job. Every time I run that job, it always says that Found 0 unreferenced blobs. and does nothing.
                      But "DockerGarbageCollection" job (which I didn't notice before you said about it) really cleaned storage from unused blobs.

                      What for FeedCleanup job then? I checked logs, it never found anything unreferenced.

                      1 Reply Last reply Reply Quote 0
                      • rhessingerR Offline
                        rhessinger inedo-engineer
                        last edited by

                        Hi @pariv_0352,

                        The "FeedCleanup" job will do two main things; clearing the Multipart Upload Temp files and running retention rules. For Docker feeds, it will also run the deletion of blobs, but only for Feed specific blobs. If you are using common blob storage (which is enabled by default in feeds created in ProGet 5.3+), then that is where the "DockerGarbageCollection" job comes into play. That one handles cleaning up/deleting the blobs that are shared across multiple feeds.

                        Hope this helps!

                        Thanks,
                        Rich

                        Products Engineer, Inedo

                        P 1 Reply Last reply Reply Quote 0
                        • P Offline
                          pariv_0352 @rhessinger
                          last edited by

                          Thank you, @rhessinger, for clarification.

                          Yes, I use common blob storage, and all that makes sense now.

                          1 Reply Last reply Reply Quote 0

                          Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                          Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                          With your input, this post could be even better 💗

                          Register Login
                          • 1 / 1
                          • First post
                            Last post
                          Inedo Website Home • Support Home • Code of Conduct • Forums Guide • Documentation