[ci skip] iSCSI migration, healthcheck fixes, health probes, etcd backup

- Migrate MySQL/PostgreSQL storage from local-path to iscsi-truenas
- Add democratic-csi iSCSI driver module for TrueNAS
- Add open-iscsi to cloud-init VM template
- Fix Shlink health probe path (/api/v3 -> /rest/v3 for Shlink 5.0)
- Fix etcd backup: use etcd 3.5.21-0 (3.6.x is distroless, no /bin/sh)
- Fix cluster healthcheck CronJob: always exit 0 to prevent circular
  JobFailed alerts (reporting via Slack, not exit codes)
- Fix Uptime Kuma nested list handling in cluster-health.sh
- Add health probes to: audiobookshelf, immich ML, ntfy, headscale,
  uptime-kuma, vaultwarden, rybbit (clickhouse + server + client),
  shlink, shlink-web
- Add iSCSI storage documentation to CLAUDE.md
This commit is contained in:
Viktor Barzin 2026-03-06 19:54:21 +00:00
parent a8e07ad930
commit 1d80c49201
No known key found for this signature in database
GPG key ID: 0EB088298288D958
17 changed files with 378 additions and 13 deletions

View file

@ -128,7 +128,7 @@ resource "helm_release" "mysql_cluster" {
}
datadirVolumeClaimTemplate = {
storageClassName = "local-path"
storageClassName = "iscsi-truenas"
resources = {
requests = {
storage = "30Gi"
@ -799,7 +799,7 @@ resource "null_resource" "pg_cluster" {
instances = "2"
image = "ghcr.io/cloudnative-pg/postgis:16"
storage_size = "20Gi"
storage_class = "local-path"
storage_class = "iscsi-truenas"
memory_limit = "4Gi"
cpu_limit = "2"
}
@ -822,7 +822,7 @@ resource "null_resource" "pg_cluster" {
enableSuperuserAccess: true
storage:
size: 20Gi
storageClass: local-path
storageClass: iscsi-truenas
resources:
requests:
cpu: "250m"

View file

@ -92,6 +92,27 @@ resource "kubernetes_deployment" "headscale" {
container_port = 41641
}
liveness_probe {
http_get {
path = "/health"
port = 8080
}
initial_delay_seconds = 15
period_seconds = 30
timeout_seconds = 5
failure_threshold = 5
}
readiness_probe {
http_get {
path = "/health"
port = 8080
}
initial_delay_seconds = 5
period_seconds = 30
timeout_seconds = 5
failure_threshold = 3
}
volume_mount {
name = "config-volume"
mount_path = "/etc/headscale"

View file

@ -100,9 +100,9 @@ resource "kubernetes_cron_job_v1" "backup-etcd" {
host_network = true
container {
name = "backup-etcd"
image = "registry.k8s.io/etcd:3.6.5-0"
command = ["/bin/sh"]
args = ["-c", "etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key snapshot save /backup/etcd-snapshot-$(date +%Y_%m_%d_%H:%M:%S_%Z).db"]
image = "registry.k8s.io/etcd:3.5.21-0"
command = ["etcdctl"]
args = ["--endpoints=https://127.0.0.1:2379", "--cacert=/etc/kubernetes/pki/etcd/ca.crt", "--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt", "--key=/etc/kubernetes/pki/etcd/healthcheck-client.key", "snapshot", "save", "/backup/etcd-snapshot-latest.db"]
env {
name = "ETCDCTL_API"
value = "3"

View file

@ -0,0 +1,102 @@
resource "kubernetes_namespace" "iscsi_csi" {
metadata {
name = "iscsi-csi"
labels = {
tier = var.tier
}
}
}
resource "helm_release" "democratic_csi" {
namespace = kubernetes_namespace.iscsi_csi.metadata[0].name
create_namespace = false
name = "democratic-csi-iscsi"
atomic = true
timeout = 300
repository = "https://democratic-csi.github.io/charts/"
chart = "democratic-csi"
values = [yamlencode({
csiDriver = {
name = "org.democratic-csi.iscsi"
}
storageClasses = [{
name = "iscsi-truenas"
defaultClass = false
reclaimPolicy = "Retain"
volumeBindingMode = "Immediate"
allowVolumeExpansion = true
parameters = {
fsType = "ext4"
}
mountOptions = []
}]
controller = {
driver = {
resources = {
requests = { cpu = "25m", memory = "64Mi" }
limits = { cpu = "250m", memory = "256Mi" }
}
}
}
node = {
driver = {
resources = {
requests = { cpu = "25m", memory = "64Mi" }
limits = { cpu = "250m", memory = "256Mi" }
}
}
hostPID = true
hostPath = "/lib/modules"
}
driver = {
config = {
driver = "freenas-iscsi"
instance_id = "truenas-iscsi"
httpConnection = {
protocol = "http"
host = var.truenas_host
port = 80
apiKey = var.truenas_api_key
}
sshConnection = {
host = var.truenas_host
port = 22
username = "root"
privateKey = var.truenas_ssh_private_key
}
zfs = {
datasetParentName = "main/iscsi"
detachedSnapshotsDatasetParentName = "main/iscsi-snaps"
}
iscsi = {
targetPortal = "${var.truenas_host}:3260"
namePrefix = "csi-"
nameSuffix = ""
targetGroups = [{
targetGroupPortalGroup = 1
targetGroupInitiatorGroup = 1
targetGroupAuthType = "None"
}]
extentInsecureTpc = true
extentXenCompat = false
extentDisablePhysicalBlocksize = true
extentBlocksize = 512
extentRpm = "SSD"
extentAvailThreshold = 0
}
}
}
})]
}

View file

@ -0,0 +1,10 @@
variable "tier" { type = string }
variable "truenas_host" { type = string }
variable "truenas_api_key" {
type = string
sensitive = true
}
variable "truenas_ssh_private_key" {
type = string
sensitive = true
}

View file

@ -79,6 +79,26 @@ resource "kubernetes_deployment" "uptime-kuma" {
port {
container_port = 3001
}
liveness_probe {
http_get {
path = "/"
port = 3001
}
initial_delay_seconds = 15
period_seconds = 30
timeout_seconds = 5
failure_threshold = 5
}
readiness_probe {
http_get {
path = "/"
port = 3001
}
initial_delay_seconds = 5
period_seconds = 30
timeout_seconds = 5
failure_threshold = 3
}
volume_mount {
name = "data"
mount_path = "/app/data"

View file

@ -109,6 +109,26 @@ resource "kubernetes_deployment" "vaultwarden" {
port {
container_port = 80
}
liveness_probe {
http_get {
path = "/alive"
port = 80
}
initial_delay_seconds = 15
period_seconds = 30
timeout_seconds = 5
failure_threshold = 5
}
readiness_probe {
http_get {
path = "/alive"
port = 80
}
initial_delay_seconds = 5
period_seconds = 30
timeout_seconds = 5
failure_threshold = 3
}
volume_mount {
name = "data"
mount_path = "/data"