Declarative pipeline, apply a particular tag to images built by agent -> dockerfile

Hi All,
I would like to keep clean my jenkins infrastructure also for storage space matter, composed by controller and 2 jenkins agents I use agents mainly with docker or dockerfile, to do so, I implemented a scheduled job on jenkins that loops on every agent and get rid of unused images created more than 720h ago, with this command:

docker system prune -f  --filter "until=720h"

This is almost ok, the problem it is if I currently use again an old image in my jenkins, I remove it; I do the most is like this for agent configuration:

agent {
                        dockerfile {
                            label 'general'
                            reuseNode true
                            filename 'Dockerfile'
                            dir 'ci_dockerfiles/build_wheel'
                    }

So I thought that if I am able, since Jenkins build this image during pipeline (if missing) could be a nice thing to apply a tag to the image, if there is a dockerfile option, that includes for example:

jenkins_<project_name>_<stage_name>

Since, for what I know, if another image holds the same tag, it loses, so I can safely add something like `–filter label!=jenkins_*’ to the previous prune command and I will be sure no image still used in jenkins will be removed but only images created outside jenkins or images that lost the tag.

This idea is mine, if you think there is another better way to do it, I will listen your advices.

Thanks in advance,

Jenkins setup:
Version 2.440.2 used inside docker compose

Hello @magowiz and welcome to this community. :wave:

Your idea of tagging the images built by Jenkins and then excluding them from the prune command using a filter looks good to me.
This should ensure that the images currently in use by Jenkins are not removed.

To implement this, I think you could modify your Jenkins pipeline to tag the images it builds.
You could use the docker.build command in your Jenkins pipeline script to build and tag the image, such as:

stage('Build Docker Image') {
    steps {
        script {
            def dockerImage = docker.build("jenkins_${env.JOB_NAME}_${env.STAGE_NAME}")
        }
    }
}

In this example, docker.build is used to build the Docker image and assign it a tag.
The tag is composed of the string “jenkins”, the name of the Jenkins job (env.JOB_NAME), and the name of the current stage (env.STAGE_NAME).
This should result (I haven’t tested) in tags like “jenkins_my-job_my-stage”.

You could then modify your prune command to exclude images with these tags:

docker system prune -f --filter "until=720h" --filter "label!=jenkins_*"

This command should remove all unused images that were created more than 720 hours ago and do not have a label that starts with “jenkins_”. :thinking:

How the ‘Build Docker Image’ stage will get the information about the docker image to build? Is it possible to refer to another stage dockerfile section?
In mine pipeline mainly I have one dockerfile definition for each stage, I don’t think I have to define the agent in the stage in the same way, otherwise gets executed by another docker container.

For what I understood from your code I need to implement a new stage before others, to do this, hopefully looping for each stage that ships a dockerfile agent and build all before starting using. But I don’t know how can I reference other stage dockerfile block. What do you think?

I completed this, I’m writing how I did just in case someone may find it useful:
In global environment variables I declared a comma-separated list which includes all folders in which I placed Dockerfiles to build, I already had a variable containing project name:

environment {
     PROJECT_NAME = "foo"
     Dockerfolders = "build_wheel,buildozer,generate_doc,megalinter,pytest_x11"
}

then I created a stage that I put as first one in the Jenkinsfile, like this:

stage ('docker build') {
            agent {
                label 'amd64'
            }
            steps {
                script {
                    env.Dockerfolders.tokenize(",").each { item ->
                        docker_path = "ci_dockerfiles/${item}"
                        sh "echo ci_dockerfiles/${item}"
                        sh "echo $docker_path"
                        sh "echo jenkins_${env.PROJECT_NAME}_${env.BRANCH_NAME}_${item}"
                        def dockerImage = docker.build("jenkins_${env.PROJECT_NAME}_${env.BRANCH_NAME}_${item}",
                                                       "$docker_path")
                    }
                }
            }
        }

as you may see I iterate my string and for each item I invoke a docker build in proper path and I use a combination of jenkins_<project_name>_<branch_name>_<folder_name> to set the image name.
Unlikely docker system prune and docker image ls doesn’t allow you to invert selection on name (repository) so in order to remove the not-matching ones I had to produce a text file with all images, another with jenkins in the name (ones to keep) and subtract the lines from second file to the first one to have a list of images to delete, then loop across the resulting file and issue docker image rm on each file line.
I know this last part is a bit out of scope, by the way I thought that at least describing it could be useful.