Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal for growing FlexVolume size. #1700

Merged
merged 1 commit into from
Jun 27, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions contributors/design-proposals/storage/grow-flexvolume-size.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Proposal for Growing FlexVolume Size

**Authors:** [xingzhou](https://github.com/xingzhou)

## Goals

Since PVC resizing is introduced in Kubernetes v1.8, several volume plugins have already supported this feature, e.g. GlusterFS, AWS EBS. In this proposal, we are proposing to support FlexVolume expansion. So when user uses FlexVolume and corresponding volume driver to connect to his/her backend storage system, he/she can expand the PV size by updating PVC in Kubernetes.

## Non Goals

* We only consider expanding FlexVolume size in this proposal. Decreasing size of FlexVolume will be designed in the future.
* In this proposal, user can only expand the FlexVolume size manually by updating PVC. Auto-expansion of FlexVolume based on specific meterings is not considered.
* The proposal only contains the changes made in FlexVolume, volume driver changes which should be made by user are not included.

## Implementation Designs

### Prerequisites

* Kubernetes should be at least v1.8.
* Enable resizing by setting feature gate `ExpandPersistentVolumeGate` to `true`.
* Enable `PersistentVolumeClaimResize` admission plugin(optional).
* Follow the UI of PV resizing, including:
* Only dynamic provisioning supports volume resizing
* Set StorageClass attribute `allowVolumeExpansion` to `true`

### Admission Control Changes

Whether or not a specific volume plugin supports volume expansion is validated and checked in PV resize admission plugin. In general, we can list FlexVolume as the ones that support volume expansion and leave the actual expansion capability check to the underneath volume driver when PV resize controller calls the `ExpandVolumeDevice` method of FlexVolume.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that we will end up accepting a volume resize request at the Kubernetes API layer, only to possibly later discover (when ExpandVolumeDevice is executed) that the volume is not re-sizable. Leaving it in a bad state.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like currently the resize admission controller has a hard-coded list of plugins that support resize. Is it worth augmenting it to dynamically check resize support?

Copy link
Member

@gnufied gnufied Apr 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to do that, but there was a concern raised before that doing so will force us to load all volume plugins in admission controller. It may not be an issue for api-server since it probably already loads all volume plugins but this needs to be carefully considered. Last time I tried something like this - it ended up increasing size of kubectl and few other binaries quite a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other thing is, if we do not add a capability like Resize for flex volume driver, we can not tell whether or not a flex volume driver supports resize.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saad-ali CSI will also likely run into this admission controller issue, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking of removing static checking we are currently doing and not do any checks at all. The reasoning is - if admission controller is enabled, we only allow resizing of PVC created from storageClasses where allowVolumeExpansion property is set to true. Now if somehow - k8s admin enabled that field for a SC that does not support resizing, we will log an PVC event and that will enable k8s admin to fix the SC.

Or to rephrase - I think the fact that, we allow resizing of only dynamically provisioned PVC with allowExpansion set to true should be more than enough. if a k8s admin misconfigures the SC then it will be considered an admin error (and he should know better).


In PV resize admission plugin, add the following check to `checkVolumePlugin` method:
```
// checkVolumePlugin checks whether the volume plugin supports resize
func (pvcr *persistentVolumeClaimResize) checkVolumePlugin(pv *api.PersistentVolume) bool {
...
if pv.Spec.FlexVolume != nil {
return true
}
...
}
```

### FlexVolume Plugin Changes

FlexVolume relies on underneath volume driver to implement various volume functions, e.g. attach/detach. As a result, volume driver will decide whether volume can be expanded or not.

By default, we assume all kinds of flex volume drivers support resizing. If they do not, flex volume plugin can detect this during resizing call to flex volume driver and always throw out error to stop the resizing process. So as a result, to implement resizing feature in flex volume plugin, the plugin itself must implement the following `ExpandableVolumePlugin` interfaces:

#### ExpandVolumeDevice

Volume resizing controller invokes this method while receiving a valid PVC resizing request. FlexVolume plugin calls the underneath volume driver’s corresponding `expandvolume` method with three parameters, including new size of volume(number in bytes), old size of volume(number in bytes) and volume spec, to expand PV. Once the expansion is done, volume driver should return the new size(number in bytes) of the volume to FlexVolume.

A sample implementation of `ExpandVolumeDevice` method is like:
```
func (plugin *flexVolumePlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
const timeout = 10*time.Minute

call := plugin.NewDriverCallWithTimeout(expandVolumeCmd, timeout)
call.Append(newSize.Value())
call.Append(oldSize.Value())
call.AppendSpec(spec, plugin.host, nil)

// If the volume driver does not support resizing, Flex Volume Plugin can throw out error here
// to stop expand controller's resizing process.
ds, err := call.Run()
if err != nil {
return resource.NewQuantity(0, resource.BinarySI), err
}

return resource.NewQuantity(ds.ActualVolumeSize, resource.BinarySI), nil
}
```

Add a new field in type `DriverStatus` named `ActualVolumeSize` to identify the new expanded size of the volume returned by underneath volume driver:
```
// DriverStatus represents the return value of the driver callout.
type DriverStatus struct {
...
ActualVolumeSize int64 `json:"volumeNewSize,omitempty"`
}
```

#### RequiresFSResize

`RequiresFSResize` is a method to implement `ExpandableVolumePlugin` interface. The return value of this method identifies whether or not a file system resize is required once physical volume get expanded. If the return value is `true`, PV resize controller will consider the volume resize operation is done and then update the PV object’s capacity in K8s directly; If the return value is `false`, PV resize controller will leave kubelet to do the file system resize, and kubelet on worker node will call `ExpandFS` method of FlexVolume to finish the file system resize step(at present, only offline FS resize is supportted, online resize support is under community discussion [here](https://github.com/kubernetes/community/pull/1535)).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If the return value is false, PV resize controller will consider the volume resize operation is done..."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's my mistake, need to exchange the behavior of the two return values. I'll update this in the next patch update.


The return value of `RequiresFSResize` is collected from underneath volume driver when FlexVolume invokes `init` method of volume driver. The sample code of `RequiresFSResize` in FlexVolume looks like:
```
func (plugin *flexVolumePlugin) RequiresFSResize() bool {
return plugin.capabilities.RequiresFSResize
}
```

And as a result, the FlexVolume type `DriverCapability` can be redefined as:
```
type DriverCapabilities struct {
Attach bool `json:"attach"`
RequiresFSResize bool `json:"requiresFSResize"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a capability for the Resize alone? Not just FSResize?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One difficulty is that in order for Kubernetes to distinguish between different Flex driver types, we end up with an exponential explosion of plugin structs in the Kubernetes Flexvolume code - AttachablePlugin, ExpandablePlugin, AttachableExpandablePlugin, and the regular plugin. I'm not sure if there's a better way to do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, if we need to add more capability for Flex Volume in the future, may produce more type of plugin.

SELinuxRelabel bool `json:"selinuxRelabel"`
}

func defaultCapabilities() *DriverCapabilities {
return &DriverCapabilities{
Attach: true,
RequiresFSResize: true, //By default, we require file system resize which will be done by kubelet
SELinuxRelabel: true,
}
}
```

#### ExpandFS

`ExpandFS` is another method to implement `ExpandableVolumePlugin` interface. This method allows volume plugin itself instead of kubelet to resize the file system. If volume plugin returns `true` for `RequiresFSResize`, PV resize controller will leave FS resize to kubelet on worker node. Kubelet then will call FlexVolume `ExpandFS` to resize file system once physical volume expansion is done.

As `ExpandFS` is called on worker node, volume driver can also take this chance to do physical volume resize together with file system resize as well. Also, current code only supports offline FS resize, online resize support is under dicsussion [here](https://github.com/kubernetes/community/pull/1535). Once online resize is implemented, we can also leverage online resize for FlexVolume by `ExpandFS` method.

Note that `ExpandFS` is a new API for `ExpandableVolumeDriver`, the community ticket can be found [here](https://github.com/kubernetes/kubernetes/issues/58786).

`ExpandFS` will call underneath volume driver `expandfs` method to finish FS resize. The sample code looks like:
```
func (plugin *flexVolumePlugin) ExpandFS(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) error {
const timeout = 10*time.Minute

call := plugin.NewDriverCallWithTimeout(expandFSCmd, timeout)
call.Append(newSize.Value())
call.Append(oldSize.Value())
call.AppendSpec(spec, plugin.host, nil)

_, err := call.Run()

return err
}
```

For more design and details on how kubelet resizes volume file system, please refer to volume resizing proposal at:
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/grow-volume-size.md


Based on the above design, the resizing process for flex volume can be summarized as:
* If flex volume driver does not support resizing, driver shall not implement `expandvolume` method and flex volume plugin will throw out error to stop the expand volume controller's resizing process.
* If flex volume driver supports resizing, it shall implement `expandvolume` method and at least, the volume driver shall be installed on master node.
* If flex volume driver supports resizing and does not need file system resizing, it shall set "requiresFSResize" capability to `false`. Otherwise kubelet on worker node will call `ExpandFS` to resize the file system.
* If flex volume driver supports resizing and requires file system resizing(`RequiresFSResize` returns `true`), after the physical volume resizing is done, `ExpandFS` will be called from kubelet on worker node.
* If flex volume driver supports resizing and requires to resize the physical volume from worker node, the driver shall be installed on both master node and worker node. The driver on master node can do a non-op process for `ExpandVolumeDevice` and returns success message. For `RequiresFSResize`, driver on master node must return `true`. This process gives drivers on worker nodes a chance to make `physical volume resize` and `file system resize` together through `ExpandFS` call from kubelet. This scenario is useful for some local storage resizing cases.

### Volume Driver Changes

Volume driver needs to implement two new interfaces: `expandvolume` and `expandfs` to support volume resizing.

For `expandvolume`, it takes three parameters: new size of volume(number in bytes), old size of volume(number in bytes) and volume spec json string. `expandvolume` expands the physical backend volume size and return the new size(number in bytes) of volume.

For those volume plugins who need file system resize after physical volume is expanded, the `expandfs` method can take the FS resize work. If volume driver set the `requiresFSResize` capability to true, this method will be called from kubelet on worker node. Volume driver can do the file system resize (or physical volume resize together with file system resize) inside this method

In addition, those volume drivers who support resizing but do not require fils system resizing shall set `requiresFSResize` capability to `false`:
```
if [ "$op" = "init" ]; then
log '{"status": "Success", "capabilities": {“requiresFSResize”: false}}'
exit 0
fi
```

### UI

Expand FlexVolume size follows the same process as expanding other volume plugins, like GlusterFS. User creates and binds PVC and PV first. Then by using `kubectl edit pvc xxx` command, user can update the new size of PVC.

## References

* [Proposal for Growing Persistent Volume Size](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/grow-volume-size.md)
* [PR for Volume Resizing Controller](https://github.com/kubernetes/kubernetes/commit/cd2a68473a5a5966fa79f455415cb3269a3f7462)
* [Online FS resize support](https://github.com/kubernetes/community/pull/1535)
* [Add “ExpandFS” method to “ExpandableVolumePlugin” interface](https://github.com/kubernetes/kubernetes/issues/58786)