mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-09 01:26:30 +00:00
Preserve nodePort support with --preserve-nodeports flag (#3095)
* -> Preserve nodePort support when restoring via "--preserve-nodeports" flag Signed-off-by: Yusuf Güngör <yusuf.gungor@hepsiburada.com> * -> Added changelog. Signed-off-by: Yusuf Güngör <yusuf.gungor@hepsiburada.com> * -> Unit test added. -> Using boolptr.IsSetToTrue for bool ptr check. Signed-off-by: Yusuf Güngör <yusuf.gungor@hepsiburada.com> * -> Unit test added. -> Using boolptr.IsSetToTrue for bool ptr check. Signed-off-by: Yusuf Güngör <yusuf.gungor@hepsiburada.com> * -> Other restore errors log level changed from info to error. -> Documentation updated about Velero nodePort restore logic and preservation of them. Signed-off-by: Yusuf Güngör <yusuf.gungor@hepsiburada.com> Co-authored-by: Yusuf Güngör <yusuf.gungor@hepsiburada.com>
This commit is contained in:
co-authored by
Yusuf Güngör
parent
d09b4d60bb
commit
3b2e9036d1
@@ -0,0 +1 @@
|
||||
Added "--preserve-nodeports" flag to preserve original nodePorts when restoring.
|
||||
@@ -1574,6 +1574,11 @@ spec:
|
||||
target namespace names to restore into. Any source namespaces not
|
||||
included in the map will be restored into namespaces of the same name.
|
||||
type: object
|
||||
preserveNodePorts:
|
||||
description: PreserveNodePorts specifies whether to restore old nodePorts
|
||||
from backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
restorePVs:
|
||||
description: RestorePVs specifies whether to restore all included PVs
|
||||
from snapshot (via the cloudprovider).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -679,6 +679,10 @@ spec:
|
||||
PVs from snapshot (via the cloudprovider).
|
||||
nullable: true
|
||||
type: boolean
|
||||
preserveNodePorts:
|
||||
description: PreserveNodePorts specifies whether to restore old nodePorts from backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
scheduleName:
|
||||
description: ScheduleName is the unique name of the Velero schedule
|
||||
to restore from. If specified, and BackupName is empty, Velero will
|
||||
|
||||
@@ -77,6 +77,11 @@ type RestoreSpec struct {
|
||||
// +nullable
|
||||
RestorePVs *bool `json:"restorePVs,omitempty"`
|
||||
|
||||
// PreserveNodePorts specifies whether to restore old nodePorts from backup.
|
||||
// +optional
|
||||
// +nullable
|
||||
PreserveNodePorts *bool `json:"preserveNodePorts,omitempty"`
|
||||
|
||||
// IncludeClusterResources specifies whether cluster-scoped resources
|
||||
// should be included for consideration in the restore. If null, defaults
|
||||
// to true.
|
||||
|
||||
@@ -1272,6 +1272,11 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) {
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.PreserveNodePorts != nil {
|
||||
in, out := &in.PreserveNodePorts, &out.PreserveNodePorts
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.IncludeClusterResources != nil {
|
||||
in, out := &in.IncludeClusterResources, &out.IncludeClusterResources
|
||||
*out = new(bool)
|
||||
|
||||
@@ -136,6 +136,12 @@ func (b *RestoreBuilder) RestorePVs(val bool) *RestoreBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
// PreserveNodePorts sets the Restore's preserved NodePorts.
|
||||
func (b *RestoreBuilder) PreserveNodePorts(val bool) *RestoreBuilder {
|
||||
b.object.Spec.PreserveNodePorts = &val
|
||||
return b
|
||||
}
|
||||
|
||||
// StartTimestamp sets the Restore's start timestamp.
|
||||
func (b *RestoreBuilder) StartTimestamp(val time.Time) *RestoreBuilder {
|
||||
b.object.Status.StartTimestamp = &metav1.Time{Time: val}
|
||||
|
||||
@@ -78,6 +78,7 @@ type CreateOptions struct {
|
||||
ScheduleName string
|
||||
RestoreName string
|
||||
RestoreVolumes flag.OptionalBool
|
||||
PreserveNodePorts flag.OptionalBool
|
||||
Labels flag.Map
|
||||
IncludeNamespaces flag.StringArray
|
||||
ExcludeNamespaces flag.StringArray
|
||||
@@ -98,6 +99,7 @@ func NewCreateOptions() *CreateOptions {
|
||||
IncludeNamespaces: flag.NewStringArray("*"),
|
||||
NamespaceMappings: flag.NewMap().WithEntryDelimiter(",").WithKeyValueDelimiter(":"),
|
||||
RestoreVolumes: flag.NewOptionalBool(nil),
|
||||
PreserveNodePorts: flag.NewOptionalBool(nil),
|
||||
IncludeClusterResources: flag.NewOptionalBool(nil),
|
||||
}
|
||||
}
|
||||
@@ -117,6 +119,11 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
// like a normal bool flag
|
||||
f.NoOptDefVal = "true"
|
||||
|
||||
f = flags.VarPF(&o.PreserveNodePorts, "preserve-nodeports", "", "Whether to preserve nodeports of Services when restoring.")
|
||||
// this allows the user to just specify "--preserve-nodeports" as shorthand for "--preserve-nodeports=true"
|
||||
// like a normal bool flag
|
||||
f.NoOptDefVal = "true"
|
||||
|
||||
f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "Include cluster-scoped resources in the restore.")
|
||||
f.NoOptDefVal = "true"
|
||||
|
||||
@@ -260,6 +267,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
|
||||
NamespaceMapping: o.NamespaceMappings.Data(),
|
||||
LabelSelector: o.Selector.LabelSelector,
|
||||
RestorePVs: o.RestoreVolumes.Value,
|
||||
PreserveNodePorts: o.PreserveNodePorts.Value,
|
||||
IncludeClusterResources: o.IncludeClusterResources.Value,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -127,6 +127,10 @@ func DescribeRestore(restore *v1.Restore, podVolumeRestores []v1.PodVolumeRestor
|
||||
d.Println()
|
||||
describePodVolumeRestores(d, podVolumeRestores, details)
|
||||
}
|
||||
|
||||
d.Println()
|
||||
d.Printf("Preserve Service NodePorts:\t%s\n", BoolPointerString(restore.Spec.PreserveNodePorts, "false", "true", "auto"))
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1167,7 +1167,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
|
||||
// Error was something other than an AlreadyExists
|
||||
if restoreErr != nil {
|
||||
ctx.log.Infof("error restoring %s: %v", name, restoreErr)
|
||||
ctx.log.Errorf("error restoring %s: %+v", name, restoreErr)
|
||||
errs.Add(namespace, fmt.Errorf("error restoring %s: %v", resourceID, restoreErr))
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
)
|
||||
|
||||
const annotationLastAppliedConfig = "kubectl.kubernetes.io/last-applied-configuration"
|
||||
@@ -55,8 +56,13 @@ func (a *ServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*v
|
||||
service.Spec.ClusterIP = ""
|
||||
}
|
||||
|
||||
if err := deleteNodePorts(service); err != nil {
|
||||
return nil, err
|
||||
/* Do not delete NodePorts if restore triggered with "--preserve-nodeports" flag */
|
||||
if boolptr.IsSetToTrue(input.Restore.Spec.PreserveNodePorts) {
|
||||
a.log.Info("Restoring Services with original NodePort(s)")
|
||||
} else {
|
||||
if err := deleteNodePorts(service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
res, err := runtime.DefaultUnstructuredConverter.ToUnstructured(service)
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
@@ -51,6 +53,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
obj corev1api.Service
|
||||
restore *api.Restore
|
||||
expectedErr bool
|
||||
expectedRes corev1api.Service
|
||||
}{
|
||||
@@ -65,6 +68,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
LoadBalancerIP: "should-be-kept",
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
|
||||
expectedErr: false,
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -85,6 +89,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
ClusterIP: "None",
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
@@ -113,6 +118,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
@@ -146,6 +152,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
@@ -177,6 +184,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
@@ -210,6 +218,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
@@ -246,6 +255,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").Result(),
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
@@ -266,6 +276,46 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "If PreserveNodePorts is True in restore spec then nodePort always preserved.",
|
||||
obj: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
},
|
||||
Spec: corev1api.ServiceSpec{
|
||||
Ports: []corev1api.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
NodePort: 8080,
|
||||
},
|
||||
{
|
||||
Name: "hepsiburada",
|
||||
NodePort: 9025,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
restore: builder.ForRestore(api.DefaultNamespace, "").PreserveNodePorts(true).Result(),
|
||||
expectedRes: corev1api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc-1",
|
||||
},
|
||||
Spec: corev1api.ServiceSpec{
|
||||
Ports: []corev1api.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
NodePort: 8080,
|
||||
},
|
||||
{
|
||||
Name: "hepsiburada",
|
||||
NodePort: 9025,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -278,7 +328,7 @@ func TestServiceActionExecute(t *testing.T) {
|
||||
res, err := action.Execute(&velero.RestoreItemActionExecuteInput{
|
||||
Item: &unstructured.Unstructured{Object: unstructuredSvc},
|
||||
ItemFromBackup: &unstructured.Unstructured{Object: unstructuredSvc},
|
||||
Restore: nil,
|
||||
Restore: test.restore,
|
||||
})
|
||||
|
||||
if assert.Equal(t, test.expectedErr, err != nil) && !test.expectedErr {
|
||||
|
||||
@@ -46,6 +46,8 @@ Label selector: <none>
|
||||
|
||||
Restore PVs: auto
|
||||
|
||||
Preserve Service NodePorts: auto
|
||||
|
||||
Phase: Completed
|
||||
|
||||
Validation errors: <none>
|
||||
|
||||
@@ -36,6 +36,56 @@ Available Commands:
|
||||
logs Get restore logs
|
||||
```
|
||||
|
||||
## What happens to NodePorts when restoring Services
|
||||
|
||||
**Auto assigned** NodePorts **deleted** by default and Services get new **auto assigned** nodePorts after restore.
|
||||
|
||||
**Explicitly specified** NodePorts auto detected using **`last-applied-config`** annotation and **preserved** after restore. NodePorts can be explicitly specified as .spec.ports[*].nodePort field on Service definition.
|
||||
|
||||
#### Always Preserve NodePorts
|
||||
|
||||
It is not always possible to set nodePorts explicitly on some big clusters because of operation complexity. Official Kubernetes documents states that preventing port collisions is responsibility of the user when explicitly specifying nodePorts:
|
||||
|
||||
```
|
||||
If you want a specific port number, you can specify a value in the `nodePort` field. The control plane will either allocate you that port or report that the API transaction failed. This means that you need to take care of possible port collisions yourself. You also have to use a valid port number, one that's inside the range configured for NodePort use.
|
||||
|
||||
https://kubernetes.io/docs/concepts/services-networking/service/#nodeport
|
||||
```
|
||||
|
||||
The clusters which are not explicitly specifying nodePorts still may need to restore original NodePorts in case of disaster. Auto assigned nodePorts most probably defined on Load Balancers which located front side of cluster. Changing all these nodePorts on Load Balancers is another operation complexity after disaster if nodePorts are changed.
|
||||
|
||||
Velero has a flag to let user deciding the preservation of nodePorts. **`velero restore create`** sub command has **`--preserve-nodeports`** flag to **preserve** Service nodePorts **always** regardless of nodePorts **explicitly specified** or **not**. This flag used for preserving the original nodePorts from backup and can be used as **`--preserve-nodeports`** or **`--preserve-nodeports=true`**
|
||||
|
||||
If this flag given and/or set to true, Velero does not remove the nodePorts when restoring Service and tries to use the nodePorts which written on backup.
|
||||
|
||||
Trying to preserve nodePorts may cause **port conflicts** when restoring on situations below:
|
||||
|
||||
- If the nodePort from the backup already allocated on the target cluster then Velero prints error log as shown below and continue to restore operation.
|
||||
|
||||
```
|
||||
time="2020-11-23T12:58:31+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123125825
|
||||
|
||||
time="2020-11-23T12:58:31+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123125825
|
||||
|
||||
time="2020-11-23T12:58:31+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123125825
|
||||
|
||||
time="2020-11-23T12:58:31+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is already allocated" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123125825
|
||||
```
|
||||
|
||||
|
||||
|
||||
- If the nodePort from the backup is not in the nodePort range of target cluster then Velero prints error log as below and continue to restore operation. Kubernetes default nodePort range is 30000-32767 but on the example cluster nodePort range is 20000-22767 and tried to restore Service with nodePort 31536
|
||||
|
||||
```
|
||||
time="2020-11-23T13:09:17+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123130915
|
||||
|
||||
time="2020-11-23T13:09:17+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123130915
|
||||
|
||||
time="2020-11-23T13:09:17+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123130915
|
||||
|
||||
time="2020-11-23T13:09:17+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is not in the valid range. The range of valid ports is 20000-22767" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123130915
|
||||
```
|
||||
|
||||
## Changing PV/PVC Storage Classes
|
||||
|
||||
Velero can change the storage class of persistent volumes and persistent volume claims during restores. To configure a storage class mapping, create a config map in the Velero namespace like the following:
|
||||
|
||||
@@ -46,6 +46,8 @@ Label selector: <none>
|
||||
|
||||
Restore PVs: auto
|
||||
|
||||
Preserve Service NodePorts: auto
|
||||
|
||||
Phase: Completed
|
||||
|
||||
Validation errors: <none>
|
||||
|
||||
@@ -36,6 +36,56 @@ Available Commands:
|
||||
logs Get restore logs
|
||||
```
|
||||
|
||||
## What happens to NodePorts when restoring Services
|
||||
|
||||
**Auto assigned** NodePorts **deleted** by default and Services get new **auto assigned** nodePorts after restore.
|
||||
|
||||
**Explicitly specified** NodePorts auto detected using **`last-applied-config`** annotation and **preserved** after restore. NodePorts can be explicitly specified as .spec.ports[*].nodePort field on Service definition.
|
||||
|
||||
#### Always Preserve NodePorts
|
||||
|
||||
It is not always possible to set nodePorts explicitly on some big clusters because of operation complexity. Official Kubernetes documents states that preventing port collisions is responsibility of the user when explicitly specifying nodePorts:
|
||||
|
||||
```
|
||||
If you want a specific port number, you can specify a value in the `nodePort` field. The control plane will either allocate you that port or report that the API transaction failed. This means that you need to take care of possible port collisions yourself. You also have to use a valid port number, one that's inside the range configured for NodePort use.
|
||||
|
||||
https://kubernetes.io/docs/concepts/services-networking/service/#nodeport
|
||||
```
|
||||
|
||||
The clusters which are not explicitly specifying nodePorts still may need to restore original NodePorts in case of disaster. Auto assigned nodePorts most probably defined on Load Balancers which located front side of cluster. Changing all these nodePorts on Load Balancers is another operation complexity after disaster if nodePorts are changed.
|
||||
|
||||
Velero has a flag to let user deciding the preservation of nodePorts. **`velero restore create`** sub command has **`--preserve-nodeports`** flag to **preserve** Service nodePorts **always** regardless of nodePorts **explicitly specified** or **not**. This flag used for preserving the original nodePorts from backup and can be used as **`--preserve-nodeports`** or **`--preserve-nodeports=true`**
|
||||
|
||||
If this flag given and/or set to true, Velero does not remove the nodePorts when restoring Service and tries to use the nodePorts which written on backup.
|
||||
|
||||
Trying to preserve nodePorts may cause **port conflicts** when restoring on situations below:
|
||||
|
||||
- If the nodePort from the backup already allocated on the target cluster then Velero prints error log as shown below and continue to restore operation.
|
||||
|
||||
```
|
||||
time="2020-11-23T12:58:31+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123125825
|
||||
|
||||
time="2020-11-23T12:58:31+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123125825
|
||||
|
||||
time="2020-11-23T12:58:31+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123125825
|
||||
|
||||
time="2020-11-23T12:58:31+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is already allocated" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123125825
|
||||
```
|
||||
|
||||
|
||||
|
||||
- If the nodePort from the backup is not in the nodePort range of target cluster then Velero prints error log as below and continue to restore operation. Kubernetes default nodePort range is 30000-32767 but on the example cluster nodePort range is 20000-22767 and tried to restore Service with nodePort 31536
|
||||
|
||||
```
|
||||
time="2020-11-23T13:09:17+03:00" level=info msg="Executing item action for services" logSource="pkg/restore/restore.go:1002" restore=velero/test-with-3-svc-20201123130915
|
||||
|
||||
time="2020-11-23T13:09:17+03:00" level=info msg="Restoring Services with original NodePort(s)" cmd=_output/bin/linux/amd64/velero logSource="pkg/restore/service_action.go:61" pluginName=velero restore=velero/test-with-3-svc-20201123130915
|
||||
|
||||
time="2020-11-23T13:09:17+03:00" level=info msg="Attempting to restore Service: hello-service" logSource="pkg/restore/restore.go:1107" restore=velero/test-with-3-svc-20201123130915
|
||||
|
||||
time="2020-11-23T13:09:17+03:00" level=error msg="error restoring hello-service: Service \"hello-service\" is invalid: spec.ports[0].nodePort: Invalid value: 31536: provided port is not in the valid range. The range of valid ports is 20000-22767" logSource="pkg/restore/restore.go:1170" restore=velero/test-with-3-svc-20201123130915
|
||||
```
|
||||
|
||||
## Changing PV/PVC Storage Classes
|
||||
|
||||
Velero can change the storage class of persistent volumes and persistent volume claims during restores. To configure a storage class mapping, create a config map in the Velero namespace like the following:
|
||||
|
||||
Reference in New Issue
Block a user