Edge matrix vhost, node-exporter scrape, arr/rpi refinements
- bootstrap/edge: add matrix-vhost template + matrix.conf mount (isolated cert path), config_hash includes matrix.conf so tofu regenerates edge config when it changes; add node-exporter sidecar - bootstrap/platform: prometheus additionalScrapeConfigs for rpi-node and oci-edge-node targets, plus node-exporter edge port 9100 - apps/homelab-alerts: host filesystem >85% alert - infra/arr-stack + rpi-services: compose refinements - .gitignore: ignore tofu tfstate backups (*.tfstate.*.backup)
This commit is contained in:
parent
fe661f9b24
commit
dce4fa6022
|
|
@ -37,3 +37,7 @@ sops-age.key
|
||||||
# Ignore local Python bytecode from validation helpers
|
# Ignore local Python bytecode from validation helpers
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
||||||
|
# Tofu state backups
|
||||||
|
*.tfstate.*.backup
|
||||||
|
*.tfstate.bak*
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,14 @@ spec:
|
||||||
annotations:
|
annotations:
|
||||||
summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} is over 85% full"
|
summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} is over 85% full"
|
||||||
description: "PVC capacity is near exhaustion. Review app storage before it becomes an outage."
|
description: "PVC capacity is near exhaustion. Review app storage before it becomes an outage."
|
||||||
|
- alert: HomelabHostFilesystemNearlyFull
|
||||||
|
expr: 100 * (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|squashfs",mountpoint=~"/|/data|/var/lib/docker"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|squashfs",mountpoint=~"/|/data|/var/lib/docker"}) > 85
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: "Host filesystem {{ $labels.mountpoint }} on {{ $labels.instance }} is over 85% full"
|
||||||
|
description: "The {{ $labels.mountpoint }} mount on {{ $labels.instance }} is near exhaustion. Review disk usage before it becomes an outage."
|
||||||
- name: homelab.edge
|
- name: homelab.edge
|
||||||
rules:
|
rules:
|
||||||
- alert: HomelabTraefik5xxSpike
|
- alert: HomelabTraefik5xxSpike
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,22 @@ locals {
|
||||||
backend_host = var.backend_host
|
backend_host = var.backend_host
|
||||||
backend_port = tostring(var.backend_port)
|
backend_port = tostring(var.backend_port)
|
||||||
})
|
})
|
||||||
|
default_conf_matrix = var.matrix_enabled ? templatefile("${path.module}/templates/matrix-vhost.conf.tftpl", {
|
||||||
|
matrix_server_name = var.matrix_server_name
|
||||||
|
matrix_backend_host = var.matrix_backend_host
|
||||||
|
matrix_backend_port = tostring(var.matrix_backend_port)
|
||||||
|
matrix_cert_dir = var.matrix_cert_dir
|
||||||
|
element_server_name = var.element_server_name
|
||||||
|
element_backend_host = var.element_backend_host
|
||||||
|
element_backend_port = tostring(var.element_backend_port)
|
||||||
|
}) : ""
|
||||||
config_hash = sha256(join("\n---\n", [
|
config_hash = sha256(join("\n---\n", [
|
||||||
local.compose_file,
|
local.compose_file,
|
||||||
local.default_conf,
|
local.default_conf,
|
||||||
local.default_vcl,
|
local.default_vcl,
|
||||||
local.haproxy_cfg,
|
local.haproxy_cfg,
|
||||||
local.squid_conf,
|
local.squid_conf,
|
||||||
|
local.default_conf_matrix,
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +119,11 @@ resource "null_resource" "edge_services" {
|
||||||
destination = "/tmp/homelab-edge/config_files/squid.conf"
|
destination = "/tmp/homelab-edge/config_files/squid.conf"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = local.default_conf_matrix
|
||||||
|
destination = "/tmp/homelab-edge/config_files/matrix.conf"
|
||||||
|
}
|
||||||
|
|
||||||
provisioner "remote-exec" {
|
provisioner "remote-exec" {
|
||||||
inline = [
|
inline = [
|
||||||
<<EOT
|
<<EOT
|
||||||
|
|
@ -184,6 +199,7 @@ sudo cp /tmp/homelab-edge/config_files/default.conf "$install_dir/config_files/d
|
||||||
sudo cp /tmp/homelab-edge/config_files/default.vcl "$install_dir/config_files/default.vcl"
|
sudo cp /tmp/homelab-edge/config_files/default.vcl "$install_dir/config_files/default.vcl"
|
||||||
sudo cp /tmp/homelab-edge/config_files/haproxy.cfg "$install_dir/config_files/haproxy.cfg"
|
sudo cp /tmp/homelab-edge/config_files/haproxy.cfg "$install_dir/config_files/haproxy.cfg"
|
||||||
sudo cp /tmp/homelab-edge/config_files/squid.conf "$install_dir/config_files/squid.conf"
|
sudo cp /tmp/homelab-edge/config_files/squid.conf "$install_dir/config_files/squid.conf"
|
||||||
|
sudo cp /tmp/homelab-edge/config_files/matrix.conf "$install_dir/config_files/matrix.conf"
|
||||||
|
|
||||||
if [ ! -s "$install_dir/certs/current.crt" ] || [ ! -s "$install_dir/certs/current.key" ]; then
|
if [ ! -s "$install_dir/certs/current.crt" ] || [ ! -s "$install_dir/certs/current.key" ]; then
|
||||||
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ services:
|
||||||
- "443:443"
|
- "443:443"
|
||||||
volumes:
|
volumes:
|
||||||
- ./config_files/default.conf:/etc/nginx/conf.d/default.conf:ro
|
- ./config_files/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
- ./config_files/matrix.conf:/etc/nginx/conf.d/matrix.conf:ro
|
||||||
- ./certs:/etc/nginx/certs:ro
|
- ./certs:/etc/nginx/certs:ro
|
||||||
- ./certbot/www:/var/www/certbot:ro
|
- ./certbot/www:/var/www/certbot:ro
|
||||||
- nginx_cache:/var/cache/nginx
|
- nginx_cache:/var/cache/nginx
|
||||||
|
|
@ -37,6 +38,21 @@ services:
|
||||||
- squid_cache:/var/spool/squid
|
- squid_cache:/var/spool/squid
|
||||||
- squid_logs:/var/log/squid
|
- squid_logs:/var/log/squid
|
||||||
|
|
||||||
|
node-exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
pid: host
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:9100:9100/tcp"
|
||||||
|
command:
|
||||||
|
- --path.procfs=/host/proc
|
||||||
|
- --path.sysfs=/host/sys
|
||||||
|
- --path.rootfs=/rootfs
|
||||||
|
volumes:
|
||||||
|
- /proc:/host/proc:ro
|
||||||
|
- /sys:/host/sys:ro
|
||||||
|
- /:/rootfs:ro
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
nginx_cache:
|
nginx_cache:
|
||||||
nginx_dynamic_cache:
|
nginx_dynamic_cache:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
# MATRIX + ELEMENT (isolated certs, does NOT touch shared current.crt)
|
||||||
|
# Rendered by bootstrap/edge terraform. Keep this file idempotent:
|
||||||
|
# it regenerates identically on every apply.
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# --- ACME + HTTP redirect for matrix / element ---
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name ${matrix_server_name} ${element_server_name};
|
||||||
|
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
default_type "text/plain";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- matrix server_name — Synapse client-server API ---
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
http2 on;
|
||||||
|
server_name ${matrix_server_name};
|
||||||
|
|
||||||
|
ssl_certificate ${matrix_cert_dir}/matrix.crt;
|
||||||
|
ssl_certificate_key ${matrix_cert_dir}/matrix.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_session_cache shared:matrix_ssl:10m;
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://${matrix_backend_host}:${matrix_backend_port};
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
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 https;
|
||||||
|
proxy_read_timeout 90s;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /.well-known/matrix {
|
||||||
|
proxy_pass http://${matrix_backend_host}:${matrix_backend_port}/.well-known/matrix;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- element server_name — Element web UI ---
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
http2 on;
|
||||||
|
server_name ${element_server_name};
|
||||||
|
|
||||||
|
ssl_certificate ${matrix_cert_dir}/matrix.crt;
|
||||||
|
ssl_certificate_key ${matrix_cert_dir}/matrix.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_session_cache shared:matrix_ssl:10m;
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://${element_backend_host}:${element_backend_port};
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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 https;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -103,6 +103,49 @@ variable "heimdall_backend_port" {
|
||||||
default = 8082
|
default = 8082
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Matrix (isolated vhost + its own cert; does NOT touch shared current.crt)
|
||||||
|
variable "matrix_enabled" {
|
||||||
|
type = bool
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "matrix_server_name" {
|
||||||
|
type = string
|
||||||
|
default = "matrix.lab2025.duckdns.org"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Synapse client-server port on the LAN host. Override to use a Tailscale IP.
|
||||||
|
variable "matrix_backend_host" {
|
||||||
|
type = string
|
||||||
|
default = "192.168.100.73"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "matrix_backend_port" {
|
||||||
|
type = number
|
||||||
|
default = 8008
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "matrix_cert_dir" {
|
||||||
|
type = string
|
||||||
|
default = "/etc/nginx/certs"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Element (shares the matrix isolated cert)
|
||||||
|
variable "element_server_name" {
|
||||||
|
type = string
|
||||||
|
default = "element.lab2025.duckdns.org"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "element_backend_host" {
|
||||||
|
type = string
|
||||||
|
default = "192.168.100.73"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "element_backend_port" {
|
||||||
|
type = number
|
||||||
|
default = 8081
|
||||||
|
}
|
||||||
|
|
||||||
variable "haproxy_stats_user" {
|
variable "haproxy_stats_user" {
|
||||||
type = string
|
type = string
|
||||||
default = "admin"
|
default = "admin"
|
||||||
|
|
|
||||||
|
|
@ -1865,6 +1865,46 @@ resource "helm_release" "prometheus_stack" {
|
||||||
prometheusSpec = {
|
prometheusSpec = {
|
||||||
nodeSelector = local.prometheus_stack_node_selector
|
nodeSelector = local.prometheus_stack_node_selector
|
||||||
retention = var.observability.prometheus.retention
|
retention = var.observability.prometheus.retention
|
||||||
|
additionalScrapeConfigs = yamlencode([
|
||||||
|
{
|
||||||
|
job_name = "rpi-node"
|
||||||
|
static_configs = [
|
||||||
|
{
|
||||||
|
targets = ["192.168.100.89:9100"]
|
||||||
|
labels = {
|
||||||
|
host_group = "edge-dns-worker"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
metrics_path = "/metrics"
|
||||||
|
scheme = "http"
|
||||||
|
relabel_configs = [
|
||||||
|
{
|
||||||
|
source_labels = ["__address__"]
|
||||||
|
target_label = "instance"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
job_name = "oci-edge-node"
|
||||||
|
static_configs = [
|
||||||
|
{
|
||||||
|
targets = ["132.145.170.74:9100"]
|
||||||
|
labels = {
|
||||||
|
host_group = "public-edge"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
metrics_path = "/metrics"
|
||||||
|
scheme = "http"
|
||||||
|
relabel_configs = [
|
||||||
|
{
|
||||||
|
source_labels = ["__address__"]
|
||||||
|
target_label = "instance"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
resources = {
|
resources = {
|
||||||
requests = {
|
requests = {
|
||||||
cpu = "100m"
|
cpu = "100m"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ FLARESOLVERR_PORT=8191
|
||||||
KAPOWARR_PORT=5656
|
KAPOWARR_PORT=5656
|
||||||
SUWAYOMI_PORT=4567
|
SUWAYOMI_PORT=4567
|
||||||
MAINTAINERR_PORT=6246
|
MAINTAINERR_PORT=6246
|
||||||
|
JELLYFIN_PORT=8096
|
||||||
|
JELLYSEERR_PORT=5055
|
||||||
|
|
||||||
# Optional bootstrap values. Leave blank until the apps have created API keys.
|
# Optional bootstrap values. Leave blank until the apps have created API keys.
|
||||||
PROWLARR_API_KEY=
|
PROWLARR_API_KEY=
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,26 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- arr
|
- arr
|
||||||
|
|
||||||
|
jellyfin:
|
||||||
|
<<: *linuxserver-common
|
||||||
|
image: ${JELLYFIN_IMAGE:-lscr.io/linuxserver/jellyfin:latest}
|
||||||
|
container_name: ${JELLYFIN_CONTAINER_NAME:-arr-jellyfin}
|
||||||
|
dns:
|
||||||
|
- ${ARR_DNS_PRIMARY:-1.1.1.1}
|
||||||
|
- ${ARR_DNS_SECONDARY:-8.8.8.8}
|
||||||
|
group_add:
|
||||||
|
- "44" # video group (for access to DRM render node on the host)
|
||||||
|
devices:
|
||||||
|
- "/dev/dri/renderD128:/dev/dri/renderD128"
|
||||||
|
- "/dev/dri/card0:/dev/dri/card0"
|
||||||
|
ports:
|
||||||
|
- "${JELLYFIN_PORT:-8096}:8096"
|
||||||
|
# /config keeps its state; /data/media carries the media root so libraries
|
||||||
|
# can point at movies/tv/comics/manga/youtube. See README/media layout.
|
||||||
|
volumes:
|
||||||
|
- ${ARR_ROOT:-/data/arr}/config/jellyfin:/config
|
||||||
|
- ${ARR_ROOT:-/data/arr}/media:/data/media
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
arr:
|
arr:
|
||||||
name: homelab-arr
|
name: homelab-arr
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,24 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- dns
|
- dns
|
||||||
|
|
||||||
|
node-exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
container_name: ${NODE_EXPORTER_CONTAINER_NAME:-homelab-node-exporter}
|
||||||
|
restart: unless-stopped
|
||||||
|
pid: host
|
||||||
|
ports:
|
||||||
|
- "${NODE_EXPORTER_BIND_IP:-192.168.100.89}:9100:9100/tcp"
|
||||||
|
command:
|
||||||
|
- --path.procfs=/host/proc
|
||||||
|
- --path.sysfs=/host/sys
|
||||||
|
- --path.rootfs=/rootfs
|
||||||
|
volumes:
|
||||||
|
- /proc:/host/proc:ro
|
||||||
|
- /sys:/host/sys:ro
|
||||||
|
- /:/rootfs:ro
|
||||||
|
networks:
|
||||||
|
- dns
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
dns:
|
dns:
|
||||||
name: ${RPI_SERVICES_NETWORK:-homelab-rpi-services}
|
name: ${RPI_SERVICES_NETWORK:-homelab-rpi-services}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue