Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/kamal/cli/app/boot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ def start_new_version
if running_proxy?
endpoint = capture_with_info(*app.container_id_for_version(version)).strip
raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, did the container boot?" if endpoint.empty?
execute *app.deploy(target: endpoint)
begin
execute *app.deploy(target: endpoint)
rescue SSHKit::Command::Failed
show_health_check_response(endpoint) if role.proxy.healthcheck_debug?
raise
end
Comment on lines +57 to +62
else
Kamal::Cli::Healthcheck::Poller.wait_for_healthy { capture_with_info(*app.status(version: version)) }
end
Expand All @@ -63,6 +68,13 @@ def start_new_version
raise e
end

def show_health_check_response(endpoint)
response = capture_with_info(*app.health_check_response(target: endpoint), raise_on_non_zero_exit: false)
error "Health check response:\n#{response}" if response.present?
rescue StandardError => e
info "Could not fetch health check response: #{e.class}: #{e.message}"
end
Comment thread
zirni marked this conversation as resolved.

def stop_new_version
execute *app.stop(version: version), raise_on_non_zero_exit: false
end
Expand Down
10 changes: 10 additions & 0 deletions lib/kamal/commands/app/proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ def remove_proxy_app_directory
remove_directory config.proxy_boot.app_directory
end

def health_check_response(target:)
health_check_path = role.proxy.proxy_config.dig("healthcheck", "path") || "/up"
app_port = role.proxy.app_port

pipe \
docker(:inspect, "--format", "'{{.NetworkSettings.Networks.kamal.IPAddress}}'", target),
[ :xargs, "-I{}", :curl, "-s", "-o", "-", "-w", "'\\n%{http_code}'", "--max-time", "5",
"http://{}:#{app_port}#{health_check_path}" ]
end
Comment on lines +24 to +32
Comment on lines +24 to +32

def create_ssl_directory
make_directory(File.join(config.proxy_boot.tls_directory, role.name))
end
Expand Down
6 changes: 5 additions & 1 deletion lib/kamal/configuration/docs/proxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,14 @@ proxy:
# the deploy timeout, with a 5-second timeout for each request.
#
# Once the app is up, the proxy will stop hitting the healthcheck endpoint.
healthcheck:
#
# Set `debug` to true to log the HTTP status code and response body from an additional
# debug healthcheck request made after a deploy fails (this may include sensitive data),
# which can help diagnose why the container is unhealthy.
interval: 3
path: /health
timeout: 3
debug: false
Comment thread
zirni marked this conversation as resolved.

# Buffering
#
Expand Down
4 changes: 4 additions & 0 deletions lib/kamal/configuration/proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ def path_prefixes
proxy_config["path_prefixes"] || proxy_config["path_prefix"]&.split(",") || []
end

def healthcheck_debug?
!!proxy_config.dig("healthcheck", "debug")
end

def deploy_options
{
host: hosts,
Expand Down
12 changes: 12 additions & 0 deletions test/commands/app_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,18 @@ class CommandsAppTest < ActiveSupport::TestCase
new_command.remove_proxy_app_directory.join(" ")
end

test "health_check_response" do
assert_equal \
"docker inspect --format '{{.NetworkSettings.Networks.kamal.IPAddress}}' abc123 | xargs -I{} curl -s -o - -w '\\n%{http_code}' --max-time 5 http://{}:80/up",
new_command.health_check_response(target: "abc123").join(" ")
end

test "health_check_response with custom path" do
assert_equal \
"docker inspect --format '{{.NetworkSettings.Networks.kamal.IPAddress}}' abc123 | xargs -I{} curl -s -o - -w '\\n%{http_code}' --max-time 5 http://{}:80/healthz",
new_command(proxy: { "healthcheck" => { "path" => "/healthz" } }).health_check_response(target: "abc123").join(" ")
end
Comment on lines +602 to +612

private
def new_command(role: "web", host: "1.1.1.1", **additional_config)
config = Kamal::Configuration.new(@config.merge(additional_config), destination: @destination, version: "999")
Expand Down