diff --git a/core/migrations/0024_categorytag_category_tags.py b/core/migrations/0024_categorytag_category_tags.py index de31021a..9b140e9a 100644 --- a/core/migrations/0024_categorytag_category_tags.py +++ b/core/migrations/0024_categorytag_category_tags.py @@ -1,8 +1,9 @@ # Generated by Django 5.2 on 2025-06-10 02:42 +import uuid + import django_extensions.db.fields import django_prometheus.models -import uuid from django.db import migrations, models diff --git a/docker-compose.yml b/docker-compose.yml index 21d75259..9d34afae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,10 +25,19 @@ services: env_file: - .env depends_on: - - database - - redis - - elasticsearch + database: + condition: service_healthy + redis: + condition: service_healthy + elasticsearch: + condition: service_healthy logging: *default-logging + healthcheck: + test: [ "CMD-SHELL", "curl -f http://localhost:8000/health || exit 1" ] + interval: 30s + timeout: 10s + retries: 5 + start_period: 10s database: container_name: database @@ -41,6 +50,11 @@ services: env_file: - .env logging: *default-logging + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\" -h localhost" ] + interval: 30s + timeout: 10s + retries: 5 database_exporter: container_name: postgres_exporter @@ -51,8 +65,14 @@ services: environment: - DATA_SOURCE_NAME=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database:5432/${POSTGRES_DB}?sslmode=disable depends_on: - - database + database: + condition: service_healthy logging: *default-logging + healthcheck: + test: [ "CMD-SHELL", "curl -f http://localhost:9187/metrics || exit 1" ] + interval: 30s + timeout: 10s + retries: 5 redis: container_name: redis @@ -66,6 +86,11 @@ services: env_file: - .env logging: *default-logging + healthcheck: + test: [ "CMD", "redis-cli", "-a", "$REDIS_PASSWORD", "ping" ] + interval: 30s + timeout: 10s + retries: 5 redis_exporter: container_name: redis_exporter @@ -77,9 +102,14 @@ services: - REDIS_ADDR=redis:6379 - REDIS_PASSWORD=${REDIS_PASSWORD} depends_on: - - redis - - prometheus + redis: + condition: service_healthy logging: *default-logging + healthcheck: + test: [ "CMD-SHELL", "curl -f http://localhost:9121/metrics || exit 1" ] + interval: 30s + timeout: 10s + retries: 5 elasticsearch: container_name: elasticsearch @@ -96,10 +126,15 @@ services: volumes: - es-data:/usr/share/elasticsearch/data logging: *default-logging + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:9200" ] + interval: 10s + timeout: 5s + retries: 5 elasticsearch_exporter: container_name: elasticsearch_exporter - image: justwatch/elasticsearch_exporter:1.1.0 + image: quay.io/prometheuscommunity/elasticsearch-exporter:v1.9.0 restart: always env_file: - .env @@ -108,8 +143,14 @@ services: ports: - "9114:9114" depends_on: - - elasticsearch + elasticsearch: + condition: service_healthy logging: *default-logging + healthcheck: + test: [ "CMD-SHELL", "curl -f http://localhost:9114/metrics || exit 1" ] + interval: 30s + timeout: 10s + retries: 5 worker: container_name: worker @@ -126,15 +167,19 @@ services: environment: - BROKER_URL=${CELERY_BROKER_URL} depends_on: - - app - - redis - - elasticsearch + app: + condition: service_healthy + redis: + condition: service_healthy + elasticsearch: + condition: service_healthy logging: *default-logging healthcheck: - test: [ "CMD", "celery", "-A", "evibes", "status" ] + test: [ "CMD-SHELL", "celery -A evibes status | grep -q 'OK'" ] interval: 30s timeout: 10s retries: 5 + start_period: 15s beat: container_name: beat @@ -149,8 +194,15 @@ services: env_file: - .env depends_on: - - worker + worker: + condition: service_healthy logging: *default-logging + healthcheck: + test: [ "CMD", "bash", "-c", "pgrep -f 'celery beat' >/dev/null" ] + interval: 30s + timeout: 10s + retries: 5 + start_period: 15s prometheus: container_name: prometheus @@ -166,11 +218,6 @@ services: command: - --config.file=/etc/prometheus/prometheus.yml - --web.config.file=/etc/prometheus/web.yml - depends_on: - - app - - worker - - redis - - elasticsearch logging: *default-logging volumes: diff --git a/evibes/api_urls.py b/evibes/api_urls.py index 9c5be57c..2afc2176 100644 --- a/evibes/api_urls.py +++ b/evibes/api_urls.py @@ -11,6 +11,7 @@ from core.views import CustomGraphQLView, CustomRedocView, CustomSwaggerView, fa from evibes.settings import SPECTACULAR_PLATFORM_SETTINGS urlpatterns = [ + path(r'health/', include('health_check.urls')), path("prometheus/", include("django_prometheus.urls")), path(r"graphql/", csrf_exempt(CustomGraphQLView.as_view(graphiql=True, schema=schema))), path( diff --git a/evibes/settings/base.py b/evibes/settings/base.py index ae1167a0..127ba17c 100644 --- a/evibes/settings/base.py +++ b/evibes/settings/base.py @@ -86,6 +86,16 @@ INSTALLED_APPS = [ "django.contrib.sitemaps", "django.contrib.gis", "django.contrib.humanize", + "health_check", + "health_check.db", + "health_check.cache", + "health_check.storage", + "health_check.contrib.migrations", + "health_check.contrib.celery", + "health_check.contrib.celery_ping", + "health_check.contrib.psutil", + "health_check.contrib.redis", + "health_check.contrib.db_heartbeat", "cacheops", "django_hosts", "django_celery_beat", diff --git a/poetry.lock b/poetry.lock index 4bf28ea4..945774e0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1045,6 +1045,25 @@ files = [ [package.dependencies] Django = ">=4.2" +[[package]] +name = "django-health-check" +version = "3.19.1" +description = "Monitor the health of your Django app and its connected services." +optional = false +python-versions = ">=3.9" +files = [ + { file = "django_health_check-3.19.1-py3-none-any.whl", hash = "sha256:558aa260102ae5c53a2925692e1b08fc1a9b181db3675b00a8e320e088a669db" }, + { file = "django_health_check-3.19.1.tar.gz", hash = "sha256:f1e287190a15d5c2f53db67877a4360ed0256bf5f3d6773f0fe4903d052a6c14" }, +] + +[package.dependencies] +Django = ">=4.2" + +[package.extras] +docs = ["sphinx"] +lint = ["ruff (==0.11.13)"] +test = ["boto3", "celery", "django-storages", "pytest", "pytest-cov", "pytest-django", "redis"] + [[package]] name = "django-hosts" version = "6.0" @@ -3115,7 +3134,7 @@ wcwidth = "*" name = "psutil" version = "7.0.0" description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." -optional = true +optional = false python-versions = ">=3.6" files = [ { file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25" }, @@ -4522,4 +4541,4 @@ worker = ["celery", "celery-prometheus-exporter", "django-celery-beat", "django- [metadata] lock-version = "2.0" python-versions = ">=3.12,<3.13" -content-hash = "8381fcab87bd578eaae8f88e24133866b78914a1967df332cdc09189d1fbd937" +content-hash = "3a1db2fcb5ccc1bb9c2656923d453c0ca1b335ea320126a84843bab7828bc707" diff --git a/pyproject.toml b/pyproject.toml index e545c5c3..fe72fa7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ poetry-core = "2.1.3" ruff = "0.11.8" six = "1.17.0" cryptography = "44.0.3" +psutil = "7.0.0" redis = "6.0.0" httpx = "0.28.1" celery = { version = "5.5.2", optional = true } @@ -53,6 +54,7 @@ django-mailbox = "4.10.1" django-prometheus = "^2.3.1" django-model-utils = "5.0.0" django-widget-tweaks = "1.5.0" +django-health-check = "3.19.1" django-cors-headers = "4.7.0" django-celery-beat = { version = "2.8.0", optional = true } django-celery-results = { version = "2.6.0", optional = true }