Prune unused images correctly

dangling=true doesn't prune any images, as we are not creating dangling
images.

Using --all should remove unused images, but it considers the Git SHA
tag on the latest image to be unused (presumably because there are two
tags, the SHA and latest and the running container is only considered to
be using "latest"). As a result it deletes the tag, which means that we
can't rollback to that SHA later.

Its a bit more complicated to only remove images that are not referenced
by any containers.

First we find the tags we want to keep from the containers (running and
stopped).

Then we append the latest tag to that list.

Then we get a full list of image tags and remove those tags from that
list (using `grep -v -w`).

Finally we pass the tags to `docker rmi`. That either deletes the tag if
there are other references to the image or both the tag and the image if
it is the only one.
This commit is contained in:
Donal McBreen
2023-05-25 16:32:54 +01:00
parent 602aa43496
commit ff7a1e6726
3 changed files with 15 additions and 4 deletions

View File

@@ -3,12 +3,15 @@ require "active_support/core_ext/numeric/time"
class Mrsk::Commands::Prune < Mrsk::Commands::Base class Mrsk::Commands::Prune < Mrsk::Commands::Base
def images def images
docker :image, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true" pipe \
docker(:image, :ls, *service_filter, "--format", "'{{.Repository}}:{{.Tag}}'"),
"grep -v -w \"#{active_image_list}\"",
"while read tag; do docker rmi $tag; done"
end end
def containers(keep_last: 5) def containers(keep_last: 5)
pipe \ pipe \
docker(:ps, "-q", "-a", "--filter", "label=service=#{config.service}", *stopped_containers_filters), docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
"tail -n +#{keep_last + 1}", "tail -n +#{keep_last + 1}",
"while read container_id; do docker rm $container_id; done" "while read container_id; do docker rm $container_id; done"
end end
@@ -17,4 +20,12 @@ class Mrsk::Commands::Prune < Mrsk::Commands::Base
def stopped_containers_filters def stopped_containers_filters
[ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] } [ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] }
end end
def active_image_list
"$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=#{config.service} | tr -d '\\n')#{config.latest_image}"
end
def service_filter
[ "--filter", "label=service=#{config.service}" ]
end
end end

View File

@@ -10,7 +10,7 @@ class CliPruneTest < CliTestCase
test "images" do test "images" do
run_command("images").tap do |output| run_command("images").tap do |output|
assert_match /docker image prune --force --filter label=service=app --filter dangling=true on 1.1.1.\d/, output assert_match "docker image ls --filter label=service=app --format '{{.Repository}}:{{.Tag}}' | grep -v -w \"$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=app | tr -d '\\n')dhh/app:latest\" | while read tag; do docker rmi $tag; done on 1.1.1.", output
end end
end end

View File

@@ -10,7 +10,7 @@ class CommandsPruneTest < ActiveSupport::TestCase
test "images" do test "images" do
assert_equal \ assert_equal \
"docker image prune --force --filter label=service=app --filter dangling=true", "docker image ls --filter label=service=app --format '{{.Repository}}:{{.Tag}}' | grep -v -w \"$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=app | tr -d '\\n')dhh/app:latest\" | while read tag; do docker rmi $tag; done",
new_command.images.join(" ") new_command.images.join(" ")
end end