[FEATURE] Memory Limits (#494, @konradmalik)
This commit is contained in:
parent
16fa1076ff
commit
e495fe83a8
@ -24,12 +24,13 @@ package cluster
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
@ -316,6 +317,12 @@ func NewCmdClusterCreate() *cobra.Command {
|
|||||||
cmd.Flags().String("gpus", "", "GPU devices to add to the cluster node containers ('all' to pass all GPUs) [From docker]")
|
cmd.Flags().String("gpus", "", "GPU devices to add to the cluster node containers ('all' to pass all GPUs) [From docker]")
|
||||||
_ = cfgViper.BindPFlag("options.runtime.gpurequest", cmd.Flags().Lookup("gpus"))
|
_ = cfgViper.BindPFlag("options.runtime.gpurequest", cmd.Flags().Lookup("gpus"))
|
||||||
|
|
||||||
|
cmd.Flags().String("servers-memory", "", "Memory limit imposed on the server nodes [From docker]")
|
||||||
|
_ = cfgViper.BindPFlag("options.runtime.serversmemory", cmd.Flags().Lookup("servers-memory"))
|
||||||
|
|
||||||
|
cmd.Flags().String("agents-memory", "", "Memory limit imposed on the agents nodes [From docker]")
|
||||||
|
_ = cfgViper.BindPFlag("options.runtime.agentsmemory", cmd.Flags().Lookup("agents-memory"))
|
||||||
|
|
||||||
/* Image Importing */
|
/* Image Importing */
|
||||||
cmd.Flags().Bool("no-image-volume", false, "Disable the creation of a volume for importing images")
|
cmd.Flags().Bool("no-image-volume", false, "Disable the creation of a volume for importing images")
|
||||||
_ = cfgViper.BindPFlag("options.k3d.disableimagevolume", cmd.Flags().Lookup("no-image-volume"))
|
_ = cfgViper.BindPFlag("options.k3d.disableimagevolume", cmd.Flags().Lookup("no-image-volume"))
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
dockerunits "github.com/docker/go-units"
|
||||||
"github.com/rancher/k3d/v4/cmd/util"
|
"github.com/rancher/k3d/v4/cmd/util"
|
||||||
k3dc "github.com/rancher/k3d/v4/pkg/client"
|
k3dc "github.com/rancher/k3d/v4/pkg/client"
|
||||||
"github.com/rancher/k3d/v4/pkg/runtimes"
|
"github.com/rancher/k3d/v4/pkg/runtimes"
|
||||||
@ -67,6 +68,7 @@ func NewCmdNodeCreate() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().StringP("image", "i", fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.GetK3sVersion(false)), "Specify k3s image used for the node(s)")
|
cmd.Flags().StringP("image", "i", fmt.Sprintf("%s:%s", k3d.DefaultK3sImageRepo, version.GetK3sVersion(false)), "Specify k3s image used for the node(s)")
|
||||||
|
cmd.Flags().String("memory", "", "Memory limit imposed on the node [From docker]")
|
||||||
|
|
||||||
cmd.Flags().BoolVar(&createNodeOpts.Wait, "wait", false, "Wait for the node(s) to be ready before returning.")
|
cmd.Flags().BoolVar(&createNodeOpts.Wait, "wait", false, "Wait for the node(s) to be ready before returning.")
|
||||||
cmd.Flags().DurationVar(&createNodeOpts.Timeout, "timeout", 0*time.Second, "Maximum waiting time for '--wait' before canceling/returning.")
|
cmd.Flags().DurationVar(&createNodeOpts.Timeout, "timeout", 0*time.Second, "Maximum waiting time for '--wait' before canceling/returning.")
|
||||||
@ -112,6 +114,16 @@ func parseCreateNodeCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, *k3d.Cl
|
|||||||
Name: clusterName,
|
Name: clusterName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --memory
|
||||||
|
memory, err := cmd.Flags().GetString("memory")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("No memory specified")
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
if _, err := dockerunits.RAMInBytes(memory); memory != "" && err != nil {
|
||||||
|
log.Errorf("Provided memory limit value is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
// generate list of nodes
|
// generate list of nodes
|
||||||
nodes := []*k3d.Node{}
|
nodes := []*k3d.Node{}
|
||||||
for i := 0; i < replicas; i++ {
|
for i := 0; i < replicas; i++ {
|
||||||
@ -123,6 +135,7 @@ func parseCreateNodeCmd(cmd *cobra.Command, args []string) ([]*k3d.Node, *k3d.Cl
|
|||||||
k3d.LabelRole: roleStr,
|
k3d.LabelRole: roleStr,
|
||||||
},
|
},
|
||||||
Restart: true,
|
Restart: true,
|
||||||
|
Memory: memory,
|
||||||
}
|
}
|
||||||
nodes = append(nodes, node)
|
nodes = append(nodes, node)
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -12,6 +12,7 @@ require (
|
|||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||||
github.com/docker/docker v20.10.5+incompatible
|
github.com/docker/docker v20.10.5+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
|
github.com/docker/go-units v0.4.0
|
||||||
github.com/go-test/deep v1.0.4
|
github.com/go-test/deep v1.0.4
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5
|
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5
|
||||||
|
@ -26,13 +26,17 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
dockerunits "github.com/docker/go-units"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/rancher/k3d/v4/pkg/runtimes"
|
"github.com/rancher/k3d/v4/pkg/runtimes"
|
||||||
|
"github.com/rancher/k3d/v4/pkg/runtimes/docker"
|
||||||
k3d "github.com/rancher/k3d/v4/pkg/types"
|
k3d "github.com/rancher/k3d/v4/pkg/types"
|
||||||
|
"github.com/rancher/k3d/v4/pkg/util"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
@ -329,6 +333,37 @@ func NodeCreate(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// memory limits
|
||||||
|
if node.Memory != "" {
|
||||||
|
if runtime != runtimes.Docker {
|
||||||
|
log.Warn("ignoring specified memory limits as runtime is not Docker")
|
||||||
|
} else {
|
||||||
|
memory, err := dockerunits.RAMInBytes(node.Memory)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Invalid memory limit format: %+v", err)
|
||||||
|
}
|
||||||
|
// mount fake meminfo as readonly
|
||||||
|
fakemempath, err := util.MakeFakeMeminfo(memory, node.Name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to create fake meminfo: %+v", err)
|
||||||
|
}
|
||||||
|
node.Volumes = append(node.Volumes, fmt.Sprintf("%s:%s:ro", fakemempath, util.MemInfoPath))
|
||||||
|
// mount empty edac folder, but only if it exists
|
||||||
|
exists, err := docker.CheckIfDirectoryExists(ctx, node.Image, util.EdacFolderPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to check for the existence of edac folder: %+v", err)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
log.Debugln("Found edac folder")
|
||||||
|
fakeedacpath, err := util.MakeFakeEdac(node.Name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to create fake edac: %+v", err)
|
||||||
|
}
|
||||||
|
node.Volumes = append(node.Volumes, fmt.Sprintf("%s:%s:ro", fakeedacpath, util.EdacFolderPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CREATION
|
* CREATION
|
||||||
*/
|
*/
|
||||||
@ -346,6 +381,17 @@ func NodeDelete(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, o
|
|||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete fake folder created for limits
|
||||||
|
if node.Memory != "" {
|
||||||
|
log.Debug("Cleaning fake files folder from k3d config dir for this node...")
|
||||||
|
filepath, err := util.GetNodeFakerDirOrCreate(node.Name)
|
||||||
|
err = os.RemoveAll(filepath)
|
||||||
|
if err != nil {
|
||||||
|
// this err prob should not be fatal, just log it
|
||||||
|
log.Errorf("Could not remove fake files folder for node %s: %+v", node.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update the server loadbalancer
|
// update the server loadbalancer
|
||||||
if !opts.SkipLBUpdate && (node.Role == k3d.ServerRole || node.Role == k3d.AgentRole) {
|
if !opts.SkipLBUpdate && (node.Role == k3d.ServerRole || node.Role == k3d.AgentRole) {
|
||||||
cluster, err := ClusterGet(ctx, runtime, &k3d.Cluster{Name: node.Labels[k3d.LabelClusterName]})
|
cluster, err := ClusterGet(ctx, runtime, &k3d.Cluster{Name: node.Labels[k3d.LabelClusterName]})
|
||||||
|
@ -107,6 +107,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
|
|||||||
Image: simpleConfig.Image,
|
Image: simpleConfig.Image,
|
||||||
Args: simpleConfig.Options.K3sOptions.ExtraServerArgs,
|
Args: simpleConfig.Options.K3sOptions.ExtraServerArgs,
|
||||||
ServerOpts: k3d.ServerOpts{},
|
ServerOpts: k3d.ServerOpts{},
|
||||||
|
Memory: simpleConfig.Options.Runtime.ServersMemory,
|
||||||
}
|
}
|
||||||
|
|
||||||
// first server node will be init node if we have more than one server specified but no external datastore
|
// first server node will be init node if we have more than one server specified but no external datastore
|
||||||
@ -120,9 +121,10 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
|
|||||||
|
|
||||||
for i := 0; i < simpleConfig.Agents; i++ {
|
for i := 0; i < simpleConfig.Agents; i++ {
|
||||||
agentNode := k3d.Node{
|
agentNode := k3d.Node{
|
||||||
Role: k3d.AgentRole,
|
Role: k3d.AgentRole,
|
||||||
Image: simpleConfig.Image,
|
Image: simpleConfig.Image,
|
||||||
Args: simpleConfig.Options.K3sOptions.ExtraAgentArgs,
|
Args: simpleConfig.Options.K3sOptions.ExtraAgentArgs,
|
||||||
|
Memory: simpleConfig.Options.Runtime.AgentsMemory,
|
||||||
}
|
}
|
||||||
newCluster.Nodes = append(newCluster.Nodes, &agentNode)
|
newCluster.Nodes = append(newCluster.Nodes, &agentNode)
|
||||||
}
|
}
|
||||||
@ -226,6 +228,9 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
|
|||||||
DisableLoadBalancer: simpleConfig.Options.K3dOptions.DisableLoadbalancer,
|
DisableLoadBalancer: simpleConfig.Options.K3dOptions.DisableLoadbalancer,
|
||||||
K3sServerArgs: simpleConfig.Options.K3sOptions.ExtraServerArgs,
|
K3sServerArgs: simpleConfig.Options.K3sOptions.ExtraServerArgs,
|
||||||
K3sAgentArgs: simpleConfig.Options.K3sOptions.ExtraAgentArgs,
|
K3sAgentArgs: simpleConfig.Options.K3sOptions.ExtraAgentArgs,
|
||||||
|
GPURequest: simpleConfig.Options.Runtime.GPURequest,
|
||||||
|
ServersMemory: simpleConfig.Options.Runtime.ServersMemory,
|
||||||
|
AgentsMemory: simpleConfig.Options.Runtime.AgentsMemory,
|
||||||
GlobalLabels: map[string]string{}, // empty init
|
GlobalLabels: map[string]string{}, // empty init
|
||||||
GlobalEnv: []string{}, // empty init
|
GlobalEnv: []string{}, // empty init
|
||||||
}
|
}
|
||||||
|
@ -172,6 +172,12 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"gpuRequest": {
|
"gpuRequest": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"serversMemory": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"agentsMemory": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,9 @@ type SimpleConfigOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SimpleConfigOptionsRuntime struct {
|
type SimpleConfigOptionsRuntime struct {
|
||||||
GPURequest string `mapstructure:"gpuRequest" yaml:"gpuRequest"`
|
GPURequest string `mapstructure:"gpuRequest" yaml:"gpuRequest"`
|
||||||
|
ServersMemory string `mapstructure:"serversMemory" yaml:"serversMemory"`
|
||||||
|
AgentsMemory string `mapstructure:"agentsMemory" yaml:"agentsMemory"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SimpleConfigOptionsK3d struct {
|
type SimpleConfigOptionsK3d struct {
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
dockerunits "github.com/docker/go-units"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -63,6 +64,21 @@ func ValidateClusterConfig(ctx context.Context, runtime runtimes.Runtime, config
|
|||||||
return fmt.Errorf("The API Port can not be changed when using 'host' network")
|
return fmt.Errorf("The API Port can not be changed when using 'host' network")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// memory limits must have proper format
|
||||||
|
// if empty we don't care about errors in parsing
|
||||||
|
if config.ClusterCreateOpts.ServersMemory != "" {
|
||||||
|
if _, err := dockerunits.RAMInBytes(config.ClusterCreateOpts.ServersMemory); err != nil {
|
||||||
|
return fmt.Errorf("Provided servers memory limit value is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ClusterCreateOpts.AgentsMemory != "" {
|
||||||
|
if _, err := dockerunits.RAMInBytes(config.ClusterCreateOpts.AgentsMemory); err != nil {
|
||||||
|
return fmt.Errorf("Provided agents memory limit value is invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validate nodes one by one
|
// validate nodes one by one
|
||||||
for _, node := range config.Cluster.Nodes {
|
for _, node := range config.Cluster.Nodes {
|
||||||
|
|
||||||
|
@ -180,3 +180,62 @@ func getNodeContainer(ctx context.Context, node *k3d.Node) (*types.Container, er
|
|||||||
return &containers[0], nil
|
return &containers[0], nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// executes an arbitrary command in a container while returning its exit code.
|
||||||
|
// useful to check something in docker env
|
||||||
|
func executeCheckInContainer(ctx context.Context, image string, cmd []string) (int64, error) {
|
||||||
|
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Failed to create docker client")
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
defer docker.Close()
|
||||||
|
|
||||||
|
if err = pullImage(ctx, docker, image); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := docker.ContainerCreate(ctx, &container.Config{
|
||||||
|
Image: image,
|
||||||
|
Cmd: cmd,
|
||||||
|
Tty: false,
|
||||||
|
Entrypoint: []string{},
|
||||||
|
}, nil, nil, nil, "")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to create container from image %s with cmd %s", image, cmd)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = startContainer(ctx, resp.ID); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
exitCode := -1
|
||||||
|
statusCh, errCh := docker.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
|
||||||
|
select {
|
||||||
|
case err := <-errCh:
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error while waiting for container %s to exit", resp.ID)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
case status := <-statusCh:
|
||||||
|
exitCode = int(status.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = removeContainer(ctx, resp.ID); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(exitCode), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckIfDirectoryExists checks for the existence of a given path inside the docker environment
|
||||||
|
func CheckIfDirectoryExists(ctx context.Context, image string, dir string) (bool, error) {
|
||||||
|
log.Tracef("checking if dir %s exists in docker environment...", dir)
|
||||||
|
shellCmd := fmt.Sprintf("[ -d \"%s\" ] && exit 0 || exit 1", dir)
|
||||||
|
cmd := []string{"sh", "-c", shellCmd}
|
||||||
|
exitCode, err := executeCheckInContainer(ctx, image, cmd)
|
||||||
|
log.Tracef("check dir container returned %d exist code", exitCode)
|
||||||
|
return exitCode == 0, err
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
dockercliopts "github.com/docker/cli/opts"
|
dockercliopts "github.com/docker/cli/opts"
|
||||||
|
dockerunits "github.com/docker/go-units"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TranslateNodeToContainer translates a k3d node specification to a docker container representation
|
// TranslateNodeToContainer translates a k3d node specification to a docker container representation
|
||||||
@ -85,6 +86,16 @@ func TranslateNodeToContainer(node *k3d.Node) (*NodeInDocker, error) {
|
|||||||
hostConfig.DeviceRequests = gpuopts.Value()
|
hostConfig.DeviceRequests = gpuopts.Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// memory limits
|
||||||
|
// fake meminfo is mounted to hostConfig.Binds
|
||||||
|
if node.Memory != "" {
|
||||||
|
memory, err := dockerunits.RAMInBytes(node.Memory)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to set memory limit: %+v", err)
|
||||||
|
}
|
||||||
|
hostConfig.Memory = memory
|
||||||
|
}
|
||||||
|
|
||||||
/* They have to run in privileged mode */
|
/* They have to run in privileged mode */
|
||||||
// TODO: can we replace this by a reduced set of capabilities?
|
// TODO: can we replace this by a reduced set of capabilities?
|
||||||
hostConfig.Privileged = true
|
hostConfig.Privileged = true
|
||||||
@ -233,6 +244,13 @@ func TranslateContainerDetailsToNode(containerDetails types.ContainerJSON) (*k3d
|
|||||||
Status: containerDetails.ContainerJSONBase.State.Status,
|
Status: containerDetails.ContainerJSONBase.State.Status,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// memory limit
|
||||||
|
memoryStr := dockerunits.HumanSize(float64(containerDetails.HostConfig.Memory))
|
||||||
|
// no-limit is returned as 0B, filter this out
|
||||||
|
if memoryStr == "0B" {
|
||||||
|
memoryStr = ""
|
||||||
|
}
|
||||||
|
|
||||||
node := &k3d.Node{
|
node := &k3d.Node{
|
||||||
Name: strings.TrimPrefix(containerDetails.Name, "/"), // container name with leading '/' cut off
|
Name: strings.TrimPrefix(containerDetails.Name, "/"), // container name with leading '/' cut off
|
||||||
Role: k3d.NodeRoles[containerDetails.Config.Labels[k3d.LabelRole]],
|
Role: k3d.NodeRoles[containerDetails.Config.Labels[k3d.LabelRole]],
|
||||||
@ -249,6 +267,7 @@ func TranslateContainerDetailsToNode(containerDetails types.ContainerJSON) (*k3d
|
|||||||
ServerOpts: serverOpts,
|
ServerOpts: serverOpts,
|
||||||
AgentOpts: k3d.AgentOpts{},
|
AgentOpts: k3d.AgentOpts{},
|
||||||
State: nodeState,
|
State: nodeState,
|
||||||
|
Memory: memoryStr,
|
||||||
}
|
}
|
||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,8 @@ type ClusterCreateOpts struct {
|
|||||||
K3sServerArgs []string `yaml:"k3sServerArgs" json:"k3sServerArgs,omitempty"`
|
K3sServerArgs []string `yaml:"k3sServerArgs" json:"k3sServerArgs,omitempty"`
|
||||||
K3sAgentArgs []string `yaml:"k3sAgentArgs" json:"k3sAgentArgs,omitempty"`
|
K3sAgentArgs []string `yaml:"k3sAgentArgs" json:"k3sAgentArgs,omitempty"`
|
||||||
GPURequest string `yaml:"gpuRequest" json:"gpuRequest,omitempty"`
|
GPURequest string `yaml:"gpuRequest" json:"gpuRequest,omitempty"`
|
||||||
|
ServersMemory string `yaml:"serversMemory" json:"serversMemory,omitempty"`
|
||||||
|
AgentsMemory string `yaml:"agentsMemory" json:"agentsMemory,omitempty"`
|
||||||
NodeHooks []NodeHook `yaml:"nodeHooks,omitempty" json:"nodeHooks,omitempty"`
|
NodeHooks []NodeHook `yaml:"nodeHooks,omitempty" json:"nodeHooks,omitempty"`
|
||||||
GlobalLabels map[string]string `yaml:"globalLabels,omitempty" json:"globalLabels,omitempty"`
|
GlobalLabels map[string]string `yaml:"globalLabels,omitempty" json:"globalLabels,omitempty"`
|
||||||
GlobalEnv []string `yaml:"globalEnv,omitempty" json:"globalEnv,omitempty"`
|
GlobalEnv []string `yaml:"globalEnv,omitempty" json:"globalEnv,omitempty"`
|
||||||
@ -328,6 +330,7 @@ type Node struct {
|
|||||||
ServerOpts ServerOpts `yaml:"serverOpts" json:"serverOpts,omitempty"`
|
ServerOpts ServerOpts `yaml:"serverOpts" json:"serverOpts,omitempty"`
|
||||||
AgentOpts AgentOpts `yaml:"agentOpts" json:"agentOpts,omitempty"`
|
AgentOpts AgentOpts `yaml:"agentOpts" json:"agentOpts,omitempty"`
|
||||||
GPURequest string // filled automatically
|
GPURequest string // filled automatically
|
||||||
|
Memory string // filled automatically
|
||||||
State NodeState // filled automatically
|
State NodeState // filled automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
|
124
pkg/util/infofaker.go
Normal file
124
pkg/util/infofaker.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2020 The k3d Author(s)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
dockerunits "github.com/docker/go-units"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EdacFolderPath = "/sys/devices/system/edac"
|
||||||
|
MemInfoPath = "/proc/meminfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// creates a mininal fake meminfo with fields required by cadvisor (see machine.go in cadvisor)
|
||||||
|
func meminfoContent(totalKB int64) string {
|
||||||
|
var lines = []string{
|
||||||
|
fmt.Sprintf("MemTotal: %d kB", totalKB),
|
||||||
|
// this may be configurable later
|
||||||
|
"SwapTotal: 0 kB",
|
||||||
|
}
|
||||||
|
return strings.Join(lines, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeFakerDirOrCreate creates or gets a hidden folder in k3d home dir
|
||||||
|
// to keep container(node)-specific fake files in it
|
||||||
|
func GetNodeFakerDirOrCreate(name string) (string, error) {
|
||||||
|
// this folder needs to be kept across reboots, keep it in ~/.k3d
|
||||||
|
configdir, err := GetConfigDirOrCreate()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fakeDir := path.Join(configdir, fmt.Sprintf(".%s", name))
|
||||||
|
|
||||||
|
// create directories if necessary
|
||||||
|
if err := createDirIfNotExists(fakeDir); err != nil {
|
||||||
|
log.Errorf("Failed to create fake files path '%s'", fakeDir)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fakeDir, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFakeMeminfoPathForName returns a path to (existent or not) fake meminfo file for a given node/container name
|
||||||
|
func GetFakeMeminfoPathForName(nodeName string) (string, error) {
|
||||||
|
return fakeInfoPathForName("meminfo", nodeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFakeMeminfo creates a fake meminfo file to be mounted and provide a specific RAM capacity.
|
||||||
|
// This file is created on a per specific container/node basis, uniqueName must ensure that.
|
||||||
|
// Returns a path to the file
|
||||||
|
func MakeFakeMeminfo(memoryBytes int64, nodeName string) (string, error) {
|
||||||
|
fakeMeminfoPath, err := GetFakeMeminfoPathForName(nodeName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fakememinfo, err := os.Create(fakeMeminfoPath)
|
||||||
|
defer fakememinfo.Close()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write content, must be kB
|
||||||
|
memoryKb := memoryBytes / dockerunits.KB
|
||||||
|
content := meminfoContent(memoryKb)
|
||||||
|
_, err = fakememinfo.WriteString(content)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fakememinfo.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFakeEdac creates an empty edac folder to force cadvisor
|
||||||
|
// to use meminfo even for ECC memory
|
||||||
|
func MakeFakeEdac(nodeName string) (string, error) {
|
||||||
|
dir, err := GetNodeFakerDirOrCreate(nodeName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
edacPath := path.Join(dir, "edac")
|
||||||
|
// create directories if necessary
|
||||||
|
if err := createDirIfNotExists(edacPath); err != nil {
|
||||||
|
log.Errorf("Failed to create fake edac path '%s'", edacPath)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return edacPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a path to (existent or not) fake (mem or cpu)info file for a given node/container name
|
||||||
|
func fakeInfoPathForName(infoType string, nodeName string) (string, error) {
|
||||||
|
// this file needs to be kept across reboots, keep it in ~/.k3d
|
||||||
|
dir, err := GetNodeFakerDirOrCreate(nodeName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return path.Join(dir, infoType), nil
|
||||||
|
}
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -89,6 +89,7 @@ github.com/docker/go-connections/nat
|
|||||||
github.com/docker/go-connections/sockets
|
github.com/docker/go-connections/sockets
|
||||||
github.com/docker/go-connections/tlsconfig
|
github.com/docker/go-connections/tlsconfig
|
||||||
# github.com/docker/go-units v0.4.0
|
# github.com/docker/go-units v0.4.0
|
||||||
|
## explicit
|
||||||
github.com/docker/go-units
|
github.com/docker/go-units
|
||||||
# github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7
|
# github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7
|
||||||
github.com/docker/libtrust
|
github.com/docker/libtrust
|
||||||
|
Loading…
Reference in New Issue
Block a user