Skip to content

Commit a64e758

Browse files
authored
feat: set resources on inner container (#8)
1 parent 4e2cfd7 commit a64e758

File tree

3 files changed

+63
-17
lines changed

3 files changed

+63
-17
lines changed

cli/docker.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ const (
5858
OuterTUNPath = "/tmp/coder-tun"
5959
InnerTUNPath = "/dev/net/tun"
6060

61+
InnerContainerName = "workspace_cvm"
62+
6163
// Required for userns mapping.
6264
// This is the ID of the user we apply in `envbox/Dockerfile`.
6365
//
@@ -85,6 +87,8 @@ var (
8587
EnvAgentToken = "CODER_AGENT_TOKEN"
8688
EnvBootstrap = "CODER_BOOTSTRAP_SCRIPT"
8789
EnvMounts = "CODER_MOUNTS"
90+
EnvCPUs = "CODER_CPUS"
91+
EnvMemory = "CODER_MEMORY"
8892
)
8993

9094
var envboxPrivateMounts = map[string]struct{}{
@@ -261,6 +265,8 @@ func dockerCmd() *cobra.Command {
261265
cliflag.StringVarP(cmd.Flags(), &flag.ethlink, "ethlink", "", "", defaultNetLink, "The ethernet link to query for the MTU that is passed to docerd. Used for tests.")
262266
cliflag.BoolVarP(cmd.Flags(), &flag.addTUN, "add-tun", "", EnvAddTun, false, "Add a TUN device to the inner container.")
263267
cliflag.BoolVarP(cmd.Flags(), &flag.addFUSE, "add-fuse", "", EnvAddFuse, false, "Add a FUSE device to the inner container.")
268+
cliflag.IntVarP(cmd.Flags(), &flag.cpus, "cpus", "", EnvCPUs, 0, "Number of CPUs to allocate inner container. e.g. 2")
269+
cliflag.IntVarP(cmd.Flags(), &flag.memory, "memory", "", EnvMemory, 0, "Max memory to allocate to the inner container in bytes.")
264270

265271
return cmd
266272
}
@@ -282,6 +288,8 @@ type flags struct {
282288
boostrapScript string
283289
ethlink string
284290
containerMounts string
291+
cpus int
292+
memory int
285293
}
286294

287295
func runDockerCVM(ctx context.Context, log slog.Logger, client dockerutil.DockerClient, flags flags) error {
@@ -471,15 +479,16 @@ func runDockerCVM(ctx context.Context, log slog.Logger, client dockerutil.Docker
471479

472480
// Create the inner container.
473481
containerID, err := dockerutil.CreateContainer(ctx, client, &dockerutil.ContainerConfig{
474-
Log: log,
475-
Mounts: mounts,
476-
Devices: devices,
477-
Envs: envs,
478-
Name: "workspace_cvm",
479-
WorkingDir: flags.innerWorkDir,
480-
HasInit: imgMeta.HasInit,
481-
Image: flags.innerImage,
482-
// TODO set CPUQuota,MemoryLimit
482+
Log: log,
483+
Mounts: mounts,
484+
Devices: devices,
485+
Envs: envs,
486+
Name: InnerContainerName,
487+
WorkingDir: flags.innerWorkDir,
488+
HasInit: imgMeta.HasInit,
489+
Image: flags.innerImage,
490+
CPUs: int64(flags.cpus),
491+
MemoryLimit: int64(flags.memory),
483492
})
484493
if err != nil {
485494
return xerrors.Errorf("create container: %w", err)

cli/docker_test.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/coder/envbox/cli"
2525
"github.com/coder/envbox/cli/clitest"
26+
"github.com/coder/envbox/dockerutil"
2627
"github.com/coder/envbox/xunix"
2728
"github.com/coder/envbox/xunix/xunixfake"
2829
)
@@ -164,7 +165,7 @@ func TestDocker(t *testing.T) {
164165
client := clitest.DockerClient(t, ctx)
165166
var called bool
166167
client.ContainerCreateFn = func(_ context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, _ *v1.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
167-
if containerName == "workspace_cvm" {
168+
if containerName == cli.InnerContainerName {
168169
called = true
169170
require.Equal(t, expectedEnvs, config.Env)
170171
}
@@ -217,7 +218,7 @@ func TestDocker(t *testing.T) {
217218

218219
var called bool
219220
client.ContainerCreateFn = func(_ context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, _ *v1.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
220-
if containerName == "workspace_cvm" {
221+
if containerName == cli.InnerContainerName {
221222
called = true
222223
require.Equal(t, expectedMounts, hostConfig.Binds)
223224
}
@@ -301,7 +302,7 @@ func TestDocker(t *testing.T) {
301302

302303
var called bool
303304
client.ContainerCreateFn = func(_ context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, _ *v1.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
304-
if containerName == "workspace_cvm" {
305+
if containerName == cli.InnerContainerName {
305306
called = true
306307
require.Equal(t, expectedDevices, hostConfig.Devices)
307308
}
@@ -368,7 +369,7 @@ func TestDocker(t *testing.T) {
368369

369370
var called bool
370371
client.ContainerCreateFn = func(_ context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, _ *v1.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
371-
if containerName == "workspace_cvm" {
372+
if containerName == cli.InnerContainerName {
372373
called = true
373374
require.Equal(t, []string{"sleep", "infinity"}, []string(config.Entrypoint))
374375
}
@@ -406,6 +407,42 @@ func TestDocker(t *testing.T) {
406407
err := cmd.ExecuteContext(ctx)
407408
require.NoError(t, err)
408409
})
410+
411+
t.Run("SetsResources", func(t *testing.T) {
412+
t.Parallel()
413+
414+
const (
415+
// 4GB.
416+
memory = 4 << 30
417+
cpus = 6
418+
)
419+
name := namesgenerator.GetRandomName(1)
420+
ctx, cmd := clitest.New(t, "docker",
421+
"--image=ubuntu",
422+
"--username=root",
423+
fmt.Sprintf("--container-name=%s", name),
424+
"--agent-token=hi",
425+
fmt.Sprintf("--cpus=%d", cpus),
426+
fmt.Sprintf("--memory=%d", memory),
427+
)
428+
429+
var called bool
430+
client := clitest.DockerClient(t, ctx)
431+
client.ContainerCreateFn = func(_ context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, _ *v1.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
432+
if containerName == cli.InnerContainerName {
433+
called = true
434+
require.Equal(t, int64(memory), hostConfig.Memory)
435+
require.Equal(t, int64(cpus*dockerutil.DefaultCPUPeriod), hostConfig.CPUQuota)
436+
require.Equal(t, int64(dockerutil.DefaultCPUPeriod), hostConfig.CPUPeriod)
437+
}
438+
439+
return container.ContainerCreateCreatedBody{}, nil
440+
}
441+
442+
err := cmd.ExecuteContext(ctx)
443+
require.NoError(t, err)
444+
require.True(t, called, "create function was not called for inner container")
445+
})
409446
}
410447

411448
// rawDockerAuth is sample input for a kubernetes secret to a gcr.io private

dockerutil/container.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
const (
2222
runtime = "sysbox-runc"
2323
// Default CPU period for containers.
24-
defaultCPUPeriod uint64 = 1e5
24+
DefaultCPUPeriod uint64 = 1e5
2525
)
2626

2727
type DockerClient interface {
@@ -42,7 +42,7 @@ type ContainerConfig struct {
4242
// HasInit dictates whether the entrypoint of the container is /sbin/init
4343
// or 'sleep infinity'.
4444
HasInit bool
45-
CPUQuota int64
45+
CPUs int64
4646
MemoryLimit int64
4747
}
4848

@@ -59,8 +59,8 @@ func CreateContainer(ctx context.Context, client DockerClient, conf *ContainerCo
5959
// TODO: Sysbox does not copy cpu.cfs_{period,quota}_us into syscont-cgroup-root cgroup.
6060
// These will not be visible inside the child container.
6161
// See: https://github.com/nestybox/sysbox/issues/582
62-
CPUPeriod: int64(defaultCPUPeriod),
63-
CPUQuota: conf.CPUQuota,
62+
CPUPeriod: int64(DefaultCPUPeriod),
63+
CPUQuota: conf.CPUs * int64(DefaultCPUPeriod),
6464
Memory: conf.MemoryLimit,
6565
},
6666
ExtraHosts: []string{"host.docker.internal:host-gateway"},

0 commit comments

Comments
 (0)