Skip to content

Commit

Permalink
Implement resource constraints on nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
aojea committed Dec 10, 2019
1 parent 69e0c92 commit 547f356
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 1 deletion.
15 changes: 15 additions & 0 deletions pkg/apis/config/v1alpha4/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ limitations under the License.

package v1alpha4

import (
"k8s.io/apimachinery/pkg/api/resource"
)

// Cluster contains kind cluster configuration
type Cluster struct {
TypeMeta `yaml:",inline"`
Expand Down Expand Up @@ -100,6 +104,9 @@ type Node struct {
// binded to a host Port
ExtraPortMappings []PortMapping `yaml:"extraPortMappings,omitempty"`

// Constraints describes the node resources constraints
Constraints NodeResources `json:"constraints,omitempty"`

// KubeadmConfigPatches are applied to the generated kubeadm config as
// merge patches. The `kind` field must match the target object, and
// if `apiVersion` is specified it will only be applied to matching objects.
Expand Down Expand Up @@ -265,3 +272,11 @@ const (
// PortMappingProtocolSCTP specifies SCTP protocol
PortMappingProtocolSCTP PortMappingProtocol = "SCTP"
)

// NodeResources represents the node resources (CPU/Memory)
type NodeResources struct {
// The maximum amount of memory the node can use.
Memory resource.Quantity `json:"memory,omitempty"`
// Specify how much of the available CPU resources a node can use
Cpus resource.Quantity `json:"cpus,omitempty"`
}
19 changes: 19 additions & 0 deletions pkg/apis/config/v1alpha4/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 20 additions & 1 deletion pkg/cluster/internal/providers/docker/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,10 @@ func runArgsForNode(node *config.Node, name string, args []string) []string {
args...,
)

// convert mounts and port mappings to container run args
// convert mounts, port mappings and resource constraints to container run args
args = append(args, generateMountBindings(node.ExtraMounts...)...)
args = append(args, generatePortMappings(node.ExtraPortMappings...)...)
args = append(args, generateNodeConstraints(node.Constraints)...)

// finally, specify the image to run
return append(args, node.Image)
Expand Down Expand Up @@ -308,3 +309,21 @@ func generatePortMappings(portMappings ...config.PortMapping) []string {
}
return args
}

// generateNodeConstraints converts the nodesConstraints to a list of args for docker
// https://docs.docker.com/config/containers/resource_constraints/
func generateNodeConstraints(resources config.NodeResources) []string {
var args []string
if resources.Cpus.Sign() > 0 {
args = append(args, fmt.Sprintf("--cpus=%s", resources.Cpus.String()))
}

if resources.Memory.Sign() > 0 {
args = append(args, fmt.Sprintf("--memory=%s", resources.Memory.String()))
// prevent a container from using swap because we want to emulate a real node
// https://docs.docker.com/config/containers/resource_constraints/#prevent-a-container-from-using-swap
args = append(args, fmt.Sprintf("--memory-swap=%s", resources.Memory.String()))
}

return args
}
15 changes: 15 additions & 0 deletions pkg/internal/apis/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ limitations under the License.

package config

import (
"k8s.io/apimachinery/pkg/api/resource"
)

// Cluster contains kind cluster configuration
type Cluster struct {
// Nodes contains the list of nodes defined in the `kind` Cluster
Expand Down Expand Up @@ -83,6 +87,9 @@ type Node struct {
// KubeadmConfigPatchesJSON6902 are applied to the generated kubeadm config
// as patchesJson6902 to `kustomize build`
KubeadmConfigPatchesJSON6902 []PatchJSON6902

// Constraints describes the node resources constraints
Constraints NodeResources
}

// NodeRole defines possible role for nodes in a Kubernetes cluster managed by `kind`
Expand Down Expand Up @@ -216,3 +223,11 @@ const (
// PortMappingProtocolSCTP specifies SCTP protocol
PortMappingProtocolSCTP PortMappingProtocol = "SCTP"
)

// NodeResources represents the node resources (CPU/Memory)
type NodeResources struct {
// The maximum amount of memory the node can use.
Memory resource.Quantity
// Specify how much of the available CPU resources a node can use.
Cpus resource.Quantity
}
13 changes: 13 additions & 0 deletions pkg/internal/apis/config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package config
import (
"net"

"k8s.io/apimachinery/pkg/api/resource"

"sigs.k8s.io/kind/pkg/errors"
)

Expand Down Expand Up @@ -101,6 +103,17 @@ func (n *Node) Validate() error {
}
}

// validate node resource constraints
if n.Constraints.Cpus.Sign() < 0 {
errs = append(errs, errors.New("invalid number of Cpus"))
}

// minimum memory size is 4m
minMemory := resource.MustParse("4m")
if n.Constraints.Memory.Sign() != 0 && n.Constraints.Memory.Cmp(minMemory) < 0 {
errs = append(errs, errors.New("invalid Memory Size (minimum 4m)"))
}

if len(errs) > 0 {
return errors.NewAggregate(errs)
}
Expand Down
35 changes: 35 additions & 0 deletions pkg/internal/apis/config/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package config
import (
"testing"

"k8s.io/apimachinery/pkg/api/resource"

"sigs.k8s.io/kind/pkg/errors"
)

Expand Down Expand Up @@ -107,6 +109,21 @@ func TestClusterValidate(t *testing.T) {
}(),
ExpectErrors: 1,
},
{
Name: "wrong resources constraints node",
Cluster: func() Cluster {
c := Cluster{}
SetDefaultsCluster(&c)
n, n2 := Node{}, Node{}
SetDefaultsNode(&n)
SetDefaultsNode(&n2)
n.Constraints.Cpus = resource.MustParse("-12")
n.Constraints.Memory = resource.MustParse("1m")
c.Nodes = []Node{n, n2}
return c
}(),
ExpectErrors: 1,
},
}

for _, tc := range cases {
Expand Down Expand Up @@ -202,6 +219,24 @@ func TestNodeValidate(t *testing.T) {
}(),
ExpectErrors: 1,
},
{
TestName: "Negative CPU constraint",
Node: func() Node {
cfg := newDefaultedNode(ControlPlaneRole)
cfg.Constraints.Cpus = resource.MustParse("-12")
return cfg
}(),
ExpectErrors: 1,
},
{
TestName: "Minimum value for memory constraint",
Node: func() Node {
cfg := newDefaultedNode(ControlPlaneRole)
cfg.Constraints.Memory = resource.MustParse("2m")
return cfg
}(),
ExpectErrors: 1,
},
{
TestName: "Invalid HostPort",
Node: func() Node {
Expand Down
19 changes: 19 additions & 0 deletions pkg/internal/apis/config/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions site/content/docs/user/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,25 @@ nodes:
- role: worker
```

#### Limit node resources
You can set CPU and memory limits for your cluster nodes in the configuration file.

```yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
nodes:
# the control plane node
- role: control-plane
constraints:
memory: "200m"
cpus: "2"
- role: worker
constraints:
memory: "100m"
cpus: "1"
```


### Configure kind to use a proxy
If you are running kind in an environment that requires a proxy, you may need to configure kind to use it.

Expand Down

0 comments on commit 547f356

Please sign in to comment.