Building a container image and re-using pulled layers - docker

Suppose there is a container image published to a Docker registry, and I want to rebuild the image with modifications while re-using as many of the published layers as possible.
Concretely, suppose the image foo/bar is built from this Dockerfile, and I only want to modify the layer that contains script.sh:
FROM ubuntu:focal
COPY script.sh .
Even though pulling the image downloads the layers for ubuntu:focal, when I re-build my local machine may resolve the ubuntu:focal tag to another version, producing a new image with no common layers with the one I pulled.
6a9e8d7 <foo/bar:new>
<foo/bar:old> c7632a5 |
| |
+----+----+
|
3b15784 <ubuntu:focal (then)>
|
...
DESIRABLE
<foo/bar:old> c7632a5 48fead0 <foo/bar:new>
| |
| |
<ubuntu:focal (then)> 3b15784 9a634f5 <ubuntu:focal (now)>
| |
... ...
UNDESIRABLE
The desired outcome could possibly be achieved by looking at the pulled layers and tagging the correct one (3b15784) as ubuntu:focal before building. But I'm not sure if Docker exposes enough information to do this in an automatic way.

As a workaround, I could explicitly include the base image's digest as a label on the built image:
FROM ${UBUNTU_IMAGE_ID:-ubuntu:focal}
COPY script.sh .
Then I would build with:
# First build
UBUNTU_IMAGE_ID=$( \
docker inspect \
--format '{{ index .RepoTags 0 }}#{{ index . "Id" }}' \
ubuntu:focal \
)
# Subsequent builds
docker pull foo/bar:old
UBUNTU_IMAGE_ID=$( \
docker inspect \
--format '{{ index .Config.Labels "ubuntu_image_id" }}' \
foo/bar:old \
)
docker build . \
--build-arg "UBUNTU_IMAGE_ID=${UBUNTU_IMAGE_ID}" \
--label "ubuntu_image_id=${UBUNTU_IMAGE_ID}" \
--tag foo/bar:new
However, a more elegant solution would definitely be welcome, especially one that does not involve any specific assumptions about how the original pulled image was built.

Maybe Docker supports this built in with --cache-from:
docker pull foo/bar:old
docker build . \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--cache-from foo/bar:old
--tag foo/bar:new

Related

Build all containers before pushing to registry using Buildkit

I've the following Dockerfile:
FROM php:7.4 as base
RUN apt-get install -y libzip-dev && \
docker-php-ext-install zip
# install some other things...
FROM base as intermediate
COPY upgrade.sh /usr/local/bin
FROM base as final
COPY start-app.sh /usr/local/bin
As you can see, I've 3 stages:
base
intermedia
final
At first, I'm building the base container and then both "derived" containers. My problem is that I need to push the intermediate and the final container to my (gitlab) registry. The containers are built using the following gitlab-ci.yml:
.build_container: &build_container
stage: build
image:
name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/moby/buildkit:rootless
entrypoint: ["sh", "-c"]
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
script:
- mkdir ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)\"}}}" > ~/.docker/config.json
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=${CI_PROJECT_DIR} \
--local dockerfile=${CI_PROJECT_DIR} \
--opt filename=./${DOCKERFILE} \
--import-cache type=registry,ref=${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG} \
--export-cache type=inline \
--output type=image,name=${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG},push=true
Build:Container:
<<: *build_container
variables:
DOCKERFILE: "Dockerfile"
Ok, I can simply add another "buildctl-daemonless.sh"-command and use a separate file, but I want to make sure that both containers (intermediate and final) are build successfully before pushing them. So I'm looking for a solution that builds the intermediate and the final containers at first and then pushing both to the registry, e.g. something like this:
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=${CI_PROJECT_DIR} \
--local dockerfile=${CI_PROJECT_DIR} \
--opt filename=./${DOCKERFILE} \
--import-cache type=registry,ref=${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG} \
--export-cache type=inline
buildctl-daemonless.sh push intermediate final
Unfortunately there's no "push" command in buildctl-daemonless.sh as far as I know. So es anyone having an idea how I can to this?
Directly from buildkit, I don't think there's a separate push command. The output of a multi-platform image is usually directly to a registry, but could also be an OCI Layout tar file. To write to that tar file, you would add the following option:
--output type=oci,dest=path/to/output.tar
With that tar file, there are various standalone tools that can help. Pretty sure RedHat's skopeo and Google's go-containerregistry/crane both have options. My own tool is regclient which includes the following regctl command:
regctl image import ${image_name_tag} path/to/output.tar
There's a regclient/regctl:alpine image that's made to be embedded in CI pipelines like GitLab, and it will read registry auths from the same ~/.docker/config.json.

How to delete old image that created in build process automaticly?

I have a ci/cd automation in my project with gitlab, and after push my code on master branch, gitlab runner create a new docker image on my server and set latest commit hash as tag on that image, and recreate container with new image. after a while, there is a lot of unused image, and I want to delete them automaticly.
I delete old image manually.
This is my Makefile
NAME := farvisun/javabina
TAG := $$(git log -1 --pretty=%h)
IMG := ${NAME}:${TAG}
LATEST := ${NAME}:latest
app_build:
docker build -t ${IMG} -f javabina.dockerfile . && \
docker tag ${IMG} ${LATEST}
app_up:
docker-compose -p farvisun-javabina up -d javabina
And after all of this, I want a simple bash code, or other tools, to delete unused image, for example keep 3 lastest image, or keep last 2 day past builds, and delete others.
If you are fine with keeping a single image, you can use docker image prune -f, which will remove all images but the ones associated with a container, so if you run this command while the container is running, it will remove the rest of the images.
Don't forget to run docker system prune every often as well to further reduce storage usage.
In your situation where you need to keep more than one image, you can try this:
#!/bin/bash
for tag in $(docker image ls | sed 1,4d | awk '{print $3}')
do
docker image rm -f $tag
done
The first line will list all docker images, remove from the list the first 3 ones which you want to keep, and select only the column with the image ID. Then, for each of the IDs, we remove the image.
If you want to remove more, change the 4d for another number. Notice that the first line is a header, so it must always be removed.
If you want to filter the images by a tag first, you can make your own filters.
You can schedule (e.g. once a day or once a week) in the compilation machine a "docker image prune" command.
https://docs.docker.com/engine/reference/commandline/image_prune/
Here is a way we can remove old images, after a new successful build in our pipeline
# build
- docker build -t APP_NAME:$(git describe --tags --no-abbrev) .
# docker tag
- docker tag APP_NAME:$(git describe --tags --no-abbrev) artifactory.XYZ.com/APP_NAME:latest
# remote old images
- sha=$(docker image inspect artifactory.XYZ.com/APP_NAME:latest -f '{{.ID}}')
- image_sha=$(echo $sha | cut -d':' -f2)
- image_id=$(echo $image_sha | head -c 12)
- docker image ls | grep APP_NAME | while read name tag id others; do if ! [ $id = $image_id ]; then docker image rm --force $id; fi ; done

Dockerhub: listing all available versions of a given image?

I'm looking for a way to list all publicly available versions of an image from Dockerhub. Is there a way this could be achieved?
Specifically, I'm interested in the openjdk:8-jdk-alpine images.
Dockerhub typically only lists the latest version of each image, and there are no linking to historic versions. For openjdk, it's currently 8u191-jdk-alpine3.8:
However, it possible to pull older versions if we know their image digest ID:
openjdk:8-jdk-alpine#sha256:1fd5a77d82536c88486e526da26ae79b6cd8a14006eb3da3a25eb8d2d682ccd6
openjdk:8-jdk-alpine#sha256:c5c705b462abab858066d412b3f871865684d8f837571c98b68e78c505dc7549
With some luck, I was able to find these digests for OpenJDK 8 (Java versions 1.8.0_171 and 1.8.0_151 respectively), by googling openjdk8 alpine digest and looking at github tickets, which included the image digest.
But, is there a systematic way for listing all publicly available digests?
Looking at docker search documentation, there's doesn't seem to be an option for listing the image version, only search by name.
You don't need digests to pull "old" images, you would rather use their tags (even if they are not displayed in Docker Hub).
I use the following command to retrieve tags of a particular image, parsing the output of https://registry.hub.docker.com/v1/repositories/$REPOSITORY/tags :
REPOSITORY=openjdk # can be "<registry>/<image_name>" ("google/cloud-sdk" for example)
wget -q https://registry.hub.docker.com/v1/repositories/$REPOSITORY/tags -O - | \
jq -r '.[].name'
Result for REPOSITORY=openjdk (1593 tags at the time of writing) looks like :
latest
10
10-ea
10-ea-32
10-ea-32-experimental
10-ea-32-jdk
10-ea-32-jdk-experimental
10-ea-32-jdk-slim
10-ea-32-jdk-slim-experimental
10-ea-32-jre
[...]
If you can't/don't want to install jq (tool to manipulate JSON), then you could use :
wget -q https://registry.hub.docker.com/v1/repositories/$REPOSITORY/tags -O - | \
sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | \
tr '}' '\n' | \
awk -F: '{print $3}'
(I'm pretty sure I got this command from another question, but I can't find where)
You can of course filter the output of this command and keep only tags you're interested in :
wget -q https://registry.hub.docker.com/v1/repositories/$REPOSITORY/tags -O - | \
jq -r '.[].name | select(match("^8.*jdk-alpine"))'
or :
wget -q https://registry.hub.docker.com/v1/repositories/$REPOSITORY/tags -O - | \
jq -r '.[].name' \
grep -E '^8.*jdk-alpine'

Docker image size

I pull a Docker image of about 0.5 GB in size on the Docker Hub. After pulling it on my Centos machine the size of the image became 1.6 GB. Pushing the image with a new name show 2 GB on the Docker hub.
How can I obtain an image with the same size on the Docker hub?
There are several ways to achieve that, depending on which repository you are querying.
TLDR; See this repo: schnatterer/docker-image-size.
Details:
The former two work for DockerHub and returned same size for the test image debian:stretch-20190204-slim on DockerHub:
I didn't find a working solution for quay.io and for private Repos in general, yet.
docker manifest
Requires jq.
export DOCKER_CLI_EXPERIMENTAL=enabled
echo $(( ( $(docker manifest inspect -v docker.io/debian:stretch-20190204-slim \
| jq '.[] | select(.Descriptor.platform.architecture == "amd64").SchemaV2Manifest.layers[0].size') \
+ 500000) \
/ 1000 \
/ 1000)) MB
23 MB
If you care about the details:
export DOCKER_CLI_EXPERIMENTAL=enabled - docker manifest is still considered experimental (as of 18.06.1-ce). Can be made permanent in ~/.docker/config.json - See docker manifest | Docker Documentation
docker manifest inspect -v docker.io/debian:stretch-20190204-slim - queries verbose manifest from docker hub
jq '.[] | select(.Descriptor.platform.architecture == "amd64").SchemaV2Manifest.layers[0].size - filters the compressed size of all layers (bytes)
+ 500000 - for rounding
/1000 - divide to KB/MB
reg
Requires reg and jq.
➜ echo $(( ( $(reg manifest debian:stretch-20190204-slim | \
jq '.layers[].size' \
| paste -sd+ | bc) \
+ 500000) \
/ 1000 \
/ 1000)) MB
23 MB
If you care about the details:
reg manifest debian:stretch-20190204-slim - queries manifest from docker hub
jq '.layers[].size' - filters the compressed size of each layer (bytes)
paste -sd+ | bc - creates a sum of the individual sizes (bytes)
+ 500000 - for rounding
/1000 - divide to KB/MB
curl
reg and docker manifest didn't work out of the box for some other repos.
Here, curl might be sufficient, however. This, OTOH, is a bit more difficult with docker hub (because we need a token).
➜ echo $(( ( $(curl -s https://gcr.io/v2/distroless/java/manifests/11-debug | \
jq '.layers[].size' \
| paste -sd+ | bc) \
+ 500000) \
/ 1000 \
/ 1000)) MB
69 MB
Further Registry examples:
https://quay.io/v2/calico/node/manifests/v3.2.4-2-g41efb10-amd64
https://mcr.microsoft.com/v2/dotnet/core/samples/manifests/aspnetapp
A similar issue was reported in issue 14204, for docker 1.7.0 on Ubuntu.
(And by default, CentOS might not have the latest version of docker, so the first step would be to upgrade if possible)
The questions to check were:
How did you install docker?
Can you provide the list the steps to reproduce the issue?
Can you post the output from sudo du -sh /var/lib/docker/*
You are using devicemapper as a storage driver, can you try using aufs?
For the last point, as described in this article, check /etc/default/docker
# Use DOCKER_OPTS to modify the daemon startup options.
DOCKER_OPTS="--storage-driver=devicemapper"
As the OP Dragomir Adrian confirms in the comments, it is a docker version issue: upgrading to 1.9.1 helps.

How to delete images from a private docker registry?

I run a private docker registry, and I want to delete all images but the latest from a repository. I don't want to delete the entire repository, just some of the images inside it. The API docs don't mention a way to do this, but surely it's possible?
Currently you cannot use the Registry API for that task. It only allows you to delete a repository or a specific tag.
In general, deleting a repository means, that all the tags associated to this repo are deleted.
Deleting a tag means, that the association between an image and a tag is deleted.
None of the above will delete a single image. They are left on your disk.
Workaround
For this workaround you need to have your docker images stored locally.
A workaround for your solution would be to delete all but the latest tags and thereby potentially removing the reference to the associated images. Then you can run this script to remove all images, that are not referenced by any tag or the ancestry of any used image.
Terminology (images and tags)
Consider an image graph like this where the capital letters (A, B, ...) represent short image IDs and <- means that an image is based on another image:
A <- B <- C <- D
Now we add tags to the picture:
A <- B <- C <- D
| |
| <version2>
<version1>
Here, the tag <version1> references the image C and the tag <version2> references the image D.
Refining your question
In your question you said that you wanted to remove
all images but the latest
. Now, this terminology is not quite correct. You've mixed images and tags. Looking at the graph I think you would agree that the tag <version2> represents the latest version. In fact, according to this question you can have a tag that represents the latest version:
A <- B <- C <- D
| |
| <version2>
| <latest>
<version1>
Since the <latest> tag references image D I ask you: do you really want to delete all but image D? Probably not!
What happens if you delete a tag?
If you delete the tag <version1> using the Docker REST API you will get this:
A <- B <- C <- D
|
<version2>
<latest>
Remember: Docker will never delete an image! Even if it did, in this case it cannot delete an image, since the image C is part of the ancestry for the image D which is tagged.
Even if you use this script, no image will be deleted.
When an image can be deleted
Under the condition that you can control when somebody can pull or push to your registry (e.g. by disabling the REST interface). You can delete an image from an image graph if no other image is based on it and no tag refers to it.
Notice that in the following graph, the image D is not based on C but on B. Therefore, D doesn't depend on C. If you delete tag <version1> in this graph, the image C will not be used by any image and this script can remove it.
A <- B <--------- D
\ |
\ <version2>
\ <latest>
\ <- C
|
<version1>
After the cleanup your image graph looks like this:
A <- B <- D
|
<version2>
<latest>
Is this what you want?
I've faced the same problem with my registry, then I tried the solution listed below from a blog page. It works.
Do note, the deletion must be enabled for it to work. You can do it by providing a custom config, or by setting REGISTRY_STORAGE_DELETE_ENABLED=true.
Step 1: List the repositories
$ curl -sS <domain-on-ip>:5000/v2/_catalog
The response will be in the following format:
{
"repositories": [
<repo>,
...
]
}
Step 2: List the repository tags
$ curl -sS <domain-on-ip>:5000/v2/<repo>/tags/list
The response will be in the following format:
{
"name": <repo>,
"tags": [
<tag>,
...
]
}
Step 3: Determine the digest of the target tag
$ curl -sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
-o /dev/null \
-w '%header{Docker-Content-Digest}' \
<domain-or-ip>:5000/v2/<repo>/manifests/<tag>
Do note, the Accept header is needed here. Without it you'll get a different value and the deletion will fail.
The response will be in the following format:
sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073
Step 4: Delete the manifest
$ curl -sS -X DELETE <domain-or-ip>:5000/v2/<repo>/manifests/<digest>
Step 5: Garbage collect the image
Run this command in your docker registry container:
$ registry garbage-collect /etc/docker/registry/config.yml
Here is my config.yml:
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
The current v2 registry now supports deleting via DELETE /v2/<name>/manifests/<reference>.
See: https://github.com/docker/distribution/blob/master/docs/spec/api.md#deleting-an-image
The <reference> can be taken from the Docker-Content-Digest header of a GET /v2/<name>/manifests/<tag> request (do note that the Accept: application/vnd.docker.distribution.manifest.v2+json header is needed for this request).
A script that makes use of it: https://github.com/byrnedo/docker-reg-tool
For it to work deletion must be enabled (REGISTRY_STORAGE_DELETE_ENABLED=true).
And to really free the disk space you need to run garbage collection.
Problem 1
You mentioned it was your private docker registry, so you probably need to check Registry API instead of Hub registry API doc, which is the link you provided.
Problem 2
docker registry API is a client/server protocol, it is up to the server's implementation on whether to remove the images in the back-end. (I guess)
DELETE /v1/repositories/(namespace)/(repository)/tags/(tag*)
Detailed explanation
Below I demo how it works now from your description as my understanding for your questions.
I run a private docker registry.
I use the default one, and listen on port 5000.
docker run -d -p 5000:5000 registry
Then I tag the local image and push into it.
$ docker tag ubuntu localhost:5000/ubuntu
$ docker push localhost:5000/ubuntu
The push refers to a repository [localhost:5000/ubuntu] (len: 1)
Sending image list
Pushing repository localhost:5000/ubuntu (1 tags)
511136ea3c5a: Image successfully pushed
d7ac5e4f1812: Image successfully pushed
2f4b4d6a4a06: Image successfully pushed
83ff768040a0: Image successfully pushed
6c37f792ddac: Image successfully pushed
e54ca5efa2e9: Image successfully pushed
Pushing tag for rev [e54ca5efa2e9] on {http://localhost:5000/v1/repositories/ubuntu/tags/latest}
After that I can use Registry API to check it exists in your private docker registry
$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags
{"latest": "e54ca5efa2e962582a223ca9810f7f1b62ea9b5c3975d14a5da79d3bf6020f37"}
Now I can delete the tag using that API !!
$ curl -X DELETE localhost:5000/v1/repositories/ubuntu/tags/latest
true
Check again, the tag doesn't exist in my private registry server
$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}
This is really ugly but it works, text is tested on registry:2.5.1.
I did not manage to get delete working smoothly even after updating configuration to enable delete. The ID was really difficult to retrieve, had to login to get it, maybe some misunderstanding. Anyway, the following works:
Enter the container
docker exec -it registry sh
Define variables matching your container and container version:
export NAME="google/cadvisor"
export VERSION="v0.24.1"
Move to the the registry directory:
cd /var/lib/registry/docker/registry/v2
Delete files related to your hash:
find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
Delete manifests:
rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
Logout
exit
Run the GC:
docker exec -it registry bin/registry garbage-collect /etc/docker/registry/config.yml
If all was done properly some information about deleted blobs is shown.
There are some clients (in Python, Ruby, etc) which do exactly that. For my taste, it isn't sustainable to install a runtime (e.g. Python) on my registry server, just to housekeep my registry!
So deckschrubber is my solution:
go install github.com/fraunhoferfokus/deckschrubber#latest
$GOPATH/bin/deckschrubber
images older than a given age are automatically deleted. Age can be specified using -year, -month, -day, or a combination of them:
$GOPATH/bin/deckschrubber -month 2 -day 13 -registry http://registry:5000
UPDATE: here's a short introduction on deckschrubber.
The requirement to delete all tags except latest gets complicated because the same image manifest can be pointed to by multiple tags, so when you delete a manifest for one tag, you may effectively delete multiple tags.
There are a few options to make that workable. One is to track the digest for the latest tag and only delete manifests for other digests, or you can use some different API calls to delete the tags themselves.
Regardless of how you implement this, first your registry needs to be configured to allow the delete API's. With the minimal registry:2 image, that involves starting it with an environment variable REGISTRY_STORAGE_DELETE_ENABLED=true (or the equivalent yaml config).
Then for a simple script to loop through the tags and delete, there's:
#!/bin/sh
repo="localhost:5000/repo/to/prune"
for tag in $(regctl tag ls $repo); do
if [ "$tag" != "latest" ]; then
echo "Deleting: $(regctl image digest --list "${repo}:${tag}") [$tag]"
regctl tag rm "${repo}:${tag}"
fi
done
The regctl command used here comes from regclient and the regctl tag rm logic first attempts to perform the tag delete API added recently to the distribution-spec. Since most registries haven't implemented that spec, it falls back to the manifest delete API, but it first creates a dummy manifest to overwrite the tag, and then deletes that new digest. In doing so, if the old manifest was in use by other tags, it doesn't delete those other tags.
An alternative version of the script that deletes manifests except those pointing to the latest digest looks like:
#!/bin/sh
repo="localhost:5000/repo/to/prune"
save="$(regctl image digest --list "${repo}:latest")"
for tag in $(regctl tag ls $repo); do
digest="$(regctl image digest --list "${repo}:${tag}")"
if [ "$digest" != "$save" ]; then
echo "Deleting: $digest [$tag]"
regctl manifest rm "${repo}#${digest}"
fi
done
If you find yourself needing to create a deletion policy to automate the deleting of lots of images, I'd recommend looking at regclient/regbot from the same repo which allows you to define that policy and leave it running to continuously prune your registry.
Once the images have been deleted, you'll need to garbage collect your registry in most use cases. For example with the registry:2 image that looks like:
docker exec registry /bin/registry garbage-collect \
/etc/docker/registry/config.yml --delete-untagged
Briefly;
1) You must typed following command for RepoDigests of a docker repo;
## docker inspect <registry-host>:<registry-port>/<image-name>:<tag>
> docker inspect 174.24.100.50:8448/example-image:latest
[
{
"Id": "sha256:16c5af74ed970b1671fe095e063e255e0160900a0e12e1f8a93d75afe2fb860c",
"RepoTags": [
"174.24.100.50:8448/example-image:latest",
"example-image:latest"
],
"RepoDigests": [
"174.24.100.50:8448/example-image#sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6"
],
...
...
${digest} =
sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6
2) Use registry REST API
##curl -u username:password -vk -X DELETE registry-host>:<registry-port>/v2/<image-name>/manifests/${digest}
>curl -u example-user:example-password -vk -X DELETE http://174.24.100.50:8448/v2/example-image/manifests/sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6
You should get a 202 Accepted for a successful invocation.
3-) Run Garbage Collector
docker exec registry bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml
registry — registry container name.
For more detail explanation enter link description here
Another tool you can use is registry-cli. For example, this command:
registry.py -l "login:password" -r https://your-registry.example.com --delete
will delete all but the last 10 images.
There is also a way you can remove some old images from repository just based on the date when it was created.
To do that enter your docker registry container and get the list of manifest's revisions for some specific repository:
ls -latr /var/lib/registry/docker/registry/v2/repositories/YOUR_REPO/_manifests/revisions/sha256/
The output then may be used within the request (with sha256 prefix):
curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://DOCKER_REGISTRY_HOST:5000/v2/YOUR_REPO/manifests/sha256:OUTPUT_LINE
And of course do not forget to execute 'garbage-collect' command after that:
bin/registry garbage-collect /etc/docker/registry/config.yml
I am usually all for doing things with scripts, but if you are already running
a registry UI container built from Joxit/docker-registry-ui, I found it easier to just opt-click the delete button in the UI and delete a page of images at a time, then garbage collect after.
This docker image includes a bash script that can be used to remove images from a remote v2 registry :
https://hub.docker.com/r/vidarl/remove_image_from_registry/
Below Bash Script Deletes all the tags located in registry except the latest.
for D in /registry-data/docker/registry/v2/repositories/*; do
if [ -d "${D}" ]; then
if [ -z "$(ls -A ${D}/_manifests/tags/)" ]; then
echo ''
else
for R in $(ls -t ${D}/_manifests/tags/ | tail -n +2); do
digest=$(curl -k -I -s -H -X GET http://xx.xx.xx.xx:5000/v2/$(basename ${D})/manifests/${R} -H 'accept: application/vnd.docker.distribution.manifest.v2+json' | grep Docker-Content-Digest | awk '{print $2}' )
url="http://xx.xx.xx.xx:5000/v2/$(basename ${D})/manifests/$digest"
url=${url%$'\r'}
curl -X DELETE -k -I -s $url -H 'accept: application/vnd.docker.distribution.manifest.v2+json'
done
fi
fi
done
After this Run
docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml
Simple ruby script based on this answer: registry_cleaner.
You can run it on local machine:
./registry_cleaner.rb --host=https://registry.exmpl.com --repository=name --tags_count=4
And then on the registry machine remove blobs with /bin/registry garbage-collect /etc/docker/registry/config.yml.
Here is a script based on Yavuz Sert's answer.
It deletes all tags that are not the latest version, and their tag is greater than 950.
#!/usr/bin/env bash
CheckTag(){
Name=$1
Tag=$2
Skip=0
if [[ "${Tag}" == "latest" ]]; then
Skip=1
fi
if [[ "${Tag}" -ge "950" ]]; then
Skip=1
fi
if [[ "${Skip}" == "1" ]]; then
echo "skip ${Name} ${Tag}"
else
echo "delete ${Name} ${Tag}"
Sha=$(curl -v -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://127.0.0.1:5000/v2/${Name}/manifests/${Tag} 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}')
Sha="${Sha/$'\r'/}"
curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE "http://127.0.0.1:5000/v2/${Name}/manifests/${Sha}"
fi
}
ScanRepository(){
Name=$1
echo "Repository ${Name}"
curl -s http://127.0.0.1:5000/v2/${Name}/tags/list | jq '.tags[]' |
while IFS=$"\n" read -r line; do
line="${line%\"}"
line="${line#\"}"
CheckTag $Name $line
done
}
JqPath=$(which jq)
if [[ "x${JqPath}" == "x" ]]; then
echo "Couldn't find jq executable."
exit 2
fi
curl -s http://127.0.0.1:5000/v2/_catalog | jq '.repositories[]' |
while IFS=$"\n" read -r line; do
line="${line%\"}"
line="${line#\"}"
ScanRepository $line
done
A script to remove all but the latest tag from an insecure registry (private, no auth):
#!/bin/sh -eu
repo=$1
registry=${2-localhost:5000}
tags=`curl -sS "$registry/v2/$repo/tags/list" | jq -r .tags[]`
tag2digest() {
local tag=$1
curl -sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
-o /dev/null \
-w '%header{Docker-Content-Digest}' \
"$registry/v2/$repo/manifests/$tag"
}
latest_digest=`tag2digest latest`
digests=`echo "$tags" \
| while IFS= read -r tag; do
tag2digest "$tag"
echo
done \
| sort \
| uniq`
digests=`echo "$digests" \
| grep -Fvx "$latest_digest"`
echo "$digests" \
| while IFS= read -r digest; do
curl -sS -X DELETE "$registry/v2/$repo/manifests/$digest"
done
Usage:
$ ./rm-tags.sh <image> [<registry>]
After removing tags (or manifests to be more precise) run garbage collection:
$ registry garbage-collect /etc/docker/registry/config.yml
To support Docker Hub and/or auth see these answers.

Resources