Skip to content

Commit 07ff7cf

Browse files
authored
feat: Add health check (#2247)
In order to effectively manage Kubernetes applications we want to have startup, liveness, and readiness checks. This adds a package for exposing healthchecks that include information about the runtime status of the application.
1 parent 195b2ef commit 07ff7cf

File tree

7 files changed

+117
-59
lines changed

7 files changed

+117
-59
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: Install poetry
4040
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1
4141
with:
42-
version: 1.8.5
42+
version: 2.1.3
4343
virtualenvs-create: true
4444
virtualenvs-in-project: true
4545

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ ENV \
3131
PYTHONDONTWRITEBYTECODE=1 \
3232
PIP_DISABLE_PIP_VERSION_CHECK=on \
3333
POETRY_NO_INTERACTION=1 \
34-
POETRY_VERSION=1.8.5 \
34+
POETRY_VERSION=2.1.3 \
3535
POETRY_VIRTUALENVS_CREATE=true \
3636
POETRY_CACHE_DIR='/tmp/cache/poetry' \
3737
POETRY_HOME='/home/mitodl/.local' \

main/settings.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,46 @@
130130
"data_fixtures",
131131
"vector_search",
132132
"mitol.scim.apps.ScimApp",
133-
)
133+
"health_check",
134+
"health_check.cache",
135+
"health_check.contrib.migrations",
136+
"health_check.contrib.celery_ping",
137+
"health_check.contrib.redis",
138+
"health_check.contrib.db_heartbeat",
139+
)
140+
141+
HEALTH_CHECK = {
142+
"SUBSETS": {
143+
# The 'startup' subset includes checks that must pass before the application can
144+
# start.
145+
"startup": [
146+
"MigrationsHealthCheck", # Ensures database migrations are applied.
147+
"CacheBackend", # Verifies the cache backend is operational.
148+
"RedisHealthCheck", # Confirms Redis is reachable and functional.
149+
"DatabaseHeartBeatCheck", # Checks the database connection is alive.
150+
],
151+
# The 'liveness' subset includes checks to determine if the application is
152+
# running.
153+
"liveness": ["DatabaseHeartBeatCheck"], # Minimal check to ensure the app is
154+
# alive.
155+
# The 'readiness' subset includes checks to determine if the application is
156+
# ready to serve requests.
157+
"readiness": [
158+
"CacheBackend", # Ensures the cache is ready for use.
159+
"RedisHealthCheck", # Confirms Redis is ready for use.
160+
"DatabaseHeartBeatCheck", # Verifies the database is ready for queries.
161+
],
162+
# The 'full' subset includes all available health checks for a comprehensive
163+
# status report.
164+
"full": [
165+
"MigrationsHealthCheck", # Ensures database migrations are applied.
166+
"CacheBackend", # Verifies the cache backend is operational.
167+
"RedisHealthCheck", # Confirms Redis is reachable and functional.
168+
"DatabaseHeartBeatCheck", # Checks the database connection is alive.
169+
"CeleryPingHealthCheck", # Verifies Celery workers are responsive.
170+
],
171+
}
172+
}
134173

135174
if not get_bool("RUN_DATA_MIGRATIONS", default=False):
136175
MIGRATION_MODULES = {"data_fixtures": None}
@@ -453,7 +492,6 @@
453492
}
454493

455494
STATUS_TOKEN = get_string("STATUS_TOKEN", "")
456-
HEALTH_CHECK = ["CELERY", "REDIS", "POSTGRES", "OPEN_SEARCH"]
457495

458496
GA_TRACKING_ID = get_string("GA_TRACKING_ID", "")
459497
GA_G_TRACKING_ID = get_string("GA_G_TRACKING_ID", "")

main/settings_celery.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717

1818
DEV_ENV = get_bool("DEV_ENV", False) # noqa: FBT003
1919
USE_CELERY = True
20-
CELERY_BROKER_URL = get_string("CELERY_BROKER_URL", get_string("REDISCLOUD_URL", None))
20+
REDIS_URL = get_string("REDIS_URL", get_string("REDISCLOUD_URL", None))
21+
CELERY_BROKER_URL = get_string("CELERY_BROKER_URL", REDIS_URL)
22+
CELERY_RESULT_BACKEND = get_string("CELERY_RESULT_BACKEND", REDIS_URL)
2123
CELERY_BEAT_SCHEDULER = RedBeatScheduler
22-
CELERY_REDBEAT_REDIS_URL = CELERY_BROKER_URL
2324
redbeat_redis_url = CELERY_BROKER_URL
24-
CELERY_RESULT_BACKEND = get_string(
25-
"CELERY_RESULT_BACKEND", get_string("REDISCLOUD_URL", None)
26-
)
2725
CELERY_TASK_ALWAYS_EAGER = get_bool("CELERY_TASK_ALWAYS_EAGER", False) # noqa: FBT003
2826
CELERY_TASK_EAGER_PROPAGATES = get_bool(
2927
"CELERY_TASK_EAGER_PROPAGATES",

main/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
re_path(r"^app", RedirectView.as_view(url=settings.APP_BASE_URL)),
6262
# Hijack
6363
re_path(r"^hijack/", include("hijack.urls", namespace="hijack")),
64+
re_path(r"^health/", include("health_check.urls")),
6465
]
6566
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
6667
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

poetry.lock

Lines changed: 24 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,108 +8,110 @@ license = "BSD-3"
88
readme = "README.md"
99
packages = []
1010
authors = ["MIT ODL"]
11+
requires-poetry = ">2.1,<3"
1112

1213

1314
[tool.poetry.dependencies]
1415
python = "~3.12"
16+
Django = "4.2.21"
1517
attrs = "^25.0.0"
1618
base36 = "^0.1.1"
1719
beautifulsoup4 = "^4.8.2"
1820
boto3 = "^1.26.155"
1921
cairosvg = "2.7.1"
2022
celery = "^5.3.1"
23+
celery-redbeat = "^2.3.2"
2124
cffi = "^1.15.1"
2225
cryptography = "^44.0.0"
26+
dateparser = "^1.2.0"
27+
deepmerge = "^2.0"
2328
dj-database-url = "^2.0.0"
2429
dj-static = "^0.0.6"
25-
Django = "4.2.21"
2630
django-anymail = {extras = ["mailgun"], version = "^12.0"}
2731
django-bitfield = "^2.2.0"
2832
django-cache-memoize = "^0.2.0"
2933
django-cors-headers = "^4.0.0"
3034
django-filter = "^2.4.0"
3135
django-guardian = "^2.4.0"
36+
django-health-check = { git = "https://github.com/revsys/django-health-check", rev="9cfe2eaec5a15219513a36210b34875c03c64fe4" } # pragma: allowlist secret
3237
django-hijack = "^3.4.1"
3338
django-imagekit = "^5.0.0"
3439
django-ipware = "^7.0.0"
3540
django-json-widget = "^2.0.0"
41+
django-oauth-toolkit = "^2.3.0"
3642
django-redis = "^5.2.0"
43+
django-scim2 = "^0.19.1"
3744
django-server-status = "^0.7.0"
3845
django-storages = "^1.13.2"
3946
djangorestframework = "^3.14.0"
4047
drf-jwt = "^1.19.2"
48+
drf-nested-routers = "^0.94.0"
4149
drf-spectacular = "^0.28.0"
4250
feedparser = "^6.0.10"
4351
google-api-python-client = "^2.89.0"
52+
html2text = "^2025.0.0"
4453
html5lib = "^1.1"
4554
ipython = "^9.0.0"
55+
isodate = "^0.7.2"
4656
jedi = "^0.19.0"
57+
langchain = "^0.3.11"
58+
langchain-experimental = "^0.3.4"
59+
langchain-openai = "^0.3.2"
60+
litellm = "1.66.1"
61+
llama-index = "^0.12.6"
62+
llama-index-agent-openai = "^0.4.1"
63+
llama-index-llms-openai = "^0.3.12"
4764
lxml = "^5.0.0"
65+
markdown = "^3.7"
4866
markdown2 = "^2.4.8"
67+
mitol-django-scim = "^2025.3.31"
68+
named-enum = "^1.4.0"
4969
nested-lookup = "^0.2.25"
70+
nh3 = "^0.2.14"
5071
ocw-data-parser = "^0.35.1"
72+
onnxruntime = "1.21.0"
73+
openai = "^1.55.3"
5174
opensearch-dsl = "^2.0.0"
5275
opensearch-py = "^2.0.0"
76+
opentelemetry-api = ">=1.31.0"
77+
opentelemetry-exporter-otlp = ">=1.31.0"
78+
opentelemetry-instrumentation-celery = ">=0.52b0"
79+
opentelemetry-instrumentation-django = ">=0.52b0"
80+
opentelemetry-instrumentation-psycopg = ">=0.52b0"
81+
opentelemetry-instrumentation-redis = ">=0.52b0"
82+
opentelemetry-instrumentation-requests = ">=0.52b0"
83+
opentelemetry-sdk = ">=1.31.0"
84+
pluggy = "^1.3.0"
85+
posthog = "^3.5.0"
86+
psycopg = "^3.2.4"
5387
psycopg2 = "^2.9.6"
88+
pycountry = "^24.6.1"
5489
pygithub = "^2.0.0"
90+
pyparsing = "^3.2.1"
91+
pytest-lazy-fixtures = "^1.1.1"
5592
python-dateutil = "^2.8.2"
5693
python-rapidjson = "^1.8"
5794
pyyaml = "^6.0.0"
95+
qdrant-client = {extras = ["fastembed"], version = "^1.12.0"}
5896
redis = "^5.0.0"
5997
requests = "^2.31.0"
60-
sentry-sdk = "2.25.1"
98+
retry2 = "^0.9.5"
99+
ruff = "0.11.5"
100+
selenium = "^4.30.0"
101+
sentry-sdk = "^2.25.1"
61102
social-auth-app-django = "^5.2.0"
103+
social-auth-core = {extras = ["openidconnect"], version = "^4.4.2"}
62104
static3 = "^0.7.0"
63105
tika = "^2.6.0"
106+
tiktoken = "^0.9.0"
64107
tldextract = "^5.0.0"
65108
toolz = "^1.0.0"
66109
ulid-py = "^0.2.0"
67110
urllib3 = "^2.0.0"
68-
uwsgi = "^2.0.21"
111+
uwsgi = "^2.0.29"
112+
uwsgitop = "^0.12"
69113
wrapt = "^1.14.1"
70-
social-auth-core = {extras = ["openidconnect"], version = "^4.4.2"}
71-
nh3 = "^0.2.14"
72-
retry2 = "^0.9.5"
73-
pluggy = "^1.3.0"
74-
named-enum = "^1.4.0"
75-
drf-nested-routers = "^0.94.0"
76-
django-scim2 = "^0.19.1"
77-
django-oauth-toolkit = "^2.3.0"
78114
youtube-transcript-api = "^1.0.0"
79-
posthog = "^3.5.0"
80-
ruff = "0.11.5"
81-
dateparser = "^1.2.0"
82-
uwsgitop = "^0.12"
83-
pytest-lazy-fixtures = "^1.1.1"
84-
pycountry = "^24.6.1"
85-
qdrant-client = {extras = ["fastembed"], version = "^1.12.0"}
86-
onnxruntime = "1.21.0"
87-
openai = "^1.55.3"
88-
litellm = "1.66.1"
89-
langchain = "^0.3.11"
90-
tiktoken = "^0.9.0"
91-
llama-index = "^0.12.6"
92-
llama-index-llms-openai = "^0.3.12"
93-
llama-index-agent-openai = "^0.4.1"
94-
langchain-experimental = "^0.3.4"
95-
langchain-openai = "^0.3.2"
96-
deepmerge = "^2.0"
97-
pyparsing = "^3.2.1"
98-
html2text = "^2025.0.0"
99-
markdown = "^3.7"
100-
isodate = "^0.7.2"
101-
selenium = "^4.30.0"
102-
mitol-django-scim = "^2025.3.31"
103-
opentelemetry-api = ">=1.31.0"
104-
opentelemetry-sdk = ">=1.31.0"
105-
opentelemetry-instrumentation-django = ">=0.52b0"
106-
opentelemetry-instrumentation-psycopg = ">=0.52b0"
107-
opentelemetry-instrumentation-redis = ">=0.52b0"
108-
opentelemetry-instrumentation-celery = ">=0.52b0"
109-
opentelemetry-instrumentation-requests = ">=0.52b0"
110-
opentelemetry-exporter-otlp = ">=1.31.0"
111-
psycopg = "^3.2.4"
112-
celery-redbeat = "^2.3.2"
113115

114116
[tool.poetry.group.dev.dependencies]
115117
bpython = "^0.25"

0 commit comments

Comments
 (0)