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
|
||||
- passes the website image produced by the build step into Argo CD as a
|
||||
Kustomize image override
|
||||
- default apps are `container-registry`, `website-production`, and
|
||||
`demos-static`
|
||||
- default apps are `container-registry`, `website-production`,
|
||||
`demos-static`, and `heimdall`
|
||||
|
||||
5. `bootstrap/edge`
|
||||
- connects to the OCI jump box
|
||||
|
|
@ -151,6 +151,7 @@ kubectl -n argocd get applications
|
|||
kubectl -n container-registry get pods
|
||||
kubectl -n website-production 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'
|
||||
|
||||
|
|
@ -263,7 +264,8 @@ tailscale_nodeport_access = {
|
|||
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
|
||||
|
|
@ -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
|
||||
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
|
||||
|
||||
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
|
||||
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" {
|
||||
type = list(number)
|
||||
default = [30081]
|
||||
default = [30081, 30082, 30083, 30084, 30085, 30086]
|
||||
}
|
||||
|
||||
variable "tailscale_nodeport_extra_target_ports" {
|
||||
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" \
|
||||
"Application" \
|
||||
"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() {
|
||||
|
|
@ -2690,6 +2698,11 @@ apps() {
|
|||
write_demos_image_state "${demos_image_state_file}" "${demos_source_hash}" "${demos_platforms}" "${demos_image_ref}"
|
||||
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."
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue