Merge pull request #748 from docker/aci_resource_req

Aci resource reservation & limits
This commit is contained in:
Guillaume Tardif 2020-10-09 12:13:35 +02:00 committed by GitHub
commit 7fb976732f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 731 additions and 449 deletions

View File

@ -54,7 +54,7 @@ func ContainerToComposeProject(r containers.ContainerConfig) (types.Project, err
Environment: toComposeEnvs(r.Environment), Environment: toComposeEnvs(r.Environment),
Deploy: &types.DeployConfig{ Deploy: &types.DeployConfig{
Resources: types.Resources{ Resources: types.Resources{
Limits: &types.Resource{ Reservations: &types.Resource{
NanoCPUs: fmt.Sprintf("%f", r.CPULimit), NanoCPUs: fmt.Sprintf("%f", r.CPULimit),
MemoryBytes: types.UnitBytes(r.MemLimit.Value()), MemoryBytes: types.UnitBytes(r.MemLimit.Value()),
}, },

View File

@ -175,10 +175,6 @@ func getDNSSidecar(containers []containerinstance.Container) containerinstance.C
Image: to.StringPtr(dnsSidecarImage), Image: to.StringPtr(dnsSidecarImage),
Command: &alpineCmd, Command: &alpineCmd,
Resources: &containerinstance.ResourceRequirements{ Resources: &containerinstance.ResourceRequirements{
Limits: &containerinstance.ResourceLimits{
MemoryInGB: to.Float64Ptr(0.1), // "The memory requirement should be in incrememts of 0.1 GB."
CPU: to.Float64Ptr(0.01), // "The CPU requirement should be in incrememts of 0.01."
},
Requests: &containerinstance.ResourceRequests{ Requests: &containerinstance.ResourceRequests{
MemoryInGB: to.Float64Ptr(0.1), MemoryInGB: to.Float64Ptr(0.1),
CPU: to.Float64Ptr(0.01), CPU: to.Float64Ptr(0.01),
@ -357,40 +353,75 @@ func (s serviceConfigAciHelper) getAciContainer(volumesCache map[string]bool) (c
volumes = &allVolumes volumes = &allVolumes
} }
memLimit := 1. // Default 1 Gb resource, err := s.getResourceRequestsLimits()
var cpuLimit float64 = 1 if err != nil {
if s.Deploy != nil && s.Deploy.Resources.Limits != nil { return containerinstance.Container{}, err
if s.Deploy.Resources.Limits.MemoryBytes != 0 {
memLimit = bytesToGb(s.Deploy.Resources.Limits.MemoryBytes)
}
if s.Deploy.Resources.Limits.NanoCPUs != "" {
cpuLimit, err = strconv.ParseFloat(s.Deploy.Resources.Limits.NanoCPUs, 0)
if err != nil {
return containerinstance.Container{}, err
}
}
} }
return containerinstance.Container{ return containerinstance.Container{
Name: to.StringPtr(s.Name), Name: to.StringPtr(s.Name),
ContainerProperties: &containerinstance.ContainerProperties{ ContainerProperties: &containerinstance.ContainerProperties{
Image: to.StringPtr(s.Image), Image: to.StringPtr(s.Image),
Command: to.StringSlicePtr(s.Command), Command: to.StringSlicePtr(s.Command),
EnvironmentVariables: getEnvVariables(s.Environment), EnvironmentVariables: getEnvVariables(s.Environment),
Resources: &containerinstance.ResourceRequirements{ Resources: resource,
Limits: &containerinstance.ResourceLimits{ VolumeMounts: volumes,
MemoryInGB: to.Float64Ptr(memLimit),
CPU: to.Float64Ptr(cpuLimit),
},
Requests: &containerinstance.ResourceRequests{
MemoryInGB: to.Float64Ptr(memLimit), // TODO: use the memory requests here and not limits
CPU: to.Float64Ptr(cpuLimit), // TODO: use the cpu requests here and not limits
},
},
VolumeMounts: volumes,
}, },
}, nil }, nil
} }
func (s serviceConfigAciHelper) getResourceRequestsLimits() (*containerinstance.ResourceRequirements, error) {
memRequest := 1. // Default 1 Gb
var cpuRequest float64 = 1
var err error
hasMemoryRequest := func() bool {
return s.Deploy != nil && s.Deploy.Resources.Reservations != nil && s.Deploy.Resources.Reservations.MemoryBytes != 0
}
hasCPURequest := func() bool {
return s.Deploy != nil && s.Deploy.Resources.Reservations != nil && s.Deploy.Resources.Reservations.NanoCPUs != ""
}
if hasMemoryRequest() {
memRequest = bytesToGb(s.Deploy.Resources.Reservations.MemoryBytes)
}
if hasCPURequest() {
cpuRequest, err = strconv.ParseFloat(s.Deploy.Resources.Reservations.NanoCPUs, 0)
if err != nil {
return nil, err
}
}
memLimit := memRequest
cpuLimit := cpuRequest
if s.Deploy != nil && s.Deploy.Resources.Limits != nil {
if s.Deploy.Resources.Limits.MemoryBytes != 0 {
memLimit = bytesToGb(s.Deploy.Resources.Limits.MemoryBytes)
if !hasMemoryRequest() {
memRequest = memLimit
}
}
if s.Deploy.Resources.Limits.NanoCPUs != "" {
cpuLimit, err = strconv.ParseFloat(s.Deploy.Resources.Limits.NanoCPUs, 0)
if err != nil {
return nil, err
}
if !hasCPURequest() {
cpuRequest = cpuLimit
}
}
}
resources := containerinstance.ResourceRequirements{
Requests: &containerinstance.ResourceRequests{
MemoryInGB: to.Float64Ptr(memRequest),
CPU: to.Float64Ptr(cpuRequest),
},
Limits: &containerinstance.ResourceLimits{
MemoryInGB: to.Float64Ptr(memLimit),
CPU: to.Float64Ptr(cpuLimit),
},
}
return &resources, nil
}
func getEnvVariables(composeEnv types.MappingWithEquals) *[]containerinstance.EnvironmentVariable { func getEnvVariables(composeEnv types.MappingWithEquals) *[]containerinstance.EnvironmentVariable {
result := []containerinstance.EnvironmentVariable{} result := []containerinstance.EnvironmentVariable{}
for key, value := range composeEnv { for key, value := range composeEnv {
@ -413,6 +444,10 @@ func bytesToGb(b types.UnitBytes) float64 {
return math.Round(f*100) / 100 return math.Round(f*100) / 100
} }
func gbToBytes(memInBytes float64) uint64 {
return uint64(memInBytes * 1024 * 1024 * 1024)
}
// ContainerGroupToServiceStatus convert from an ACI container definition to service status // ContainerGroupToServiceStatus convert from an ACI container definition to service status
func ContainerGroupToServiceStatus(containerID string, group containerinstance.ContainerGroup, container containerinstance.Container, region string) compose.ServiceStatus { func ContainerGroupToServiceStatus(containerID string, group containerinstance.ContainerGroup, container containerinstance.Container, region string) compose.ServiceStatus {
var replicas = 1 var replicas = 1
@ -438,18 +473,27 @@ func fqdn(group containerinstance.ContainerGroup, region string) string {
// ContainerGroupToContainer composes a Container from an ACI container definition // ContainerGroupToContainer composes a Container from an ACI container definition
func ContainerGroupToContainer(containerID string, cg containerinstance.ContainerGroup, cc containerinstance.Container, region string) containers.Container { func ContainerGroupToContainer(containerID string, cg containerinstance.ContainerGroup, cc containerinstance.Container, region string) containers.Container {
memLimits := 0. memLimits := uint64(0)
if cc.Resources != nil && memRequest := uint64(0)
cc.Resources.Limits != nil &&
cc.Resources.Limits.MemoryInGB != nil {
memLimits = *cc.Resources.Limits.MemoryInGB * 1024 * 1024 * 1024
}
cpuLimit := 0. cpuLimit := 0.
if cc.Resources != nil && cpuReservation := 0.
cc.Resources.Limits != nil && if cc.Resources != nil {
cc.Resources.Limits.CPU != nil { if cc.Resources.Limits != nil {
cpuLimit = *cc.Resources.Limits.CPU if cc.Resources.Limits.MemoryInGB != nil {
memLimits = gbToBytes(*cc.Resources.Limits.MemoryInGB)
}
if cc.Resources.Limits.CPU != nil {
cpuLimit = *cc.Resources.Limits.CPU
}
}
if cc.Resources.Requests != nil {
if cc.Resources.Requests.MemoryInGB != nil {
memRequest = gbToBytes(*cc.Resources.Requests.MemoryInGB)
}
if cc.Resources.Requests.CPU != nil {
cpuReservation = *cc.Resources.Requests.CPU
}
}
} }
command := "" command := ""
@ -468,26 +512,30 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
} }
} }
var config *containers.RuntimeConfig = &containers.RuntimeConfig{FQDN: fqdn(cg, region)} config := &containers.RuntimeConfig{
if envVars != nil { FQDN: fqdn(cg, region),
config.Env = envVars Env: envVars,
}
hostConfig := &containers.HostConfig{
CPULimit: cpuLimit,
CPUReservation: cpuReservation,
MemoryLimit: memLimits,
MemoryReservation: memRequest,
RestartPolicy: toContainerRestartPolicy(cg.RestartPolicy),
} }
c := containers.Container{ c := containers.Container{
ID: containerID, ID: containerID,
Status: status, Status: status,
Image: to.String(cc.Image), Image: to.String(cc.Image),
Command: command, Command: command,
CPUTime: 0, CPUTime: 0,
CPULimit: cpuLimit, MemoryUsage: 0,
MemoryUsage: 0, PidsCurrent: 0,
MemoryLimit: uint64(memLimits), PidsLimit: 0,
PidsCurrent: 0, Ports: ToPorts(cg.IPAddress, *cc.Ports),
PidsLimit: 0, Platform: platform,
Labels: nil, Config: config,
Ports: ToPorts(cg.IPAddress, *cc.Ports), HostConfig: hostConfig,
Platform: platform,
RestartPolicyCondition: toContainerRestartPolicy(cg.RestartPolicy),
Config: config,
} }
return c return c

View File

@ -83,6 +83,10 @@ func TestContainerGroupToContainer(t *testing.T) {
Resources: &containerinstance.ResourceRequirements{ Resources: &containerinstance.ResourceRequirements{
Limits: &containerinstance.ResourceLimits{ Limits: &containerinstance.ResourceLimits{
CPU: to.Float64Ptr(3), CPU: to.Float64Ptr(3),
MemoryInGB: to.Float64Ptr(0.2),
},
Requests: &containerinstance.ResourceRequests{
CPU: to.Float64Ptr(2),
MemoryInGB: to.Float64Ptr(0.1), MemoryInGB: to.Float64Ptr(0.1),
}, },
}, },
@ -90,13 +94,11 @@ func TestContainerGroupToContainer(t *testing.T) {
} }
var expectedContainer = containers.Container{ var expectedContainer = containers.Container{
ID: "myContainerID", ID: "myContainerID",
Status: "Running", Status: "Running",
Image: "sha256:666", Image: "sha256:666",
Command: "mycommand", Command: "mycommand",
CPULimit: 3, Platform: "Linux",
MemoryLimit: 107374182,
Platform: "Linux",
Ports: []containers.Port{{ Ports: []containers.Port{{
HostPort: uint32(80), HostPort: uint32(80),
ContainerPort: uint32(80), ContainerPort: uint32(80),
@ -106,7 +108,13 @@ func TestContainerGroupToContainer(t *testing.T) {
Config: &containers.RuntimeConfig{ Config: &containers.RuntimeConfig{
FQDN: "myapp.eastus.azurecontainer.io", FQDN: "myapp.eastus.azurecontainer.io",
}, },
RestartPolicyCondition: "any", HostConfig: &containers.HostConfig{
CPULimit: 3,
CPUReservation: 2,
MemoryLimit: gbToBytes(0.2),
MemoryReservation: gbToBytes(0.1),
RestartPolicy: "any",
},
} }
container := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer, "eastus") container := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer, "eastus")
@ -536,8 +544,9 @@ func TestComposeContainerGroupToContainerIgnoreDomainNameWithoutPorts(t *testing
assert.Assert(t, group.IPAddress == nil) assert.Assert(t, group.IPAddress == nil)
} }
func TestComposeContainerGroupToContainerResourceLimits(t *testing.T) { var _0_1Gb = gbToBytes(0.1)
_0_1Gb := 0.1 * 1024 * 1024 * 1024
func TestComposeContainerGroupToContainerResourceRequests(t *testing.T) {
project := types.Project{ project := types.Project{
Services: []types.ServiceConfig{ Services: []types.ServiceConfig{
{ {
@ -545,7 +554,7 @@ func TestComposeContainerGroupToContainerResourceLimits(t *testing.T) {
Image: "image1", Image: "image1",
Deploy: &types.DeployConfig{ Deploy: &types.DeployConfig{
Resources: types.Resources{ Resources: types.Resources{
Limits: &types.Resource{ Reservations: &types.Resource{
NanoCPUs: "0.1", NanoCPUs: "0.1",
MemoryBytes: types.UnitBytes(_0_1Gb), MemoryBytes: types.UnitBytes(_0_1Gb),
}, },
@ -558,12 +567,48 @@ func TestComposeContainerGroupToContainerResourceLimits(t *testing.T) {
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper) group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
assert.NilError(t, err) assert.NilError(t, err)
request := *((*group.Containers)[0]).Resources.Requests
assert.Equal(t, *request.CPU, float64(0.1))
assert.Equal(t, *request.MemoryInGB, float64(0.1))
limits := *((*group.Containers)[0]).Resources.Limits limits := *((*group.Containers)[0]).Resources.Limits
assert.Equal(t, *limits.CPU, float64(0.1)) assert.Equal(t, *limits.CPU, float64(0.1))
assert.Equal(t, *limits.MemoryInGB, float64(0.1)) assert.Equal(t, *limits.MemoryInGB, float64(0.1))
} }
func TestComposeContainerGroupToContainerResourceLimitsDefaults(t *testing.T) { func TestComposeContainerGroupToContainerResourceRequestsAndLimits(t *testing.T) {
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Deploy: &types.DeployConfig{
Resources: types.Resources{
Reservations: &types.Resource{
NanoCPUs: "0.1",
MemoryBytes: types.UnitBytes(_0_1Gb),
},
Limits: &types.Resource{
NanoCPUs: "0.3",
MemoryBytes: types.UnitBytes(2 * _0_1Gb),
},
},
},
},
},
}
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
assert.NilError(t, err)
request := *((*group.Containers)[0]).Resources.Requests
assert.Equal(t, *request.CPU, float64(0.1))
assert.Equal(t, *request.MemoryInGB, float64(0.1))
limits := *((*group.Containers)[0]).Resources.Limits
assert.Equal(t, *limits.CPU, float64(0.3))
assert.Equal(t, *limits.MemoryInGB, float64(0.2))
}
func TestComposeContainerGroupToContainerResourceLimitsOnly(t *testing.T) {
project := types.Project{ project := types.Project{
Services: []types.ServiceConfig{ Services: []types.ServiceConfig{
{ {
@ -572,6 +617,35 @@ func TestComposeContainerGroupToContainerResourceLimitsDefaults(t *testing.T) {
Deploy: &types.DeployConfig{ Deploy: &types.DeployConfig{
Resources: types.Resources{ Resources: types.Resources{
Limits: &types.Resource{ Limits: &types.Resource{
NanoCPUs: "0.3",
MemoryBytes: types.UnitBytes(2 * _0_1Gb),
},
},
},
},
},
}
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
assert.NilError(t, err)
request := *((*group.Containers)[0]).Resources.Requests
assert.Equal(t, *request.CPU, float64(0.3))
assert.Equal(t, *request.MemoryInGB, float64(0.2))
limits := *((*group.Containers)[0]).Resources.Limits
assert.Equal(t, *limits.CPU, float64(0.3))
assert.Equal(t, *limits.MemoryInGB, float64(0.2))
}
func TestComposeContainerGroupToContainerResourceRequestsDefaults(t *testing.T) {
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Deploy: &types.DeployConfig{
Resources: types.Resources{
Reservations: &types.Resource{
NanoCPUs: "", NanoCPUs: "",
MemoryBytes: 0, MemoryBytes: 0,
}, },
@ -584,9 +658,9 @@ func TestComposeContainerGroupToContainerResourceLimitsDefaults(t *testing.T) {
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper) group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
assert.NilError(t, err) assert.NilError(t, err)
limits := *((*group.Containers)[0]).Resources.Limits request := *((*group.Containers)[0]).Resources.Requests
assert.Equal(t, *limits.CPU, float64(1)) assert.Equal(t, *request.CPU, float64(1))
assert.Equal(t, *limits.MemoryInGB, float64(1)) assert.Equal(t, *request.MemoryInGB, float64(1))
} }
func TestComposeContainerGroupToContainerenvVar(t *testing.T) { func TestComposeContainerGroupToContainerenvVar(t *testing.T) {

View File

@ -37,30 +37,37 @@ var RestartPolicyList = []string{RestartPolicyNone, RestartPolicyAny, RestartPol
// Container represents a created container // Container represents a created container
type Container struct { type Container struct {
ID string ID string
Status string Status string
Image string Image string
Command string Command string
CPUTime uint64 CPUTime uint64
CPULimit float64 MemoryUsage uint64
MemoryUsage uint64 PidsCurrent uint64
MemoryLimit uint64 PidsLimit uint64
PidsCurrent uint64 Config *RuntimeConfig `json:",omitempty"`
PidsLimit uint64 HostConfig *HostConfig `json:",omitempty"`
Config *RuntimeConfig `json:",omitempty"` Ports []Port `json:",omitempty"`
Labels []string `json:",omitempty"` Platform string
Ports []Port `json:",omitempty"`
Platform string
RestartPolicyCondition string
} }
// RuntimeConfig config of a created container // RuntimeConfig config of a created container
type RuntimeConfig struct { type RuntimeConfig struct {
Env map[string]string `json:",omitempty"` Labels []string `json:",omitempty"`
Env map[string]string `json:",omitempty"`
// FQDN is the fqdn to use // FQDN is the fqdn to use
FQDN string `json:"fqdn,omitempty"` FQDN string `json:"fqdn,omitempty"`
} }
// HostConfig config of the container host
type HostConfig struct {
RestartPolicy string
CPUReservation float64
CPULimit float64
MemoryReservation uint64
MemoryLimit uint64
}
// Port represents a published port of a container // Port represents a published port of a container
type Port struct { type Port struct {
// HostPort is the port number on the host // HostPort is the port number on the host

View File

@ -4,11 +4,15 @@
"Image": "nginx", "Image": "nginx",
"Command": "", "Command": "",
"CPUTime": 0, "CPUTime": 0,
"CPULimit": 0,
"MemoryUsage": 0, "MemoryUsage": 0,
"MemoryLimit": 0,
"PidsCurrent": 0, "PidsCurrent": 0,
"PidsLimit": 0, "PidsLimit": 0,
"Platform": "Linux", "HostConfig": {
"RestartPolicyCondition": "none" "RestartPolicy": "none",
"CPUReservation": 0,
"CPULimit": 0,
"MemoryReservation": 0,
"MemoryLimit": 0
},
"Platform": "Linux"
} }

View File

@ -71,7 +71,9 @@ func (cs *containerService) Inspect(ctx context.Context, id string) (containers.
ID: "id", ID: "id",
Image: "nginx", Image: "nginx",
Platform: "Linux", Platform: "Linux",
RestartPolicyCondition: "none", HostConfig: &containers.HostConfig{
RestartPolicy: "none",
},
}, nil }, nil
} }

File diff suppressed because it is too large Load Diff

View File

@ -45,14 +45,20 @@ message Container {
string command = 4; string command = 4;
uint64 cpu_time = 5; uint64 cpu_time = 5;
uint64 memory_usage = 6; uint64 memory_usage = 6;
uint64 memory_limit = 7; uint64 pids_current = 7;
uint64 pids_current = 8; uint64 pids_limit = 8;
uint64 pids_limit = 9; repeated string labels = 9;
repeated string labels = 10; repeated Port ports = 10;
repeated Port ports = 11; string platform = 11;
uint64 cpu_limit = 12; HostConfig host_config = 12;
string platform = 13; }
string restart_policy_condition = 14;
message HostConfig {
uint64 memory_reservation = 1;
uint64 memory_limit = 2;
uint64 cpu_reservation = 3;
uint64 cpu_limit = 4;
string restart_policy = 5;
} }
message InspectRequest { message InspectRequest {

View File

@ -122,20 +122,24 @@ func (p *proxy) Logs(request *containersv1.LogsRequest, stream containersv1.Cont
func toGrpcContainer(c containers.Container) *containersv1.Container { func toGrpcContainer(c containers.Container) *containersv1.Container {
return &containersv1.Container{ return &containersv1.Container{
Id: c.ID, Id: c.ID,
Image: c.Image, Image: c.Image,
Status: c.Status, Status: c.Status,
Command: c.Command, Command: c.Command,
CpuTime: c.CPUTime, CpuTime: c.CPUTime,
MemoryUsage: c.MemoryUsage, MemoryUsage: c.MemoryUsage,
MemoryLimit: c.MemoryLimit, Platform: c.Platform,
Platform: c.Platform, PidsCurrent: c.PidsCurrent,
PidsCurrent: c.PidsCurrent, PidsLimit: c.PidsLimit,
PidsLimit: c.PidsLimit, Labels: c.Config.Labels,
Labels: c.Labels, Ports: portsToGrpc(c.Ports),
Ports: portsToGrpc(c.Ports), HostConfig: &containersv1.HostConfig{
CpuLimit: uint64(c.CPULimit), MemoryReservation: c.HostConfig.MemoryReservation,
RestartPolicyCondition: c.RestartPolicyCondition, MemoryLimit: c.HostConfig.MemoryLimit,
CpuReservation: uint64(c.HostConfig.CPUReservation),
CpuLimit: uint64(c.HostConfig.CPULimit),
RestartPolicy: c.HostConfig.RestartPolicy,
},
} }
} }

View File

@ -249,8 +249,9 @@ func TestRunVolume(t *testing.T) {
containerInspect, err := ParseContainerInspect(res.Stdout()) containerInspect, err := ParseContainerInspect(res.Stdout())
assert.NilError(t, err) assert.NilError(t, err)
assert.Equal(t, containerInspect.Platform, "Linux") assert.Equal(t, containerInspect.Platform, "Linux")
assert.Equal(t, containerInspect.CPULimit, 1.0) assert.Equal(t, containerInspect.HostConfig.CPULimit, 1.0)
assert.Equal(t, containerInspect.RestartPolicyCondition, containers.RestartPolicyNone) assert.Equal(t, containerInspect.HostConfig.CPUReservation, 1.0)
assert.Equal(t, containerInspect.HostConfig.RestartPolicy, containers.RestartPolicyNone)
assert.Assert(t, is.Len(containerInspect.Ports, 1)) assert.Assert(t, is.Len(containerInspect.Ports, 1))
hostIP = containerInspect.Ports[0].HostIP hostIP = containerInspect.Ports[0].HostIP
@ -388,16 +389,18 @@ func TestContainerRunAttached(t *testing.T) {
} }
return poll.Continue("waiting for container to be running, current inspect result: \n%s", res.Combined()) return poll.Continue("waiting for container to be running, current inspect result: \n%s", res.Combined())
} }
poll.WaitOn(t, checkRunning, poll.WithDelay(5*time.Second), poll.WithTimeout(60*time.Second)) poll.WaitOn(t, checkRunning, poll.WithDelay(5*time.Second), poll.WithTimeout(90*time.Second))
inspectRes := c.RunDockerCmd("inspect", container) inspectRes := c.RunDockerCmd("inspect", container)
containerInspect, err := ParseContainerInspect(inspectRes.Stdout()) containerInspect, err := ParseContainerInspect(inspectRes.Stdout())
assert.NilError(t, err) assert.NilError(t, err)
assert.Equal(t, containerInspect.Platform, "Linux") assert.Equal(t, containerInspect.Platform, "Linux")
assert.Equal(t, containerInspect.CPULimit, 0.1) assert.Equal(t, containerInspect.HostConfig.CPULimit, 0.1)
assert.Equal(t, containerInspect.MemoryLimit, uint64(107374182)) assert.Equal(t, containerInspect.HostConfig.MemoryLimit, uint64(107374182))
assert.Equal(t, containerInspect.RestartPolicyCondition, containers.RestartPolicyOnFailure) assert.Equal(t, containerInspect.HostConfig.CPUReservation, 0.1)
assert.Equal(t, containerInspect.HostConfig.MemoryReservation, uint64(107374182))
assert.Equal(t, containerInspect.HostConfig.RestartPolicy, containers.RestartPolicyOnFailure)
assert.Assert(t, is.Len(containerInspect.Ports, 1)) assert.Assert(t, is.Len(containerInspect.Ports, 1))
port := containerInspect.Ports[0] port := containerInspect.Ports[0]
@ -480,6 +483,39 @@ func overwriteFileStorageAccount(t *testing.T, absComposefileName string, storag
assert.NilError(t, err) assert.NilError(t, err)
} }
func TestUpResources(t *testing.T) {
const (
composeProjectName = "testresources"
serverContainer = composeProjectName + "_web"
wordsContainer = composeProjectName + "_words"
)
c := NewParallelE2eCLI(t, binDir)
setupTestResourceGroup(t, c)
t.Run("compose up", func(t *testing.T) {
c.RunDockerCmd("compose", "up", "-f", "../composefiles/aci-demo/aci_demo_port_resources.yaml", "--project-name", composeProjectName)
res := c.RunDockerCmd("inspect", serverContainer)
webInspect, err := ParseContainerInspect(res.Stdout())
assert.NilError(t, err)
assert.Equal(t, webInspect.HostConfig.CPULimit, 0.7)
assert.Equal(t, webInspect.HostConfig.MemoryLimit, uint64(1073741824))
assert.Equal(t, webInspect.HostConfig.CPUReservation, 0.5)
assert.Equal(t, webInspect.HostConfig.MemoryReservation, uint64(536870912))
res = c.RunDockerCmd("inspect", wordsContainer)
wordsInspect, err := ParseContainerInspect(res.Stdout())
assert.NilError(t, err)
assert.Equal(t, wordsInspect.HostConfig.CPULimit, 0.5)
assert.Equal(t, wordsInspect.HostConfig.MemoryLimit, uint64(751619276))
assert.Equal(t, wordsInspect.HostConfig.CPUReservation, 0.5)
assert.Equal(t, wordsInspect.HostConfig.MemoryReservation, uint64(751619276))
})
}
func TestUpUpdate(t *testing.T) { func TestUpUpdate(t *testing.T) {
const ( const (
composeProjectName = "acidemo" composeProjectName = "acidemo"

View File

@ -1,14 +0,0 @@
services:
db:
build: db
image: gtardif/sentences-db
words:
build: words
image: gtardif/sentences-api
web:
build: web
image: gtardif/sentences-web
ports:
- "80:80"

View File

@ -0,0 +1,24 @@
services:
db:
image: gtardif/sentences-db
words:
image: gtardif/sentences-api
deploy:
resources:
reservations:
cpus: '0.5'
memory: 0.7G
web:
image: gtardif/sentences-web
ports:
- "80:80"
deploy:
resources:
limits:
cpus: '0.7'
memory: 1G
reservations:
cpus: '0.5'
memory: 0.5G

View File

@ -4,11 +4,15 @@
"Image": "nginx", "Image": "nginx",
"Command": "", "Command": "",
"CPUTime": 0, "CPUTime": 0,
"CPULimit": 0,
"MemoryUsage": 0, "MemoryUsage": 0,
"MemoryLimit": 0,
"PidsCurrent": 0, "PidsCurrent": 0,
"PidsLimit": 0, "PidsLimit": 0,
"Platform": "Linux", "HostConfig": {
"RestartPolicyCondition": "none" "RestartPolicy": "none",
"CPUReservation": 0,
"CPULimit": 0,
"MemoryReservation": 0,
"MemoryLimit": 0
},
"Platform": "Linux"
} }