| # local-path-provisioner v0.0.37, based on upstream deploy/local-path-storage.yaml |
| # with these local changes: |
| # - PVs live under /var/k8s; setup.sh/teardown.sh create and unmount one |
| # ZFS dataset per PV (rpool/var/k8s/<pv>) in the host mount namespace. |
| # - The helper pod needs hostPID and privileged for nsenter. |
| # - The namespace (PSA enforce=privileged) is not managed here. |
| # setup and teardown below must stay in sync with setup.sh and teardown.sh. |
| # The provisioner reads helperPod.yaml only at startup; restart it after |
| # changing that. |
| apiVersion: apps/v1 |
| kind: Deployment |
| metadata: |
| name: local-path-provisioner |
| namespace: local-path-storage |
| labels: |
| app: local-path-provisioner |
| 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 |
| # The helper pod needs hostPID for nsenter (see setup.sh). |
| - --allow-unsafe-helper-pod-template |
| 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: v1 |
| kind: ConfigMap |
| metadata: |
| name: local-path-config |
| namespace: local-path-storage |
| data: |
| config.json: |- |
| { |
| "nodePathMap":[ |
| { |
| "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES", |
| "paths":["/var/k8s"] |
| } |
| ] |
| } |
| setup: | |
| #!/bin/sh |
| # Creates a ZFS dataset per PV. The helper pod runs with hostPID, and all |
| # ZFS and filesystem operations are performed in the host's mount namespace |
| # (nsenter -t 1 -m). Running zfs directly inside the helper container would |
| # mount the dataset only inside the container's own mount namespace; the |
| # workload would then write into the parent dataset, and the child dataset |
| # would shadow that data after the next host reboot. |
| # |
| # The provisioner passes the volume path as $VOL_DIR (v0.0.2x and later); |
| # older versions passed it as -p. |
| set -eu |
| |
| absolutePath=${VOL_DIR:-} |
| while getopts "m:s:p:" opt |
| do |
| case $opt in |
| p) |
| absolutePath=$OPTARG |
| ;; |
| s) |
| sizeInBytes=$OPTARG |
| ;; |
| m) |
| volMode=$OPTARG |
| ;; |
| esac |
| done |
| [ -n "$absolutePath" ] || { echo "setup: no volume path given" >&2; exit 1; } |
| |
| host() { nsenter -t 1 -m -- "$@"; } |
| |
| host zfs create "rpool${absolutePath}" |
| host zfs mount "rpool${absolutePath}" 2>/dev/null || : |
| host findmnt -n -o SOURCE "${absolutePath}" | grep -qx "rpool${absolutePath}" |
| host chmod 0777 "${absolutePath}" |
| teardown: | |
| #!/bin/sh |
| # Unmounts (but keeps) the PV's ZFS dataset. Runs in the host's mount |
| # namespace; see setup.sh. |
| set -eu |
| |
| absolutePath=${VOL_DIR:-} |
| while getopts "m:s:p:" opt |
| do |
| case $opt in |
| p) |
| absolutePath=$OPTARG |
| ;; |
| s) |
| sizeInBytes=$OPTARG |
| ;; |
| m) |
| volMode=$OPTARG |
| ;; |
| esac |
| done |
| [ -n "$absolutePath" ] || { echo "teardown: no volume path given" >&2; exit 1; } |
| |
| nsenter -t 1 -m -- zfs set mountpoint=none "rpool${absolutePath}" |
| 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 |
| hostPID: true |
| containers: |
| - name: helper-pod |
| image: alpine:3.20 |
| imagePullPolicy: IfNotPresent |
| securityContext: |
| privileged: true |
| --- |
| apiVersion: v1 |
| kind: ServiceAccount |
| metadata: |
| name: local-path-provisioner-service-account |
| namespace: local-path-storage |
| --- |
| # Since v0.0.24 helper pods run in the provisioner's namespace. |
| 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: 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 |
| --- |
| # Leftover from PodSecurityPolicy times (psp:default no longer grants anything). |
| apiVersion: rbac.authorization.k8s.io/v1 |
| kind: RoleBinding |
| metadata: |
| name: local-path-provisioner:psp:default |
| namespace: local-path-storage |
| roleRef: |
| apiGroup: rbac.authorization.k8s.io |
| kind: ClusterRole |
| name: psp:default |
| subjects: |
| - kind: ServiceAccount |
| name: local-path-provisioner-service-account |
| namespace: local-path-storage |
| --- |
| 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: 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: storage.k8s.io/v1 |
| kind: StorageClass |
| metadata: |
| name: local-path |
| provisioner: rancher.io/local-path |
| volumeBindingMode: WaitForFirstConsumer |
| reclaimPolicy: Delete |