From ebb1b9b02decc07f84a916c101bf2aa4de121ea6 Mon Sep 17 00:00:00 2001 From: rock Date: Tue, 6 Oct 2026 12:45:12 +0800 Subject: [PATCH] feat(storage): add local-path-provisioner as the fleet's persistent-storage backend --- k8s/argocd/apps/10-storage-observability.yaml | 23 ++ k8s/infra/local-path/kustomization.yaml | 7 + k8s/infra/local-path/local-path-storage.yaml | 200 ++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 k8s/infra/local-path/kustomization.yaml create mode 100644 k8s/infra/local-path/local-path-storage.yaml diff --git a/k8s/argocd/apps/10-storage-observability.yaml b/k8s/argocd/apps/10-storage-observability.yaml index 1b57f05..f4e0823 100644 --- a/k8s/argocd/apps/10-storage-observability.yaml +++ b/k8s/argocd/apps/10-storage-observability.yaml @@ -249,3 +249,26 @@ spec: selfHeal: true syncOptions: - 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 diff --git a/k8s/infra/local-path/kustomization.yaml b/k8s/infra/local-path/kustomization.yaml new file mode 100644 index 0000000..e315384 --- /dev/null +++ b/k8s/infra/local-path/kustomization.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 diff --git a/k8s/infra/local-path/local-path-storage.yaml b/k8s/infra/local-path/local-path-storage.yaml new file mode 100644 index 0000000..111feb7 --- /dev/null +++ b/k8s/infra/local-path/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