Support online volume expansion
Summary: Online volume expansion is a 2 phase process: 1. The backing storage, in this case the raw file, needs to be resized. (i.e. `truncate -s`) 2. The node should be notified, so that it can both refresh its device capacity (i.e. `losetup -c`) and resize the filesystem (`resize2fs`) accordingly. Although in our case both steps could be performed on the node itself, for the sake of following the semantics of how volume expansion works, we perform step 1 from the controller, and step 2 from the node. Also, the `external-resizer` component is added which watches for PVC size updates, and notifies the CSI controller about it. Test Plan: Setup: - Deploy - Create a rawfile-backed pvc, and attach a Deployment to it - Keep an eye on `rawfile` pod logs in `kube-system` namespace to see if any errors pop out during all scenarios Scenario 1: - Increase the size of the pvc - Exec into the pod and verify that the volume is resized indeed (using `df`) Scenario 2: - Decrease deployment's replica to 0 - Increase the size of the pvc. Wait for a couple of minutes. - Increase deployment's replica to 1 - Exec into the pod and verify that the volume is resized indeed. Reviewers: bghadiri, mhyousefi, h.marvi, sina_rad Reviewed By: bghadiri, mhyousefi, sina_rad Differential Revision: https://phab.hamravesh.ir/D817
This commit is contained in:
parent
be2cd1b72c
commit
d1c0d49cf0
@ -20,6 +20,7 @@ metadata:
|
|||||||
provisioner: rawfile.hamravesh.com
|
provisioner: rawfile.hamravesh.com
|
||||||
reclaimPolicy: Delete
|
reclaimPolicy: Delete
|
||||||
volumeBindingMode: WaitForFirstConsumer
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
allowVolumeExpansion: true
|
||||||
```
|
```
|
||||||
|
|
||||||
Features
|
Features
|
||||||
@ -38,7 +39,7 @@ Features
|
|||||||
- [ ] `Block` mode
|
- [ ] `Block` mode
|
||||||
- [x] Volume metrics
|
- [x] Volume metrics
|
||||||
- [ ] Supports fsTypes
|
- [ ] Supports fsTypes
|
||||||
- [ ] Online expansion: If fs supports it (e.g. ext4, btrfs)
|
- [x] Online expansion: If fs supports it (e.g. ext4, btrfs)
|
||||||
- [ ] Online shrinking: If fs supports it (e.g. btrfs)
|
- [ ] Online shrinking: If fs supports it (e.g. btrfs)
|
||||||
- [ ] Offline expansion/shrinking
|
- [ ] Offline expansion/shrinking
|
||||||
- [ ] Ephemeral inline volume
|
- [ ] Ephemeral inline volume
|
||||||
|
@ -2,5 +2,5 @@ apiVersion: v2
|
|||||||
name: rawfile-csi
|
name: rawfile-csi
|
||||||
description: RawFile Driver Container Storage Interface
|
description: RawFile Driver Container Storage Interface
|
||||||
type: application
|
type: application
|
||||||
version: 0.1.3
|
version: 0.1.4
|
||||||
appVersion: 0.0.1
|
appVersion: 0.0.1
|
||||||
|
@ -75,3 +75,40 @@ roleRef:
|
|||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
name: {{ include "rawfile-csi.fullname" . }}-broker
|
name: {{ include "rawfile-csi.fullname" . }}-broker
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
|
kind: ClusterRole
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: {{ include "rawfile-csi.fullname" . }}-resizer
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["secrets"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["persistentvolumes"]
|
||||||
|
verbs: ["get", "list", "watch", "patch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["persistentvolumeclaims"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["persistentvolumeclaims/status"]
|
||||||
|
verbs: ["patch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["events"]
|
||||||
|
verbs: ["list", "watch", "create", "update", "patch"]
|
||||||
|
---
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: {{ include "rawfile-csi.fullname" . }}-resizer
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: {{ include "rawfile-csi.fullname" . }}-driver
|
||||||
|
namespace: {{ .Release.Namespace }}
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: {{ include "rawfile-csi.fullname" . }}-resizer
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
@ -64,3 +64,14 @@ spec:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: socket-dir
|
- name: socket-dir
|
||||||
mountPath: /csi
|
mountPath: /csi
|
||||||
|
- name: csi-resizer
|
||||||
|
image: quay.io/k8scsi/csi-resizer:v0.5.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- "--csi-address=$(ADDRESS)"
|
||||||
|
env:
|
||||||
|
- name: ADDRESS
|
||||||
|
value: /csi/csi.sock
|
||||||
|
volumeMounts:
|
||||||
|
- name: socket-dir
|
||||||
|
mountPath: /csi
|
||||||
|
@ -7,7 +7,7 @@ import rawfile_util
|
|||||||
from csi import csi_pb2, csi_pb2_grpc
|
from csi import csi_pb2, csi_pb2_grpc
|
||||||
from orchestrator.k8s import volume_to_node, run_on_node
|
from orchestrator.k8s import volume_to_node, run_on_node
|
||||||
from rawfile_util import attach_loop, detach_loops
|
from rawfile_util import attach_loop, detach_loops
|
||||||
from remote import init_rawfile, scrub
|
from remote import init_rawfile, scrub, expand_rawfile
|
||||||
from util import log_grpc_request, run
|
from util import log_grpc_request, run
|
||||||
|
|
||||||
NODE_NAME_TOPOLOGY_KEY = "hostname"
|
NODE_NAME_TOPOLOGY_KEY = "hostname"
|
||||||
@ -31,6 +31,11 @@ class RawFileIdentityServicer(csi_pb2_grpc.IdentityServicer):
|
|||||||
type=Cap.Service.VOLUME_ACCESSIBILITY_CONSTRAINTS
|
type=Cap.Service.VOLUME_ACCESSIBILITY_CONSTRAINTS
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
Cap(
|
||||||
|
volume_expansion=Cap.VolumeExpansion(
|
||||||
|
type=Cap.VolumeExpansion.ONLINE
|
||||||
|
)
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,7 +52,10 @@ class RawFileNodeServicer(csi_pb2_grpc.NodeServicer):
|
|||||||
def NodeGetCapabilities(self, request, context):
|
def NodeGetCapabilities(self, request, context):
|
||||||
Cap = csi_pb2.NodeServiceCapability
|
Cap = csi_pb2.NodeServiceCapability
|
||||||
return csi_pb2.NodeGetCapabilitiesResponse(
|
return csi_pb2.NodeGetCapabilitiesResponse(
|
||||||
capabilities=[Cap(rpc=Cap.RPC(type=Cap.RPC.STAGE_UNSTAGE_VOLUME))]
|
capabilities=[
|
||||||
|
Cap(rpc=Cap.RPC(type=Cap.RPC.STAGE_UNSTAGE_VOLUME)),
|
||||||
|
Cap(rpc=Cap.RPC(type=Cap.RPC.EXPAND_VOLUME)),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@log_grpc_request
|
@log_grpc_request
|
||||||
@ -100,13 +108,28 @@ class RawFileNodeServicer(csi_pb2_grpc.NodeServicer):
|
|||||||
detach_loops(img_file)
|
detach_loops(img_file)
|
||||||
return csi_pb2.NodeUnstageVolumeResponse()
|
return csi_pb2.NodeUnstageVolumeResponse()
|
||||||
|
|
||||||
|
@log_grpc_request
|
||||||
|
def NodeExpandVolume(self, request, context):
|
||||||
|
volume_id = request.volume_id
|
||||||
|
size = request.capacity_range.required_bytes
|
||||||
|
img_file = rawfile_util.img_file(volume_id)
|
||||||
|
for dev in rawfile_util.attached_loops(img_file):
|
||||||
|
run(f"losetup -c {dev}")
|
||||||
|
if True: # TODO: is ext2/ext3/ext4
|
||||||
|
run(f"resize2fs {dev}")
|
||||||
|
break
|
||||||
|
return csi_pb2.NodeExpandVolumeResponse(capacity_bytes=size)
|
||||||
|
|
||||||
|
|
||||||
class RawFileControllerServicer(csi_pb2_grpc.ControllerServicer):
|
class RawFileControllerServicer(csi_pb2_grpc.ControllerServicer):
|
||||||
@log_grpc_request
|
@log_grpc_request
|
||||||
def ControllerGetCapabilities(self, request, context):
|
def ControllerGetCapabilities(self, request, context):
|
||||||
Cap = csi_pb2.ControllerServiceCapability
|
Cap = csi_pb2.ControllerServiceCapability
|
||||||
return csi_pb2.ControllerGetCapabilitiesResponse(
|
return csi_pb2.ControllerGetCapabilitiesResponse(
|
||||||
capabilities=[Cap(rpc=Cap.RPC(type=Cap.RPC.CREATE_DELETE_VOLUME))]
|
capabilities=[
|
||||||
|
Cap(rpc=Cap.RPC(type=Cap.RPC.CREATE_DELETE_VOLUME)),
|
||||||
|
Cap(rpc=Cap.RPC(type=Cap.RPC.EXPAND_VOLUME)),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@log_grpc_request
|
@log_grpc_request
|
||||||
@ -177,3 +200,16 @@ class RawFileControllerServicer(csi_pb2_grpc.ControllerServicer):
|
|||||||
node_name = volume_to_node(request.volume_id)
|
node_name = volume_to_node(request.volume_id)
|
||||||
run_on_node(scrub.as_cmd(volume_id=request.volume_id), node=node_name)
|
run_on_node(scrub.as_cmd(volume_id=request.volume_id), node=node_name)
|
||||||
return csi_pb2.DeleteVolumeResponse()
|
return csi_pb2.DeleteVolumeResponse()
|
||||||
|
|
||||||
|
@log_grpc_request
|
||||||
|
def ControllerExpandVolume(self, request, context):
|
||||||
|
volume_id = request.volume_id
|
||||||
|
node_name = volume_to_node(volume_id)
|
||||||
|
size = request.capacity_range.required_bytes
|
||||||
|
run_on_node(
|
||||||
|
expand_rawfile.as_cmd(volume_id=volume_id, size=size), node=node_name
|
||||||
|
)
|
||||||
|
|
||||||
|
return csi_pb2.ControllerExpandVolumeResponse(
|
||||||
|
capacity_bytes=size, node_expansion_required=True,
|
||||||
|
)
|
||||||
|
12
remote.py
12
remote.py
@ -29,3 +29,15 @@ def init_rawfile(volume_id, size):
|
|||||||
)
|
)
|
||||||
run(f"truncate -s {size} {img_file}")
|
run(f"truncate -s {size} {img_file}")
|
||||||
run(f"mkfs.ext4 {img_file}")
|
run(f"mkfs.ext4 {img_file}")
|
||||||
|
|
||||||
|
|
||||||
|
@remote_fn
|
||||||
|
def expand_rawfile(volume_id, size):
|
||||||
|
import rawfile_util
|
||||||
|
from util import run
|
||||||
|
|
||||||
|
img_file = rawfile_util.img_file(volume_id)
|
||||||
|
rawfile_util.patch_metadata(
|
||||||
|
volume_id, {"size": size},
|
||||||
|
)
|
||||||
|
run(f"truncate -s {size} {img_file}")
|
||||||
|
Loading…
Reference in New Issue
Block a user