Redefine what the "latest" container means

Currently the latest container is the one that was created last. But if
we have had a failed deployment that left two containers running that
would not be the one we want. The second container could be in a
restart loop for example.

Instead we want the container that is running the image tagged as
latest. As we now tag as latest after a successful deployment we can
trust that that is a healthy container.

In the case that there is no container running the latest image tag,
we'll fall back to the latest container.

This could happen if the deploy was halted in between the old container
being stopped and the image being tagged as latest.
This commit is contained in:
Donal McBreen
2024-03-28 11:21:24 +00:00
parent 55dd2f49c1
commit bade195e93
5 changed files with 75 additions and 37 deletions

View File

@@ -49,7 +49,9 @@ class Kamal::Commands::App < Kamal::Commands::Base
def current_running_container_id
docker :ps, "--quiet", *filter_args(statuses: ACTIVE_DOCKER_STATUSES), "--latest"
pipe \
[ shell(chain(latest_image_container_id, latest_container_id)) ],
[ :head, "-1" ]
end
def container_id_for_version(version, only_running: false)
@@ -57,13 +59,16 @@ class Kamal::Commands::App < Kamal::Commands::Base
end
def current_running_version
list_versions("--latest", statuses: ACTIVE_DOCKER_STATUSES)
pipe \
[ shell(chain(latest_image_container_name, latest_container_name)) ],
[ :head, "-1" ],
extract_version_from_name
end
def list_versions(*docker_args, statuses: nil)
pipe \
docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
%(while read line; do echo ${line##{role.container_prefix}-}; done) # Extract SHA from "service-role-dest-SHA"
extract_version_from_name
end
@@ -81,10 +86,43 @@ class Kamal::Commands::App < Kamal::Commands::Base
[ role.container_prefix, version || config.version ].compact.join("-")
end
def latest_image_id
docker :image, :ls, *argumentize("--filter", "reference=#{config.latest_image}"), "--format", "'{{.ID}}'"
end
def latest_image_container_id
latest_image_container format: "--quiet"
end
def latest_container_id
latest_container format: "--quiet"
end
def latest_image_container_name
latest_image_container format: "--format '{{.Names}}'"
end
def latest_container_name
latest_container format: "--format '{{.Names}}'"
end
def latest_image_container(format: nil)
docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES), "--filter", "ancestor=$(#{latest_image_id.join(" ")})"
end
def latest_container(format:)
docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES)
end
def filter_args(statuses: nil)
argumentize "--filter", filters(statuses: statuses)
end
def extract_version_from_name
# Extract SHA from "service-role-dest-SHA"
%(while read line; do echo ${line##{role.container_prefix}-}; done)
end
def filters(statuses: nil)
[ "label=service=#{config.service}" ].tap do |filters|
filters << "label=destination=#{config.destination}" if config.destination

View File

@@ -71,7 +71,7 @@ module Kamal::Commands
end
def shell(command)
[ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\''")}'" ]
[ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\\\''")}'" ]
end
def docker(*args)