From b18030f5e563541a1cf346c306312e4b91c57d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Thu, 18 Jun 2020 19:09:46 +0200 Subject: [PATCH] Add static support and health-check (#17) --- README.md | 2 +- bite/bite/settings.py | 5 +++ bite/bite/urls.py | 1 + bite/telemetry/__init__.py | 1 + bite/telemetry/apps.py | 5 +++ bite/telemetry/health.py | 24 +++++++++++++ .../management/commands/mqtt-to-db.py | 8 +++-- docker/django/Dockerfile | 5 ++- docker/docker-compose.dev.yml | 21 +++++++++++ docker/docker-compose.prod.yml | 11 ++---- docker/docker-compose.yml | 35 ++++++------------- docker/edge/README.md | 2 +- docker/{ => edge}/docker-compose.edge.yml | 0 ...compose.yml => docker-compose.modules.yml} | 0 docker/ingress/docker-compose.traefik.yml | 21 +++++++++++ docker/{nginx => ingress}/nginx.conf | 4 +++ requirements.txt | 1 + 17 files changed, 109 insertions(+), 37 deletions(-) create mode 100644 bite/telemetry/health.py create mode 100644 docker/docker-compose.dev.yml rename docker/{ => edge}/docker-compose.edge.yml (100%) rename docker/edge/{docker-compose.yml => docker-compose.modules.yml} (100%) create mode 100644 docker/ingress/docker-compose.traefik.yml rename docker/{nginx => ingress}/nginx.conf (95%) diff --git a/README.md b/README.md index 6a18730..683f84c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# BITE - BasicIoTExample +# BITE - Basic/IoT/Example Playing with IoT diff --git a/bite/bite/settings.py b/bite/bite/settings.py index 18be70f..ac11422 100644 --- a/bite/bite/settings.py +++ b/bite/bite/settings.py @@ -37,6 +37,9 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'health_check', + 'health_check.db', + # 'health_check.storage', 'rest_framework', 'api', 'telemetry', @@ -126,6 +129,8 @@ USE_TZ = True STATIC_URL = '/static/' +STATIC_ROOT = '/srv/appdata/bite/static' + SKIP_WHITELIST = True MQTT_BROKER = { diff --git a/bite/bite/urls.py b/bite/bite/urls.py index 956a99a..651d14c 100644 --- a/bite/bite/urls.py +++ b/bite/bite/urls.py @@ -22,6 +22,7 @@ from telemetry import urls as telemetry_urls urlpatterns = [ path('admin/', admin.site.urls), + path('ht/', include('health_check.urls')), path('api/', include(api_urls)), path('telemetry/', include(telemetry_urls)), ] diff --git a/bite/telemetry/__init__.py b/bite/telemetry/__init__.py index e69de29..b03e03d 100644 --- a/bite/telemetry/__init__.py +++ b/bite/telemetry/__init__.py @@ -0,0 +1 @@ +default_app_config = 'telemetry.apps.TelemetryConfig' diff --git a/bite/telemetry/apps.py b/bite/telemetry/apps.py index 99ef582..9e1fd9c 100644 --- a/bite/telemetry/apps.py +++ b/bite/telemetry/apps.py @@ -1,5 +1,10 @@ from django.apps import AppConfig +from health_check.plugins import plugin_dir class TelemetryConfig(AppConfig): name = 'telemetry' + + def ready(self): + from telemetry.health import MQTTHealthCheck + plugin_dir.register(MQTTHealthCheck) diff --git a/bite/telemetry/health.py b/bite/telemetry/health.py new file mode 100644 index 0000000..82870ac --- /dev/null +++ b/bite/telemetry/health.py @@ -0,0 +1,24 @@ +import socket +import paho.mqtt.client as mqtt +from health_check.backends import BaseHealthCheckBackend +from health_check.exceptions import ServiceUnavailable + +from django.conf import settings + +MQTT_HOST = settings.MQTT_BROKER['HOST'] +MQTT_PORT = int(settings.MQTT_BROKER['PORT']) + + +class MQTTHealthCheck(BaseHealthCheckBackend): + critical_service = True + + def check_status(self): + client = mqtt.Client(client_id="django-hc") + try: + client.connect(MQTT_HOST, port=MQTT_PORT) + client.disconnect() + except (socket.gaierror, ConnectionRefusedError): + self.add_error(ServiceUnavailable("Connection refused")) + + def identifier(self): + return self.__class__.__name__ diff --git a/bite/telemetry/management/commands/mqtt-to-db.py b/bite/telemetry/management/commands/mqtt-to-db.py index de9e0c7..0d2de1b 100644 --- a/bite/telemetry/management/commands/mqtt-to-db.py +++ b/bite/telemetry/management/commands/mqtt-to-db.py @@ -1,3 +1,4 @@ +import socket import asyncio import json import time @@ -46,8 +47,11 @@ class Command(BaseCommand): try: client.connect(MQTT_HOST, MQTT_PORT) break - except ConnectionRefusedError: - self.stdout.write('WARNING: Broker not available') + except (socket.gaierror, ConnectionRefusedError): + self.stdout.write( + self.style.WARNING('WARNING: Broker not available')) time.sleep(5) + + self.stdout.write(self.style.SUCCESS('INFO: Broker subscribed')) client.disconnect() asyncio.run(self.mqtt_broker()) diff --git a/docker/django/Dockerfile b/docker/django/Dockerfile index ffe2b41..2c60e75 100644 --- a/docker/django/Dockerfile +++ b/docker/django/Dockerfile @@ -15,7 +15,10 @@ COPY --from=builder /usr/local/lib/python3.8/site-packages/ /usr/local/lib/pytho COPY --chown=1000:1000 bite /srv/app/bite COPY --chown=1000:1000 requirements.txt /tmp/requirements.txt -RUN pip3 install -r /tmp/requirements.txt && rm /tmp/requirements.txt +RUN mkdir -p /srv/appdata/bite/static \ + && chown -R 1000:1000 /srv/appdata/bite \ + && pip3 install -r /tmp/requirements.txt \ + && rm /tmp/requirements.txt USER 1000:1000 WORKDIR /srv/app/bite diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml new file mode 100644 index 0000000..17098b4 --- /dev/null +++ b/docker/docker-compose.dev.yml @@ -0,0 +1,21 @@ +version: "3.7" + +services: + timescale: + ports: + - "5432:5432" + + bite: + volumes: + - ../bite:/srv/app/bite + command: ["python3", "manage.py", "runserver", "0.0.0.0:8000"] + ports: + - "8000:8000" + + data-migration: + volumes: + - ../bite:/srv/app/bite + + mqtt-to-db: + volumes: + - ../bite:/srv/app/bite diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index e7b0e05..52bffeb 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -1,20 +1,15 @@ version: "3.7" services: - ingress: - # command: --providers.docker # For traefik only - ports: - - "80:80" - bite: volumes: - - "./django/production.py.sample:/srv/app/bite/bite/production.py" + - ./django/production.py.sample:/srv/app/bite/bite/production.py command: ["gunicorn", "-b", "0.0.0.0:8000", "bite.wsgi:application"] data-migration: volumes: - - "./django/production.py.sample:/srv/app/bite/bite/production.py" + - ./django/production.py.sample:/srv/app/bite/bite/production.py mqtt-to-db: volumes: - - "./django/production.py.sample:/srv/app/bite/bite/production.py" + - ./django/production.py.sample:/srv/app/bite/bite/production.py diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1b46ff5..1838361 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -5,6 +5,7 @@ networks: volumes: pgdata: + staticdata: x-op-service-default: &service_default restart: always # unless-stopped @@ -44,25 +45,13 @@ services: <<: *service_default image: nginx:stable-alpine ports: - - "8000:80" + - "80:80" networks: - net volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - staticdata:/srv/appdata/bite/static + - ./ingress/nginx.conf:/etc/nginx/nginx.conf - # User traefix instead of nginx - # ingress: - # <<: *service_default - # image: traefik:v2.2 - # command: --api.insecure=true --providers.docker - # ports: - # - "8000:80" - # - "8080:8080" - # networks: - # - net - # volumes: - # # So that Traefik can listen to the Docker events - # - /var/run/docker.sock:/var/run/docker.sock bite: <<: *service_default @@ -70,33 +59,31 @@ services: context: .. dockerfile: ./docker/django/Dockerfile image: daniviga/bite - volumes: - - "../bite:/srv/app/bite" - command: ["python3", "manage.py", "runserver", "0.0.0.0:8000"] + command: ["gunicorn", "-b", "0.0.0.0:8000", "bite.wsgi:application"] networks: - net depends_on: - ingress - data-migration - timescale - labels: - - "traefik.http.routers.bite.rule=PathPrefix(`/`)" data-migration: image: daniviga/bite - volumes: - - "../bite:/srv/app/bite" command: ["dockerize", "-wait", "tcp://timescale:5432", "python3", "manage.py", "migrate", "--noinput"] networks: - net depends_on: - timescale + static-files: + image: daniviga/bite + volumes: + - staticdata:/srv/appdata/bite/static + command: ["python3", "manage.py", "collectstatic", "--noinput"] + mqtt-to-db: <<: *service_default image: daniviga/bite - volumes: - - "../bite:/srv/app/bite" command: ["python3", "manage.py", "mqtt-to-db"] networks: - net diff --git a/docker/edge/README.md b/docker/edge/README.md index c7dfc98..1954b0b 100644 --- a/docker/edge/README.md +++ b/docker/edge/README.md @@ -2,5 +2,5 @@ ```bash export DOCKER_HOST='127.0.0.1:22375' -docker-compose -f docker-compose.edge.yml up -d --scale device-http=4 +docker-compose -f docker-compose.modules.yml up -d --scale device-http=4 ``` diff --git a/docker/docker-compose.edge.yml b/docker/edge/docker-compose.edge.yml similarity index 100% rename from docker/docker-compose.edge.yml rename to docker/edge/docker-compose.edge.yml diff --git a/docker/edge/docker-compose.yml b/docker/edge/docker-compose.modules.yml similarity index 100% rename from docker/edge/docker-compose.yml rename to docker/edge/docker-compose.modules.yml diff --git a/docker/ingress/docker-compose.traefik.yml b/docker/ingress/docker-compose.traefik.yml new file mode 100644 index 0000000..e553934 --- /dev/null +++ b/docker/ingress/docker-compose.traefik.yml @@ -0,0 +1,21 @@ +version: "3.7" + +services: + ingress: + <<: *service_default + image: traefik:v2.2 + command: --api.insecure=true --providers.docker + ports: + - "8000:80" + - "8080:8080" + networks: + - net + volumes: + # So that Traefik can listen to the Docker events + - /var/run/docker.sock:/var/run/docker.sock + restart: always + init: true + + bite: + labels: + - "traefik.http.routers.bite.rule=PathPrefix(`/`)" diff --git a/docker/nginx/nginx.conf b/docker/ingress/nginx.conf similarity index 95% rename from docker/nginx/nginx.conf rename to docker/ingress/nginx.conf index 5c80d1c..0b8bef3 100644 --- a/docker/nginx/nginx.conf +++ b/docker/ingress/nginx.conf @@ -51,6 +51,10 @@ http { proxy_connect_timeout 300; } + location /static/ { + root /srv/appdata/bite; + } + error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; diff --git a/requirements.txt b/requirements.txt index 820ab80..fa70155 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ Django djangorestframework +django-health-check psycopg2-binary paho-mqtt==1.5.0 asyncio-mqtt==0.5.0