Monitoring Server
This server was set up to provide monitoring for many systems using Prometheus and Grafana.
Installation
- Submitted the following ticket to have IS set up the VM
- Request_Submitted_By: kgomes@mbari.org - VM_Name: monitoring - VM_Purpose: I am working with Travis on setting up a monitoring server that will host Prometheus and Grafana. This server will be a central monitoring service for various applications that are slated to be built as well as upgrades to existing infrastructure - VM_Expiration: 5 years - VM_Support: IS_Supported - VM_Support_Alt_Adm: - VM_OS: Ubuntu 24.04 LTS - CPU: 2 - CPU_Addl_Reason: - RAM: 4 - RAM_Addl_Reason: - GPU_Extra: - GPU: NO - Disk_Extra: - Network: (SHORE) Internal - Resource_Priority: Low - Resource_Priority_Reason: - Conf_Firewall: - Conf_Share_Access: - Conf_vCenter_Access: - Conf_Desktop_Extra: - Conf_Desktop: NO - Conf_Logins: ad+nfshome - Conf_Docker: YES - Conf_Docker_Extra: - Conf_WebServer: nginx-docker - Conf_ACME_SSLcert: YES - Conf_ACME_SSLcert_Reload: - Conf_sudo: kgomes and tbaraki - VM_Comments: Hey Peter, I'm thinking I will use Docker compose for this and it would be good to be able to run this stack as a shared account so Travis and I can both control it (or whatever way works best that allows us both to control it). So, I'm thinking I will just include Nginx in my docker compose and then have your put SSL certs somewhere that I can mount them in the containers. I don't know if there are any real heavy disk requirements yet, that will be something we figure out as we go. I am sure I'm forgetting something, but let me know if you need something else. - And Peter set it up and responded:
The VM has been created with all that you specified. Your and Travis's users have full sudo permissions. The ACME SSL certs are located in `/etc/ssl/acme-certs/`. Take a look around and let me know if anything is missing. - I created the necessary directories using
sudo mkdir -p /opt/monitoring/{nginx,prometheus/data,grafana/data} - Set ownership using
sudo chown -R kgomes:docker /opt/monitoring - Set permissions usig
sudo chmod -R 2775 /opt/monitoring - Next, I created the
/opt/monitoring/docker-compose.ymlfile:services: prometheus: image: prom/prometheus:latest restart: unless-stopped command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.path=/prometheus" - "--storage.tsdb.retention.time=30d" - "--web.external-url=/prometheus" - "--web.route-prefix=/prometheus" volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./prometheus/data:/prometheus expose: - "9090" networks: - internal grafana: image: grafana/grafana:latest restart: unless-stopped environment: GF_SERVER_ROOT_URL: "https://monitoring.shore.mbari.org/grafana" GF_SERVER_SERVE_FROM_SUB_PATH: "true" GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: xxxxxxxxxxxxx GF_USERS_ALLOW_SIGN_UP: "false" volumes: - ./grafana/data:/var/lib/grafana expose: - "3000" networks: - internal nginx: image: nginx:alpine restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - /etc/ssl/acme-certs:/etc/ssl/acme-certs:ro depends_on: - prometheus - grafana networks: - internal networks: internal: driver: bridge - Then I created the
/opt/monitoring/nginx/nginx.conffile:events { worker_connections 1024; } http { upstream prometheus { server prometheus:9090; } upstream grafana { server grafana:3000; } # Redirect HTTP → HTTPS server { listen 80; server_name monitoring.shore.mbari.org; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name monitoring.shore.mbari.org; ssl_certificate /etc/ssl/acme-certs/monitoring.shore.mbari.org-chain.crt; ssl_certificate_key /etc/ssl/acme-certs/monitoring.shore.mbari.org.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # Prometheus — available at /prometheus location /prometheus { proxy_pass http://prometheus; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Grafana — available at /grafana location /grafana { proxy_pass http://grafana; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket support — Grafana Live uses it proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # Optional: redirect bare / to Grafana location = / { return 302 /grafana; } } } - Then, I create the
/opt/monitoring/prometheus/prometheus.ymlfile:global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: # Prometheus scraping itself - job_name: prometheus metrics_path: /prometheus/metrics static_configs: - targets: ["localhost:9090"] # Prefect Server metrics - job_name: prefect metrics_path: /api/metrics scheme: https tls_config: insecure_skip_verify: false # set true only if certs are self-signed static_configs: - targets: ["prefect.shore.mbari.org"] # If you want to scrape more frequently for Prefect: scrape_interval: 30s - I created the
/etc/systemd/system/monitoring.servicefile:[Unit] Description=Monitoring Stack (Prometheus + Grafana) After=docker.service Requires=docker.service [Service] Type=oneshot RemainAfterExit=yes User=kgomes Group=docker WorkingDirectory=/opt/monitoring ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down [Install] WantedBy=multi-user.target - Then, enabled and started it all with:
sudo systemctl daemon-reload sudo systemctl enable monitoring sudo systemctl start monitoring - It failed to start the first time and I had to adjust permission on the opt files using
sudo chown -R 65534:65534 /opt/monitoring/prometheus/dataandsudo chown -R 472:472 /opt/monitoring/grafana/data. - All is up and running and you can find Prometheus at https://monitoring.shore.mbari.org/prometheus and Grafana on https://monitoring.shore.mbari.org/grafana.
- Lastly, I had to wire Prometheus up to Grafana:
- Logged into Granfana using the credentials from the docker-compose file
- Went to
Connections → Data Sources → Add data source → Prometheus - Set the URL to
http://prometheus:9090/prometheus(used the internal Docker network name) - Clicked Save & Test and got green check mark
- Then imported a dashboard:
Dashboards → Import → ID 19105(a solid general Prometheus dashboard)- For Prefect specifically there isn't an official Grafana dashboard ID yet, but you can build panels querying metrics like:
prefect_server_flow_runs_total— flow run counts by stateprefect_server_subflow_runs_totalprefect_server_work_queues_totalprefect_server_deployments_total
- For Prefect specifically there isn't an official Grafana dashboard ID yet, but you can build panels querying metrics like:
- Run curl
https://prefect.shore.mbari.org/api/metricsfrom the monitoring server to see the full list of available metrics once Prefect is being scraped. - Just to get started, I imported the following JSON to get a basic Prefect dashboard running:
{ "__inputs": [ { "name": "DS_PROMETHEUS", "label": "Prometheus", "description": "", "type": "datasource", "pluginId": "prometheus", "pluginName": "Prometheus" } ], "__requires": [ { "type": "grafana", "id": "grafana", "name": "Grafana", "version": "10.0.0" }, { "type": "datasource", "id": "prometheus", "name": "Prometheus", "version": "1.0.0" }, { "type": "panel", "id": "stat", "name": "Stat", "version": "" }, { "type": "panel", "id": "timeseries", "name": "Time series", "version": "" }, { "type": "panel", "id": "gauge", "name": "Gauge", "version": "" } ], "annotations": { "list": [] }, "description": "Prefect Server Health Dashboard", "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 1, "id": null, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 100, "title": "Process Health", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": 1 } ] }, "mappings": [ { "options": { "0": { "text": "Down" }, "1": { "text": "Up" } }, "type": "value" } ] } }, "gridPos": { "h": 4, "w": 4, "x": 0, "y": 1 }, "id": 1, "options": { "colorMode": "background", "graphMode": "none", "textMode": "auto" }, "title": "Prefect Server Status", "type": "stat", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "up{job=\"prefect\"}", "legendFormat": "Status" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "unit": "bytes", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 500000000 }, { "color": "red", "value": 1000000000 } ] } } }, "gridPos": { "h": 4, "w": 4, "x": 4, "y": 1 }, "id": 2, "options": { "colorMode": "value", "graphMode": "area", "textMode": "auto" }, "title": "Resident Memory", "type": "stat", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "process_resident_memory_bytes{job=\"prefect\"}", "legendFormat": "RSS" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "unit": "bytes", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 1000000000 }, { "color": "red", "value": 2000000000 } ] } } }, "gridPos": { "h": 4, "w": 4, "x": 8, "y": 1 }, "id": 3, "options": { "colorMode": "value", "graphMode": "area", "textMode": "auto" }, "title": "Virtual Memory", "type": "stat", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "process_virtual_memory_bytes{job=\"prefect\"}", "legendFormat": "VSZ" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 800 }, { "color": "red", "value": 950 } ] } } }, "gridPos": { "h": 4, "w": 4, "x": 12, "y": 1 }, "id": 4, "options": { "colorMode": "value", "graphMode": "none", "textMode": "auto" }, "title": "Open File Descriptors", "type": "stat", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "process_open_fds{job=\"prefect\"}", "legendFormat": "Open FDs" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "unit": "percentunit", "max": 1, "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 0.7 }, { "color": "red", "value": 0.9 } ] } } }, "gridPos": { "h": 4, "w": 4, "x": 16, "y": 1 }, "id": 5, "options": { "colorMode": "value", "graphMode": "none", "textMode": "auto" }, "title": "FD Usage %", "type": "stat", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "process_open_fds{job=\"prefect\"} / process_max_fds{job=\"prefect\"}", "legendFormat": "FD Usage" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "unit": "s", "thresholds": { "mode": "absolute", "steps": [{ "color": "blue", "value": null }] } } }, "gridPos": { "h": 4, "w": 4, "x": 20, "y": 1 }, "id": 6, "options": { "colorMode": "value", "graphMode": "none", "textMode": "auto" }, "title": "Process Uptime", "type": "stat", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "time() - process_start_time_seconds{job=\"prefect\"}", "legendFormat": "Uptime" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "bytes", "custom": { "lineWidth": 2, "fillOpacity": 10 } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 5 }, "id": 7, "options": { "legend": { "calcs": ["last", "max"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "Memory Usage Over Time", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "process_resident_memory_bytes{job=\"prefect\"}", "legendFormat": "Resident (RSS)" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "process_virtual_memory_bytes{job=\"prefect\"}", "legendFormat": "Virtual (VSZ)" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "percentunit", "custom": { "lineWidth": 2, "fillOpacity": 10 } } }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 5 }, "id": 8, "options": { "legend": { "calcs": ["last", "mean"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "CPU Usage Rate", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(process_cpu_seconds_total{job=\"prefect\"}[5m])", "legendFormat": "CPU %" } ] }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 13 }, "id": 101, "title": "Prefect Events", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "short", "custom": { "lineWidth": 2, "fillOpacity": 10 } } }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 14 }, "id": 9, "options": { "legend": { "calcs": ["last", "mean"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "Events Emitted & Observed Rate", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(prefect_events_emitted_total[5m])", "legendFormat": "Emitted/s" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(prefect_events_observed_total[5m])", "legendFormat": "Observed/s" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "short", "custom": { "lineWidth": 2, "fillOpacity": 10 } } }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 14 }, "id": 10, "options": { "legend": { "calcs": ["last", "mean"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "Log Events Rate", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(prefect_logs_observed_total[5m])", "legendFormat": "Logs Observed/s" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "short", "custom": { "lineWidth": 2, "fillOpacity": 10 } } }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 22 }, "id": 11, "options": { "legend": { "calcs": ["last"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "WebSocket Connections (Events)", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(prefect_event_websocket_connections_total[5m])", "legendFormat": "{{direction}} / {{connection}}" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "short", "custom": { "lineWidth": 2, "fillOpacity": 10 } } }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 22 }, "id": 12, "options": { "legend": { "calcs": ["last"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "WebSocket Connections (Logs)", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(prefect_log_websocket_connections_total[5m])", "legendFormat": "{{direction}} / {{connection}}" } ] }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 30 }, "id": 102, "title": "Python Runtime / GC", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "short", "custom": { "lineWidth": 2, "fillOpacity": 10 } } }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 31 }, "id": 13, "options": { "legend": { "calcs": ["last", "mean"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "GC Collections Rate (by generation)", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(python_gc_collections_total{job=\"prefect\"}[5m])", "legendFormat": "Gen {{generation}}" } ] }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "short", "custom": { "lineWidth": 2, "fillOpacity": 10 } } }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 31 }, "id": 14, "options": { "legend": { "calcs": ["last", "mean"], "displayMode": "table", "placement": "bottom" }, "tooltip": { "mode": "multi" } }, "title": "GC Objects Collected Rate (by generation)", "type": "timeseries", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(python_gc_objects_collected_total{job=\"prefect\"}[5m])", "legendFormat": "Gen {{generation}}" } ] } ], "refresh": "30s", "schemaVersion": 38, "tags": ["prefect", "mbari"], "templating": { "list": [] }, "time": { "from": "now-1h", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Prefect Server", "uid": "prefect-server-mbari", "version": 1 }