From 971a91da1569798bb10cb2cb63e64a395b18039c Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Mon, 1 May 2023 14:48:19 +0100 Subject: [PATCH] Retain a fixed number of containers when pruning Time based container and image retention can have variable space requirements depending on how often we deploy. - Only prune stopped containers, retaining the 5 newest - Then prune dangling images so we only keep images for the retained containers. --- lib/mrsk/cli/prune.rb | 4 ++-- lib/mrsk/commands/prune.rb | 16 ++++++++++++---- test/cli/prune_test.rb | 4 ++-- test/commands/prune_test.rb | 4 ++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/mrsk/cli/prune.rb b/lib/mrsk/cli/prune.rb index 6698ba27..bcfdd5bf 100644 --- a/lib/mrsk/cli/prune.rb +++ b/lib/mrsk/cli/prune.rb @@ -7,7 +7,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base end end - desc "images", "Prune unused images older than 7 days" + desc "images", "Prune dangling images" def images with_lock do on(MRSK.hosts) do @@ -17,7 +17,7 @@ class Mrsk::Cli::Prune < Mrsk::Cli::Base end end - desc "containers", "Prune stopped containers older than 3 days" + desc "containers", "Prune all stopped containers, except the last 5" def containers with_lock do on(MRSK.hosts) do diff --git a/lib/mrsk/commands/prune.rb b/lib/mrsk/commands/prune.rb index 71218cda..0ac779d5 100644 --- a/lib/mrsk/commands/prune.rb +++ b/lib/mrsk/commands/prune.rb @@ -2,11 +2,19 @@ require "active_support/duration" require "active_support/core_ext/numeric/time" class Mrsk::Commands::Prune < Mrsk::Commands::Base - def images(until_hours: 7.days.in_hours.to_i) - docker :image, :prune, "--all", "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{until_hours}h" + def images + docker :image, :prune, "--all", "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true" end - def containers(until_hours: 3.days.in_hours.to_i) - docker :container, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "until=#{until_hours}h" + def containers(keep_last: 5) + pipe \ + docker(:ps, "-q", "-a", "--filter", "label=service=#{config.service}", *stopped_containers_filters), + "tail -n +#{keep_last + 1}", + "while read container_id; do docker rm $container_id; done" end + + private + def stopped_containers_filters + [ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] } + end end diff --git a/test/cli/prune_test.rb b/test/cli/prune_test.rb index 4ff9eedc..40a1d80f 100644 --- a/test/cli/prune_test.rb +++ b/test/cli/prune_test.rb @@ -10,13 +10,13 @@ class CliPruneTest < CliTestCase test "images" do run_command("images").tap do |output| - assert_match /docker image prune --all --force --filter label=service=app --filter until=168h on 1.1.1.\d/, output + assert_match /docker image prune --all --force --filter label=service=app --filter dangling=true on 1.1.1.\d/, output end end test "containers" do run_command("containers").tap do |output| - assert_match /docker container prune --force --filter label=service=app --filter until=72h on 1.1.1.\d/, output + assert_match /docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +6 | while read container_id; do docker rm $container_id; done on 1.1.1.\d/, output end end diff --git a/test/commands/prune_test.rb b/test/commands/prune_test.rb index adbe2816..bd6a561d 100644 --- a/test/commands/prune_test.rb +++ b/test/commands/prune_test.rb @@ -10,13 +10,13 @@ class CommandsPruneTest < ActiveSupport::TestCase test "images" do assert_equal \ - "docker image prune --all --force --filter label=service=app --filter until=168h", + "docker image prune --all --force --filter label=service=app --filter dangling=true", new_command.images.join(" ") end test "containers" do assert_equal \ - "docker container prune --force --filter label=service=app --filter until=72h", + "docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +6 | while read container_id; do docker rm $container_id; done", new_command.containers.join(" ") end