diff --git a/ansible/roles/alloy/defaults/main.yml b/ansible/roles/alloy/defaults/main.yml index f4cf069..45cc52b 100644 --- a/ansible/roles/alloy/defaults/main.yml +++ b/ansible/roles/alloy/defaults/main.yml @@ -74,3 +74,15 @@ alloy_plex_logs: # Enable log collection (requires Loki to be running) alloy_collect_logs: true + +# PostgreSQL metrics collection +alloy_collect_postgres: true +alloy_postgres_host: localhost +alloy_postgres_port: 5432 +alloy_postgres_user: alloy +alloy_postgres_database: postgres + +# 1Password settings for PostgreSQL metrics +alloy_op_vault: vg6xf6vvfmoh5hqjjhlhbeoaie +alloy_op_postgres_item: guxu3j7ajhjyey6xxl2ovsl2ui +alloy_op_postgres_field: alloy-user-pw diff --git a/ansible/roles/alloy/tasks/main.yml b/ansible/roles/alloy/tasks/main.yml index b6f5dad..568dbbb 100644 --- a/ansible/roles/alloy/tasks/main.yml +++ b/ansible/roles/alloy/tasks/main.yml @@ -25,12 +25,32 @@ state: directory mode: '0755' +# === Fetch PostgreSQL password from 1Password === + +- name: Fetch PostgreSQL metrics password from 1Password + ansible.builtin.command: + cmd: op --vault {{ alloy_op_vault }} item get {{ alloy_op_postgres_item }} --fields {{ alloy_op_postgres_field }} --reveal + delegate_to: localhost + register: alloy_postgres_password_result + changed_when: false + no_log: true + when: alloy_collect_postgres | default(false) + +- name: Set PostgreSQL password fact + ansible.builtin.set_fact: + alloy_postgres_password: "{{ alloy_postgres_password_result.stdout }}" + no_log: true + when: alloy_collect_postgres | default(false) + +# === Deploy configuration === + - name: Deploy alloy configuration ansible.builtin.template: src: config.alloy.j2 dest: "{{ alloy_config_dir }}/config.alloy" - mode: '0644' + mode: '0600' notify: restart alloy + no_log: true - name: Ensure alloy service is started ansible.builtin.command: brew services start grafana-alloy diff --git a/ansible/roles/alloy/templates/config.alloy.j2 b/ansible/roles/alloy/templates/config.alloy.j2 index 069d8d4..5b73991 100644 --- a/ansible/roles/alloy/templates/config.alloy.j2 +++ b/ansible/roles/alloy/templates/config.alloy.j2 @@ -35,6 +35,22 @@ prometheus.remote_write "prometheus" { } } +{% if alloy_collect_postgres | default(false) %} +// ============== POSTGRESQL METRICS ============== + +// PostgreSQL exporter (read-only metrics via pg_monitor role) +prometheus.exporter.postgres "postgresql" { + data_source_names = ["postgresql://{{ alloy_postgres_user }}:{{ alloy_postgres_password }}@{{ alloy_postgres_host }}:{{ alloy_postgres_port }}/{{ alloy_postgres_database }}?sslmode=disable"] +} + +// Scrape PostgreSQL metrics +prometheus.scrape "postgresql" { + targets = prometheus.exporter.postgres.postgresql.targets + forward_to = [prometheus.relabel.instance.receiver] + scrape_interval = "{{ alloy_scrape_interval }}" +} +{% endif %} + {% if alloy_collect_logs %} // ============== LOG COLLECTION ============== diff --git a/ansible/roles/grafana/files/dashboards/postgresql.json b/ansible/roles/grafana/files/dashboards/postgresql.json new file mode 100644 index 0000000..990a278 --- /dev/null +++ b/ansible/roles/grafana/files/dashboards/postgresql.json @@ -0,0 +1,519 @@ +{ + "annotations": { + "list": [] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null }, + { "color": "yellow", "value": 80 }, + { "color": "red", "value": 100 } + ] + }, + "unit": "short" + } + }, + "gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "pg_up", + "refId": "A" + } + ], + "title": "PostgreSQL Up", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null } + ] + }, + "unit": "short" + } + }, + "gridPos": { "h": 4, "w": 6, "x": 6, "y": 0 }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "pg_stat_activity_count{state=\"active\"}", + "refId": "A" + } + ], + "title": "Active Connections", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null } + ] + }, + "unit": "short" + } + }, + "gridPos": { "h": 4, "w": 6, "x": 12, "y": 0 }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "sum(pg_stat_activity_count)", + "refId": "A" + } + ], + "title": "Total Connections", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null } + ] + }, + "unit": "decbytes" + } + }, + "gridPos": { "h": 4, "w": 6, "x": 18, "y": 0 }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "expr": "sum(pg_database_size_bytes)", + "refId": "A" + } + ], + "title": "Total Database Size", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null } + ] + }, + "unit": "short" + } + }, + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "expr": "pg_stat_activity_count", + "legendFormat": "{{state}}", + "refId": "A" + } + ], + "title": "Connections by State", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null } + ] + }, + "unit": "decbytes" + } + }, + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "expr": "pg_database_size_bytes{datname!~\"template.*\"}", + "legendFormat": "{{datname}}", + "refId": "A" + } + ], + "title": "Database Size", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null } + ] + }, + "unit": "ops" + } + }, + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 12 }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "expr": "rate(pg_stat_database_tup_fetched{datname!~\"template.*\"}[5m])", + "legendFormat": "{{datname}} fetched", + "refId": "A" + }, + { + "expr": "rate(pg_stat_database_tup_inserted{datname!~\"template.*\"}[5m])", + "legendFormat": "{{datname}} inserted", + "refId": "B" + }, + { + "expr": "rate(pg_stat_database_tup_updated{datname!~\"template.*\"}[5m])", + "legendFormat": "{{datname}} updated", + "refId": "C" + }, + { + "expr": "rate(pg_stat_database_tup_deleted{datname!~\"template.*\"}[5m])", + "legendFormat": "{{datname}} deleted", + "refId": "D" + } + ], + "title": "Tuple Operations Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "green", "value": null } + ] + }, + "unit": "short" + } + }, + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 12 }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "expr": "rate(pg_stat_database_xact_commit{datname!~\"template.*\"}[5m])", + "legendFormat": "{{datname}} commits", + "refId": "A" + }, + { + "expr": "rate(pg_stat_database_xact_rollback{datname!~\"template.*\"}[5m])", + "legendFormat": "{{datname}} rollbacks", + "refId": "B" + } + ], + "title": "Transactions Rate", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": ["postgresql", "database"], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "PostgreSQL", + "uid": "postgresql", + "version": 1 +} diff --git a/ansible/roles/postgresql/defaults/main.yml b/ansible/roles/postgresql/defaults/main.yml index eb18646..5e145bf 100644 --- a/ansible/roles/postgresql/defaults/main.yml +++ b/ansible/roles/postgresql/defaults/main.yml @@ -34,3 +34,8 @@ postgresql_users: op_field: db-password roles: - pg_read_all_data + - name: alloy + op_item: "{{ postgresql_op_superuser_item }}" + op_field: alloy-user-pw + roles: + - pg_monitor