PMM2: PowerDNS exporter


Mediante la monitorización de PowerDNS con PMM2 seremos capaces de detectar problemas en nuestros DNS, caídas de servicio, errores, timeouts, paquetes corruptos e incluso problemas de carga en los servidores.

Siguiendo con el artículo anterior vamos a configurar el exporter para PowerDNS, el exporter solo soporta PowerDNS-4.1.13(26/05/2020), si instalamos 4.2.1 solo veremos tres métricas:

powerdns_authoritative_exporter_json_parse_failures 0
powerdns_authoritative_exporter_total_scrapes 1
powerdns_authoritative_up 0

Consultamos las versiones disponibles:

eix pdns
     Available versions:  4.1.13^t ~4.2.0^t [m]4.2.1^t [m]~4.2.1-r1^t [m]~4.2.2^t [m]~4.3.0^t {botan debug doc geoip ldap libressl lua lua-records luajit mydns mysql opendbx postgres protobuf remote sodium sqlite systemd test tinydns tools}

Downgradeamos hasta que actualicen el exporter:

mkdir -p /etc/portage/package.mask
echo ">=net-dns/pdns-4.2.1" > /etc/portage/package.mask/pdns

Compilamos e instalamos la versión indicada:

emerge -av net-dns/pdns

Para que el exporter pueda consultar los datos hay que habilitar la API de PowerDNS.

MASTER:

cat >/etc/powerdns/pdns.conf  <<EOL
launch=gmysql
gmysql-host=127.0.0.1
gmysql-dbname=pdns
gmysql-user=pdns
gmysql-password=PASSWORD
setgid=pdns
setuid=pdns

local-address=MASTER_IP
master=yes
default-soa-name=ns1.alfaexploit.com.
allow-axfr-ips=SLAVE_IP
also-notify=SLAVE_IP

api=yes
api-key=PDNS_KEY
webserver=yes
webserver-address=127.0.0.1
webserver-allow-from=127.0.0.1/32
EOL

SLAVE:

cat >/etc/powerdns/pdns.conf  <<EOL
launch=gmysql
gmysql-host=127.0.0.1
gmysql-dbname=pdns
gmysql-user=pdns
gmysql-password=PASSWORD
setgid=pdns
setuid=pdns

local-address=SLAVE_IP
slave=yes
default-soa-name=ns1.alfaexploit.com.
allow-notify-from=MASTER_IP/32

api=yes
api-key=PDNS_KEY
webserver=yes
webserver-address=127.0.0.1
webserver-allow-from=127.0.0.1/32
EOL

Reiniciamos PowerDNS:

/etc/init.d/pdns restart

Comprobamos que la API funcione:

curl -v -H 'X-API-Key: PDNS_KEY' http://127.0.0.1:8081/api/v1/servers/localhost | jq .
{
  "config_url": "/api/v1/servers/localhost/config{/config_setting}",
  "daemon_type": "authoritative",
  "id": "localhost",
  "type": "Server",
  "url": "/api/v1/servers/localhost",
  "version": "4.1.13",
  "zones_url": "/api/v1/servers/localhost/zones{/zone}"
}

El exporter está escrito en Go así que instalamos el compilador de Go:

emerge -av dev-lang/go

Nos bajamos el exporter:

https://github.com/ledgr/powerdns_exporter

mkdir go
export GOPATH=/root/go
vi .bashrc
export GOPATH=/root/go
export GOPATH=$(go env GOPATH)
go get github.com/ledgr/powerdns_exporter

Lo copiamos a un path mas apropiado:

cp go/bin/powerdns_exporter /usr/local/sbin/powerdns_exporter

Arrancamos el exporter indicando la api-url como indica su documentación y bindeándolo a la loopback ya que vamos a meter un Nginx para que pida credenciales:

https://github.com/ledgr/powerdns_exporter

Demonizamos el exporter y lo arrancamos:

echo "nohup /usr/local/sbin/powerdns_exporter -listen-address="127.0.0.1:9120" -api-key="PDNS_KEY" -api-url="http://localhost:8081/api/v1/" &" > /etc/local.d/pdnsExporter.start
chmod 700 /etc/local.d/pdnsExporter.start
/etc/local.d/pdnsExporter.start

Compilamos e instalamos Nginx:

emerge -av nginx

Realizamos una copia del fichero de configuración de Nginx:

cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.ori

Dejamos la siguiente:

vi /etc/nginx/nginx.conf
user nginx nginx;
worker_processes 1;
error_log /var/log/nginx/error_log info;
events {
 worker_connections 1024;
 use epoll;
}

http {
 include /etc/nginx/mime.types;
 default_type application/octet-stream;
 log_format main
  '$remote_addr - $remote_user [$time_local] '
  '"$request" $status $bytes_sent '
  '"$http_referer" "$http_user_agent" '
  '"$gzip_ratio"';
 client_header_timeout 10m;
 client_body_timeout 10m;
 send_timeout 10m;
 connection_pool_size 256;
 client_header_buffer_size 1k;
 large_client_header_buffers 4 2k;
 request_pool_size 4k;
 gzip off;
 output_buffers 1 32k;
 postpone_output 1460;
 sendfile on;
 tcp_nopush on;
 tcp_nodelay on;
 keepalive_timeout 75 20;
 ignore_invalid_headers on;
 index index.html;

    server {
        listen SERVER_IP:9120;
        server_name _;

        location / {
            satisfy any;
            allow  PMM_SERVER_IP/32;
            auth_basic "Restricted Content";
            auth_basic_user_file /etc/nginx/.htpasswd;
            deny   all;
            
            proxy_pass http://localhost:9120;
        }
    }
}

Con esta configuración si la petición procede de PMM se servirán las métricas, en caso contrario serán necesarias las credenciales.

Generamos el fichero de autenticación:

emerge -av app-admin/apache-tools
htpasswd -c /etc/nginx/.htpasswd admin

Arrancamos Nginx:

/etc/init.d/nginx start
rc-update add nginx default

Comprobamos que podamos acceder con las credenciales:

curl -u admin:PASSWORD SERVER_IP:9120/metrics|grep -v '#'

Comprobamos que desde PMM podamos acceder sin credenciales:

curl SERVER_IP:9120/metrics|grep -v '#'

Añadimos el scrape a Prometheus:

vi prometheusConf/prometheus.base.yml
scrape_configs:
- job_name: PowerDNS
  scrape_interval: 1m
  scrape_timeout: 10s
  metrics_path: /metrics
  scheme: http
  static_configs:
  - targets:
    - kr0mtest:9120
    - kr0mtest2:9120

Recargamos la configuración:

docker stop pmm-server && docker start pmm-server

Podemos ver los exporters en targets:

Por desgracia no hay ningún dashboard de Grafana, si queremos uno tendremos que crear los paneles con las querys pertinentes.

Las métricas mostradas por el exporter solo suben, por lo tanto para detectar problemas debemos calcular un delta de los datos:

groups:
- name: pdnsRules
  rules:
  - alert: PowerDNSExporterDown
    expr: sum(up{job="PowerDNS"} == 0) by (instance)
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSBackendOverload
    expr: delta(powerdns_authoritative_exceptions_total{error="backend_overload"}[5m]) > 5
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSCorruptPackets
    expr: delta(powerdns_authoritative_exceptions_total{error="corrupt_packets"}[5m]) > 5
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSRcvBufferErrors
    expr: delta(powerdns_authoritative_exceptions_total{error="recvbuf_errors"}[5m]) > 5
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSServerDown
    expr: delta(powerdns_authoritative_exceptions_total{error="servfail"}[5m]) > 5
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSSndBufferErrors
    expr: delta(powerdns_authoritative_exceptions_total{error="sndbuf_errors"}[5m]) > 5
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSTimeoutErrors
    expr: delta(powerdns_authoritative_exceptions_total{error="timeout"}[5m]) > 5
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSUdpInputErrors
    expr: delta(powerdns_authoritative_exceptions_total{error="udo_in_errors"}[5m]) > 5
    for: 5m
    labels:
      severity: critical
  - alert: PowerDNSDown
    expr: delta(powerdns_authoritative_exporter_json_parse_failures[5m]) > 0
    for: 1m
    labels:
      severity: critical

Si paramos un PowerDNS veremos la alerta:

Si hemos seguido la guía sobre Alertmanager: https://www.alfaexploit.com/readArticle/363 veremos en Telegram alertas como esta:

Si te ha gustado el artículo puedes invitarme a un redbull aquí.
Autor: kr0m -- 28/05/2020 02:12:57