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 May 1, 2020
1 parent 38f9bf5 commit 0c4efba
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 4 deletions.
6 changes: 3 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand All @@ -60,8 +61,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
Expand Down Expand Up @@ -109,7 +110,6 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
Expand Down Expand Up @@ -201,6 +201,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
Expand All @@ -220,7 +221,6 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
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 @@ -106,6 +110,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 @@ -283,3 +290,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 @@ -231,13 +231,14 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n
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...)...)
mappingArgs, err := generatePortMappings(clusterIPFamily, node.ExtraPortMappings...)
if err != nil {
return nil, err
}
args = append(args, mappingArgs...)
args = append(args, generateNodeConstraints(node.Constraints)...)

// finally, specify the image to run
return append(args, node.Image), nil
Expand Down Expand Up @@ -376,3 +377,21 @@ func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings .
}
return args, nil
}

// 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 @@ -89,6 +93,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 @@ -234,3 +241,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 @@ -398,6 +398,25 @@ networking:
ipFamily: ipv6
```

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

```yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
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 0c4efba

Please sign in to comment.