Compare commits
87 Commits
ac7863802a
...
a08a2189e1
| Author | SHA1 | Date | |
|---|---|---|---|
| a08a2189e1 | |||
| 45f0100784 | |||
| d5da49e566 | |||
| e0e27445c7 | |||
| 9f61854bc2 | |||
| ded87979c5 | |||
| 538fca4195 | |||
| 5ffcfc7d01 | |||
| f958d65528 | |||
| 4197072593 | |||
| d6f0f375b7 | |||
| 051691e71f | |||
| 4a709391e6 | |||
| 1880df2525 | |||
| 02ed3e3145 | |||
| b59025d495 | |||
| 306b4b8458 | |||
| 2e6f811d12 | |||
| ea08411128 | |||
| a09333ba38 | |||
| bff6b83d11 | |||
| a94bd95248 | |||
| 2c0622583e | |||
| 86490b74c4 | |||
| 2ef8a7bbc2 | |||
| ae85dcfeaa | |||
| 41292eff0b | |||
| a69bd45455 | |||
| a3a5b1a9bd | |||
| 938f6b336c | |||
| 3c97a02fa7 | |||
| 980892a5b4 | |||
| adf7d7eb31 | |||
| 2fe8f7ea6a | |||
| c00b760976 | |||
| d78fc77825 | |||
| a6ab2b44af | |||
| 3a207c7d94 | |||
| d45cf950ec | |||
| 193c820fc6 | |||
| c3524cec3d | |||
| f214e394d0 | |||
| 07cffbeec0 | |||
| 576221c47d | |||
| f63d39e5aa | |||
| 48bce52660 | |||
| 5b1a209d9a | |||
| 5437b985e8 | |||
| f49e341445 | |||
| 8c64a4b067 | |||
| 7b5001c581 | |||
| fc0c5c1250 | |||
| 39fc2aacde | |||
| 33f0d67b34 | |||
| 48a2a53023 | |||
| 269b6cd7ad | |||
| b06b5d7612 | |||
| 0f1994c384 | |||
| 3df06948a9 | |||
| 30ac7e5ac1 | |||
| 0b8e4f012a | |||
| 2eecba7f55 | |||
| bd5f1b3a67 | |||
| 9ff70673e3 | |||
| 755c54f26b | |||
| f4588b4304 | |||
| e36f7059ea | |||
| 6deefc514e | |||
| 33ff3d20aa | |||
| 65de7602c9 | |||
| 9b77a89b0d | |||
| 6a86590484 | |||
| 8cc80f695f | |||
| 50c25b1b92 | |||
| a85fac9002 | |||
| 5bfeffe31f | |||
| 8459ea7058 | |||
| 6efe79819f | |||
| 33d07dcf5c | |||
| 7257762c45 | |||
| bff64dba65 | |||
| f72dc43f76 | |||
| 47a73af27e | |||
| 1ee60d9534 | |||
| 63d82af268 | |||
| 47cbc9b9f6 | |||
| 001e9c36fe |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
AGENTS.md
|
||||
12
clusters/atlas/applications/kustomization.yaml
Normal file
12
clusters/atlas/applications/kustomization.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
# clusters/atlas/applications/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../services/crypto
|
||||
- ../../services/gitea
|
||||
- ../../services/jellyfin
|
||||
- ../../services/jitsi
|
||||
- ../../services/monitoring
|
||||
- ../../services/pegasus
|
||||
- ../../services/vault
|
||||
- ../../services/zot
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-crypto.yaml
|
||||
# clusters/atlas/flux-system/applications/crypto/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-gitea.yaml
|
||||
# clusters/atlas/flux-system/applications/gitea/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-jellyfin.yaml
|
||||
# clusters/atlas/flux-system/applications/jellyfin/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -0,0 +1,19 @@
|
||||
# clusters/atlas/flux-system/applications/jitsi/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: jitsi
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/jitsi
|
||||
targetNamespace: jitsi
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
dependsOn:
|
||||
- name: core
|
||||
wait: true
|
||||
timeout: 5m
|
||||
15
clusters/atlas/flux-system/applications/kustomization.yaml
Normal file
15
clusters/atlas/flux-system/applications/kustomization.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
# clusters/atlas/flux-system/applications/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- zot/kustomization.yaml
|
||||
- gitea/kustomization.yaml
|
||||
- vault/kustomization.yaml
|
||||
- jitsi/kustomization.yaml
|
||||
- crypto/kustomization.yaml
|
||||
- monerod/kustomization.yaml
|
||||
- pegasus/kustomization.yaml
|
||||
- pegasus/image-automation.yaml
|
||||
- jellyfin/kustomization.yaml
|
||||
- xmr-miner/kustomization.yaml
|
||||
- sui-metrics/kustomization.yaml
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-monerod.yaml
|
||||
# clusters/atlas/flux-system/applications/monerod/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -0,0 +1,20 @@
|
||||
# clusters/atlas/flux-system/applications/pegasus/image-automation.yaml
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta1
|
||||
kind: ImageUpdateAutomation
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m0s
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
git:
|
||||
commit:
|
||||
author:
|
||||
email: ops@bstein.dev
|
||||
name: flux-bot
|
||||
messageTemplate: "chore(pegasus): update image to {{range .Updated.Images}}{{.}}{{end}}"
|
||||
update:
|
||||
strategy: Setters
|
||||
path: ./services/pegasus
|
||||
@ -0,0 +1,19 @@
|
||||
# clusters/atlas/flux-system/applications/pegasus/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/pegasus
|
||||
targetNamespace: jellyfin
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
dependsOn:
|
||||
- name: core
|
||||
wait: true
|
||||
timeout: 5m
|
||||
@ -0,0 +1,19 @@
|
||||
# clusters/atlas/flux-system/applications/sui-metrics/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: sui-metrics
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/sui-metrics/overlays/atlas
|
||||
prune: true
|
||||
dependsOn:
|
||||
- name: monitoring
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
wait: true
|
||||
timeout: 5m
|
||||
targetNamespace: sui-metrics
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-vault.yaml
|
||||
# clusters/atlas/flux-system/applications/vault/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-core.yaml
|
||||
# clusters/atlas/flux-system/applications/xmr-miner/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-zot.yaml
|
||||
# clusters/atlas/flux-system/applications/zot/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
# This manifest was generated by flux. DO NOT EDIT.
|
||||
# Flux Version: v2.5.1
|
||||
# Flux Version: v2.5.1f reconzaq1= zaq1= aq1= 1= w2cile kustomization flux-system --namespace flux-system --with-source
|
||||
# Components: source-controller,kustomize-controller,helm-controller,notification-controller
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
@ -8,7 +8,7 @@ metadata:
|
||||
spec:
|
||||
interval: 1m0s
|
||||
ref:
|
||||
branch: main
|
||||
branch: restructure/hybrid-clusters
|
||||
secretRef:
|
||||
name: flux-system-gitea
|
||||
url: ssh://git@scm.bstein.dev:2242/bstein/titan-iac.git
|
||||
@ -20,7 +20,7 @@ metadata:
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m0s
|
||||
path: ./
|
||||
path: ./clusters/atlas/flux-system
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
8
clusters/atlas/flux-system/kustomization.yaml
Normal file
8
clusters/atlas/flux-system/kustomization.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
# clusters/atlas/flux-system/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gotk-components.yaml
|
||||
- gotk-sync.yaml
|
||||
- platform
|
||||
- applications
|
||||
15
clusters/atlas/flux-system/platform/core/kustomization.yaml
Normal file
15
clusters/atlas/flux-system/platform/core/kustomization.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
# clusters/atlas/flux-system/platform/core/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: core
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/core
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
wait: false
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/flux-system/kustomization-helm.yaml
|
||||
# clusters/atlas/flux-system/platform/helm/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
9
clusters/atlas/flux-system/platform/kustomization.yaml
Normal file
9
clusters/atlas/flux-system/platform/kustomization.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
# clusters/atlas/flux-system/platform/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- core/kustomization.yaml
|
||||
- helm/kustomization.yaml
|
||||
- traefik/kustomization.yaml
|
||||
- monitoring/kustomization.yaml
|
||||
- longhorn-ui/kustomization.yaml
|
||||
@ -1,3 +1,4 @@
|
||||
# clusters/atlas/flux-system/platform/longhorn-ui/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
@ -0,0 +1,14 @@
|
||||
# clusters/atlas/flux-system/platform/monitoring/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: monitoring
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/monitoring
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
wait: true
|
||||
@ -0,0 +1,18 @@
|
||||
# clusters/atlas/flux-system/platform/traefik/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: traefik
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/traefik
|
||||
targetNamespace: traefik
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
dependsOn:
|
||||
- name: core
|
||||
wait: true
|
||||
7
clusters/atlas/platform/kustomization.yaml
Normal file
7
clusters/atlas/platform/kustomization.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
# clusters/atlas/platform/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../../infrastructure/modules/base
|
||||
- ../../../infrastructure/modules/profiles/atlas-ha
|
||||
- ../../../infrastructure/sources/cert-manager/letsencrypt.yaml
|
||||
5
clusters/oceanus/README.md
Normal file
5
clusters/oceanus/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Oceanus Cluster Scaffold
|
||||
|
||||
This directory prepares the Flux and Kustomize layout for a future Oceanus-managed cluster.
|
||||
Populate `flux-system/` with `gotk-components.yaml` and related manifests after running `flux bootstrap`.
|
||||
Define node-specific resources under `infrastructure/modules/profiles/oceanus-validator/` and reference workloads in `applications/` as they come online.
|
||||
4
clusters/oceanus/applications/kustomization.yaml
Normal file
4
clusters/oceanus/applications/kustomization.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
# clusters/oceanus/applications/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources: []
|
||||
9
clusters/oceanus/flux-system/kustomization.yaml
Normal file
9
clusters/oceanus/flux-system/kustomization.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
# clusters/oceanus/flux-system/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
# Populate when oceanus cluster is bootstrapped with Flux.
|
||||
# - gotk-components.yaml
|
||||
# - gotk-sync.yaml
|
||||
- ../platform
|
||||
- ../applications
|
||||
6
clusters/oceanus/platform/kustomization.yaml
Normal file
6
clusters/oceanus/platform/kustomization.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# clusters/oceanus/platform/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../infrastructure/modules/base
|
||||
- ../../infrastructure/modules/profiles/oceanus-validator
|
||||
16
docs/topology.md
Normal file
16
docs/topology.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Titan Homelab Topology
|
||||
|
||||
| Hostname | Role / Function | Managed By | Notes |
|
||||
|------------|--------------------------------|---------------------|-------|
|
||||
| titan-0a | Kubernetes control-plane | Flux (atlas cluster)| HA leader, tainted for control only |
|
||||
| titan-0b | Kubernetes control-plane | Flux (atlas cluster)| Standby control node |
|
||||
| titan-0c | Kubernetes control-plane | Flux (atlas cluster)| Standby control node |
|
||||
| titan-04-19| Raspberry Pi workers | Flux (atlas cluster)| Workload nodes, labelled per hardware |
|
||||
| titan-22 | GPU mini-PC (Jellyfin) | Flux + Ansible | NVIDIA runtime managed via `modules/profiles/atlas-ha` |
|
||||
| titan-24 | Tethys hybrid node | Flux + Ansible | Runs SUI metrics via K8s, validator via Ansible |
|
||||
| titan-db | HA control plane database | Ansible | PostgreSQL / etcd backing services |
|
||||
| titan-jh | Jumphost & bastion | Ansible | Entry point / future KVM services |
|
||||
| oceanus | Dedicated SUI validator host | Ansible / Flux prep | Baremetal validator workloads, exposes metrics to atlas; Kustomize scaffold under `clusters/oceanus/` |
|
||||
| styx | Air-gapped workstation | Manual / Scripts | Remains isolated, scripts tracked in `hosts/styx` |
|
||||
|
||||
Use the `clusters/` directory for cluster-scoped state and the `hosts/` directory for baremetal orchestration.
|
||||
2
hosts/group_vars/all.yaml
Normal file
2
hosts/group_vars/all.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
# hosts/group_vars/all.yaml
|
||||
validator_version: latest
|
||||
2
hosts/host_vars/titan-24.yaml
Normal file
2
hosts/host_vars/titan-24.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
# hosts/host_vars/titan-24.yaml
|
||||
validator_compose_path: /opt/sui-validator
|
||||
28
hosts/inventory/lab.yaml
Normal file
28
hosts/inventory/lab.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
# hosts/inventory/lab.yaml
|
||||
# Replace ansible_host and ansible_user values with real connectivity details.
|
||||
all:
|
||||
children:
|
||||
atlas:
|
||||
hosts:
|
||||
titan-24:
|
||||
ansible_host: REPLACE_ME
|
||||
ansible_user: ubuntu
|
||||
roleset: tethys_hybrid
|
||||
titan-22:
|
||||
ansible_host: REPLACE_ME
|
||||
ansible_user: debian
|
||||
roleset: minipc_gpu
|
||||
baremetal:
|
||||
hosts:
|
||||
titan-db:
|
||||
ansible_host: REPLACE_ME
|
||||
ansible_user: postgres
|
||||
roleset: database
|
||||
titan-jh:
|
||||
ansible_host: REPLACE_ME
|
||||
ansible_user: jump
|
||||
roleset: jumphost
|
||||
oceanus:
|
||||
ansible_host: REPLACE_ME
|
||||
ansible_user: validator
|
||||
roleset: validator
|
||||
29
hosts/playbooks/site.yaml
Normal file
29
hosts/playbooks/site.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
# hosts/playbooks/site.yaml
|
||||
---
|
||||
- name: Configure titan-db
|
||||
hosts: titan-db
|
||||
gather_facts: true
|
||||
roles:
|
||||
- common
|
||||
- titan_db
|
||||
|
||||
- name: Configure titan-jh
|
||||
hosts: titan-jh
|
||||
gather_facts: true
|
||||
roles:
|
||||
- common
|
||||
- titan_jh
|
||||
|
||||
- name: Configure oceanus validator host
|
||||
hosts: oceanus
|
||||
gather_facts: true
|
||||
roles:
|
||||
- common
|
||||
- oceanus_base
|
||||
|
||||
- name: Prepare hybrid tethys node
|
||||
hosts: titan-24
|
||||
gather_facts: true
|
||||
roles:
|
||||
- common
|
||||
- tethys_canary
|
||||
9
hosts/roles/common/tasks/main.yaml
Normal file
9
hosts/roles/common/tasks/main.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
# hosts/roles/common/tasks/main.yaml
|
||||
---
|
||||
- name: Ensure base packages present
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- curl
|
||||
- vim
|
||||
state: present
|
||||
tags: ['common', 'packages']
|
||||
6
hosts/roles/oceanus_base/tasks/main.yaml
Normal file
6
hosts/roles/oceanus_base/tasks/main.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# hosts/roles/oceanus_base/tasks/main.yaml
|
||||
---
|
||||
- name: Placeholder for oceanus base configuration
|
||||
ansible.builtin.debug:
|
||||
msg: "Install validator prerequisites and monitoring exporters here."
|
||||
tags: ['oceanus']
|
||||
6
hosts/roles/tethys_canary/tasks/main.yaml
Normal file
6
hosts/roles/tethys_canary/tasks/main.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# hosts/roles/tethys_canary/tasks/main.yaml
|
||||
---
|
||||
- name: Placeholder for SUI validator container runtime setup
|
||||
ansible.builtin.debug:
|
||||
msg: "Configure container runtime and validator compose stack here."
|
||||
tags: ['tethys', 'validator']
|
||||
6
hosts/roles/titan_db/tasks/main.yaml
Normal file
6
hosts/roles/titan_db/tasks/main.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# hosts/roles/titan_db/tasks/main.yaml
|
||||
---
|
||||
- name: Placeholder for titan-db provisioning
|
||||
ansible.builtin.debug:
|
||||
msg: "Install database packages, configure backups, and manage users here."
|
||||
tags: ['titan_db']
|
||||
6
hosts/roles/titan_jh/tasks/main.yaml
Normal file
6
hosts/roles/titan_jh/tasks/main.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# hosts/roles/titan_jh/tasks/main.yaml
|
||||
---
|
||||
- name: Placeholder for jumphost hardening
|
||||
ansible.builtin.debug:
|
||||
msg: "Harden SSH, manage bastion tooling, and configure audit logging here."
|
||||
tags: ['jumphost']
|
||||
2
hosts/styx/README.md
Normal file
2
hosts/styx/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# hosts/styx/README.md
|
||||
Styx is air-gapped; provisioning scripts live under `scripts/`.
|
||||
@ -1,5 +0,0 @@
|
||||
# infrastructure/core/gpu/daemonsets/profiles/jetson-only/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../device-plugin-jetson
|
||||
@ -1,6 +0,0 @@
|
||||
# infrastructure/core/gpu/daemonsets/profiles/minipc-and-jetson/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../device-plugin-minipc
|
||||
- ../../device-plugin-jetson
|
||||
@ -1,5 +0,0 @@
|
||||
# infrastructure/core/gpu/daemonsets/profiles/minipc-only/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../device-plugin-minipc
|
||||
@ -2,7 +2,6 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- base
|
||||
# - gpu/profiles/jetson-only
|
||||
# - gpu/profiles/minipc-and-jetson
|
||||
- gpu/profiles/minipc-only
|
||||
- ../modules/base
|
||||
- ../modules/profiles/atlas-ha
|
||||
- ../sources/cert-manager/letsencrypt.yaml
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
# infrastructure/flux-system/kustomization-core.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: core
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/core
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
wait: true
|
||||
|
||||
# Only wait for the NVIDIA device-plugin DaemonSet on titan-22
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
name: nvidia-device-plugin-minipc
|
||||
namespace: kube-system
|
||||
@ -2,15 +2,4 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gotk-components.yaml
|
||||
- gotk-sync.yaml
|
||||
- kustomization-zot.yaml
|
||||
- kustomization-core.yaml
|
||||
- kustomization-helm.yaml
|
||||
- kustomization-gitea.yaml
|
||||
- kustomization-vault.yaml
|
||||
- kustomization-crypto.yaml
|
||||
- kustomization-monerod.yaml
|
||||
- kustomization-jellyfin.yaml
|
||||
- kustomization-xmr-miner.yaml
|
||||
- kustomization-longhorn-ui.yaml
|
||||
- ../clusters/atlas/flux-system
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/base/kustomization.yaml
|
||||
# infrastructure/modules/base/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/base/priorityclass/kustomization.yaml
|
||||
# infrastructure/modules/base/priorityclass/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/base/priorityclass/scavenger.yaml
|
||||
# infrastructure/modules/base/priorityclass/scavenger.yaml
|
||||
apiVersion: scheduling.k8s.io/v1
|
||||
kind: PriorityClass
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/base/storageclass/kustomization.yaml
|
||||
# infrastructure/modules/base/runtimeclass/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
@ -1,4 +1,4 @@
|
||||
# services/jellyfin/runtimeclass.yaml
|
||||
# infrastructure/modules/base/runtimeclass/runtimeclass.yaml
|
||||
apiVersion: node.k8s.io/v1
|
||||
kind: RuntimeClass
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/base/storageclass/asteria.yaml
|
||||
# infrastructure/modules/base/storageclass/asteria.yaml
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
@ -8,6 +8,9 @@ parameters:
|
||||
fromBackup: ""
|
||||
numberOfReplicas: "2"
|
||||
staleReplicaTimeout: "30"
|
||||
fsType: "ext4"
|
||||
replicaAutoBalance: "least-effort"
|
||||
dataLocality: "disabled"
|
||||
provisioner: driver.longhorn.io
|
||||
reclaimPolicy: Retain
|
||||
allowVolumeExpansion: true
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/base/storageclass/astreae.yaml
|
||||
# infrastructure/modules/base/storageclass/astreae.yaml
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/base/storageclass/kustomization.yaml
|
||||
# infrastructure/modules/base/storageclass/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
@ -0,0 +1,7 @@
|
||||
# infrastructure/modules/profiles/atlas-ha/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../components/device-plugin-jetson
|
||||
- ../components/device-plugin-minipc
|
||||
- ../components/device-plugin-tethys
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/gpu/daemonsets/device-plugin-jetson/daemonset.yaml
|
||||
# infrastructure/modules/profiles/components/device-plugin-jetson/daemonset.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/gpu/daemonsets/device-plugin-jetson/kustomization.yaml
|
||||
# infrastructure/modules/profiles/components/device-plugin-jetson/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/gpu/daemonsets/device-plugin-minipc/daemonset.yaml
|
||||
# infrastructure/modules/profiles/components/device-plugin-minipc/daemonset.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
@ -24,7 +24,6 @@ spec:
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
priorityClassName: system-node-critical
|
||||
runtimeClassName: nvidia
|
||||
containers:
|
||||
- name: nvidia-device-plugin-ctr
|
||||
image: nvcr.io/nvidia/k8s-device-plugin:v0.16.2
|
||||
@ -1,4 +1,4 @@
|
||||
# infrastructure/core/gpu/daemonsets/device-plugin-minipc/kustomization.yaml
|
||||
# infrastructure/modules/profiles/components/device-plugin-minipc/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
@ -0,0 +1,49 @@
|
||||
# infrastructure/modules/profiles/components/device-plugin-tethys/daemonset.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: nvidia-device-plugin-tethys
|
||||
namespace: kube-system
|
||||
labels:
|
||||
app.kubernetes.io/name: nvidia-device-plugin
|
||||
app.kubernetes.io/instance: titan24
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: nvidia-device-plugin
|
||||
app.kubernetes.io/instance: titan24
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: nvidia-device-plugin
|
||||
app.kubernetes.io/instance: titan24
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-24
|
||||
kubernetes.io/arch: amd64
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
priorityClassName: system-node-critical
|
||||
runtimeClassName: nvidia
|
||||
containers:
|
||||
- name: nvidia-device-plugin-ctr
|
||||
image: nvcr.io/nvidia/k8s-device-plugin:v0.16.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- "--fail-on-init-error=false"
|
||||
- "--device-list-strategy=envvar"
|
||||
- "--mig-strategy=none"
|
||||
securityContext:
|
||||
privileged: true
|
||||
env:
|
||||
- name: NVIDIA_VISIBLE_DEVICES
|
||||
value: "all"
|
||||
- name: NVIDIA_DRIVER_CAPABILITIES
|
||||
value: "compute,video,utility"
|
||||
volumeMounts:
|
||||
- name: device-plugin
|
||||
mountPath: /var/lib/kubelet/device-plugins
|
||||
volumes:
|
||||
- name: device-plugin
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/device-plugins
|
||||
@ -0,0 +1,5 @@
|
||||
# infrastructure/modules/profiles/components/device-plugin-tethys/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- daemonset.yaml
|
||||
@ -0,0 +1,4 @@
|
||||
# infrastructure/modules/profiles/oceanus-validator/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources: []
|
||||
@ -0,0 +1,5 @@
|
||||
# infrastructure/modules/profiles/tethys-hybrid/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../components/device-plugin-tethys
|
||||
@ -4,7 +4,7 @@ metadata:
|
||||
name: letsencrypt
|
||||
spec:
|
||||
acme:
|
||||
email: you@bstein.dev
|
||||
email: brad.stein@gmail.com
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-account-key
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
# infrastructure/sources/helm/grafana.yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
# infrastructure/sources/helm/hashicorp.yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
# infrastructure/sources/helm/jetstack.yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
# infrastructure/sources/helm/prometheus.yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
|
||||
9
infrastructure/sources/helm/victoria-metrics.yaml
Normal file
9
infrastructure/sources/helm/victoria-metrics.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
# infrastructure/sources/helm/victoria-metrics.yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: victoria-metrics
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1h
|
||||
url: https://victoriametrics.github.io/helm-charts/
|
||||
@ -35,6 +35,12 @@ items:
|
||||
- --entrypoints.web.address=:80
|
||||
- --entrypoints.websecure.address=:443
|
||||
- --api.dashboard=true
|
||||
- --metrics.prometheus=true
|
||||
- --metrics.prometheus.addEntryPointsLabels=true
|
||||
- --metrics.prometheus.addRoutersLabels=true
|
||||
- --metrics.prometheus.addServicesLabels=true
|
||||
- --entrypoints.metrics.address=:9100
|
||||
- --metrics.prometheus.entryPoint=metrics
|
||||
image: traefik:v3.3.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: traefik
|
||||
@ -48,6 +54,9 @@ items:
|
||||
- containerPort: 8080
|
||||
name: admin
|
||||
protocol: TCP
|
||||
- containerPort: 9100
|
||||
name: metrics
|
||||
protocol: TCP
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
dnsPolicy: ClusterFirst
|
||||
|
||||
@ -9,3 +9,4 @@ resources:
|
||||
- serviceaccount.yaml
|
||||
- clusterrole.yaml
|
||||
- clusterrolebinding.yaml
|
||||
- service.yaml
|
||||
|
||||
20
infrastructure/traefik/service.yaml
Normal file
20
infrastructure/traefik/service.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
# infrastructure/traefik/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: traefik-metrics
|
||||
namespace: traefik
|
||||
labels:
|
||||
app: traefik
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "9100"
|
||||
prometheus.io/path: "/metrics"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: traefik
|
||||
ports:
|
||||
- name: metrics
|
||||
port: 9100
|
||||
targetPort: metrics
|
||||
2
scripts/longhorn_volume_usage.fish
Normal file → Executable file
2
scripts/longhorn_volume_usage.fish
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env fish
|
||||
|
||||
function pvc-usage --description "Show Longhorn PVC usage (human-readable) mapped to namespace/name"
|
||||
begin
|
||||
kubectl -n longhorn-system get volumes.longhorn.io -o json \
|
||||
|
||||
218
scripts/styx_kioskification.sh
Normal file
218
scripts/styx_kioskification.sh
Normal file
@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 0) Create dedicated user if it doesn't exist
|
||||
if ! id -u styx >/dev/null 2>&1; then
|
||||
sudo useradd -m -s /bin/bash styx
|
||||
echo "Created user 'styx'"
|
||||
fi
|
||||
|
||||
# 1) App directory
|
||||
sudo mkdir -p /opt/styx-kiosk/keys
|
||||
sudo chown -R styx:styx /opt/styx-kiosk
|
||||
|
||||
# 2) Drop the kiosk app (written below) into place
|
||||
sudo tee /opt/styx-kiosk/kiosk.py >/dev/null <<'PY'
|
||||
#!/usr/bin/env python3
|
||||
import base64, json, os, subprocess, threading, tempfile
|
||||
from datetime import datetime
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
|
||||
APP_TITLE = "STYX Airgap Signer"
|
||||
CAMERA_DEV = os.environ.get("ZBAR_DEV", "/dev/video0")
|
||||
KEY_PATH = os.environ.get("STYX_KEY", "/vault/keys/signer_ed25519.pem") # in the LUKS vault
|
||||
ALGO = os.environ.get("STYX_ALGO", "ed25519") # or 'secp256r1'
|
||||
QR_TMP = "/tmp/styx_signed.png"
|
||||
|
||||
def zbar_scan_oneshot():
|
||||
# --raw -> data only; --nodisplay -> no preview window; --oneshot -> exit after first code
|
||||
# (zbarcam supports --oneshot; prints one code and exits). :contentReference[oaicite:2]{index=2}
|
||||
cmd = ["zbarcam", "--raw", "--nodisplay", "--oneshot", CAMERA_DEV]
|
||||
try:
|
||||
out = subprocess.check_output(cmd, text=True, timeout=30)
|
||||
out = out.strip()
|
||||
return out if out else None
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
def openssl_pub_der_b64(key_path):
|
||||
der = subprocess.check_output(["openssl","pkey","-in",key_path,"-pubout","-outform","DER"])
|
||||
return base64.b64encode(der).decode()
|
||||
|
||||
def sign_bytes(msg: bytes, key_path: str, algo: str) -> bytes:
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
f.write(msg)
|
||||
msg_path = f.name
|
||||
try:
|
||||
if algo.lower() == "ed25519":
|
||||
# Ed25519 expects raw message; OpenSSL handles hashing internally.
|
||||
sig = subprocess.check_output(
|
||||
["openssl","pkeyutl","-sign","-inkey",key_path,"-rawin","-in",msg_path]
|
||||
)
|
||||
return sig
|
||||
elif algo.lower() in ("secp256r1","prime256v1","p256"):
|
||||
# ECDSA over P-256; hash with SHA-256; OpenSSL returns DER-encoded (r,s)
|
||||
sig = subprocess.check_output(
|
||||
["openssl","dgst","-sha256","-sign",key_path,msg_path]
|
||||
)
|
||||
return sig
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported algo: {algo}")
|
||||
finally:
|
||||
try: os.unlink(msg_path)
|
||||
except: pass
|
||||
|
||||
def make_signed_envelope(scanned_text: str, key_path: str, algo: str) -> dict:
|
||||
# Accept either raw string or JSON with 'tx_bytes' (base64) or 'message'
|
||||
try:
|
||||
obj = json.loads(scanned_text)
|
||||
if "tx_bytes" in obj:
|
||||
msg = base64.b64decode(obj["tx_bytes"])
|
||||
elif "message" in obj:
|
||||
msg = obj["message"].encode()
|
||||
else:
|
||||
# If it's JSON but doesn't carry known fields, sign canonical JSON bytes
|
||||
msg = json.dumps(obj, sort_keys=True, separators=(",",":")).encode()
|
||||
request_id = obj.get("request_id")
|
||||
except Exception:
|
||||
# Non-JSON → treat the scanned text as the message to sign
|
||||
msg = scanned_text.encode()
|
||||
request_id = None
|
||||
|
||||
sig = sign_bytes(msg, key_path, algo)
|
||||
env = {
|
||||
"algo": algo.lower(),
|
||||
"signature_b64": base64.b64encode(sig).decode(),
|
||||
"pubkey_spki_der_b64": openssl_pub_der_b64(key_path),
|
||||
"payload_sha256_b64": base64.b64encode(subprocess.check_output(["openssl","dgst","-sha256","-binary"], input=msg)).decode(),
|
||||
"quote_raw": scanned_text,
|
||||
"request_id": request_id,
|
||||
"device": os.uname().nodename,
|
||||
"ts_utc": datetime.utcnow().isoformat(timespec="seconds") + "Z",
|
||||
}
|
||||
return env
|
||||
|
||||
def qrencode_to_file(text: str, path: str):
|
||||
# Use qrencode CLI to render a PNG we can display.
|
||||
subprocess.run(["qrencode","-l","M","-s","16","-t","PNG","-o",path], input=text.encode(), check=True)
|
||||
|
||||
class App(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title(APP_TITLE)
|
||||
self.attributes("-fullscreen", True)
|
||||
self.configure(background="black")
|
||||
self.bind("<Escape>", lambda e: self.quit()) # for maintenance only
|
||||
|
||||
s = ttk.Style(self)
|
||||
s.configure("Big.TButton", font=("DejaVu Sans", 48), padding=24)
|
||||
s.configure("Big.TLabel", font=("DejaVu Sans", 32), foreground="white", background="black")
|
||||
|
||||
self.container = tk.Frame(self, bg="black")
|
||||
self.container.pack(expand=True, fill="both")
|
||||
|
||||
self.status = ttk.Label(self.container, text="Ready", style="Big.TLabel")
|
||||
self.status.pack(pady=20)
|
||||
|
||||
self.scan_btn = ttk.Button(self.container, text="SCAN", style="Big.TButton", command=self.start_scan)
|
||||
self.scan_btn.pack(pady=20)
|
||||
|
||||
self.image_label = tk.Label(self.container, bg="black")
|
||||
self.image_label.pack(pady=10)
|
||||
|
||||
self.new_btn = ttk.Button(self.container, text="NEW SCAN", style="Big.TButton", command=self.reset)
|
||||
self.new_btn.pack_forget()
|
||||
|
||||
self.note = ttk.Label(self.container, text="", style="Big.TLabel")
|
||||
self.note.pack(pady=10)
|
||||
|
||||
if not os.path.exists(KEY_PATH):
|
||||
self.status.config(text=f"Key not found at {KEY_PATH}\nInsert/unlock vault to proceed.")
|
||||
|
||||
def reset(self):
|
||||
self.image_label.configure(image="")
|
||||
self.image_label.image = None
|
||||
self.new_btn.pack_forget()
|
||||
self.note.config(text="")
|
||||
self.status.config(text="Ready")
|
||||
self.scan_btn.config(state="normal")
|
||||
|
||||
def start_scan(self):
|
||||
if not os.path.exists(KEY_PATH):
|
||||
messagebox.showerror("Key missing", f"Signing key not found at:\n{KEY_PATH}\nUnlock your vault.")
|
||||
return
|
||||
self.status.config(text="Scanning…")
|
||||
self.scan_btn.config(state="disabled")
|
||||
threading.Thread(target=self._do_scan_and_sign, daemon=True).start()
|
||||
|
||||
def _do_scan_and_sign(self):
|
||||
scanned = zbar_scan_oneshot()
|
||||
if not scanned:
|
||||
self.after(0, self._scan_failed)
|
||||
return
|
||||
try:
|
||||
envelope = make_signed_envelope(scanned, KEY_PATH, ALGO)
|
||||
payload = json.dumps(envelope, separators=(",",":"))
|
||||
qrencode_to_file(payload, QR_TMP)
|
||||
self.after(0, self._show_qr, envelope)
|
||||
except Exception as e:
|
||||
self.after(0, lambda: self._error(str(e)))
|
||||
|
||||
def _scan_failed(self):
|
||||
self.status.config(text="No QR detected. Try again.")
|
||||
self.scan_btn.config(state="normal")
|
||||
|
||||
def _show_qr(self, envelope):
|
||||
# Display the PNG produced by qrencode
|
||||
try:
|
||||
img = tk.PhotoImage(file=QR_TMP)
|
||||
self.image_label.configure(image=img)
|
||||
self.image_label.image = img
|
||||
except Exception as e:
|
||||
self.status.config(text=f"QR render failed: {e}")
|
||||
self.scan_btn.config(state="normal")
|
||||
return
|
||||
self.status.config(text="Signed. Show this QR to your online box.")
|
||||
self.note.config(text=f"Algo: {envelope['algo']} Host: {envelope['device']}")
|
||||
self.new_btn.pack(pady=20)
|
||||
|
||||
if __name__ == "__main__":
|
||||
App().mainloop()
|
||||
PY
|
||||
sudo chmod +x /opt/styx-kiosk/kiosk.py
|
||||
sudo chown -R styx:styx /opt/styx-kiosk
|
||||
|
||||
# 3) Minimal X session: openbox + kiosk; no mouse pointer
|
||||
sudo -u styx tee /home/styx/.xinitrc >/dev/null <<'XRC'
|
||||
xset -dpms
|
||||
xset s off
|
||||
xset s noblank
|
||||
# If 'unclutter' is installed, uncomment the next line to hide cursor:
|
||||
# unclutter -idle 0 -root &
|
||||
openbox-session &
|
||||
/opt/styx-kiosk/kiosk.py
|
||||
XRC
|
||||
sudo chown styx:styx /home/styx/.xinitrc
|
||||
sudo chmod 0755 /home/styx/.xinitrc
|
||||
|
||||
# 4) Autologin the 'styx' user on tty1, auto-start X
|
||||
sudo mkdir -p /etc/systemd/system/getty@tty1.service.d
|
||||
sudo tee /etc/systemd/system/getty@tty1.service.d/override.conf >/dev/null <<'OVR'
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=-/sbin/agetty --autologin styx --noclear %I $TERM
|
||||
Type=idle
|
||||
OVR
|
||||
|
||||
sudo -u styx tee -a /home/styx/.bash_profile >/dev/null <<'BRC'
|
||||
# Start X on the first tty automatically, headless
|
||||
if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then
|
||||
exec startx -- -nocursor
|
||||
fi
|
||||
BRC
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable getty@tty1.service
|
||||
|
||||
echo "Done. Reboot to try the kiosk."
|
||||
195
scripts/styx_prep.sh
Executable file
195
scripts/styx_prep.sh
Executable file
@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# === CONFIG ===
|
||||
STYX_USER="styx"
|
||||
STYX_PASS="TempPass#123" # change at first login
|
||||
STYX_HOSTNAME="styx"
|
||||
SSH_PUBKEY="" # e.g., 'ssh-ed25519 AAAA... your@host' (optional)
|
||||
|
||||
# === helpers ===
|
||||
require_root() {
|
||||
if [[ $EUID -ne 0 ]]; then exec sudo -E "$0" "$@"; fi
|
||||
}
|
||||
|
||||
ensure_binfmt_arm64() {
|
||||
# If binfmt for arm64 isn't registered, register it via Docker (idempotent).
|
||||
if [[ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64 ]]; then
|
||||
command -v docker >/dev/null || { echo "Docker required to register binfmt (sudo pacman -S docker)"; exit 1; }
|
||||
sudo systemctl enable --now docker >/dev/null 2>&1 || true
|
||||
sudo docker run --rm --privileged tonistiigi/binfmt --install arm64
|
||||
fi
|
||||
}
|
||||
|
||||
find_parts() {
|
||||
BOOT=$(lsblk -o LABEL,PATH -nr | awk '$1=="system-boot"{print $2}' | head -n1)
|
||||
ROOT=$(lsblk -o LABEL,PATH -nr | awk '$1=="writable"{print $2}' | head -n1)
|
||||
if [[ -z "${BOOT:-}" || -z "${ROOT:-}" ]]; then
|
||||
echo "Could not find 'system-boot'/'writable' on any device."
|
||||
lsblk -o NAME,SIZE,FSTYPE,LABEL,PATH -nr
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
mount_parts() {
|
||||
mkdir -p /mnt/pi-boot /mnt/pi-root
|
||||
mount "$ROOT" /mnt/pi-root
|
||||
mount "$BOOT" /mnt/pi-boot
|
||||
|
||||
# Bind only what we need (avoid /run to prevent postinst fights)
|
||||
for d in dev dev/pts proc sys; do mount --bind "/$d" "/mnt/pi-root/$d"; done
|
||||
|
||||
# Ubuntu images use a resolv.conf symlink—replace with a real file
|
||||
if [[ -L /mnt/pi-root/etc/resolv.conf || ! -e /mnt/pi-root/etc/resolv.conf ]]; then
|
||||
rm -f /mnt/pi-root/etc/resolv.conf
|
||||
cat /etc/resolv.conf > /mnt/pi-root/etc/resolv.conf
|
||||
fi
|
||||
}
|
||||
|
||||
prep_chroot() {
|
||||
# Block service starts inside chroot (no systemd there)
|
||||
cat >/mnt/pi-root/usr/sbin/policy-rc.d <<'EOF'
|
||||
#!/bin/sh
|
||||
exit 101
|
||||
EOF
|
||||
chmod +x /mnt/pi-root/usr/sbin/policy-rc.d
|
||||
|
||||
# All the work happens inside the ARM64 rootfs
|
||||
CHCMD=$(cat <<'EOS'
|
||||
set -euo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC
|
||||
# Ensure sbin is in PATH so user/group tools work
|
||||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
apt-get update
|
||||
apt-get -y full-upgrade
|
||||
|
||||
# Remove snaps and keep them gone (Ubuntu for Pi ships with snaps)
|
||||
apt-get -y purge snapd || true
|
||||
rm -rf /snap /var/snap /var/lib/snapd /home/*/snap || true
|
||||
mkdir -p /etc/apt/preferences.d
|
||||
printf 'Package: snapd\nPin: release *\nPin-Priority: -10\n' > /etc/apt/preferences.d/nosnap.pref
|
||||
|
||||
# Ensure user/group tools exist
|
||||
apt-get install -y passwd adduser || true
|
||||
getent group i2c >/dev/null || /usr/sbin/groupadd i2c
|
||||
|
||||
# Base packages
|
||||
BASE_PKGS="openssh-server git i2c-tools python3-smbus python3-pil zbar-tools qrencode lm-sensors"
|
||||
apt-get install -y $BASE_PKGS
|
||||
|
||||
# ------- OLED (Luma) -------
|
||||
# Prefer distro package; fall back to pip if not present in this release
|
||||
if ! dpkg -s python3-luma.oled >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
if ! apt-get install -y python3-luma.oled; then
|
||||
apt-get install -y python3-pip
|
||||
pip3 install --no-input --break-system-packages luma.oled
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------- Camera apps -------
|
||||
# Ubuntu renamed libcamera-apps -> rpicam-apps for Raspberry Pi.
|
||||
# Try in order; tolerate absence (the box might be display-only).
|
||||
apt-get update
|
||||
if ! apt-get install -y rpicam-apps; then
|
||||
apt-get install -y libcamera-apps || apt-get install -y libcamera-tools || true
|
||||
fi
|
||||
|
||||
# Enable SSH on boot (no systemctl in chroot)
|
||||
mkdir -p /etc/systemd/system/multi-user.target.wants
|
||||
ln -sf /lib/systemd/system/ssh.service /etc/systemd/system/multi-user.target.wants/ssh.service
|
||||
|
||||
# Create user and set password
|
||||
if ! id -u STYX_USER >/dev/null 2>&1; then
|
||||
/usr/sbin/useradd -m -s /bin/bash -G sudo,video,i2c STYX_USER
|
||||
fi
|
||||
echo 'STYX_USER:STYX_PASS' | /usr/sbin/chpasswd
|
||||
|
||||
# Optional: preload SSH key
|
||||
if [ -n 'SSH_PUBKEY' ] && echo 'SSH_PUBKEY' | grep -q 'ssh-'; then
|
||||
install -d -m700 /home/STYX_USER/.ssh
|
||||
echo 'SSH_PUBKEY' >> /home/STYX_USER/.ssh/authorized_keys
|
||||
chmod 600 /home/STYX_USER/.ssh/authorized_keys
|
||||
chown -R STYX_USER:STYX_USER /home/STYX_USER/.ssh
|
||||
fi
|
||||
|
||||
# Freenove code
|
||||
git clone https://github.com/Freenove/Freenove_Computer_Case_Kit_for_Raspberry_Pi.git /opt/freenove || true
|
||||
|
||||
# Hostname
|
||||
echo 'STYX_HOSTNAME' > /etc/hostname
|
||||
if grep -q '^127\.0\.1\.1' /etc/hosts; then
|
||||
sed -i 's/^127\.0\.1\.1.*/127.0.1.1\tSTYX_HOSTNAME/' /etc/hosts
|
||||
else
|
||||
echo -e '127.0.1.1\tSTYX_HOSTNAME' >> /etc/hosts
|
||||
fi
|
||||
|
||||
apt-get clean
|
||||
EOS
|
||||
)
|
||||
# Inject config values safely
|
||||
CHCMD="${CHCMD//STYX_USER/${STYX_USER}}"
|
||||
CHCMD="${CHCMD//STYX_PASS/${STYX_PASS}}"
|
||||
CHCMD="${CHCMD//STYX_HOSTNAME/${STYX_HOSTNAME}}"
|
||||
CHCMD="${CHCMD//SSH_PUBKEY/${SSH_PUBKEY}}"
|
||||
|
||||
chroot /mnt/pi-root /bin/bash -lc "$CHCMD"
|
||||
}
|
||||
|
||||
install_service_host() {
|
||||
# Systemd unit for the Freenove example app
|
||||
mkdir -p /mnt/pi-root/etc/systemd/system/multi-user.target.wants
|
||||
cat >/mnt/pi-root/etc/systemd/system/freenove-case.service <<'SERVICE'
|
||||
[Unit]
|
||||
Description=Freenove Case OLED/Fans/LEDs
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /opt/freenove/Code/application.py
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SERVICE
|
||||
ln -sf /etc/systemd/system/freenove-case.service \
|
||||
/mnt/pi-root/etc/systemd/system/multi-user.target.wants/freenove-case.service || true
|
||||
}
|
||||
|
||||
boot_tweaks() {
|
||||
# Enable I2C and set DSI panel on the BOOT partition
|
||||
grep -q 'dtparam=i2c_arm=on' /mnt/pi-boot/config.txt || echo 'dtparam=i2c_arm=on' >> /mnt/pi-boot/config.txt
|
||||
# Append kernel cmdline only once
|
||||
if ! grep -q 'DSI-1:800x480@60D' /mnt/pi-boot/cmdline.txt 2>/dev/null; then
|
||||
sed -i '1 s#$# video=DSI-1:800x480@60D video=HDMI-A-1:off video=HDMI-A-2:off#' /mnt/pi-boot/cmdline.txt || true
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
rm -f /mnt/pi-root/usr/sbin/policy-rc.d || true
|
||||
for d in dev/pts dev proc sys; do umount -lf "/mnt/pi-root/$d" 2>/dev/null || true; done
|
||||
umount -lf /mnt/pi-boot 2>/dev/null || true
|
||||
umount -lf /mnt/pi-root 2>/dev/null || true
|
||||
sync || true
|
||||
}
|
||||
|
||||
main() {
|
||||
require_root
|
||||
ensure_binfmt_arm64
|
||||
find_parts
|
||||
trap 'echo "ERROR at line $LINENO" >&2; cleanup' ERR INT
|
||||
mount_parts
|
||||
prep_chroot
|
||||
install_service_host
|
||||
boot_tweaks
|
||||
cleanup
|
||||
echo "✅ Done. Move the NVMe to the Pi and boot."
|
||||
echo " Login: user '${STYX_USER}' pass '${STYX_PASS}' (change with 'passwd')."
|
||||
echo " Quick checks on the Pi:"
|
||||
echo " sudo i2cdetect -y 1"
|
||||
echo " rpicam-still -n -o test.jpg # (if rpicam-apps installed)"
|
||||
echo " libcamera-still -n -o test.jpg # (if legacy libcamera-apps installed)"
|
||||
echo " systemctl status freenove-case"
|
||||
}
|
||||
main "$@"
|
||||
@ -23,6 +23,11 @@ spec:
|
||||
spec:
|
||||
nodeSelector:
|
||||
jellyfin: "true"
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
fsGroup: 65532
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
runAsGroup: 65532
|
||||
runtimeClassName: nvidia
|
||||
containers:
|
||||
- name: jellyfin
|
||||
@ -36,6 +41,12 @@ spec:
|
||||
value: "compute,video,utility"
|
||||
- name: JELLYFIN_PublishedServerUrl
|
||||
value: "https://stream.bstein.dev"
|
||||
- name: PUID
|
||||
value: "1000"
|
||||
- name: PGID
|
||||
value: "65532"
|
||||
- name: UMASK
|
||||
value: "002"
|
||||
resources:
|
||||
limits:
|
||||
nvidia.com/gpu: 1
|
||||
@ -64,4 +75,4 @@ spec:
|
||||
claimName: jellyfin-cache-astreae
|
||||
- name: media
|
||||
persistentVolumeClaim:
|
||||
claimName: jellyfin-media-asteria
|
||||
claimName: jellyfin-media-asteria-new
|
||||
|
||||
@ -36,5 +36,19 @@ spec:
|
||||
accessModes: ["ReadWriteMany"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Ti
|
||||
storage: 4Ti
|
||||
storageClassName: asteria
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jellyfin-media-asteria-new
|
||||
namespace: jellyfin
|
||||
spec:
|
||||
accessModes: ["ReadWriteMany"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 4Ti
|
||||
storageClassName: asteria
|
||||
|
||||
171
services/jitsi/deployment.yaml
Normal file
171
services/jitsi/deployment.yaml
Normal file
@ -0,0 +1,171 @@
|
||||
# services/jitsi/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jitsi-prosody
|
||||
namespace: jitsi
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels: { app: jitsi-prosody }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: jitsi-prosody }
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-22
|
||||
kubernetes.io/arch: amd64
|
||||
containers:
|
||||
- name: prosody
|
||||
image: jitsi/prosody:stable
|
||||
ports:
|
||||
- { name: c2s, containerPort: 5222, protocol: TCP }
|
||||
- { name: http, containerPort: 5280, protocol: TCP }
|
||||
- { name: comp, containerPort: 5347, protocol: TCP }
|
||||
env:
|
||||
- { name: XMPP_DOMAIN, value: "meet.jitsi" }
|
||||
- { name: XMPP_AUTH_DOMAIN, value: "auth.meet.jitsi" }
|
||||
- { name: XMPP_MUC_DOMAIN, value: "muc.meet.jitsi" }
|
||||
- { name: XMPP_INTERNAL_MUC_DOMAIN, value: "internal-muc.meet.jitsi" }
|
||||
- { name: ENABLE_AUTH, value: "0" } # open instance, no auth (fastest path)
|
||||
- { name: ENABLE_GUESTS, value: "1" }
|
||||
- { name: JICOFO_AUTH_USER, value: "focus" }
|
||||
- { name: JVB_AUTH_USER, value: "jvb" }
|
||||
- name: JICOFO_AUTH_PASSWORD
|
||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_AUTH_PASSWORD } }
|
||||
- name: JICOFO_COMPONENT_SECRET
|
||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_COMPONENT_SECRET } }
|
||||
- name: JVB_AUTH_PASSWORD
|
||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JVB_AUTH_PASSWORD } }
|
||||
volumeMounts:
|
||||
- { name: cfg, mountPath: /config }
|
||||
volumes:
|
||||
- name: cfg
|
||||
persistentVolumeClaim: { claimName: jitsi-prosody-config }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jitsi-jicofo
|
||||
namespace: jitsi
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels: { app: jitsi-jicofo }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: jitsi-jicofo }
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-22
|
||||
kubernetes.io/arch: amd64
|
||||
containers:
|
||||
- name: jicofo
|
||||
image: jitsi/jicofo:stable
|
||||
env:
|
||||
- { name: XMPP_DOMAIN, value: "meet.jitsi" }
|
||||
- { name: XMPP_AUTH_DOMAIN, value: "auth.meet.jitsi" }
|
||||
- { name: XMPP_MUC_DOMAIN, value: "muc.meet.jitsi" }
|
||||
- { name: XMPP_INTERNAL_MUC_DOMAIN, value: "internal-muc.meet.jitsi" }
|
||||
- { name: XMPP_SERVER, value: "jitsi-prosody.jitsi.svc.cluster.local" }
|
||||
- { name: JICOFO_AUTH_USER, value: "focus" }
|
||||
- name: JICOFO_AUTH_PASSWORD
|
||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_AUTH_PASSWORD } }
|
||||
- name: JICOFO_COMPONENT_SECRET
|
||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JICOFO_COMPONENT_SECRET } }
|
||||
- { name: JVB_BREWERY_MUC, value: "jvbbrewery" }
|
||||
volumeMounts:
|
||||
- { name: cfg, mountPath: /config }
|
||||
volumes:
|
||||
- name: cfg
|
||||
persistentVolumeClaim: { claimName: jitsi-jicofo-config }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jitsi-jvb
|
||||
namespace: jitsi
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels: { app: jitsi-jvb }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: jitsi-jvb }
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-22
|
||||
kubernetes.io/arch: amd64
|
||||
containers:
|
||||
- name: jvb
|
||||
image: jitsi/jvb:stable
|
||||
ports:
|
||||
- { name: colibri-ws, containerPort: 9090, protocol: TCP } # WebSocket control channel
|
||||
- { name: rtp-udp, containerPort: 10000, hostPort: 10000, protocol: UDP } # media
|
||||
- { name: rtp-tcp, containerPort: 4443, hostPort: 4443, protocol: TCP }
|
||||
env:
|
||||
- { name: XMPP_DOMAIN, value: "meet.jitsi" }
|
||||
- { name: XMPP_AUTH_DOMAIN, value: "auth.meet.jitsi" }
|
||||
- { name: XMPP_MUC_DOMAIN, value: "muc.meet.jitsi" }
|
||||
- { name: XMPP_INTERNAL_MUC_DOMAIN, value: "internal-muc.meet.jitsi" }
|
||||
- { name: XMPP_SERVER, value: "jitsi-prosody.jitsi.svc.cluster.local" }
|
||||
- { name: JVB_AUTH_USER, value: "jvb" }
|
||||
- name: JVB_AUTH_PASSWORD
|
||||
valueFrom: { secretKeyRef: { name: jitsi-internal-secrets, key: JVB_AUTH_PASSWORD } }
|
||||
- { name: JVB_BREWERY_MUC, value: "jvbbrewery" }
|
||||
- { name: JVB_PORT, value: "10000" } # matches hostPort above
|
||||
- { name: ENABLE_COLIBRI_WEBSOCKET, value: "1" } # enables /colibri-ws
|
||||
# - { name: JVB_STUN_SERVERS, value: "stun.l.google.com:19302,stun1.l.google.com:19302,meet-jit-si-turnrelay.jitsi.net:443" }
|
||||
- { name: JVB_ENABLE_APIS, value: "rest,colibri" }
|
||||
- { name: JVB_WS_DOMAIN, value: "meet.bstein.dev:443" }
|
||||
- { name: JVB_WS_TLS, value: "true" }
|
||||
- { name: JVB_ADVERTISE_IPS, value: "38.28.125.112" }
|
||||
- { name: JVB_TCP_HARVESTER_DISABLED, value: "false" }
|
||||
- { name: JVB_TCP_PORT, value: "4443" }
|
||||
volumeMounts:
|
||||
- { name: cfg, mountPath: /config }
|
||||
volumes:
|
||||
- name: cfg
|
||||
persistentVolumeClaim: { claimName: jitsi-jvb-config }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jitsi-web
|
||||
namespace: jitsi
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels: { app: jitsi-web }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: jitsi-web }
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-22
|
||||
kubernetes.io/arch: amd64
|
||||
containers:
|
||||
- name: web
|
||||
image: jitsi/web:stable
|
||||
ports:
|
||||
- { name: http, containerPort: 80, protocol: TCP }
|
||||
env:
|
||||
- { name: PUBLIC_URL, value: "https://meet.bstein.dev" }
|
||||
- { name: XMPP_DOMAIN, value: "meet.jitsi" }
|
||||
- { name: XMPP_AUTH_DOMAIN, value: "auth.meet.jitsi" }
|
||||
- { name: XMPP_MUC_DOMAIN, value: "muc.meet.jitsi" }
|
||||
- { name: XMPP_INTERNAL_MUC_DOMAIN, value: "internal-muc.meet.jitsi" }
|
||||
- { name: XMPP_BOSH_URL_BASE, value: "https://meet.bstein.dev" }
|
||||
- { name: ENABLE_XMPP_WEBSOCKET, value: "1" }
|
||||
- { name: ENABLE_COLIBRI_WEBSOCKET, value: "1" }
|
||||
volumeMounts:
|
||||
- { name: cfg, mountPath: /config }
|
||||
volumes:
|
||||
- name: cfg
|
||||
persistentVolumeClaim: { claimName: jitsi-web-config }
|
||||
41
services/jitsi/ingress.yaml
Normal file
41
services/jitsi/ingress.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
# services/jitsi/ingress.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: jitsi
|
||||
namespace: jitsi
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts: [ "meet.bstein.dev" ]
|
||||
secretName: jitsi-meet-tls
|
||||
rules:
|
||||
- host: meet.bstein.dev
|
||||
http:
|
||||
paths:
|
||||
- path: /colibri-ws
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: jitsi-jvb
|
||||
port: { number: 9090 }
|
||||
- path: /xmpp-websocket
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: jitsi-prosody
|
||||
port: { number: 5280 }
|
||||
- path: /http-bind
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: jitsi-prosody
|
||||
port: { number: 5280 }
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: jitsi-web
|
||||
port: { number: 80 }
|
||||
10
services/jitsi/kustomization.yaml
Normal file
10
services/jitsi/kustomization.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
# services/jitsi/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- pvc.yaml
|
||||
- ingress.yaml
|
||||
- secret.yaml
|
||||
5
services/jitsi/namespace.yaml
Normal file
5
services/jitsi/namespace.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
# services/jitsi/namespace.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: jitsi
|
||||
42
services/jitsi/pvc.yaml
Normal file
42
services/jitsi/pvc.yaml
Normal file
@ -0,0 +1,42 @@
|
||||
# services/jitsi/pvc.yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jitsi-web-config
|
||||
namespace: jitsi
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources: { requests: { storage: 10Gi } }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jitsi-prosody-config
|
||||
namespace: jitsi
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources: { requests: { storage: 10Gi } }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jitsi-jicofo-config
|
||||
namespace: jitsi
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources: { requests: { storage: 10Gi } }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jitsi-jvb-config
|
||||
namespace: jitsi
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources: { requests: { storage: 10Gi } }
|
||||
11
services/jitsi/secret.yaml
Normal file
11
services/jitsi/secret.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
# services/jitsi/secret.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: jitsi-internal-secrets
|
||||
namespace: jitsi
|
||||
type: Opaque
|
||||
data:
|
||||
JICOFO_COMPONENT_SECRET: bEg5Y09hZFJBem5PUFliQlp4RHkwRTRP
|
||||
JICOFO_AUTH_PASSWORD: VVkyUmczaVRDWUZ0MzdQdmN3UDN1SFc5
|
||||
JVB_AUTH_PASSWORD: d0M5aWJ4dWlPTnhFak9lRHJqSHdYa0g5
|
||||
36
services/jitsi/service.yaml
Normal file
36
services/jitsi/service.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
# services/jitsi/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jitsi-prosody
|
||||
namespace: jitsi
|
||||
spec:
|
||||
selector: { app: jitsi-prosody }
|
||||
ports:
|
||||
- { name: c2s, port: 5222, targetPort: 5222, protocol: TCP }
|
||||
- { name: http, port: 5280, targetPort: 5280, protocol: TCP }
|
||||
- { name: comp, port: 5347, targetPort: 5347, protocol: TCP }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jitsi-jvb
|
||||
namespace: jitsi
|
||||
spec:
|
||||
selector: { app: jitsi-jvb }
|
||||
ports:
|
||||
- { name: colibri-ws, port: 9090, targetPort: 9090, protocol: TCP }
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jitsi-web
|
||||
namespace: jitsi
|
||||
spec:
|
||||
selector: { app: jitsi-web }
|
||||
ports:
|
||||
- { name: http, port: 80, targetPort: 80, protocol: TCP }
|
||||
212
services/monitoring/helmrelease.yaml
Normal file
212
services/monitoring/helmrelease.yaml
Normal file
@ -0,0 +1,212 @@
|
||||
# services/monitoring/kube-state-metrics-helmrelease.yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: kube-state-metrics
|
||||
namespace: monitoring
|
||||
spec:
|
||||
interval: 15m
|
||||
chart:
|
||||
spec:
|
||||
chart: kube-state-metrics
|
||||
version: "~6.0.0"
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: prometheus
|
||||
namespace: flux-system
|
||||
values:
|
||||
prometheusScrape: false
|
||||
|
||||
---
|
||||
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: node-exporter
|
||||
namespace: monitoring
|
||||
spec:
|
||||
interval: 15m
|
||||
install:
|
||||
disableWait: true
|
||||
chart:
|
||||
spec:
|
||||
chart: prometheus-node-exporter
|
||||
version: "~4.0.0"
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: prometheus
|
||||
namespace: flux-system
|
||||
upgrade:
|
||||
disableWait: true
|
||||
values:
|
||||
rbac:
|
||||
pspEnabled: false
|
||||
service:
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "9100"
|
||||
|
||||
---
|
||||
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: victoria-metrics-single
|
||||
namespace: monitoring
|
||||
spec:
|
||||
interval: 15m
|
||||
chart:
|
||||
spec:
|
||||
chart: victoria-metrics-single
|
||||
version: "~0.15.0" # or omit to track appVersion
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: victoria-metrics
|
||||
namespace: flux-system
|
||||
values:
|
||||
server:
|
||||
# keep ~3 months; change as you like (supports "d", "y")
|
||||
extraArgs:
|
||||
retentionPeriod: "90d" # VM flag -retentionPeriod=90d. :contentReference[oaicite:11]{index=11}
|
||||
|
||||
persistentVolume:
|
||||
enabled: true
|
||||
size: 100Gi # adjust; uses default StorageClass (Longhorn)
|
||||
# storageClassName: "" # set if you want a specific class
|
||||
|
||||
# Enable built-in Kubernetes scraping
|
||||
scrape:
|
||||
enabled: true # chart enables promscrape. :contentReference[oaicite:12]{index=12}
|
||||
config:
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
# VM self-metrics
|
||||
- job_name: victoriametrics
|
||||
static_configs:
|
||||
- targets: ["localhost:8428"]
|
||||
|
||||
# --- K8s control-plane & nodes (from VM docs guide) ---
|
||||
- job_name: "kubernetes-apiservers"
|
||||
kubernetes_sd_configs: [{ role: endpoints }]
|
||||
scheme: https
|
||||
tls_config:
|
||||
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
insecure_skip_verify: true
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
relabel_configs:
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_namespace,__meta_kubernetes_service_name,__meta_kubernetes_endpoint_port_name]
|
||||
regex: default;kubernetes;https
|
||||
|
||||
- job_name: "kubernetes-nodes"
|
||||
scheme: https
|
||||
tls_config:
|
||||
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
insecure_skip_verify: true
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
kubernetes_sd_configs: [{ role: node }]
|
||||
relabel_configs:
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_node_label_(.+)
|
||||
- target_label: __address__
|
||||
replacement: kubernetes.default.svc:443
|
||||
- source_labels: [__meta_kubernetes_node_name]
|
||||
regex: (.+)
|
||||
target_label: __metrics_path__
|
||||
replacement: /api/v1/nodes/$1/proxy/metrics
|
||||
|
||||
- job_name: "kubernetes-nodes-cadvisor"
|
||||
scheme: https
|
||||
tls_config:
|
||||
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
insecure_skip_verify: true
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
kubernetes_sd_configs: [{ role: node }]
|
||||
relabel_configs:
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_node_label_(.+)
|
||||
- target_label: __address__
|
||||
replacement: kubernetes.default.svc:443
|
||||
- source_labels: [__meta_kubernetes_node_name]
|
||||
regex: (.+)
|
||||
target_label: __metrics_path__
|
||||
replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor
|
||||
|
||||
# --- Annotated Services (generic autodiscovery) ---
|
||||
- job_name: "kubernetes-service-endpoints"
|
||||
kubernetes_sd_configs: [{ role: endpoints }]
|
||||
relabel_configs:
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
|
||||
regex: "true"
|
||||
- action: replace
|
||||
source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
|
||||
regex: (https?)
|
||||
target_label: __scheme__
|
||||
- action: replace
|
||||
source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
|
||||
target_label: __metrics_path__
|
||||
- action: replace
|
||||
regex: (.+)(?::\d+);(\d+)
|
||||
replacement: $1:$2
|
||||
source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
|
||||
target_label: __address__
|
||||
|
||||
# --- Annotated Pods (generic autodiscovery) ---
|
||||
- job_name: "kubernetes-pods"
|
||||
kubernetes_sd_configs: [{ role: pod }]
|
||||
relabel_configs:
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
|
||||
regex: "true"
|
||||
- action: drop
|
||||
source_labels: [__meta_kubernetes_pod_container_port_name]
|
||||
regex: ".*health.*"
|
||||
- action: replace
|
||||
source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
|
||||
target_label: __metrics_path__
|
||||
- action: replace
|
||||
regex: (.+):(?:\d+);(\d+)
|
||||
replacement: $1:$2
|
||||
source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
|
||||
target_label: __address__
|
||||
|
||||
# --- kube-state-metrics (via its Service) ---
|
||||
- job_name: "kube-state-metrics"
|
||||
kubernetes_sd_configs: [{ role: endpoints }]
|
||||
relabel_configs:
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name]
|
||||
regex: kube-state-metrics
|
||||
|
||||
# --- Longhorn ---
|
||||
- job_name: "longhorn-backend"
|
||||
static_configs:
|
||||
- targets: ["longhorn-backend.longhorn-system.svc:9500"]
|
||||
metrics_path: /metrics
|
||||
|
||||
# --- cert-manager (pods expose on 9402) ---
|
||||
- job_name: "cert-manager"
|
||||
kubernetes_sd_configs: [{ role: pod }]
|
||||
relabel_configs:
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_pod_label_app_kubernetes_io_name]
|
||||
regex: cert-manager;cert-manager
|
||||
- action: drop
|
||||
source_labels: [__meta_kubernetes_pod_container_port_name]
|
||||
regex: ".*health.*"
|
||||
- action: replace
|
||||
source_labels: [__address__]
|
||||
regex: "(.+):\\d+"
|
||||
replacement: "$1:9402"
|
||||
target_label: __address__
|
||||
|
||||
# --- Flux controllers (default :8080/metrics) ---
|
||||
- job_name: "flux"
|
||||
kubernetes_sd_configs: [{ role: pod }]
|
||||
relabel_configs:
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_pod_label_app_kubernetes_io_part_of]
|
||||
regex: flux-system;flux
|
||||
8
services/monitoring/kustomization.yaml
Normal file
8
services/monitoring/kustomization.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
# services/monitoring/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: monitoring
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- rbac.yaml
|
||||
- helmrelease.yaml
|
||||
4
services/monitoring/namespace.yaml
Normal file
4
services/monitoring/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: monitoring
|
||||
33
services/monitoring/rbac.yaml
Normal file
33
services/monitoring/rbac.yaml
Normal file
@ -0,0 +1,33 @@
|
||||
# services/monitoring/rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: vmsingle-scrape
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- nodes
|
||||
- nodes/proxy
|
||||
- nodes/metrics
|
||||
- services
|
||||
- endpoints
|
||||
- pods
|
||||
verbs: ["get","list","watch"]
|
||||
- apiGroups: ["networking.k8s.io","extensions"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","list","watch"]
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: vmsingle-scrape
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: vmsingle-scrape
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: victoria-metrics-single
|
||||
namespace: monitoring
|
||||
48
services/pegasus/configmap.yaml
Normal file
48
services/pegasus/configmap.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
# services/pegasus/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: pegasus-config
|
||||
namespace: jellyfin
|
||||
data:
|
||||
PEGASUS_MEDIA_ROOT: "/media"
|
||||
PEGASUS_BIND: ":8080"
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: pegasus-user-map
|
||||
namespace: jellyfin
|
||||
data:
|
||||
user-map.yaml: |
|
||||
map:
|
||||
mary_grace_allison:
|
||||
- "allison"
|
||||
- "gavilan"
|
||||
maddie_rejcek:
|
||||
- "rangeejcek"
|
||||
- "gavilan"
|
||||
carol_mcguinness:
|
||||
- "mcguinness_family"
|
||||
- "gavilan"
|
||||
ed_stein:
|
||||
- "stein_family"
|
||||
- "gavilan"
|
||||
brad_stein:
|
||||
- "stein_brad"
|
||||
- "gavilan"
|
||||
lisa_stein:
|
||||
- "scott"
|
||||
channa_cox:
|
||||
- "cox"
|
||||
- "gavilan"
|
||||
chelie_sheehan:
|
||||
- "sheehan"
|
||||
- "gavilan"
|
||||
olya_bulgakova:
|
||||
- "bulgakova"
|
||||
sean_mcguinness:
|
||||
- "mcguinness_sean"
|
||||
- "gavilan"
|
||||
114
services/pegasus/deployment.yaml
Normal file
114
services/pegasus/deployment.yaml
Normal file
@ -0,0 +1,114 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: jellyfin
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 0
|
||||
maxUnavailable: 1
|
||||
selector: { matchLabels: { app: pegasus } }
|
||||
template:
|
||||
metadata: { labels: { app: pegasus } }
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: amd64
|
||||
imagePullSecrets:
|
||||
- name: zot-regcred
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
fsGroup: 65532
|
||||
fsGroupChangePolicy: "OnRootMismatch"
|
||||
initContainers:
|
||||
- name: fix-perms
|
||||
image: alpine:3.20
|
||||
command:
|
||||
- sh
|
||||
- -lc
|
||||
- |
|
||||
set -eux
|
||||
|
||||
# Scratch area for tus uploads (always writable)
|
||||
mkdir -p /media/.pegasus-tus
|
||||
chmod 0777 /media/.pegasus-tus
|
||||
|
||||
# Make each top-level library dir group-writable and setgid,
|
||||
# and try to set its group to 65532 (so the app can write).
|
||||
for d in /media/*; do
|
||||
[ -d "$d" ] || continue
|
||||
base="$(basename "$d")"
|
||||
[ "$base" = ".pegasus-tus" ] && continue
|
||||
# chgrp can fail on some backends; don't block the pod if it does.
|
||||
chgrp 65532 "$d" || true
|
||||
chmod 2775 "$d" || true
|
||||
done
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
runAsGroup: 0
|
||||
runAsNonRoot: false
|
||||
allowPrivilegeEscalation: false
|
||||
volumeMounts:
|
||||
- { name: media, mountPath: /media }
|
||||
|
||||
containers:
|
||||
- name: pegasus
|
||||
image: registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"}
|
||||
imagePullPolicy: Always
|
||||
command: ["/pegasus"]
|
||||
env:
|
||||
- name: PEGASUS_MEDIA_ROOT
|
||||
valueFrom: { configMapKeyRef: { name: pegasus-config, key: PEGASUS_MEDIA_ROOT } }
|
||||
- name: PEGASUS_BIND
|
||||
valueFrom: { configMapKeyRef: { name: pegasus-config, key: PEGASUS_BIND } }
|
||||
- name: PEGASUS_USER_MAP_FILE
|
||||
value: "/config/user-map.yaml"
|
||||
- name: PEGASUS_SESSION_KEY
|
||||
valueFrom: { secretKeyRef: { name: pegasus-secrets, key: PEGASUS_SESSION_KEY } }
|
||||
- name: JELLYFIN_URL
|
||||
valueFrom: { secretKeyRef: { name: pegasus-secrets, key: JELLYFIN_URL } }
|
||||
- name: JELLYFIN_API_KEY
|
||||
valueFrom: { secretKeyRef: { name: pegasus-secrets, key: JELLYFIN_API_KEY } }
|
||||
- name: PEGASUS_DEBUG
|
||||
value: "1"
|
||||
- name: PEGASUS_DRY_RUN
|
||||
value: "0"
|
||||
ports: [{ name: http, containerPort: 8080 }]
|
||||
readinessProbe:
|
||||
httpGet: { path: /healthz, port: http }
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 1
|
||||
livenessProbe:
|
||||
httpGet: { path: /healthz, port: http }
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 2
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities: { drop: ["ALL"] }
|
||||
resources:
|
||||
requests: { cpu: 100m, memory: 256Mi }
|
||||
limits: { cpu: 1000m, memory: 1Gi }
|
||||
volumeMounts:
|
||||
- name: media
|
||||
mountPath: /media
|
||||
- name: config
|
||||
mountPath: /config
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: media
|
||||
persistentVolumeClaim:
|
||||
claimName: jellyfin-media-asteria-new
|
||||
- name: config
|
||||
configMap: { name: pegasus-user-map }
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
25
services/pegasus/image.yaml
Normal file
25
services/pegasus/image.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
# services/pegasus/image.yaml
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImageRepository
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: jellyfin
|
||||
spec:
|
||||
image: registry.bstein.dev/pegasus
|
||||
interval: 1m0s
|
||||
secretRef:
|
||||
name: zot-regcred
|
||||
|
||||
---
|
||||
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImagePolicy
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: jellyfin
|
||||
spec:
|
||||
imageRepositoryRef:
|
||||
name: pegasus
|
||||
policy:
|
||||
semver:
|
||||
range: "1.x"
|
||||
25
services/pegasus/ingress.yaml
Normal file
25
services/pegasus/ingress.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
# services/pegasus/ingress.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: jellyfin
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
spec:
|
||||
tls:
|
||||
- hosts: [ "pegasus.bstein.dev" ]
|
||||
secretName: pegasus-bstein-dev-tls
|
||||
rules:
|
||||
- host: pegasus.bstein.dev
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: /
|
||||
backend:
|
||||
service:
|
||||
name: pegasus
|
||||
port: { number: 80 }
|
||||
21
services/pegasus/kustomization.yaml
Normal file
21
services/pegasus/kustomization.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
# services/pegasus/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- configmap.yaml
|
||||
- service.yaml
|
||||
- deployment.yaml
|
||||
- ingress.yaml
|
||||
patches:
|
||||
- target: { kind: Deployment, name: pegasus, namespace: jellyfin }
|
||||
patch: |
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/-
|
||||
value:
|
||||
name: shell
|
||||
image: alpine:3.20
|
||||
command: ["sh","-c","sleep infinity"]
|
||||
securityContext: { runAsUser: 10001, runAsGroup: 10001, allowPrivilegeEscalation: false }
|
||||
volumeMounts:
|
||||
- { name: media, mountPath: /media }
|
||||
- { name: config, mountPath: /config, readOnly: true }
|
||||
12
services/pegasus/service.yaml
Normal file
12
services/pegasus/service.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
# services/pegasus/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: jellyfin
|
||||
spec:
|
||||
selector: { app: pegasus }
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
16
services/sui-metrics/base/configmap.yaml
Normal file
16
services/sui-metrics/base/configmap.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
# services/sui-metrics/base/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: sui-metrics-config
|
||||
namespace: sui-metrics
|
||||
data:
|
||||
vmagent.yaml: |
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 10s
|
||||
scrape_configs:
|
||||
- job_name: oceanus-node-exporter
|
||||
static_configs:
|
||||
- targets:
|
||||
- "192.168.22.23:9100"
|
||||
63
services/sui-metrics/base/deployment.yaml
Normal file
63
services/sui-metrics/base/deployment.yaml
Normal file
@ -0,0 +1,63 @@
|
||||
# services/sui-metrics/base/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: sui-metrics
|
||||
namespace: sui-metrics
|
||||
labels:
|
||||
app: sui-metrics
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: sui-metrics
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: sui-metrics
|
||||
spec:
|
||||
serviceAccountName: sui-metrics
|
||||
containers:
|
||||
- name: vmagent
|
||||
image: victoriametrics/vmagent:v1.103.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- -promscrape.config=/etc/vmagent/config/vmagent.yaml
|
||||
- -remoteWrite.url=$(REMOTE_WRITE_URL)
|
||||
- -remoteWrite.tmpDataPath=/var/lib/vmagent
|
||||
- -loggerFormat=json
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8429
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: TZ
|
||||
value: Etc/UTC
|
||||
- name: REMOTE_WRITE_URL
|
||||
value: http://victoria-metrics-single.monitoring.svc.cluster.local:8428/api/v1/write
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/vmagent/config
|
||||
readOnly: true
|
||||
- name: data
|
||||
mountPath: /var/lib/vmagent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65534
|
||||
runAsGroup: 65534
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: sui-metrics-config
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
10
services/sui-metrics/base/kustomization.yaml
Normal file
10
services/sui-metrics/base/kustomization.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
# services/sui-metrics/base/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: sui-metrics
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- serviceaccount.yaml
|
||||
- configmap.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
5
services/sui-metrics/base/namespace.yaml
Normal file
5
services/sui-metrics/base/namespace.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
# services/sui-metrics/base/namespace.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: sui-metrics
|
||||
16
services/sui-metrics/base/service.yaml
Normal file
16
services/sui-metrics/base/service.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
# services/sui-metrics/base/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sui-metrics
|
||||
namespace: sui-metrics
|
||||
labels:
|
||||
app: sui-metrics
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: sui-metrics
|
||||
ports:
|
||||
- name: http
|
||||
port: 8429
|
||||
targetPort: 8429
|
||||
6
services/sui-metrics/base/serviceaccount.yaml
Normal file
6
services/sui-metrics/base/serviceaccount.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# services/sui-metrics/base/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: sui-metrics
|
||||
namespace: sui-metrics
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user