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 Oct 2, 2019
1 parent 161112b commit 6e56fd1
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 1 deletion.
12 changes: 12 additions & 0 deletions pkg/apis/config/v1alpha3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1alpha3

import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"sigs.k8s.io/kind/pkg/container/cri"
Expand Down Expand Up @@ -75,6 +76,9 @@ type Node struct {
// ExtraPortMappings describes additional port mappings for the node container
// binded to a host Port
ExtraPortMappings []cri.PortMapping `json:"extraPortMappings,omitempty"`

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

// NodeRole defines possible role for nodes in a Kubernetes cluster managed by `kind`
Expand Down Expand Up @@ -138,3 +142,11 @@ type PatchJSON6902 struct {
// Patch should contain the contents of the json patch as a string
Patch string `json:"patch"`
}

// 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/v1alpha3/zz_generated.deepcopy.go

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

12 changes: 12 additions & 0 deletions pkg/internal/apis/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package config

import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"sigs.k8s.io/kind/pkg/container/cri"
Expand Down Expand Up @@ -74,6 +75,9 @@ type Node struct {
// ExtraPortMappings describes additional port mappings for the node container
// binded to a host Port
ExtraPortMappings []cri.PortMapping

// 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 @@ -137,3 +141,11 @@ type PatchJSON6902 struct {
// Patch should contain the contents of the json patch as a string
Patch string
}

// 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
}
38 changes: 38 additions & 0 deletions pkg/internal/apis/config/v1alpha3/zz_generated.conversion.go

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

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 @@ -70,6 +72,21 @@ func TestClusterValidate(t *testing.T) {
}(),
ExpectErrors: 1,
},
{
Name: "wrong resources constraints node",
Cluster: func() Cluster {
c := Cluster{}
SetDefaults_Cluster(&c)
n, n2 := Node{}, Node{}
SetDefaults_Node(&n)
SetDefaults_Node(&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 @@ -151,6 +168,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,
},
}

for _, tc := range cases {
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.

21 changes: 20 additions & 1 deletion pkg/internal/cluster/providers/docker/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,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 @@ -293,3 +294,21 @@ func generatePortMappings(portMappings ...cri.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 effectevely 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
}
20 changes: 20 additions & 0 deletions site/content/docs/user/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,26 @@ 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"
cpu: "2"
- role: worker
- role: worker
constraints:
memory: "100m"
cpu: "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 6e56fd1

Please sign in to comment.