Skip to content
README.md 13.9 KiB
Newer Older
# Local Path Provisioner
Sheng Yang's avatar
Sheng Yang committed
[![Build Status](https://drone-publish.rancher.io/api/badges/rancher/local-path-provisioner/status.svg)](https://drone-publish.rancher.io/rancher/local-path-provisioner)[![Go Report Card](https://goreportcard.com/badge/github.com/rancher/local-path-provisioner)](https://goreportcard.com/report/github.com/rancher/local-path-provisioner)

## Overview

AnthonyEnr1quez's avatar
AnthonyEnr1quez committed
Local Path Provisioner provides a way for the Kubernetes users to utilize the local storage in each node. Based on the user configuration, the Local Path Provisioner will create either `hostPath` or `local` based persistent volume on the node automatically. It utilizes the features introduced by Kubernetes [Local Persistent Volume feature](https://kubernetes.io/blog/2018/04/13/local-persistent-volumes-beta/), but makes it a simpler solution than the built-in `local` volume feature in Kubernetes.

## Compare to built-in Local Persistent Volume feature in Kubernetes

### Pros
Dynamic provisioning the volume using [hostPath](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath) or [local](https://kubernetes.io/docs/concepts/storage/volumes/#local).
* Currently the Kubernetes [Local Volume provisioner](https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner) cannot do dynamic provisioning for the local volumes.
* Local based persistent volumes are an experimental feature ([example usage](examples/pvc-with-local-volume/pvc.yaml)).

### Cons
1. No support for the volume capacity limit currently.
    1. The capacity limit will be ignored for now.

Sheng Yang's avatar
Sheng Yang committed
## Requirement
Kubernetes v1.12+.
Sheng Yang's avatar
Sheng Yang committed
## Deployment
Sheng Yang's avatar
Sheng Yang committed
### Installation
Sheng Yang's avatar
Sheng Yang committed
In this setup, the directory `/opt/local-path-provisioner` will be used across all the nodes as the path for provisioning (a.k.a, store the persistent volume data). The provisioner will be installed in `local-path-storage` namespace by default.
Derek Su's avatar
Derek Su committed
- Stable
```
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.26/deploy/local-path-storage.yaml
Derek Su's avatar
Derek Su committed
```

- Development
Sheng Yang's avatar
Sheng Yang committed
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
Or, use `kustomize` to deploy.
Derek Su's avatar
Derek Su committed
- Stable
```
kustomize build "github.com/rancher/local-path-provisioner/deploy?ref=v0.0.26" | kubectl apply -f -
Derek Su's avatar
Derek Su committed
```

- Development
```
kustomize build "github.com/rancher/local-path-provisioner/deploy?ref=master" | kubectl apply -f -
```

Sheng Yang's avatar
Sheng Yang committed
After installation, you should see something like the following:
```
$ kubectl -n local-path-storage get pod
NAME                                     READY     STATUS    RESTARTS   AGE
local-path-provisioner-d744ccf98-xfcbk   1/1       Running   0          7m
```
Sheng Yang's avatar
Sheng Yang committed
Check and follow the provisioner log using:
```
kubectl -n local-path-storage logs -f -l app=local-path-provisioner
Sheng Yang's avatar
Sheng Yang committed
```
Sheng Yang's avatar
Sheng Yang committed
## Usage
Create a `hostPath` backend Persistent Volume and a pod uses it:
```
kubectl create -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/examples/pvc/pvc.yaml
kubectl create -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/examples/pod/pod.yaml
```
Or, use `kustomize` to deploy them.
kustomize build "github.com/rancher/local-path-provisioner/examples/pod?ref=master" | kubectl apply -f -
Sheng Yang's avatar
Sheng Yang committed
You should see the PV has been created:
Sheng Yang's avatar
Sheng Yang committed
$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM                    STORAGECLASS   REASON    AGE
pvc-bc3117d9-c6d3-11e8-b36d-7a42907dda78   2Gi        RWO            Delete           Bound     default/local-path-pvc   local-path               4s
Sheng Yang's avatar
Sheng Yang committed
The PVC has been bound:
```
$ kubectl get pvc
NAME             STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
local-path-pvc   Bound     pvc-bc3117d9-c6d3-11e8-b36d-7a42907dda78   2Gi        RWO            local-path     16s
Sheng Yang's avatar
Sheng Yang committed
And the Pod started running:
```
$ kubectl get pod
NAME          READY     STATUS    RESTARTS   AGE
volume-test   1/1       Running   0          3s
```

Write something into the pod
```
Sheng Yang's avatar
Sheng Yang committed
kubectl exec volume-test -- sh -c "echo local-path-test > /data/test"
```

Now delete the pod using
```
kubectl delete -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/examples/pod/pod.yaml
Sheng Yang's avatar
Sheng Yang committed
```

After confirm that the pod is gone, recreated the pod using
```
kubectl create -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/examples/pod/pod.yaml
Sheng Yang's avatar
Sheng Yang committed
```

Check the volume content:
```
$ kubectl exec volume-test -- sh -c "cat /data/test"
Sheng Yang's avatar
Sheng Yang committed
local-path-test
```

Delete the pod and pvc
```
kubectl delete -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/examples/pod/pod.yaml
kubectl delete -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/examples/pvc/pvc.yaml
```

Or, use `kustomize` to delete them.
```
kustomize build "github.com/rancher/local-path-provisioner/examples/pod?ref=master" | kubectl delete -f -
Sheng Yang's avatar
Sheng Yang committed
The volume content stored on the node will be automatically cleaned up. You can check the log of `local-path-provisioner-xxx` for details.

Now you've verified that the provisioner works as expected.

## Configuration

### Customize the ConfigMap

The configuration of the provisioner is a json file `config.json`, a Pod template `helperPod.yaml` and two bash scripts `setup` and `teardown`, stored in a config map, e.g.:
Sheng Yang's avatar
Sheng Yang committed
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":["/opt/local-path-provisioner"]
                },
                {
                        "node":"yasker-lp-dev1",
                        "paths":["/opt/local-path-provisioner", "/data1"]
                },
                {
                        "node":"yasker-lp-dev3",
                        "paths":[]
                }
Terry's avatar
Terry committed
                ]
Sheng Yang's avatar
Sheng Yang committed
        }
        set -eu
        mkdir -m 0777 -p "$VOL_DIR"
        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: busybox
The helperPod is allowed to run on nodes experiencing disk pressure conditions, despite the potential resource constraints. When it runs on such a node, it can carry out specific cleanup tasks, freeing up space in PVCs, and resolving the disk-pressure issue.

#### `config.json`
Sheng Yang's avatar
Sheng Yang committed
`nodePathMap` is the place user can customize where to store the data on each node.
1. If one node is not listed on the `nodePathMap`, and Kubernetes wants to create volume on it, the paths specified in `DEFAULT_PATH_FOR_NON_LISTED_NODES` will be used for provisioning.
2. If one node is listed on the `nodePathMap`, the specified paths in `paths` will be used for provisioning.
    1. If one node is listed but with `paths` set to `[]`, the provisioner will refuse to provision on this node.
    2. If more than one path was specified, the path would be chosen randomly when provisioning.

`sharedFileSystemPath` allows the provisioner to use a filesystem that is mounted on all nodes at the same time.
In this case all access modes are supported: `ReadWriteOnce`, `ReadOnlyMany` and `ReadWriteMany` for storage claims.

`storageClassConfigs` is a map from storage class names to objects containing `nodePathMap` or `sharedFilesystemPath`, as described above.

In addition `volumeBindingMode: Immediate` can be used in  StorageClass definition.

Please note that `nodePathMap`, `sharedFileSystemPath`, and `storageClassConfigs` are mutually exclusive. If `sharedFileSystemPath` or `stroageClassConfigs` are used, then `nodePathMap` must be set to `[]`.
The `setupCommand` and `teardownCommand` allow you to specify the path to binary files in helperPod that will be called when creating or deleting pvc respectively. This can be useful if you need to use distroless images for security reasons. See the examples/distroless directory for an example. A binary file can take the following parameters:
| Parameter | Description |
| -------------------- | ----------- |
| -p | Volume directory that should be created or removed. | -m | -p | Volume directory that should be created or removed. |
| -m | The PersistentVolume mode (`Block` or `Filesystem`). | -m | The PersistentVolume mode (`Block` or `Filesystem`). |
| -s | Requested volume size in bytes. | -s | Requested volume size in bytes. |
| -a | Action type. Can be `create` or `delete` | -a | -a | Action type.

The `setupCommand` and `teardownCommand` have higher priority than the `setup` and `teardown` scripts from the ConfigMap.  

Sheng Yang's avatar
Sheng Yang committed
The configuration must obey following rules:
1. `config.json` must be a valid json file.
2. A path must start with `/`, a.k.a an absolute path.
2. Root directory(`/`) is prohibited.
3. No duplicate paths allowed for one node.
4. No duplicate node allowed.

#### Scripts `setup` and `teardown` and the `helperPod.yaml` template
* The `setup` script is run before the volume is created, to prepare the volume directory on the node.
* The `teardown` script is run after the volume is deleted, to cleanup the volume directory on the node.
* The `helperPod.yaml` template is used to create a helper Pod that runs the `setup` or `teardown` script.
The scripts receive their input as environment variables:
| Environment variable | Description |
| -------------------- | ----------- |
| `VOL_DIR` | Volume directory that should be created or removed. |
| `VOL_MODE` | The PersistentVolume mode (`Block` or `Filesystem`). |
| `VOL_SIZE_BYTES` | Requested volume size in bytes. |
Sheng Yang's avatar
Sheng Yang committed

Matthew Meinzer's avatar
Matthew Meinzer committed
The provisioner supports automatic configuration reloading. Users can change the configuration using `kubectl apply` or `kubectl edit` with config map `local-path-config`. There is a delay between when the user updates the config map and the provisioner picking it up.
Sheng Yang's avatar
Sheng Yang committed

Matthew Meinzer's avatar
Matthew Meinzer committed
When the provisioner detects the configuration changes, it will try to load the new configuration. Users can observe it in the log
Sheng Yang's avatar
Sheng Yang committed
>time="2018-10-03T05:56:13Z" level=debug msg="Applied config: {\"nodePathMap\":[{\"node\":\"DEFAULT_PATH_FOR_NON_LISTED_NODES\",\"paths\":[\"/opt/local-path-provisioner\"]},{\"node\":\"yasker-lp-dev1\",\"paths\":[\"/opt\",\"/data1\"]},{\"node\":\"yasker-lp-dev3\"}]}"

Matthew Meinzer's avatar
Matthew Meinzer committed
If the reload fails, the provisioner will log the error and **continue using the last valid configuration for provisioning in the meantime**.
Sheng Yang's avatar
Sheng Yang committed
>time="2018-10-03T05:19:25Z" level=error msg="failed to load the new config file: fail to load config file /etc/config/config.json: invalid character '#' looking for beginning of object key string"

>time="2018-10-03T05:20:10Z" level=error msg="failed to load the new config file: config canonicalization failed: path must start with / for path opt on node yasker-lp-dev1"

>time="2018-10-03T05:23:35Z" level=error msg="failed to load the new config file: config canonicalization failed: duplicate path /data1 on node yasker-lp-dev1

>time="2018-10-03T06:39:28Z" level=error msg="failed to load the new config file: config canonicalization failed: duplicate node yasker-lp-dev3"

### Volume Types

To specify the type of volume you want the provisioner to create, add either of the following annotations;

```yaml
annotations:
  volumeType: <local or hostPath>
```

- StorageClass:
```yaml
annotations:
  defaultVolumeType: <local or hostPath>
```

A few things to note; the annotation for the `StorageClass` will apply to all volumes using it and is superseded by the annotation on the PVC if one is provided. If neither of the annotations was provided then we default to `hostPath`.

Sameer Mene's avatar
Sameer Mene committed
### Storage classes

If more than one `paths` are specified in the `nodePathMap` the path is chosen randomly. To make the provisioner choose a specific path, use a `storageClass` defined with a parameter called `nodePath`. Note that this path should be defined in the `nodePathMap`
Sameer Mene's avatar
Sameer Mene committed

```
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ssd-local-path
provisioner: rancher.io/local-path
Sameer Mene's avatar
Sameer Mene committed
parameters:
  nodePath: /data/ssd
Sameer Mene's avatar
Sameer Mene committed
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
```

Here the provisioner will use the path `/data/ssd` when storage class `ssd-local-path` is used.

Sheng Yang's avatar
Sheng Yang committed
## Uninstall

Matthew Meinzer's avatar
Matthew Meinzer committed
Before uninstallation, make sure the PVs created by the provisioner have already been deleted. Use `kubectl get pv` and make sure no PV with StorageClass `local-path`.
Sheng Yang's avatar
Sheng Yang committed

To uninstall, execute:

Derek Su's avatar
Derek Su committed
- Stable
```
kubectl delete -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.26/deploy/local-path-storage.yaml
Derek Su's avatar
Derek Su committed
```

- Development
Sheng Yang's avatar
Sheng Yang committed
```
Sheng Yang's avatar
Sheng Yang committed
kubectl delete -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
Sheng Yang's avatar
Sheng Yang committed
```
Sheng Yang's avatar
Sheng Yang committed

AJ Dexter's avatar
AJ Dexter committed
> it providers a out-of-cluster debug env for developers
### debug
```Bash
git clone https://github.com/rancher/local-path-provisioner.git
cd local-path-provisioner
go build
kubectl apply -f debug/config.yaml
./local-path-provisioner --debug start --service-account-name=default
```

### example
[Usage](#usage)

### clear
```
kubectl delete -f debug/config.yaml
```

Sheng Yang's avatar
Sheng Yang committed
## License

Sheng Yang's avatar
Sheng Yang committed
Copyright (c) 2014-2020  [Rancher Labs, Inc.](http://rancher.com/)
Sheng Yang's avatar
Sheng Yang committed

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.