Add healthcheck before deploy
This commit is contained in:
29
lib/mrsk/cli/healthcheck.rb
Normal file
29
lib/mrsk/cli/healthcheck.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
|
||||
desc "perform", "Health check the current version of the app"
|
||||
def perform
|
||||
on(MRSK.primary_host) do
|
||||
begin
|
||||
execute *MRSK.healthcheck.run
|
||||
|
||||
target = "Health check against #{MRSK.config.healthcheck["path"]}"
|
||||
|
||||
if capture_with_info(*MRSK.healthcheck.curl) == "200"
|
||||
info "#{target} succeeded with 200 OK!"
|
||||
else
|
||||
# Catches 1xx, 2xx, 3xx
|
||||
raise SSHKit::Command::Failed, "#{target} failed to return 200 OK!"
|
||||
end
|
||||
rescue SSHKit::Command::Failed => e
|
||||
if e.message =~ /curl/
|
||||
# Catches 4xx, 5xx
|
||||
raise SSHKit::Command::Failed, "#{target} failed to return 200 OK!"
|
||||
else
|
||||
raise
|
||||
end
|
||||
ensure
|
||||
execute *MRSK.healthcheck.stop, raise_on_non_zero_exit: false
|
||||
execute *MRSK.healthcheck.remove, raise_on_non_zero_exit: false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -23,6 +23,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
||||
say "Ensure Traefik is running...", :magenta
|
||||
invoke "mrsk:cli:traefik:boot"
|
||||
|
||||
say "Ensure app can pass healthcheck...", :magenta
|
||||
invoke "mrsk:cli:healthcheck:perform"
|
||||
|
||||
invoke "mrsk:cli:app:boot"
|
||||
|
||||
say "Prune old containers and images...", :magenta
|
||||
@@ -38,6 +41,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
||||
say "Build and push app image...", :magenta
|
||||
invoke "mrsk:cli:build:deliver"
|
||||
|
||||
say "Ensure app can pass healthcheck...", :magenta
|
||||
invoke "mrsk:cli:healthcheck:perform"
|
||||
|
||||
invoke "mrsk:cli:app:boot"
|
||||
end
|
||||
|
||||
@@ -147,6 +153,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
|
||||
desc "build", "Build the application image"
|
||||
subcommand "build", Mrsk::Cli::Build
|
||||
|
||||
desc "healthcheck", "Healthcheck the application"
|
||||
subcommand "healthcheck", Mrsk::Cli::Healthcheck
|
||||
|
||||
desc "prune", "Prune old application images and containers"
|
||||
subcommand "prune", Mrsk::Cli::Prune
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@ class Mrsk::Commander
|
||||
@auditor ||= Mrsk::Commands::Auditor.new(config)
|
||||
end
|
||||
|
||||
def healthcheck
|
||||
@healthcheck ||= Mrsk::Commands::Healthcheck.new(config)
|
||||
end
|
||||
|
||||
|
||||
def with_verbosity(level)
|
||||
old_level = self.verbosity
|
||||
|
||||
@@ -75,10 +75,6 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
|
||||
docker :ps, "-q", *service_filter
|
||||
end
|
||||
|
||||
def container_id_for(container_name:)
|
||||
docker :container, :ls, "-a", "-f", "name=#{container_name}", "-q"
|
||||
end
|
||||
|
||||
def current_running_version
|
||||
# FIXME: Find more graceful way to extract the version from "app-version" than using sed and tail!
|
||||
pipe \
|
||||
|
||||
@@ -17,6 +17,10 @@ module Mrsk::Commands
|
||||
end
|
||||
end
|
||||
|
||||
def container_id_for(container_name:)
|
||||
docker :container, :ls, "-a", "-f", "name=#{container_name}", "-q"
|
||||
end
|
||||
|
||||
private
|
||||
def combine(*commands, by: "&&")
|
||||
commands
|
||||
|
||||
46
lib/mrsk/commands/healthcheck.rb
Normal file
46
lib/mrsk/commands/healthcheck.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
class Mrsk::Commands::Healthcheck < Mrsk::Commands::Base
|
||||
EXPOSED_PORT = 3999
|
||||
|
||||
def run
|
||||
web = config.role(:web)
|
||||
|
||||
docker :run,
|
||||
"-d",
|
||||
"--name", container_name_with_version,
|
||||
"-p", "#{EXPOSED_PORT}:#{config.healthcheck["port"]}",
|
||||
"--label", "service=#{container_name}",
|
||||
*web.env_args,
|
||||
*config.volume_args,
|
||||
config.absolute_image,
|
||||
web.cmd
|
||||
end
|
||||
|
||||
def curl
|
||||
[ :curl, "--silent", "--output", "/dev/null", "--write-out", "'%{http_code}'", health_url ]
|
||||
end
|
||||
|
||||
def stop
|
||||
pipe \
|
||||
container_id_for(container_name: container_name),
|
||||
xargs(docker(:stop))
|
||||
end
|
||||
|
||||
def remove
|
||||
pipe \
|
||||
container_id_for(container_name: container_name),
|
||||
xargs(docker(:container, :rm))
|
||||
end
|
||||
|
||||
private
|
||||
def container_name
|
||||
"healthcheck-#{config.service}"
|
||||
end
|
||||
|
||||
def container_name_with_version
|
||||
"healthcheck-#{config.service_with_version}"
|
||||
end
|
||||
|
||||
def health_url
|
||||
"http://localhost:#{EXPOSED_PORT}#{config.healthcheck["path"]}"
|
||||
end
|
||||
end
|
||||
@@ -107,6 +107,7 @@ class Mrsk::Configuration
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def ssh_user
|
||||
if raw_config.ssh.present?
|
||||
raw_config.ssh["user"] || "root"
|
||||
@@ -126,10 +127,15 @@ class Mrsk::Configuration
|
||||
{ user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ] }.compact
|
||||
end
|
||||
|
||||
|
||||
def audit_broadcast_cmd
|
||||
raw_config.audit_broadcast_cmd
|
||||
end
|
||||
|
||||
def healthcheck
|
||||
{ "path" => "/up", "port" => "3000" }.merge(raw_config.healthcheck || {})
|
||||
end
|
||||
|
||||
|
||||
def valid?
|
||||
ensure_required_keys_present && ensure_env_available
|
||||
|
||||
@@ -59,7 +59,7 @@ class Mrsk::Configuration::Role
|
||||
if running_traefik?
|
||||
{
|
||||
"traefik.http.routers.#{config.service}.rule" => "'PathPrefix(`/`)'",
|
||||
"traefik.http.services.#{config.service}.loadbalancer.healthcheck.path" => "/up",
|
||||
"traefik.http.services.#{config.service}.loadbalancer.healthcheck.path" => config.healthcheck["path"],
|
||||
"traefik.http.services.#{config.service}.loadbalancer.healthcheck.interval" => "1s",
|
||||
"traefik.http.middlewares.#{config.service}.retry.attempts" => "3",
|
||||
"traefik.http.middlewares.#{config.service}.retry.initialinterval" => "500ms"
|
||||
|
||||
Reference in New Issue
Block a user