Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebb1b9b02d |
@@ -1,134 +0,0 @@
|
|||||||
apiVersion: serving.kserve.io/v1beta1
|
|
||||||
kind: InferenceService
|
|
||||||
metadata:
|
|
||||||
name: coder
|
|
||||||
namespace: llm-serving
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: llm-coder
|
|
||||||
app.kubernetes.io/part-of: llm-serving
|
|
||||||
annotations:
|
|
||||||
serving.kserve.io/deploymentMode: RawDeployment
|
|
||||||
spec:
|
|
||||||
predictor:
|
|
||||||
minReplicas: 1
|
|
||||||
maxReplicas: 1
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: worker-1
|
|
||||||
# Two GPUs are free, so this rolls out beside `reasoning` instead of over it.
|
|
||||||
# Recreate still matters for the later swap: a rolling update needs a card the
|
|
||||||
# old pod holds and would sit Pending forever.
|
|
||||||
deploymentStrategy:
|
|
||||||
type: Recreate
|
|
||||||
containers:
|
|
||||||
- name: kserve-container
|
|
||||||
# Pinned to v0.11.0 on purpose. This is the last image measured to work on this
|
|
||||||
# node: torch 2.8.0+cu128, arch list sm_70..sm_120, driver 580.159.04.
|
|
||||||
# v0.15.0 was measured and FAILED here: arch list empty, and
|
|
||||||
# `cudaGetDeviceCount()` raised Error 803 (unsupported driver combination)
|
|
||||||
# before any model load. Do not raise this tag without re-running that probe.
|
|
||||||
image: vllm/vllm-openai:v0.11.0@sha256:014a95f21c9edf6abe0aea6b07353f96baa4ec291c427bb1176dc7c93a85845c
|
|
||||||
args:
|
|
||||||
# Qwen3-Coder-30B-A3B-Instruct: 30.5B total, 3.3B active. Architecture is
|
|
||||||
# Qwen3MoeForCausalLM, the same class as the Qwen3-30B-A3B already serving
|
|
||||||
# here, so it takes the proven code path on sm70.
|
|
||||||
#
|
|
||||||
# Qwen ships this model only as BF16 (61.1 GB) and FP8 (no FP8 on Volta).
|
|
||||||
# 4-bit GPTQ is the only form that fits one 32 GB card. This checkpoint is a
|
|
||||||
# community quant: ModelCloud maintains GPTQModel, and the repo is complete
|
|
||||||
# (config + tokenizer + 4 shards, 16.9 GB). Verified by loading, not by trust.
|
|
||||||
#
|
|
||||||
# Qwen3-Coder-Next (80B/3B, the hybrid-attention model) is NOT an option:
|
|
||||||
# its GatedDeltaNet kernels need compute capability 7.5+ and the `fla` module,
|
|
||||||
# and sm70 has neither. Measured, not assumed.
|
|
||||||
- --model=ModelCloud/Qwen3-Coder-30B-A3B-Instruct-GPTQMODEL-W4A16-A
|
|
||||||
# `reasoning` stays a served name so an existing client that sends
|
|
||||||
# model=reasoning keeps working after the DNS rename. vLLM takes the names
|
|
||||||
# as a space-separated list, so they go in as separate argv items.
|
|
||||||
- --served-model-name
|
|
||||||
- coder
|
|
||||||
- reasoning
|
|
||||||
- --quantization=gptq
|
|
||||||
- --dtype=float16
|
|
||||||
- --kv-cache-dtype=auto
|
|
||||||
# Native window is 262144 and rope_scaling is null, so 256K needs no YaRN.
|
|
||||||
# One card cannot hold it: measured KV pool on a single V100 was 132,672
|
|
||||||
# tokens. TP=2 halves the weights per card (7.8 GiB each), which frees room
|
|
||||||
# for the larger pool. Measured after boot: see the KV line in the log.
|
|
||||||
- --max-model-len=262144
|
|
||||||
- --tensor-parallel-size=2
|
|
||||||
- --pipeline-parallel-size=1
|
|
||||||
- --gpu-memory-utilization=0.90
|
|
||||||
# A single 262,144-token request consumes the whole pool, so keep few slots.
|
|
||||||
- --max-num-seqs=4
|
|
||||||
- --max-num-batched-tokens=8192
|
|
||||||
- --long-prefill-token-threshold=2048
|
|
||||||
- --enable-chunked-prefill
|
|
||||||
- --enable-prefix-caching
|
|
||||||
# Measured: with the default (custom all-reduce ON) the engine finished CUDA graph
|
|
||||||
# capture in 6s at 17:01:56 and then went silent for 14+ minutes, emitting only
|
|
||||||
# "No available shared memory broadcast block found in 60 seconds". The front-end
|
|
||||||
# never received the ready message. vLLM's custom all-reduce registers its own
|
|
||||||
# CUDA graph addresses and deadlocks on this Volta/PCIe pair. NCCL is the fallback
|
|
||||||
# and is correct here, because TP=2 across two PCIe V100s gains nothing from the
|
|
||||||
# custom kernel anyway.
|
|
||||||
- --disable-custom-all-reduce
|
|
||||||
- --reasoning-parser=qwen3
|
|
||||||
- --enable-auto-tool-choice
|
|
||||||
- --tool-call-parser=hermes
|
|
||||||
- --host=0.0.0.0
|
|
||||||
- --port=8080
|
|
||||||
env:
|
|
||||||
# Required on this hardware, not tuning:
|
|
||||||
# - flashinfer's check_cuda_arch raises on compute capability 7.0,
|
|
||||||
# so the sampler must stay off
|
|
||||||
# - FlashInfer and FlashAttention-2 need sm75+, so Triton serves attention
|
|
||||||
- name: VLLM_USE_FLASHINFER_SAMPLER
|
|
||||||
value: "0"
|
|
||||||
- name: VLLM_ATTENTION_BACKEND
|
|
||||||
value: TRITON_ATTN
|
|
||||||
- name: HF_HOME
|
|
||||||
value: /mnt/models
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 8080
|
|
||||||
periodSeconds: 10
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 8080
|
|
||||||
# TP=2 at 256K needs ~10 min before the API binds: 22s weights, 80s torch.compile,
|
|
||||||
# 85s KV profiling, then graph capture. 120 x 15s = 30 min of headroom.
|
|
||||||
failureThreshold: 120
|
|
||||||
periodSeconds: 15
|
|
||||||
resources:
|
|
||||||
# worker-1 exposes only 63,495 MiB of allocatable memory, so a 24Gi request
|
|
||||||
# never schedules. Two vLLM workers keep the weights on the GPU and need
|
|
||||||
# little host RAM, so the request stays small and the limit allows burst.
|
|
||||||
requests:
|
|
||||||
cpu: "8"
|
|
||||||
memory: 12Gi
|
|
||||||
nvidia.com/gpu: "2"
|
|
||||||
limits:
|
|
||||||
cpu: "16"
|
|
||||||
memory: 40Gi
|
|
||||||
nvidia.com/gpu: "2"
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /mnt/models
|
|
||||||
name: models
|
|
||||||
- mountPath: /dev/shm
|
|
||||||
name: shm
|
|
||||||
runtimeClassName: nvidia
|
|
||||||
volumes:
|
|
||||||
- name: models
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: llm-models
|
|
||||||
- name: shm
|
|
||||||
emptyDir:
|
|
||||||
medium: Memory
|
|
||||||
# NCCL all-reduce over TP=2 moves through /dev/shm. The 64Mi default is
|
|
||||||
# too small and shows up as an init hang or a bus error.
|
|
||||||
sizeLimit: 8Gi
|
|
||||||
@@ -10,12 +10,8 @@ kind: Kustomization
|
|||||||
# way after any edit here: a GPU predictor restart is a weights reload measured
|
# way after any edit here: a GPU predictor restart is a weights reload measured
|
||||||
# in tens of seconds, not a rolling update.
|
# in tens of seconds, not a rolling update.
|
||||||
resources:
|
resources:
|
||||||
- coder.yaml
|
|
||||||
- embeddings.yaml
|
- embeddings.yaml
|
||||||
- ornith.yaml
|
- ornith.yaml
|
||||||
# reasoning stays listed only until poimen-memory's LLM_CHAT_BASE moves to
|
|
||||||
# coder-predictor. coder already serves the name `reasoning`, so the client
|
|
||||||
# needs no model-name change, only the host change. Drop this line after that.
|
|
||||||
- reasoning.yaml
|
- reasoning.yaml
|
||||||
- reranker.yaml
|
- reranker.yaml
|
||||||
- qwen-cpu.yaml
|
- qwen-cpu.yaml
|
||||||
|
|||||||
@@ -66,19 +66,6 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 8000
|
port: 8000
|
||||||
# Allow from poimen namespace (poimen-memory ingest + query).
|
|
||||||
# The client-side egress rule already exists and is not the gap:
|
|
||||||
# NetworkPolicy poimen/poimen-memory-llm-egress permits port 8080 to pods
|
|
||||||
# labelled llm-reasoning, llm-embeddings and llm-reranker. This policy had no
|
|
||||||
# matching ingress rule, so the packet was allowed out and dropped here.
|
|
||||||
# The client then sees a bare timeout and the predictor log stays empty.
|
|
||||||
- from:
|
|
||||||
- namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: poimen
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 8080
|
|
||||||
# Allow intra-namespace (pod-to-pod within llm-serving)
|
# Allow intra-namespace (pod-to-pod within llm-serving)
|
||||||
- from:
|
- from:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
|
|||||||
@@ -249,3 +249,26 @@ spec:
|
|||||||
selfHeal: true
|
selfHeal: true
|
||||||
syncOptions:
|
syncOptions:
|
||||||
- CreateNamespace=true
|
- CreateNamespace=true
|
||||||
|
---
|
||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner
|
||||||
|
namespace: argocd
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/sync-wave: "1"
|
||||||
|
spec:
|
||||||
|
project: homelab
|
||||||
|
source:
|
||||||
|
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
|
||||||
|
targetRevision: main
|
||||||
|
path: k8s/infra/local-path
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
namespace: local-path-storage
|
||||||
|
# No CreateNamespace: the Namespace object (with its privileged PSA label,
|
||||||
|
# which the helper Pods need for their hostPath) is part of the manifest.
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: oidc-bootstrap
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "5"
|
|
||||||
spec:
|
|
||||||
project: homelab
|
|
||||||
source:
|
|
||||||
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
|
|
||||||
targetRevision: main
|
|
||||||
path: k8s/infra/oidc-bootstrap
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: kube-system
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
type: Opaque
|
|
||||||
metadata:
|
|
||||||
name: forgejo-registry-token
|
|
||||||
namespace: cicd
|
|
||||||
data:
|
|
||||||
username: ENC[AES256_GCM,data:oRNecs0a7rs=,iv:/5EFdNqpSjoxe7XDmAEDMd6lXd0SMsRiMmBM9SIwDHY=,tag:Wi+ggoP+9/vgz9rHrxB9nA==,type:str]
|
|
||||||
password: ENC[AES256_GCM,data:HbmwnWk3ST5SDWXwIhu+M5f/FOfFbJHaG0Q/IWrO63HRXmJTmLMpbjtWIXC9IMFoHeDZ1MNAV8A=,iv:ztJZgpaS//yYCiyr6/ubs/UiXkbGMnokvy4Pe45jQMk=,tag:WMpawc/SVAV4mxsCd6XqAA==,type:str]
|
|
||||||
sops:
|
|
||||||
age:
|
|
||||||
- enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjQmhxN2E3ZnNTdkR6U2c1
|
|
||||||
ZStsQktkeWUwMEdmdFIyMkRYRVk1ZGppWlZ3CmUzR2JubWw2VjdWUGt6S2lpNmQ3
|
|
||||||
cDdSWjJiYzRIWS9UVERpVDFJenFnaDAKLS0tIGhDSmNMSjZwcDVPR3U0dUcwNk9Q
|
|
||||||
WmdGS1lyS3dkSXIvOFhyRklGRll2UlUK8NGts6a6Fv10c6gxIIPqIIqfgzQ3EEyk
|
|
||||||
SFrMThwbdaOqLY4AMeDrg+k0Y2t1Xj/XOvdNbRCRictDwZUsptvO7A==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla
|
|
||||||
encrypted_regex: ^(data|stringData)$
|
|
||||||
lastmodified: "2026-10-06T02:01:24Z"
|
|
||||||
mac: ENC[AES256_GCM,data:imebczVzG9YWd42ER4a+w+Bi3dxbN0HFm/1igxzJt6Z0HG/q6i0s59swpGVruk9dQJWrlO8KKqX739ATp2KGC5szrEuRFvPemF594XFCg6ykW+ZeP5VtTvpJg2nHv7/Pu+71ljKKi4gqOJ4SJDn8owAbSN+su6PiT323wax930A=,iv:iG/G2+qQkvbBU95w/GG2PrbAHDOaamuvjqMDBxSW6zI=,tag:wy9Vn72HBDHqCyzTcV15/Q==,type:str]
|
|
||||||
version: 3.13.2
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
type: Opaque
|
|
||||||
metadata:
|
|
||||||
name: memory-agent-oidc
|
|
||||||
namespace: poimen
|
|
||||||
data:
|
|
||||||
CLIENT_ID: ENC[AES256_GCM,data:c/RvtV6anofX8h1BmlKu9w==,iv:3lQ1BR8kVvF1RHVfKstmb0Y7u/uX5OrvmGiCi/xTpjw=,tag:9JC2WkfQFWaiZroAEmPenw==,type:str]
|
|
||||||
CLIENT_SECRET: ENC[AES256_GCM,data:I258/6O43PccyllYN/wfoKvTgNgZONTivM/KFIr8TGqSl+2XQkwBXvK2VQc5Xgc5cpwlrgnjk8pjQbqr,iv:MZyA+V5z9Po2VoxqvOSlWh1fZp1lrkcbYMx7VcSIaJs=,tag:gXH0BpA8KOZiVd+4RnhRzg==,type:str]
|
|
||||||
ISSUER: ENC[AES256_GCM,data:GX/uQPDoeJWXXQZlzD6oOIvxo7H4LRAv4MrPAiCgmv6WxW59+KKkymuDT/T9MiDm4bNhxVPgB7nHbDcMHfPvU2zCNM2TWYUsNOgGwxNAqM8=,iv:5OEsdrUZvMLO2C8Ea2i5alsyFGtmEk3othTemk8fOk4=,tag:8CMlRY2JCe4z4fv4WuWjRQ==,type:str]
|
|
||||||
TOKEN_URL: ENC[AES256_GCM,data:KeU1pQzvQ2/1lQhOqNtcR7t5TGVx1J7NY8ogxCXS6IuHhVe0zWYIbC4BYFc2H9PAjsMJwCCg75N1FrIiCYBvpfK8ln0=,iv:NbPiJC/B7W1Zm+JwnEGDikE2cb3L4l6TBeQTKEb2boM=,tag:FRmllFLZzQW03lsCu9mAUA==,type:str]
|
|
||||||
sops:
|
|
||||||
age:
|
|
||||||
- enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmK0dPSmhKT01FSitDdUNW
|
|
||||||
Um1XQU9FVnBUeDdoWXpCcXRzVXh0VXkvNGxNClZaQ3R5U2NkYTJjaFdHRWhUVmFS
|
|
||||||
K0tHZVRFRDZGREVRZzZqcWJ6dnRpUUEKLS0tIE15UUNtMStKbWwzb3dEVGEwN1VQ
|
|
||||||
cktlVTM2SUhpOGJZMXJOOG84RnhCQzAKOdM0pThEaN37SJtRrKIivncMu8Z91Btq
|
|
||||||
es6NOlJdBUhzIhnEJWfDzZ9aA6I/fYzugKbC+AfUwdGgThAgHx9crg==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla
|
|
||||||
encrypted_regex: ^(data|stringData)$
|
|
||||||
lastmodified: "2026-10-06T02:01:24Z"
|
|
||||||
mac: ENC[AES256_GCM,data:/BTQ984D+hsk0LmnjD3hxopB65NAR54LhoAPNcATWA7MCTJXfdnYRu3gNxUzyiyCBd+rDaPp77TBnak4jVjMnOiYJsYLj1R7hrrhTZopz8pT+YNMupperwsirKZwK8it4Mu+fnpCyox2mtGED0hk+Nh/b6IteTRt5lS7noumo2M=,iv:RWIrVkNYj+8mqMSPiLueToexabzAtVbIR525m6bUrlg=,tag:5+wcNoNYsjGKpFlML+N70Q==,type:str]
|
|
||||||
version: 3.13.2
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
type: Opaque
|
|
||||||
metadata:
|
|
||||||
name: oauth2-credentials
|
|
||||||
namespace: iam
|
|
||||||
data:
|
|
||||||
api-gw-client-secret: ENC[AES256_GCM,data:YiUCk61cCsTJE8gTLZWhqVPsCSu3b4OnG+Oh456A+mnxip0FjLTTqV+kYBdfZivIkn9ua4RcVW+rYILu,iv:K08m1EaB7/SIVNWZ3FfeRP4tbCAo6KeiNC0NpE9VO1o=,tag:ZXV9YRWUOPJ/vdoqMtvQeA==,type:str]
|
|
||||||
minio-client-secret: ENC[AES256_GCM,data:vcwLo1cJqbZr2MwkAKO09xPjb09ss7+s6HIvEfaLxt8ka5PUAlXQ37pTkynuktlKq/t0DvSk+x1+5G7QqVD1JJlwyDGM6yuOhBQ3MwucLNl+T4RDKtgGuA==,iv:sCdd+UNX2TfTAM6Zslrh8KrvBSzqpe67F3YMGrl4YDU=,tag:9Tpzm9NEXNuXyCd7l7GeHw==,type:str]
|
|
||||||
poimen-client-secret: ENC[AES256_GCM,data:dYtCiMGZaOFokihfGD2SXJDfXrhfwEQcTOPNu7JNHDUnx0hqwAdYAPAJTEN21p0OpHYDINMs5lwUpMwY,iv:ll8USuzs0881ryHImVjHGO0iFxUzqg5XtI6Ekd+EFXE=,tag:I/4ZfB9Ss4zEP57sRtRTXQ==,type:str]
|
|
||||||
paperless-client-secret: ENC[AES256_GCM,data:XMj9rHkI20lI37vChq9wS5lUq7l95SisRX0LChuj09O5w76Zt9NdG6OEUYQFf1z8vCfMJxBInIEEIq0e,iv:UpYy2ZEZ4AgBACEyrlxEcr13V+8Ksu4p8JHYApmyYHo=,tag:o9Z+/b6T9POgowIsks56kw==,type:str]
|
|
||||||
grafana-client-secret: ENC[AES256_GCM,data:V/j0+P3HoDLqYjRalWK4v1hvJTuVmzxVra47HsF1/BZvWZjIyEIH3Pt8BekT3O1dUFynxjcaxdZMBVYg,iv:9pMp+vIsp/sA7GP9wqNDf5DdZVLDow46OURW0mMRXIc=,tag:i7VJFA5o0hToZEJ5JPWIvw==,type:str]
|
|
||||||
sops:
|
|
||||||
age:
|
|
||||||
- enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2amZPendIOU9PeStOcmJG
|
|
||||||
c1dzOFVvd3RoT3dXQlJUNkdCSytEZUVGVEJJCklMUkNjUytBMXh0OVUrNUlpMWw3
|
|
||||||
aHRFT1hBZTZHNHVtaGJCTEFyT2ZWNWsKLS0tIENxR3FKMEplV0V5Y0dsZzBkODVG
|
|
||||||
U0ZlT2R0VFdEQmpsakVvZ1FJRkRQYTAKESQcSlMAuCG0/tLWWZaeoPayf1EKxSaf
|
|
||||||
e4o8TmjmSgPEhepQ/LmNXl6obyuqUxztkh4Mm6pocqDp+gfzt7X/1g==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla
|
|
||||||
encrypted_regex: ^(data|stringData)$
|
|
||||||
lastmodified: "2026-10-06T02:01:24Z"
|
|
||||||
mac: ENC[AES256_GCM,data:AI/znHiUa/Rwg0YRz65qUZ+b6V73oi3ZfAG/V5uZZvrGFrA0EISzD5LaP5Kl4GUU1svo4G5EI4aftPDgpdPptz21CCa8iYkXGe9pTDmtaE5ZICQAUbRcVhGMTfuWFhrgEhMfCJdOpfHaIQVEsQJQJgrnkXQVuUe9aT75LQz7pnY=,iv:KbIaYT0VeBT2WeSU5V5ZB4e+04JICYaY+egGN1Pv5bM=,tag:pPbhDxeLUx6V1XC/LPwxTg==,type:str]
|
|
||||||
version: 3.13.2
|
|
||||||
|
|||||||
@@ -7,33 +7,30 @@ metadata:
|
|||||||
exec:
|
exec:
|
||||||
path: ksops
|
path: ksops
|
||||||
files:
|
files:
|
||||||
- agent-pod-models.enc.yaml
|
- agent-pod-models.enc.yaml
|
||||||
- agent-pod-ssh-key.enc.yaml
|
- agent-pod-ssh-key.enc.yaml
|
||||||
- authentik-secrets.enc.yaml
|
- authentik-secrets.enc.yaml
|
||||||
- cloudflare-secrets.enc.yaml
|
- cloudflare-secrets.enc.yaml
|
||||||
- forgejo-registry-pat.enc.yaml
|
- forgejo-registry-pat.enc.yaml
|
||||||
- forgejo-registry-pat-api.enc.yaml
|
- forgejo-registry-pat-api.enc.yaml
|
||||||
- forgejo-registry-pull.enc.yaml
|
- forgejo-registry-pull.enc.yaml
|
||||||
- forgejo-runner-token.enc.yaml
|
- forgejo-runner-token.enc.yaml
|
||||||
- forgejo-secrets.enc.yaml
|
- forgejo-secrets.enc.yaml
|
||||||
- grafana-oidc-secrets.enc.yaml
|
- grafana-oidc-secrets.enc.yaml
|
||||||
- grafana-secrets.enc.yaml
|
- grafana-secrets.enc.yaml
|
||||||
- homarr-auth-oidc.enc.yaml
|
- homarr-auth-oidc.enc.yaml
|
||||||
- homarr-db-encryption.enc.yaml
|
- homarr-db-encryption.enc.yaml
|
||||||
- homarr-secrets.enc.yaml
|
- homarr-secrets.enc.yaml
|
||||||
- homelab-ca-secrets.enc.yaml
|
- homelab-ca-secrets.enc.yaml
|
||||||
- loki-secrets.enc.yaml
|
- loki-secrets.enc.yaml
|
||||||
- minio-secrets.enc.yaml
|
- minio-secrets.enc.yaml
|
||||||
- paperless-secrets.enc.yaml
|
- paperless-secrets.enc.yaml
|
||||||
- vault-secrets.enc.yaml
|
- vault-secrets.enc.yaml
|
||||||
- vault-unseal-keys.enc.yaml
|
- vault-unseal-keys.enc.yaml
|
||||||
- portfolio-secrets.enc.yaml
|
- portfolio-secrets.enc.yaml
|
||||||
- gotify-admin-secrets.enc.yaml
|
- gotify-admin-secrets.enc.yaml
|
||||||
- gotify-tokens-secrets.enc.yaml
|
- gotify-tokens-secrets.enc.yaml
|
||||||
- gotify-smtp-secrets.enc.yaml
|
- gotify-smtp-secrets.enc.yaml
|
||||||
- forgejo-smtp-secrets.enc.yaml
|
- forgejo-smtp-secrets.enc.yaml
|
||||||
- paperless-ai-secrets.enc.yaml
|
- paperless-ai-secrets.enc.yaml
|
||||||
- memory-agent-oidc.enc.yaml
|
|
||||||
- oauth2-credentials.enc.yaml
|
|
||||||
- forgejo-registry-token.enc.yaml
|
|
||||||
- pi-sandbox-secrets.enc.yaml
|
- pi-sandbox-secrets.enc.yaml
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
# No commonLabels on purpose: this is a vendored third-party manifest and
|
||||||
|
# kustomize would also rewrite the provisioner Deployment's selector, which
|
||||||
|
# is immutable on re-apply.
|
||||||
|
resources:
|
||||||
|
- local-path-storage.yaml
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
# Vendored from https://github.com/rancher/local-path-provisioner
|
||||||
|
# deploy/local-path-storage.yaml @ v0.0.37 (release 2026-08-05)
|
||||||
|
#
|
||||||
|
# Local modifications (the only three; re-apply them on any re-vendor):
|
||||||
|
# 1. the namespace carries pod-security.kubernetes.io/enforce=privileged, because the
|
||||||
|
# provisioner's helper Pods mount a hostPath and this cluster defaults to the
|
||||||
|
# baseline policy, which forbids that;
|
||||||
|
# 2. config.json nodePathMap is /var/lib/local-path-provisioner — Talos mounts the root
|
||||||
|
# filesystem read-only, so upstream's /opt path cannot be created;
|
||||||
|
# 3. the helper Pod's busybox image is pinned to a tag instead of a floating name.
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: local-path-storage
|
||||||
|
labels:
|
||||||
|
pod-security.kubernetes.io/enforce: privileged
|
||||||
|
pod-security.kubernetes.io/audit: privileged
|
||||||
|
pod-security.kubernetes.io/warn: privileged
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
namespace: local-path-storage
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["nodes", "persistentvolumeclaims", "configmaps", "pods", "pods/log"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["persistentvolumes"]
|
||||||
|
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["events"]
|
||||||
|
verbs: ["create", "patch"]
|
||||||
|
- apiGroups: ["storage.k8s.io"]
|
||||||
|
resources: ["storageclasses"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-bind
|
||||||
|
namespace: local-path-storage
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-bind
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner
|
||||||
|
namespace: local-path-storage
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: local-path-provisioner
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: local-path-provisioner
|
||||||
|
spec:
|
||||||
|
serviceAccountName: local-path-provisioner-service-account
|
||||||
|
containers:
|
||||||
|
- name: local-path-provisioner
|
||||||
|
image: docker.io/rancher/local-path-provisioner:v0.0.37
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- local-path-provisioner
|
||||||
|
- --debug
|
||||||
|
- start
|
||||||
|
- --config
|
||||||
|
- /etc/config/config.json
|
||||||
|
ports:
|
||||||
|
- name: health
|
||||||
|
containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
startupProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: health
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
failureThreshold: 12
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: health
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ready
|
||||||
|
port: health
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
volumeMounts:
|
||||||
|
- name: config-volume
|
||||||
|
mountPath: /etc/config/
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: CONFIG_MOUNT_PATH
|
||||||
|
value: /etc/config/
|
||||||
|
- name: HEALTH_PORT
|
||||||
|
value: "8080"
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
name: local-path-config
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: local-path
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
reclaimPolicy: Delete
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: ConfigMap
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: local-path-config
|
||||||
|
namespace: local-path-storage
|
||||||
|
data:
|
||||||
|
config.json: |-
|
||||||
|
{
|
||||||
|
"nodePathMap":[
|
||||||
|
{
|
||||||
|
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
|
||||||
|
"paths":["/var/lib/local-path-provisioner"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
setup: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
mkdir -m 0777 -p "$VOL_DIR"
|
||||||
|
teardown: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
rm -rf "$VOL_DIR"
|
||||||
|
helperPod.yaml: |-
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: helper-pod
|
||||||
|
spec:
|
||||||
|
priorityClassName: system-node-critical
|
||||||
|
tolerations:
|
||||||
|
- key: node.kubernetes.io/disk-pressure
|
||||||
|
operator: Exists
|
||||||
|
effect: NoSchedule
|
||||||
|
containers:
|
||||||
|
- name: helper-pod
|
||||||
|
image: docker.io/library/busybox:1.36
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
# One-shot bootstrap: restart the control-plane apiservers ONCE so they pick up the
|
|
||||||
# OIDC authenticator (they last started before the Authentik provider existed, so OIDC
|
|
||||||
# tokens were never accepted -> every login got 401). Delete this whole directory after.
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: oidc-bootstrap
|
|
||||||
namespace: kube-system
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: oidc-bootstrap
|
|
||||||
namespace: kube-system
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["pods"]
|
|
||||||
verbs: ["get", "list", "delete"]
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: RoleBinding
|
|
||||||
metadata:
|
|
||||||
name: oidc-bootstrap
|
|
||||||
namespace: kube-system
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: oidc-bootstrap
|
|
||||||
namespace: kube-system
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: Role
|
|
||||||
name: oidc-bootstrap
|
|
||||||
---
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: oidc-bootstrap-restart-apiserver-v2
|
|
||||||
namespace: kube-system
|
|
||||||
spec:
|
|
||||||
backoffLimit: 4
|
|
||||||
ttlSecondsAfterFinished: 7200
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
serviceAccountName: oidc-bootstrap
|
|
||||||
restartPolicy: Never
|
|
||||||
containers:
|
|
||||||
- name: restart
|
|
||||||
image: docker.io/curlimages/curl:8.22.0
|
|
||||||
command: ["/bin/sh", "-c"]
|
|
||||||
args:
|
|
||||||
- |
|
|
||||||
set -u
|
|
||||||
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
|
|
||||||
CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
|
||||||
for pod in kube-apiserver-talos-cp-1 kube-apiserver-talos-cp-2 kube-apiserver-talos-cp-3; do
|
|
||||||
curl -sS --max-time 15 --cacert "$CA" -H "Authorization: Bearer $TOKEN" -X DELETE "https://kubernetes.default.svc/api/v1/namespaces/kube-system/pods/$pod?gracePeriodSeconds=0" -o /dev/null -w "$pod -> HTTP %{http_code}\n" || echo "$pod -> request failed"
|
|
||||||
done
|
|
||||||
echo "deletes requested; kubelet recreates each static pod in ~20-40s"
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
# One-shot: restart the control-plane API servers so their OIDC authenticator initialises.
|
|
||||||
#
|
|
||||||
# Two earlier mechanisms failed and are documented so nobody repeats them:
|
|
||||||
# 1. deleting the mirror pod does NOT restart a static pod (deletes returned 200, no restart);
|
|
||||||
# 2. matching /proc/*/cmdline for 'kube-apiserver' also matched this script's own cmdline,
|
|
||||||
# so every pod SIGTERMed itself (exit 143) before reaching the real process.
|
|
||||||
# This version matches /proc/<pid>/comm (the process name) and skips \$\$, so self-kill is
|
|
||||||
# impossible. hostPID + privileged are permitted in the 'cicd' namespace. Delays stagger the
|
|
||||||
# three control planes by 2 minutes each, so only one API server is down at a time.
|
|
||||||
---
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: oidc-restart-apiserver-cp1-v2
|
|
||||||
namespace: cicd
|
|
||||||
spec:
|
|
||||||
backoffLimit: 0
|
|
||||||
ttlSecondsAfterFinished: 3600
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
hostPID: true
|
|
||||||
restartPolicy: Never
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: talos-cp-1
|
|
||||||
tolerations:
|
|
||||||
- key: node-role.kubernetes.io/control-plane
|
|
||||||
operator: Exists
|
|
||||||
effect: NoSchedule
|
|
||||||
- key: node-role.kubernetes.io/master
|
|
||||||
operator: Exists
|
|
||||||
effect: NoSchedule
|
|
||||||
containers:
|
|
||||||
- name: kill
|
|
||||||
image: docker.io/library/busybox:1.36
|
|
||||||
securityContext:
|
|
||||||
privileged: true
|
|
||||||
command: ["/bin/sh", "-c"]
|
|
||||||
args:
|
|
||||||
- |
|
|
||||||
echo "sleeping 10s before restarting the API server on talos-cp-1"
|
|
||||||
sleep 10
|
|
||||||
self=$$
|
|
||||||
killed=0
|
|
||||||
for d in /proc/[0-9]*; do
|
|
||||||
pid=${d#/proc/}
|
|
||||||
[ "$pid" = "$self" ] && continue
|
|
||||||
comm=$(cat "$d/comm" 2>/dev/null)
|
|
||||||
[ "$comm" = "kube-apiserver" ] || continue
|
|
||||||
echo "SIGTERM -> pid $pid"
|
|
||||||
kill -TERM "$pid" 2>/dev/null && killed=$((killed+1))
|
|
||||||
done
|
|
||||||
echo "signalled $killed API server process(es); kubelet recreates the static pod"
|
|
||||||
|
|
||||||
---
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: oidc-restart-apiserver-cp2-v2
|
|
||||||
namespace: cicd
|
|
||||||
spec:
|
|
||||||
backoffLimit: 0
|
|
||||||
ttlSecondsAfterFinished: 3600
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
hostPID: true
|
|
||||||
restartPolicy: Never
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: talos-cp-2
|
|
||||||
tolerations:
|
|
||||||
- key: node-role.kubernetes.io/control-plane
|
|
||||||
operator: Exists
|
|
||||||
effect: NoSchedule
|
|
||||||
- key: node-role.kubernetes.io/master
|
|
||||||
operator: Exists
|
|
||||||
effect: NoSchedule
|
|
||||||
containers:
|
|
||||||
- name: kill
|
|
||||||
image: docker.io/library/busybox:1.36
|
|
||||||
securityContext:
|
|
||||||
privileged: true
|
|
||||||
command: ["/bin/sh", "-c"]
|
|
||||||
args:
|
|
||||||
- |
|
|
||||||
echo "sleeping 120s before restarting the API server on talos-cp-2"
|
|
||||||
sleep 120
|
|
||||||
self=$$
|
|
||||||
killed=0
|
|
||||||
for d in /proc/[0-9]*; do
|
|
||||||
pid=${d#/proc/}
|
|
||||||
[ "$pid" = "$self" ] && continue
|
|
||||||
comm=$(cat "$d/comm" 2>/dev/null)
|
|
||||||
[ "$comm" = "kube-apiserver" ] || continue
|
|
||||||
echo "SIGTERM -> pid $pid"
|
|
||||||
kill -TERM "$pid" 2>/dev/null && killed=$((killed+1))
|
|
||||||
done
|
|
||||||
echo "signalled $killed API server process(es); kubelet recreates the static pod"
|
|
||||||
|
|
||||||
---
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: oidc-restart-apiserver-cp3-v2
|
|
||||||
namespace: cicd
|
|
||||||
spec:
|
|
||||||
backoffLimit: 0
|
|
||||||
ttlSecondsAfterFinished: 3600
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
hostPID: true
|
|
||||||
restartPolicy: Never
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: talos-cp-3
|
|
||||||
tolerations:
|
|
||||||
- key: node-role.kubernetes.io/control-plane
|
|
||||||
operator: Exists
|
|
||||||
effect: NoSchedule
|
|
||||||
- key: node-role.kubernetes.io/master
|
|
||||||
operator: Exists
|
|
||||||
effect: NoSchedule
|
|
||||||
containers:
|
|
||||||
- name: kill
|
|
||||||
image: docker.io/library/busybox:1.36
|
|
||||||
securityContext:
|
|
||||||
privileged: true
|
|
||||||
command: ["/bin/sh", "-c"]
|
|
||||||
args:
|
|
||||||
- |
|
|
||||||
echo "sleeping 240s before restarting the API server on talos-cp-3"
|
|
||||||
sleep 240
|
|
||||||
self=$$
|
|
||||||
killed=0
|
|
||||||
for d in /proc/[0-9]*; do
|
|
||||||
pid=${d#/proc/}
|
|
||||||
[ "$pid" = "$self" ] && continue
|
|
||||||
comm=$(cat "$d/comm" 2>/dev/null)
|
|
||||||
[ "$comm" = "kube-apiserver" ] || continue
|
|
||||||
echo "SIGTERM -> pid $pid"
|
|
||||||
kill -TERM "$pid" 2>/dev/null && killed=$((killed+1))
|
|
||||||
done
|
|
||||||
echo "signalled $killed API server process(es); kubelet recreates the static pod"
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user