Add Loki logging stack

This commit is contained in:
Tero Halla-aho 2025-12-16 11:24:06 +02:00
parent c93cabb8ad
commit 070c3e1575
8 changed files with 267 additions and 0 deletions

View file

@ -66,6 +66,7 @@
- Price hint now stored in euros (schema field `priceHintPerNightEuros`); Prisma migration added to convert from cents, seeds and API/UI updated, and build now runs `prisma generate` automatically.
- Listing creation amenities UI improved with toggle cards and EV button group.
- Edit listing form now matches the create form styling, including amenity icon grid and price helpers.
- Centralized logging stack scaffolded (Loki + Promtail + Grafana) with Helm values and install script; Grafana ingress defaults to `logs.lomavuokraus.fi`.
- Mermaid docs fixed: all sequence diagrams declare their participants and avoid “->” inside message text; the listing creation diagram message was rewritten to prevent parse errors. Use mermaid.live or browser console to debug future syntax issues (errors flag the offending line/column).
- New amenities added: kitchen, dishwasher, washing machine, barbecue; API/UI/i18n updated and seeds randomized to populate missing prices/amenities. Prisma migration `20250210_more_amenities` applied to shared DB; registry pull secret added to k8s Deployment to avoid image pull errors in prod.
- Added About and Pricing pages (FI/EN), moved highlights/runtime config to About, and linked footer navigation.

56
deploy/install-logging.sh Normal file
View file

@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
if [[ -f scripts/load-secrets.sh ]]; then
source scripts/load-secrets.sh
fi
LOGGING_NAMESPACE="${LOGGING_NAMESPACE:-logging}"
LOGS_HOST="${LOGS_HOST:-logs.lomavuokraus.fi}"
GRAFANA_CLUSTER_ISSUER="${GRAFANA_CLUSTER_ISSUER:-letsencrypt-prod}"
: "${GRAFANA_ADMIN_PASSWORD:?Set GRAFANA_ADMIN_PASSWORD to provision Grafana.}"
HELM_BIN="${HELM_BIN:-$(command -v helm || true)}"
ensure_helm() {
if [[ -n "$HELM_BIN" && -x "$HELM_BIN" ]]; then
return
fi
echo "Helm not found, downloading to a temp dir..."
TMP_DIR="$(mktemp -d)"
curl -fsSL https://get.helm.sh/helm-v3.16.1-linux-amd64.tar.gz | tar -xz -C "$TMP_DIR"
HELM_BIN="$TMP_DIR/linux-amd64/helm"
}
ensure_helm
echo "Using helm at: $HELM_BIN"
$HELM_BIN repo add grafana https://grafana.github.io/helm-charts
$HELM_BIN repo update
export LOGS_HOST GRAFANA_CLUSTER_ISSUER GRAFANA_ADMIN_PASSWORD
LOKI_TMP=$(mktemp)
PROMTAIL_TMP=$(mktemp)
GRAFANA_TMP=$(mktemp)
cat k8s/logging/loki-values.yaml >"$LOKI_TMP"
cat k8s/logging/promtail-values.yaml >"$PROMTAIL_TMP"
envsubst < k8s/logging/grafana-values.yaml >"$GRAFANA_TMP"
echo "Installing/Upgrading Loki..."
$HELM_BIN upgrade --install loki grafana/loki -n "$LOGGING_NAMESPACE" -f "$LOKI_TMP" --create-namespace
echo "Installing/Upgrading Promtail..."
$HELM_BIN upgrade --install promtail grafana/promtail -n "$LOGGING_NAMESPACE" -f "$PROMTAIL_TMP"
echo "Installing/Upgrading Grafana..."
$HELM_BIN upgrade --install grafana grafana/grafana -n "$LOGGING_NAMESPACE" -f "$GRAFANA_TMP"
echo "Resources in $LOGGING_NAMESPACE:"
kubectl get pods,svc,ingress -n "$LOGGING_NAMESPACE"
echo "Done. Grafana ingress host: https://${LOGS_HOST}"

22
deploy/update-logs-dns.sh Normal file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
AUTH_FILE="creds/joker_com_dyndns_creds.txt"
if [[ ! -f "$AUTH_FILE" ]]; then
echo "Joker DYNDNS credentials missing at $AUTH_FILE" >&2
exit 1
fi
JOKER_AUTH="$(cat "$AUTH_FILE")"
TARGET_IP="${TARGET_IP:-157.180.66.64}"
LOGS_HOST="${LOGS_HOST:-logs.lomavuokraus.fi}"
echo "Updating $LOGS_HOST -> $TARGET_IP"
resp="$(curl -sS -u "$JOKER_AUTH" "https://svc.joker.com/nic/update?hostname=${LOGS_HOST}&myip=${TARGET_IP}")"
echo "$resp"
if [[ "$resp" != good* && "$resp" != nochg* ]]; then
echo "DNS update failed for $LOGS_HOST (response: $resp)" >&2
exit 1
fi

52
docs/logging.md Normal file
View file

@ -0,0 +1,52 @@
# Centralized logging (Loki + Promtail + Grafana)
We ship a lightweight logging stack into the cluster so API/UI logs are searchable.
- **Loki** (single-binary) stores logs with 14d retention by default, on a PVC.
- **Promtail** DaemonSet tails container logs and ships them to Loki with `namespace`, `pod`, and `app` labels.
- **Grafana** provides the UI with a pre-wired Loki data source and TLS ingress.
## Install / upgrade
Prereqs:
- `kubectl`/`helm` access to the cluster (the script downloads Helm if missing).
- Environment: `GRAFANA_ADMIN_PASSWORD` (required), optional `LOGS_HOST` (default `logs.lomavuokraus.fi`), `GRAFANA_CLUSTER_ISSUER` (default `letsencrypt-prod`), `LOGGING_NAMESPACE` (default `logging`).
Run:
```bash
LOGS_HOST=logs.lomavuokraus.fi \
GRAFANA_ADMIN_PASSWORD='change-me' \
GRAFANA_CLUSTER_ISSUER=letsencrypt-prod \
bash deploy/install-logging.sh
```
The script:
1. Ensures Helm is available.
2. Installs/updates Loki, Promtail, and Grafana in the logging namespace.
3. Creates a Grafana ingress with TLS via the chosen ClusterIssuer.
## Access
- Grafana: `https://<LOGS_HOST>` (admin user `admin`, password from `GRAFANA_ADMIN_PASSWORD`).
- Loki endpoint (internal): `http://loki.logging.svc.cluster.local:3100`.
## Querying
Example LogQL in Grafana Explore:
```
{namespace="lomavuokraus-test", app="lomavuokraus-web"}
```
Filter by pod:
```
{namespace="lomavuokraus-test", app="lomavuokraus-web", pod=~".*"} |= "ERROR"
```
## Tuning
- Retention: `k8s/logging/loki-values.yaml` (`limits_config.retention_period`).
- PVC sizes: adjust `persistence.size` in `k8s/logging/loki-values.yaml` and `k8s/logging/grafana-values.yaml`.
- Ingress issuer/host: override via environment when running `deploy/install-logging.sh`.

View file

@ -0,0 +1,45 @@
adminUser: admin
adminPassword: "${GRAFANA_ADMIN_PASSWORD}"
initChownData:
enabled: false
persistence:
enabled: true
size: 10Gi
storageClassName: ""
service:
type: ClusterIP
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: "${GRAFANA_CLUSTER_ISSUER}"
hosts:
- "${LOGS_HOST}"
tls:
- hosts:
- "${LOGS_HOST}"
secretName: grafana-logs-tls
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki.logging.svc.cluster.local:3100
isDefault: true
jsonData:
timeout: 60
maxLines: 5000
grafana.ini:
server:
root_url: https://${LOGS_HOST}
analytics:
reporting_enabled: false
check_for_updates: false

View file

@ -0,0 +1,61 @@
deploymentMode: SingleBinary
singleBinary:
replicas: 1
persistence:
enabled: true
size: 20Gi
storageClass: ""
# Disable simple scalable targets to satisfy chart validation
write:
replicas: 0
read:
replicas: 0
backend:
replicas: 0
loki:
auth_enabled: false
commonConfig:
replication_factor: 1
storage:
type: filesystem
filesystem:
chunks_directory: /var/loki/chunks
rules_directory: /var/loki/rules
limits_config:
retention_period: 0s
compactor:
retention_enabled: false
retention_delete_delay: 2h
retention_delete_worker_count: 1
ruler:
storage:
type: local
local:
directory: /rules
schemaConfig:
configs:
- from: "2024-01-01"
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: loki_index_
period: 24h
storage_config:
filesystem:
directory: /var/loki/chunks
ingress:
enabled: false
service:
type: ClusterIP
chunksCache:
enabled: false
resultsCache:
enabled: false

View file

@ -0,0 +1,25 @@
config:
lokiAddress: http://loki.logging.svc.cluster.local:3100/loki/api/v1/push
snippets:
extraRelabelConfigs:
# Skip Promtail's own logs to reduce noise
- source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]
action: drop
regex: promtail
# Drop logs from the logging stack itself
- source_labels: [__meta_kubernetes_namespace]
action: drop
regex: logging
# Promote common labels for easier querying
- action: replace
replacement: $1
source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- action: replace
replacement: $1
source_labels: [__meta_kubernetes_pod_name]
target_label: pod
- action: replace
replacement: $1
source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]
target_label: app

View file

@ -12,3 +12,8 @@ apiVersion: v1
kind: Namespace
metadata:
name: lomavuokraus-test
---
apiVersion: v1
kind: Namespace
metadata:
name: logging