mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
* feat: add support for ulimits in compose and container creation ([#221](https://github.com/ipaddicting/uncloud/issues/221)) * refactor: move Ulimits to ContainerResources and use map, also removed GEMINI.md.
This commit is contained in:
@@ -39,3 +39,4 @@ node_modules/
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
.devcontainer/
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
@@ -617,6 +618,7 @@ func (s *Server) CreateServiceContainer(
|
||||
Memory: spec.Container.Resources.Memory,
|
||||
MemoryReservation: spec.Container.Resources.MemoryReservation,
|
||||
DeviceRequests: spec.Container.Resources.DeviceReservations,
|
||||
Ulimits: toDockerUlimits(spec.Container.Resources.Ulimits),
|
||||
},
|
||||
// Restart service containers if they exit or a machine restarts unless they are explicitly stopped.
|
||||
// For one-off containers and batch jobs we plan to use a different service type/mode.
|
||||
@@ -877,6 +879,23 @@ func toDockerBindOptions(opts *api.BindOptions) *mount.BindOptions {
|
||||
return dockerOpts
|
||||
}
|
||||
|
||||
func toDockerUlimits(ulimits map[string]api.Ulimit) []*units.Ulimit {
|
||||
if len(ulimits) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
dockerUlimits := make([]*units.Ulimit, 0, len(ulimits))
|
||||
for name, u := range ulimits {
|
||||
dockerUlimits = append(dockerUlimits, &units.Ulimit{
|
||||
Name: name,
|
||||
Soft: u.Soft,
|
||||
Hard: u.Hard,
|
||||
})
|
||||
}
|
||||
|
||||
return dockerUlimits
|
||||
}
|
||||
|
||||
// verifyDockerVolumesExist checks if the Docker named volumes referenced in the mounts exist on the machine.
|
||||
func (s *Server) verifyDockerVolumesExist(ctx context.Context, mounts []mount.Mount) error {
|
||||
for _, m := range mounts {
|
||||
|
||||
@@ -19,4 +19,11 @@ type ContainerResources struct {
|
||||
MemoryReservation int64
|
||||
// Device reservations/requests for access to things like GPUs
|
||||
DeviceReservations []container.DeviceRequest
|
||||
// Ulimits defines the resource limits for the container.
|
||||
Ulimits map[string]Ulimit
|
||||
}
|
||||
|
||||
type Ulimit struct {
|
||||
Soft int64
|
||||
Hard int64
|
||||
}
|
||||
|
||||
@@ -363,6 +363,9 @@ func (s *ContainerSpec) Clone() ContainerSpec {
|
||||
spec.Sysctls[k] = v
|
||||
}
|
||||
}
|
||||
if s.Resources.Ulimits != nil {
|
||||
spec.Resources.Ulimits = maps.Clone(s.Resources.Ulimits)
|
||||
}
|
||||
return spec
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ func resourcesFromCompose(service types.ServiceConfig) api.ContainerResources {
|
||||
CPU: int64(service.CPUS * 1e9),
|
||||
Memory: int64(service.MemLimit),
|
||||
MemoryReservation: int64(service.MemReservation),
|
||||
Ulimits: ulimitsFromCompose(service.Ulimits),
|
||||
}
|
||||
|
||||
// Convert GPU device requests from compose format, appending "gpu" capability.
|
||||
@@ -293,6 +294,33 @@ func tmpfsVolumeSpecFromCompose(serviceVolume types.ServiceVolumeConfig) api.Vol
|
||||
return spec
|
||||
}
|
||||
|
||||
func ulimitsFromCompose(ulimits map[string]*types.UlimitsConfig) map[string]api.Ulimit {
|
||||
if len(ulimits) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := make(map[string]api.Ulimit, len(ulimits))
|
||||
for name, u := range ulimits {
|
||||
soft := u.Soft
|
||||
hard := u.Hard
|
||||
if u.Single != 0 {
|
||||
if soft == 0 {
|
||||
soft = u.Single
|
||||
}
|
||||
if hard == 0 {
|
||||
hard = u.Single
|
||||
}
|
||||
}
|
||||
|
||||
res[name] = api.Ulimit{
|
||||
Soft: int64(soft),
|
||||
Hard: int64(hard),
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// validateServicesExtensions validates extension combinations across all services in the project.
|
||||
func validateServicesExtensions(project *types.Project) error {
|
||||
for _, service := range project.Services {
|
||||
|
||||
@@ -699,6 +699,94 @@ volumes:
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceSpecFromCompose_Ulimits(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
composeYAML string
|
||||
expected map[string]api.Ulimit
|
||||
}{
|
||||
{
|
||||
name: "single ulimit with soft and hard limits",
|
||||
composeYAML: `
|
||||
services:
|
||||
db:
|
||||
image: postgres
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 20000
|
||||
hard: 40000
|
||||
`,
|
||||
expected: map[string]api.Ulimit{
|
||||
"nofile": {
|
||||
Soft: 20000,
|
||||
Hard: 40000,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single ulimit with single value (soft=hard)",
|
||||
composeYAML: `
|
||||
services:
|
||||
db:
|
||||
image: postgres
|
||||
ulimits:
|
||||
nproc: 65535
|
||||
`,
|
||||
expected: map[string]api.Ulimit{
|
||||
"nproc": {
|
||||
Soft: 65535,
|
||||
Hard: 65535,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple ulimits",
|
||||
composeYAML: `
|
||||
services:
|
||||
db:
|
||||
image: postgres
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 20000
|
||||
hard: 40000
|
||||
nproc: 65535
|
||||
`,
|
||||
expected: map[string]api.Ulimit{
|
||||
"nofile": {
|
||||
Soft: 20000,
|
||||
Hard: 40000,
|
||||
},
|
||||
"nproc": {
|
||||
Soft: 65535,
|
||||
Hard: 65535,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty ulimits",
|
||||
composeYAML: `
|
||||
services:
|
||||
db:
|
||||
image: postgres
|
||||
ulimits: {}
|
||||
`,
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
project, err := LoadProjectFromContent(context.Background(), tt.composeYAML)
|
||||
require.NoError(t, err)
|
||||
|
||||
spec, err := ServiceSpecFromCompose(project, "db")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.expected, spec.Container.Resources.Ulimits)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceSpecFromCompose_XMachinesPlacement(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user