Adding heimdal
Homelab Main / deploy (push) Failing after 6m25s
Details
Homelab Main / deploy (push) Failing after 6m25s
Details
This commit is contained in:
parent
b76075e0fc
commit
0f02fa6efb
16
README.md
16
README.md
|
|
@ -61,8 +61,8 @@ accidentally modify the cluster.
|
||||||
- registers Argo CD Applications from the `applications` map
|
- registers Argo CD Applications from the `applications` map
|
||||||
- passes the website image produced by the build step into Argo CD as a
|
- passes the website image produced by the build step into Argo CD as a
|
||||||
Kustomize image override
|
Kustomize image override
|
||||||
- default apps are `container-registry`, `website-production`, and
|
- default apps are `container-registry`, `website-production`,
|
||||||
`demos-static`
|
`demos-static`, and `heimdall`
|
||||||
|
|
||||||
5. `bootstrap/edge`
|
5. `bootstrap/edge`
|
||||||
- connects to the OCI jump box
|
- connects to the OCI jump box
|
||||||
|
|
@ -151,6 +151,7 @@ kubectl -n argocd get applications
|
||||||
kubectl -n container-registry get pods
|
kubectl -n container-registry get pods
|
||||||
kubectl -n website-production get pods -o wide
|
kubectl -n website-production get pods -o wide
|
||||||
kubectl -n demos-static get pods -o wide
|
kubectl -n demos-static get pods -o wide
|
||||||
|
kubectl -n heimdall get pods -o wide
|
||||||
|
|
||||||
ssh jv@192.168.100.89 'cd /opt/homelab-gitea && sudo docker compose ps'
|
ssh jv@192.168.100.89 'cd /opt/homelab-gitea && sudo docker compose ps'
|
||||||
|
|
||||||
|
|
@ -263,7 +264,8 @@ tailscale_nodeport_access = {
|
||||||
target_port = 8080
|
target_port = 8080
|
||||||
}
|
}
|
||||||
|
|
||||||
tailscale_nodeport_extra_ports = [30081]
|
tailscale_nodeport_extra_ports = [30081, 30082, 30083, 30084, 30085, 30086]
|
||||||
|
tailscale_nodeport_extra_target_ports = [80, 3000, 9090, 9093]
|
||||||
```
|
```
|
||||||
|
|
||||||
For `./lab.sh nuke`, set `WORKER_SSH_TARGETS` to a space-separated list of
|
For `./lab.sh nuke`, set `WORKER_SSH_TARGETS` to a space-separated list of
|
||||||
|
|
@ -355,6 +357,14 @@ Add Kubernetes manifests under `apps/<name>` and register them in
|
||||||
`bootstrap/apps`'s `applications` map. Argo CD will own sync, pruning, and
|
`bootstrap/apps`'s `applications` map. Argo CD will own sync, pruning, and
|
||||||
self-healing for the app.
|
self-healing for the app.
|
||||||
|
|
||||||
|
The `heimdall` app is intentionally waited on at the end of `./lab.sh apps`.
|
||||||
|
It runs the LinuxServer.io Heimdall dashboard, persists `/config` on
|
||||||
|
OpenEBS, and seeds tiles for the website, demo apps, Gitea, Grafana,
|
||||||
|
Prometheus, Alertmanager, Argo CD, the local registry, and Heimdall itself.
|
||||||
|
Because Heimdall does not support reverse-proxy subfolder hosting cleanly, it
|
||||||
|
is exposed through NodePort `30082`; the dashboard's internal tool links use
|
||||||
|
the Raspberry Pi Tailscale NodePort address.
|
||||||
|
|
||||||
## Storage
|
## Storage
|
||||||
|
|
||||||
OpenEBS provides the platform storage provisioner. Stateful Kubernetes apps use
|
OpenEBS provides the platform storage provisioner. Stateful Kubernetes apps use
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- namespace.yaml
|
||||||
|
- web-app.yaml
|
||||||
|
- ui-nodeports.yaml
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: heimdall
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: grafana-nodeport
|
||||||
|
namespace: monitoring
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 3000
|
||||||
|
nodePort: 30083
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/instance: prometheus-stack
|
||||||
|
app.kubernetes.io/name: grafana
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: prometheus-nodeport
|
||||||
|
namespace: monitoring
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 9090
|
||||||
|
targetPort: 9090
|
||||||
|
nodePort: 30084
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: prometheus
|
||||||
|
operator.prometheus.io/name: prometheus-stack-kube-prom-prometheus
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: alertmanager-nodeport
|
||||||
|
namespace: monitoring
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 9093
|
||||||
|
targetPort: 9093
|
||||||
|
nodePort: 30085
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: alertmanager
|
||||||
|
alertmanager: prometheus-stack-kube-prom-alertmanager
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: argocd-server-nodeport
|
||||||
|
namespace: argocd
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
ports:
|
||||||
|
- name: https
|
||||||
|
port: 443
|
||||||
|
targetPort: 8080
|
||||||
|
nodePort: 30086
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: argocd-server
|
||||||
|
|
@ -0,0 +1,285 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: heimdall-config
|
||||||
|
namespace: heimdall
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: openebs-hostpath-retain
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: heimdall-link-seed
|
||||||
|
namespace: heimdall
|
||||||
|
data:
|
||||||
|
links.json: |
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Website",
|
||||||
|
"url": "https://lab2025.duckdns.org/",
|
||||||
|
"description": "Public portfolio website",
|
||||||
|
"colour": "#0f766e",
|
||||||
|
"class": "Website"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Demo Apps",
|
||||||
|
"url": "https://lab2025.duckdns.org/demo-apps/",
|
||||||
|
"description": "Static browser-side demo catalog",
|
||||||
|
"colour": "#2563eb",
|
||||||
|
"class": "Demos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Gitea",
|
||||||
|
"url": "https://lab2025.duckdns.org/git/",
|
||||||
|
"description": "External Git service on the Raspberry Pi",
|
||||||
|
"colour": "#609926",
|
||||||
|
"class": "Gitea"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Grafana",
|
||||||
|
"url": "http://100.77.80.72:30083/",
|
||||||
|
"description": "Monitoring dashboards",
|
||||||
|
"colour": "#f97316",
|
||||||
|
"class": "Grafana"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Prometheus",
|
||||||
|
"url": "http://100.77.80.72:30084/",
|
||||||
|
"description": "Prometheus query UI",
|
||||||
|
"colour": "#dc2626",
|
||||||
|
"class": "Prometheus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Alertmanager",
|
||||||
|
"url": "http://100.77.80.72:30085/",
|
||||||
|
"description": "Alert routing and silences",
|
||||||
|
"colour": "#b91c1c",
|
||||||
|
"class": "Alertmanager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Argo CD",
|
||||||
|
"url": "https://100.77.80.72:30086/",
|
||||||
|
"description": "GitOps application sync status",
|
||||||
|
"colour": "#0ea5e9",
|
||||||
|
"class": "ArgoCD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Container Registry",
|
||||||
|
"url": "http://100.77.80.72:30500/v2/_catalog",
|
||||||
|
"description": "Local image registry catalog endpoint",
|
||||||
|
"colour": "#334155",
|
||||||
|
"class": "Docker"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Heimdall",
|
||||||
|
"url": "http://100.77.80.72:30082/",
|
||||||
|
"description": "Homelab service dashboard",
|
||||||
|
"colour": "#7c3aed",
|
||||||
|
"class": "Heimdall"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
seed.py: |
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
DB_CANDIDATES = (
|
||||||
|
"/config/www/app.sqlite",
|
||||||
|
"/config/app.sqlite",
|
||||||
|
)
|
||||||
|
LINKS_PATH = "/seed/links.json"
|
||||||
|
|
||||||
|
def find_database():
|
||||||
|
while True:
|
||||||
|
for path in DB_CANDIDATES:
|
||||||
|
if os.path.exists(path):
|
||||||
|
return path
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
def table_exists(conn, name):
|
||||||
|
row = conn.execute(
|
||||||
|
"select name from sqlite_master where type = 'table' and name = ?",
|
||||||
|
(name,),
|
||||||
|
).fetchone()
|
||||||
|
return row is not None
|
||||||
|
|
||||||
|
def columns(conn, table):
|
||||||
|
return {row[1] for row in conn.execute(f"pragma table_info({table})")}
|
||||||
|
|
||||||
|
def wait_for_items_table(db_path):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
if table_exists(conn, "items"):
|
||||||
|
return conn
|
||||||
|
conn.close()
|
||||||
|
except sqlite3.Error:
|
||||||
|
pass
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
def upsert_links(conn, links):
|
||||||
|
item_columns = columns(conn, "items")
|
||||||
|
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
for order, link in enumerate(links):
|
||||||
|
existing = conn.execute(
|
||||||
|
"select id from items where title = ?",
|
||||||
|
(link["title"],),
|
||||||
|
).fetchone()
|
||||||
|
values = {
|
||||||
|
"title": link["title"],
|
||||||
|
"url": link["url"],
|
||||||
|
"description": link.get("description"),
|
||||||
|
"colour": link.get("colour"),
|
||||||
|
"pinned": 1,
|
||||||
|
"order": order,
|
||||||
|
"type": 0,
|
||||||
|
"user_id": 1,
|
||||||
|
"class": link.get("class"),
|
||||||
|
"appid": None,
|
||||||
|
"appdescription": link.get("description"),
|
||||||
|
"created_at": now,
|
||||||
|
"updated_at": now,
|
||||||
|
"deleted_at": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
update_columns = [
|
||||||
|
name for name in values
|
||||||
|
if name in item_columns and name not in ("id", "title", "created_at")
|
||||||
|
]
|
||||||
|
assignments = ", ".join(f"{name} = ?" for name in update_columns)
|
||||||
|
params = [values[name] for name in update_columns]
|
||||||
|
params.append(existing[0])
|
||||||
|
conn.execute(f"update items set {assignments} where id = ?", params)
|
||||||
|
continue
|
||||||
|
|
||||||
|
insert_columns = [
|
||||||
|
name for name in values
|
||||||
|
if name in item_columns and name != "deleted_at"
|
||||||
|
]
|
||||||
|
placeholders = ", ".join("?" for _ in insert_columns)
|
||||||
|
conn.execute(
|
||||||
|
f"insert into items ({', '.join(insert_columns)}) values ({placeholders})",
|
||||||
|
[values[name] for name in insert_columns],
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
with open(LINKS_PATH, encoding="utf-8") as links_file:
|
||||||
|
links = json.load(links_file)
|
||||||
|
|
||||||
|
db_path = find_database()
|
||||||
|
connection = wait_for_items_table(db_path)
|
||||||
|
try:
|
||||||
|
upsert_links(connection, links)
|
||||||
|
finally:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time.sleep(3600)
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: heimdall
|
||||||
|
namespace: heimdall
|
||||||
|
labels:
|
||||||
|
app: heimdall
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: heimdall
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: heimdall
|
||||||
|
spec:
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
homelab.dev/workload-class: platform
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
fsGroupChangePolicy: OnRootMismatch
|
||||||
|
containers:
|
||||||
|
- name: heimdall
|
||||||
|
image: lscr.io/linuxserver/heimdall:version-2.7.6
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
- name: PUID
|
||||||
|
value: "1000"
|
||||||
|
- name: PGID
|
||||||
|
value: "1000"
|
||||||
|
- name: TZ
|
||||||
|
value: America/Mexico_City
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
name: http
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 60
|
||||||
|
periodSeconds: 30
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 25m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
memory: 512Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: heimdall-config
|
||||||
|
mountPath: /config
|
||||||
|
- name: link-seeder
|
||||||
|
image: python:3.12-alpine
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- python
|
||||||
|
- /seed/seed.py
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 5m
|
||||||
|
memory: 32Mi
|
||||||
|
limits:
|
||||||
|
memory: 96Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: heimdall-config
|
||||||
|
mountPath: /config
|
||||||
|
- name: link-seed
|
||||||
|
mountPath: /seed
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: heimdall-config
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: heimdall-config
|
||||||
|
- name: link-seed
|
||||||
|
configMap:
|
||||||
|
name: heimdall-link-seed
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: heimdall
|
||||||
|
namespace: heimdall
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: http
|
||||||
|
nodePort: 30082
|
||||||
|
selector:
|
||||||
|
app: heimdall
|
||||||
|
|
@ -57,5 +57,14 @@ variable "applications" {
|
||||||
self_heal = true
|
self_heal = true
|
||||||
create_namespace = true
|
create_namespace = true
|
||||||
}
|
}
|
||||||
|
heimdall = {
|
||||||
|
project = "default"
|
||||||
|
path = "apps/heimdall"
|
||||||
|
namespace = "heimdall"
|
||||||
|
target_revision = "main"
|
||||||
|
prune = true
|
||||||
|
self_heal = true
|
||||||
|
create_namespace = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,10 +93,10 @@ variable "tailscale_nodeport_access" {
|
||||||
|
|
||||||
variable "tailscale_nodeport_extra_ports" {
|
variable "tailscale_nodeport_extra_ports" {
|
||||||
type = list(number)
|
type = list(number)
|
||||||
default = [30081]
|
default = [30081, 30082, 30083, 30084, 30085, 30086]
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "tailscale_nodeport_extra_target_ports" {
|
variable "tailscale_nodeport_extra_target_ports" {
|
||||||
type = list(number)
|
type = list(number)
|
||||||
default = []
|
default = [80, 3000, 9090, 9093]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
lab.sh
13
lab.sh
|
|
@ -172,6 +172,14 @@ adopt_apps_existing_resources() {
|
||||||
"argoproj.io/v1alpha1" \
|
"argoproj.io/v1alpha1" \
|
||||||
"Application" \
|
"Application" \
|
||||||
"demos-static"
|
"demos-static"
|
||||||
|
adopt_tofu_kubernetes_manifest \
|
||||||
|
"${stack}" \
|
||||||
|
'kubernetes_manifest.argocd_application["heimdall"]' \
|
||||||
|
"${namespace}" \
|
||||||
|
"applications.argoproj.io" \
|
||||||
|
"argoproj.io/v1alpha1" \
|
||||||
|
"Application" \
|
||||||
|
"heimdall"
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_homelab_node_labels() {
|
ensure_homelab_node_labels() {
|
||||||
|
|
@ -2690,6 +2698,11 @@ apps() {
|
||||||
write_demos_image_state "${demos_image_state_file}" "${demos_source_hash}" "${demos_platforms}" "${demos_image_ref}"
|
write_demos_image_state "${demos_image_state_file}" "${demos_source_hash}" "${demos_platforms}" "${demos_image_ref}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
refresh_argocd_application heimdall
|
||||||
|
wait_for_namespace heimdall heimdall 300
|
||||||
|
wait_for_namespaced_resource heimdall deployment heimdall heimdall 300
|
||||||
|
wait_for_deployment_ready heimdall heimdall heimdall 300
|
||||||
|
|
||||||
echo "Application deployment successfully completed."
|
echo "Application deployment successfully completed."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue