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

Bug OCPBUGS-164: OpenStack: Set minimum disk of a flavor to 100 GB #6268

Merged
merged 1 commit into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
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
8 changes: 4 additions & 4 deletions docs/user/openstack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ For a successful installation it is required:
- Server Groups: 2, plus one per additional Availability zone in each machine-pool
- RAM: 112 GB
- vCPUs: 28
- Volume Storage: 175 GB
- Volume Storage: 700 GB
- Instances: 7
- Depending on the type of [image registry backend](#image-registry-requirements) either 1 Swift container or an additional 100 GB volume.
- OpenStack resource tagging
Expand All @@ -97,21 +97,21 @@ Once you configure the quota for your project, please ensure that the user for t

### Master Nodes

The default deployment stands up 3 master nodes, which is the minimum amount required for a cluster. For each master node you stand up, you will need 1 instance, and 1 port available in your quota. They should be assigned a flavor with at least 16 GB RAM, 4 vCPUs, and 25 GB Disk (or Root Volume). It is theoretically possible to run with a smaller flavor, but be aware that if it takes too long to stand up services, or certain essential services crash, the installer could time out, leading to a failed install.
The default deployment stands up 3 master nodes, which is the minimum amount required for a cluster. For each master node you stand up, you will need 1 instance, and 1 port available in your quota. They should be assigned a flavor with at least 16 GB RAM, 4 vCPUs, and 100 GB Disk (or Root Volume). It is theoretically possible to run with a smaller flavor, but be aware that if it takes too long to stand up services, or certain essential services crash, the installer could time out, leading to a failed install.
dulek marked this conversation as resolved.
Show resolved Hide resolved

The master nodes are placed in a single Server group with "soft anti-affinity" policy by default; the machines will therefore be created on separate hosts when possible.

### Worker Nodes

The default deployment stands up 3 worker nodes. Worker nodes host the applications you run on OpenShift. The flavor assigned to the worker nodes should have at least 2 vCPUs, 8 GB RAM and 25 GB Disk (or Root Volume). However, if you are experiencing `Out Of Memory` issues, or your installs are timing out, try increasing the size of your flavor to match the master nodes: 4 vCPUs and 16 GB RAM.
The default deployment stands up 3 worker nodes. Worker nodes host the applications you run on OpenShift. The flavor assigned to the worker nodes should have at least 2 vCPUs, 8 GB RAM and 100 GB Disk (or Root Volume). However, if you are experiencing `Out Of Memory` issues, or your installs are timing out, try increasing the size of your flavor to match the master nodes: 4 vCPUs and 16 GB RAM.

The worker nodes are placed in a single Server group with "soft anti-affinity" policy by default; the machines will therefore be created on separate hosts when possible.

See the [OpenShift documentation](https://docs.openshift.com/container-platform/4.4/architecture/control-plane.html#defining-workers_control-plane) for more information on the worker nodes.

### Bootstrap Node

The bootstrap node is a temporary node that is responsible for standing up the control plane on the masters. Only one bootstrap node will be stood up and it will be deprovisioned once the production control plane is ready. To do so, you need 1 instance, and 1 port. We recommend a flavor with a minimum of 16 GB RAM, 4 vCPUs, and 25 GB Disk (or Root Volume).
The bootstrap node is a temporary node that is responsible for standing up the control plane on the masters. Only one bootstrap node will be stood up and it will be deprovisioned once the production control plane is ready. To do so, you need 1 instance, and 1 port. We recommend a flavor with a minimum of 16 GB RAM, 4 vCPUs, and 100 GB Disk (or Root Volume).

### Image Registry Requirements

Expand Down
2 changes: 1 addition & 1 deletion docs/user/openstack/deploy_sriov_workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ in OpenShift, and that your tenant has access to them. Your OpenStack cluster mu
- One instance from the RHOSP quota
- One port attached to the machines subnet
- One port for each SR-IOV Virtual Function
- A flavor with at least 16 GB memory, 4 vCPUs, and 25 GB storage space
- A flavor with at least 16 GB memory, 4 vCPUs, and 100 GB storage space

For all clusters that use single-root input/output virtualization (SR-IOV), RHOSP compute nodes require a flavor that supports [huge pages][huge-pages].
Deploying worker nodes with SR-IOV networks is supported as a post-install operation for both IPI and UPI workflows. After you verify that your OpenStack cluster can support SR-IOV in OpenShift and you install an OpenShift cluster that meets the [minimum requirements](README.md#openstack-requirements), use the following steps and examples to create worker nodes with SR-IOV NICs.
Expand Down
33 changes: 22 additions & 11 deletions pkg/asset/installconfig/openstack/validation/machinepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package validation
import (
"fmt"

"github.com/sirupsen/logrus"

"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"

Expand All @@ -11,23 +13,26 @@ import (
)

type flavorRequirements struct {
RAM, VCPUs, Disk int
RAM, VCPUs, Disk, RecommendedDisk int
}

const (
minimumStorage = 25
minimumStorage = 25
recommendedStorage = 100
)

var (
ctrlPlaneFlavorMinimums = flavorRequirements{
RAM: 16384,
VCPUs: 4,
Disk: minimumStorage,
RAM: 16384,
VCPUs: 4,
Disk: minimumStorage,
RecommendedDisk: recommendedStorage,
}
computeFlavorMinimums = flavorRequirements{
RAM: 8192,
VCPUs: 2,
Disk: minimumStorage,
RAM: 8192,
VCPUs: 2,
Disk: minimumStorage,
RecommendedDisk: recommendedStorage,
}
)

Expand All @@ -40,7 +45,9 @@ func ValidateMachinePool(p *openstack.MachinePool, ci *CloudInfo, controlPlane b
if p.RootVolume != nil {
allErrs = append(allErrs, validateVolumeTypes(p.RootVolume.Type, ci.VolumeTypes, fldPath.Child("rootVolume").Child("type"))...)
if p.RootVolume.Size < minimumStorage {
allErrs = append(allErrs, field.Invalid(fldPath.Child("rootVolume").Child("size"), p.RootVolume.Size, fmt.Sprintf("Volume size must be greater than %d to use root volumes, had %d", minimumStorage, p.RootVolume.Size)))
allErrs = append(allErrs, field.Invalid(fldPath.Child("rootVolume").Child("size"), p.RootVolume.Size, fmt.Sprintf("Volume size must be greater than %d GB to use root volumes, had %d GB", minimumStorage, p.RootVolume.Size)))
} else if p.RootVolume.Size < recommendedStorage {
logrus.Warnf("Volume size is recommended to be greater than %d GB to use root volumes, had %d GB", recommendedStorage, p.RootVolume.Size)
}

allErrs = append(allErrs, validateZones(p.RootVolume.Zones, ci.VolumeZones, fldPath.Child("rootVolume").Child("zones"))...)
Expand Down Expand Up @@ -149,8 +156,12 @@ func validateFlavor(flavorName string, ci *CloudInfo, req flavorRequirements, fl
if flavor.VCPUs < req.VCPUs {
errs = append(errs, fmt.Sprintf("Must have minimum of %d VCPUs, had %d", req.VCPUs, flavor.VCPUs))
}
if flavor.Disk < req.Disk && storage {
errs = append(errs, fmt.Sprintf("Must have minimum of %d GB Disk, had %d GB", req.Disk, flavor.Disk))
if storage {
if flavor.Disk < req.Disk {
errs = append(errs, fmt.Sprintf("Must have minimum of %d GB Disk, had %d GB", req.Disk, flavor.Disk))
} else if flavor.Disk < req.RecommendedDisk {
logrus.Warnf("Flavor does not meet the following recommended requirements: It is recommended to have %d GB Disk, had %d GB", req.RecommendedDisk, flavor.Disk)
}
}

if len(errs) == 0 {
Expand Down
98 changes: 84 additions & 14 deletions pkg/asset/installconfig/openstack/validation/machinepool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/openshift/installer/pkg/types/openstack"
logrusTest "github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/util/validation/field"

Expand All @@ -21,13 +22,16 @@ const (

invalidComputeFlavor = "invalid-compute-flavor"
invalidCtrlPlaneFlavor = "invalid-control-plane-flavor"
warningComputeFlavor = "warning-compute-flavor"
warningCtrlPlaneFlavor = "warning-control-plane-flavor"

baremetalFlavor = "baremetal-flavor"

volumeType = "performance"
invalidType = "invalid-type"
volumeSmallSize = 10
volumeLargeSize = 25
volumeType = "performance"
invalidType = "invalid-type"
volumeSmallSize = 10
volumeMediumSize = 40
volumeLargeSize = 100
)

func validMachinePool() *openstack.MachinePool {
Expand All @@ -49,6 +53,18 @@ func invalidMachinePoolSmallVolume() *openstack.MachinePool {
}
}

func warningMachinePoolMediumVolume() *openstack.MachinePool {
return &openstack.MachinePool{
FlavorName: validCtrlPlaneFlavor,
Zones: []string{""},
RootVolume: &openstack.RootVolume{
Type: volumeType,
Size: volumeMediumSize,
Zones: []string{""},
},
}
}

func validMachinePoolLargeVolume() *openstack.MachinePool {
return &openstack.MachinePool{
FlavorName: validCtrlPlaneFlavor,
Expand All @@ -68,23 +84,23 @@ func validMpoolCloudInfo() *CloudInfo {
Flavor: flavors.Flavor{
Name: validCtrlPlaneFlavor,
RAM: 16384,
Disk: 25,
Disk: 100,
VCPUs: 4,
},
},
validComputeFlavor: {
Flavor: flavors.Flavor{
Name: validComputeFlavor,
RAM: 8192,
Disk: 25,
Disk: 100,
VCPUs: 2,
},
},
invalidCtrlPlaneFlavor: {
Flavor: flavors.Flavor{
Name: invalidCtrlPlaneFlavor,
RAM: 8192, // too low
Disk: 25,
Disk: 100,
VCPUs: 2, // too low
},
},
Expand All @@ -96,6 +112,22 @@ func validMpoolCloudInfo() *CloudInfo {
VCPUs: 2,
},
},
warningCtrlPlaneFlavor: {
Flavor: flavors.Flavor{
Name: warningCtrlPlaneFlavor,
RAM: 16384,
Disk: 40, // not recommended
VCPUs: 4,
},
},
warningComputeFlavor: {
Flavor: flavors.Flavor{
Name: invalidComputeFlavor,
RAM: 8192,
Disk: 40, // not recommended
VCPUs: 2,
},
},
baremetalFlavor: {
Flavor: flavors.Flavor{
Name: baremetalFlavor,
Expand All @@ -120,12 +152,13 @@ func validMpoolCloudInfo() *CloudInfo {

func TestOpenStackMachinepoolValidation(t *testing.T) {
cases := []struct {
name string
controlPlane bool // only matters for flavor
mpool *openstack.MachinePool
cloudInfo *CloudInfo
expectedError bool
expectedErrMsg string // NOTE: this is a REGEXP
name string
controlPlane bool // only matters for flavor
mpool *openstack.MachinePool
cloudInfo *CloudInfo
expectedError bool
expectedErrMsg string // NOTE: this is a REGEXP
expectedWarnMsg string //NOTE: this is a REGEXP
}{
{
name: "valid control plane",
Expand Down Expand Up @@ -232,6 +265,28 @@ func TestOpenStackMachinepoolValidation(t *testing.T) {
expectedError: true,
expectedErrMsg: `compute\[0\].platform.openstack.type: Invalid value: "invalid-compute-flavor": Flavor did not meet the following minimum requirements: Must have minimum of 25 GB Disk, had 10 GB`,
},
{
name: "warning control plane flavorName",
controlPlane: true,
mpool: func() *openstack.MachinePool {
mp := validMachinePool()
mp.FlavorName = warningCtrlPlaneFlavor
return mp
}(),
cloudInfo: validMpoolCloudInfo(),
expectedWarnMsg: `Flavor does not meet the following recommended requirements: It is recommended to have 100 GB Disk, had 40 GB`,
},
{
name: "warning compute flavorName",
controlPlane: false,
mpool: func() *openstack.MachinePool {
mp := validMachinePool()
mp.FlavorName = warningComputeFlavor
return mp
}(),
cloudInfo: validMpoolCloudInfo(),
expectedWarnMsg: `Flavor does not meet the following recommended requirements: It is recommended to have 100 GB Disk, had 40 GB`,
},
{
name: "valid baremetal compute",
controlPlane: false,
Expand All @@ -254,7 +309,18 @@ func TestOpenStackMachinepoolValidation(t *testing.T) {
}(),
cloudInfo: validMpoolCloudInfo(),
expectedError: true,
expectedErrMsg: "Volume size must be greater than 25 to use root volumes, had 10",
expectedErrMsg: "Volume size must be greater than 25 GB to use root volumes, had 10 GB",
},
{
name: "volume not recommended",
controlPlane: false,
mpool: func() *openstack.MachinePool {
mp := warningMachinePoolMediumVolume()
mp.FlavorName = invalidCtrlPlaneFlavor
return mp
}(),
cloudInfo: validMpoolCloudInfo(),
expectedWarnMsg: "Volume size is recommended to be greater than 100 GB to use root volumes, had 40 GB",
},
{
name: "volume big enough",
Expand Down Expand Up @@ -338,12 +404,16 @@ func TestOpenStackMachinepoolValidation(t *testing.T) {
fieldPath = field.NewPath("compute").Index(0).Child("platform", "openstack")
}

hook := logrusTest.NewGlobal()
aggregatedErrors := ValidateMachinePool(tc.mpool, tc.cloudInfo, tc.controlPlane, fieldPath).ToAggregate()
if tc.expectedError {
assert.Regexp(t, tc.expectedErrMsg, aggregatedErrors)
} else {
assert.NoError(t, aggregatedErrors)
}
if len(tc.expectedWarnMsg) > 0 {
assert.Regexp(t, tc.expectedWarnMsg, hook.LastEntry().Message)
}
})
}
}