Bläddra i källkod

feat(api): export Prometheus metrics and configure scraping

Peter Thomassen 5 år sedan
förälder
incheckning
a1f4a5b03b
6 ändrade filer med 34 tillägg och 14 borttagningar
  1. 5 1
      api/api/settings.py
  2. 2 0
      api/api/urls.py
  3. 14 13
      api/desecapi/models.py
  4. 1 0
      api/requirements.txt
  5. 9 0
      docker-compose.yml
  6. 3 0
      prometheus/conf/prometheus.yml

+ 5 - 1
api/api/settings.py

@@ -44,12 +44,15 @@ INSTALLED_APPS = (
     'rest_framework',
     'desecapi.apps.AppConfig',
     'corsheaders',
+    'django_prometheus',
 )
 
 MIDDLEWARE = (
+    'django_prometheus.middleware.PrometheusBeforeMiddleware',
     'corsheaders.middleware.CorsMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
+    'django_prometheus.middleware.PrometheusAfterMiddleware',
 )
 
 ROOT_URLCONF = 'api.urls'
@@ -59,7 +62,7 @@ WSGI_APPLICATION = 'desecapi.wsgi.application'
 
 DATABASES = {
     'default': {
-        'ENGINE': 'django.db.backends.mysql',
+        'ENGINE': 'django_prometheus.db.backends.mysql',
         'NAME': 'desec',
         'USER': 'desec',
         'PASSWORD': os.environ['DESECSTACK_DBAPI_PASSWORD_desec'],
@@ -78,6 +81,7 @@ DATABASES = {
 
 CACHES = {
     'default': {
+        # TODO 'BACKEND': 'django_prometheus.cache.backends.memcached.PyLibMCCache' not supported
         'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
         'LOCATION': 'memcached:11211',
     }

+ 2 - 0
api/api/urls.py

@@ -58,4 +58,6 @@ urlpatterns = [
     path('api/v2/', include('desecapi.urls.version_2', namespace='v2')),
     # the DEFAULT version
     path('api/v1/', include('desecapi.urls.version_1', namespace='v1')),
+    # monitoring
+    path('', include('django_prometheus.urls')),
 ]

+ 14 - 13
api/desecapi/models.py

@@ -23,6 +23,7 @@ from django.db import models
 from django.db.models import Manager, Q
 from django.template.loader import get_template
 from django.utils import timezone
+from django_prometheus.models import ExportModelOperationsMixin
 from dns.exception import Timeout
 from dns.resolver import NoNameservers
 from rest_framework.exceptions import APIException
@@ -73,7 +74,7 @@ class MyUserManager(BaseUserManager):
         return user
 
 
-class User(AbstractBaseUser):
+class User(ExportModelOperationsMixin('User'), AbstractBaseUser):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     email = models.EmailField(
         verbose_name='email address',
@@ -165,7 +166,7 @@ class User(AbstractBaseUser):
         ).send()
 
 
-class Token(rest_framework.authtoken.models.Token):
+class Token(ExportModelOperationsMixin('Token'), rest_framework.authtoken.models.Token):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     key = models.CharField("Key", max_length=128, db_index=True, unique=True)
     user = models.ForeignKey(
@@ -199,7 +200,7 @@ def get_minimum_ttl_default():
     return settings.MINIMUM_TTL_DEFAULT
 
 
-class Domain(models.Model):
+class Domain(ExportModelOperationsMixin('Domain'), models.Model):
     created = models.DateTimeField(auto_now_add=True)
     name = models.CharField(max_length=191,
                             unique=True,
@@ -327,7 +328,7 @@ def get_default_value_mref():
     return "ONDON" + str(time.time())
 
 
-class Donation(models.Model):
+class Donation(ExportModelOperationsMixin('Donation'), models.Model):
     created = models.DateTimeField(default=get_default_value_created)
     name = models.CharField(max_length=255)
     iban = models.CharField(max_length=34)
@@ -350,7 +351,7 @@ class RRsetManager(Manager):
         return rrset
 
 
-class RRset(models.Model):
+class RRset(ExportModelOperationsMixin('RRset'), models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     created = models.DateTimeField(auto_now_add=True)
     updated = models.DateTimeField(null=True)  # undocumented, used for debugging only
@@ -418,7 +419,7 @@ class RRManager(Manager):
         return ret
 
 
-class RR(models.Model):
+class RR(ExportModelOperationsMixin('RR'), models.Model):
     created = models.DateTimeField(auto_now_add=True)
     rrset = models.ForeignKey(RRset, on_delete=models.CASCADE, related_name='records')
     # max_length is determined based on the calculation in
@@ -431,7 +432,7 @@ class RR(models.Model):
         return '<RR %s %s rr_set=%s>' % (self.pk, self.content, self.rrset.pk)
 
 
-class AuthenticatedAction(models.Model):
+class AuthenticatedAction(ExportModelOperationsMixin('AuthenticatedAction'), models.Model):
     """
     Represents a procedure call on a defined set of arguments.
 
@@ -515,7 +516,7 @@ class AuthenticatedAction(models.Model):
         return self._act()
 
 
-class AuthenticatedUserAction(AuthenticatedAction):
+class AuthenticatedUserAction(ExportModelOperationsMixin('AuthenticatedUserAction'), AuthenticatedAction):
     """
     Abstract AuthenticatedAction involving an user instance, incorporating the user's id, email, password, and
     is_active flag into the Message Authentication Code state.
@@ -533,7 +534,7 @@ class AuthenticatedUserAction(AuthenticatedAction):
         raise NotImplementedError
 
 
-class AuthenticatedActivateUserAction(AuthenticatedUserAction):
+class AuthenticatedActivateUserAction(ExportModelOperationsMixin('AuthenticatedActivateUserAction'), AuthenticatedUserAction):
     domain = models.CharField(max_length=191)
 
     class Meta:
@@ -547,7 +548,7 @@ class AuthenticatedActivateUserAction(AuthenticatedUserAction):
         self.user.activate()
 
 
-class AuthenticatedChangeEmailUserAction(AuthenticatedUserAction):
+class AuthenticatedChangeEmailUserAction(ExportModelOperationsMixin('AuthenticatedChangeEmailUserAction'), AuthenticatedUserAction):
     new_email = models.EmailField()
 
     class Meta:
@@ -561,7 +562,7 @@ class AuthenticatedChangeEmailUserAction(AuthenticatedUserAction):
         self.user.change_email(self.new_email)
 
 
-class AuthenticatedResetPasswordUserAction(AuthenticatedUserAction):
+class AuthenticatedResetPasswordUserAction(ExportModelOperationsMixin('AuthenticatedResetPasswordUserAction'), AuthenticatedUserAction):
     new_password = models.CharField(max_length=128)
 
     class Meta:
@@ -571,7 +572,7 @@ class AuthenticatedResetPasswordUserAction(AuthenticatedUserAction):
         self.user.change_password(self.new_password)
 
 
-class AuthenticatedDeleteUserAction(AuthenticatedUserAction):
+class AuthenticatedDeleteUserAction(ExportModelOperationsMixin('AuthenticatedDeleteUserAction'), AuthenticatedUserAction):
 
     class Meta:
         managed = False
@@ -585,7 +586,7 @@ def captcha_default_content():
     return ''.join([secrets.choice(alphabet) for _ in range(5)])
 
 
-class Captcha(models.Model):
+class Captcha(ExportModelOperationsMixin('Captcha'), models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     created = models.DateTimeField(auto_now_add=True)
     content = models.CharField(

+ 1 - 0
api/requirements.txt

@@ -6,6 +6,7 @@ Django~=3.0.0
 django-cors-headers~=3.2.0
 djangorestframework~=3.11.0
 django-celery-email~=3.0.0
+django-prometheus~=2.0.0
 dnspython~=1.16.0
 httpretty~=0.9.0
 mysqlclient~=1.4.0

+ 9 - 0
docker-compose.yml

@@ -130,6 +130,7 @@ services:
       rearapi_ns:
         ipv4_address: ${DESECSTACK_IPV4_REAR_PREFIX16}.1.10
       rearwww:
+      rearmonitoring_api:
     logging:
       driver: "syslog"
       options:
@@ -293,6 +294,7 @@ services:
     networks:
       rearmonitoring_www:
         ipv4_address: ${DESECSTACK_IPV4_REAR_PREFIX16}.8.2
+      rearmonitoring_api:
     extra_hosts:
     - prometheus.localhost:${DESECSTACK_IPV4_REAR_PREFIX16}.8.2
     command: [
@@ -391,3 +393,10 @@ networks:
       config:
       - subnet: ${DESECSTACK_IPV4_REAR_PREFIX16}.8.0/29
         gateway: ${DESECSTACK_IPV4_REAR_PREFIX16}.8.1
+  rearmonitoring_api:
+    driver: bridge
+    ipam:
+      driver: default
+      config:
+      - subnet: ${DESECSTACK_IPV4_REAR_PREFIX16}.8.8/29
+        gateway: ${DESECSTACK_IPV4_REAR_PREFIX16}.8.9

+ 3 - 0
prometheus/conf/prometheus.yml

@@ -12,3 +12,6 @@ scrape_configs:
   - job_name: 'prometheus'
     static_configs:
       - targets: ['prometheus.localhost:9090']
+  - job_name: 'api'
+    static_configs:
+      - targets: ['api:8080']