Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ WORKDIR /rails

# Install base packages
RUN apt-get update -qq \
&& apt-get install --no-install-recommends -y curl libvips postgresql-client libyaml-0-2 procps \
&& apt-get install --no-install-recommends -y curl libvips postgresql-client libyaml-0-2 procps\
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Set production environment
Expand Down Expand Up @@ -58,6 +58,9 @@ COPY --chown=rails:rails --from=build /rails /rails
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than curl a full HTTP request can we do a TCP check to make sure the container is up? Always hard to decide how far to decide "healthy" app ... roundtrip to the DB included?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually, you have a liveliness check and a healthcheck endpoint. Liveliness determines if the application is up and running. Health check endpoint would check if its running as expected (connected to db, message queues, etc).

I'm not faimiliar with Ruby, so I couldnt dig up the appropriate endpoint to use. Perhaps OpenAPI spec would be a nice addition to the docs.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For compose, we can just use the liveliness endpoint, since it will most likely not boot if migrations arent applied correctly. For Kubernetes, migrations should probably be ran before the main application startup (in a separate, sidecar container).

CMD curl --fail http://localhost:3000/healthcheck || exit 1

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]
52 changes: 52 additions & 0 deletions app/controllers/health_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class HealthController < ApplicationController
skip_before_action :verify_authenticity_token
skip_before_action :authenticate_user!
skip_before_action :set_request_details
skip_before_action :set_sentry_user
skip_before_action :verify_self_host_config

# Silence logging for liveliness endpoint
around_action :silence_logger, only: :liveliness

def healthcheck
checks = {
database: check_database,
redis: check_redis
}

all_healthy = checks.values.all?

status = all_healthy ? :ok : :service_unavailable
render json: checks, status: status
end

def liveliness
head :ok
end

private

def check_database
ActiveRecord::Base.connection.execute("SELECT 1")
true
rescue StandardError => e
Rails.logger.error("Database health check failed: #{e.message}")
false
end

def check_redis
Sidekiq.redis(&:ping)
true
rescue StandardError => e
Rails.logger.error("Redis health check failed: #{e.message}")
false
end

def silence_logger
old_level = Rails.logger.level
Rails.logger.level = Logger::UNKNOWN
yield
ensure
Rails.logger.level = old_level
end
end
13 changes: 13 additions & 0 deletions compose.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ services:
ports:
- 3000:3000
restart: unless-stopped
healthcheck:
test: [ "CMD-SHELL", "curl -f http://0.0.0.0:3000 || exit 1" ]
start_period: 10s
start_interval: 10s
interval: 10s
timeout: 5s
retries: 3
environment:
<<: *rails_env
depends_on:
Expand All @@ -73,6 +80,12 @@ services:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: [ "CMD-SHELL", "pgrep -f sidekiq || exit 1" ]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
environment:
<<: *rails_env
networks:
Expand Down
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@
# Can be used by load balancers and uptime monitors to verify that the app is live.
get "up" => "rails/health#show", as: :rails_health_check

# Health check endpoints
get "healthcheck", to: "health#healthcheck"
get "liveliness", to: "health#liveliness"

# Render dynamic PWA files from app/views/pwa/*
get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
Expand Down