Skip to content
This repository was archived by the owner on May 12, 2021. It is now read-only.

virtcontainers/qemu: reduce memory footprint#296

Merged
egernst merged 2 commits intokata-containers:masterfrom
devimc:cpu/fixMemFootprint
May 15, 2018
Merged

virtcontainers/qemu: reduce memory footprint#296
egernst merged 2 commits intokata-containers:masterfrom
devimc:cpu/fixMemFootprint

Conversation

@devimc
Copy link
Copy Markdown

@devimc devimc commented May 9, 2018

There is a relation between the maximum number of vCPUs and the
memory footprint, if QEMU maxcpus option and kernel nr_cpus
cmdline argument are big, then memory footprint is big, this
issue only occurs if CPU hotplug support is enabled in the kernel,
might be because of kernel needs to allocate resources to watch all
sockets waiting for a CPU to be connected (ACPI event).

For example

+---------------+-------------------------+
|               | Memory Footprint (KB)   |
+---------------+-------------------------+
| NR_CPUS=240   | 186501                  |
+---------------+-------------------------+
| NR_CPUS=8     | 110684                  |
+---------------+-------------------------+

In order to do not affect CPU hotplug and allow to users to have containers
with the same number of physical CPUs, this patch tries to mitigate the
big memory footprint by using the actual number of physical CPUs as the
maximum number of vCPUs for each container.

Before this patch a container with 256MB of RAM

              total        used        free      shared  buff/cache   available
Mem:           195M         40M        113M         26M         41M        112M
Swap:            0B          0B          0B

With this patch

              total        used        free      shared  buff/cache   available
Mem:           236M         11M        188M         26M         36M        186M
Swap:            0B          0B          0B

fixes #295

Signed-off-by: Julio Montes julio.montes@intel.com

@devimc
Copy link
Copy Markdown
Author

devimc commented May 9, 2018

@devimc devimc force-pushed the cpu/fixMemFootprint branch from 063f4f4 to ded5fad Compare May 9, 2018 20:16
@bergwolf
Copy link
Copy Markdown
Member

bergwolf commented May 10, 2018

Nice findings! I wasn't aware the maxcpus limit can have so much impact on memory footprint! Thanks @devimc ! And it

LGTM!

Approved with PullApprove

@egernst
Copy link
Copy Markdown
Member

egernst commented May 10, 2018

LGTM, though I see the CI failures.

--- FAIL: TestQemuKernelParameters (0.00s)
qemu_test.go:50: Got: panic=1 initcall_debug nr_cpus=4 foo=foo bar=bar, Expecting: panic=1 initcall_debug foo=foo bar=bar

Perhaps the test needs updating?

Approved with PullApprove

@gnawux
Copy link
Copy Markdown
Member

gnawux commented May 10, 2018

Awesome

Never noticed the impaction of maxcpus

@WeiZhang555
Copy link
Copy Markdown
Member

This is a great improvement!
One question, should we make this configurable ? Then the service provider can choose to make the VM smaller via a smaller maxcpus

@ChristophSGR
Copy link
Copy Markdown

ChristophSGR commented May 10, 2018 via email

@egernst
Copy link
Copy Markdown
Member

egernst commented May 10, 2018

@WeiZhang555 @ChristophSGR - Yes I think we should. @devimc is OOO today -- I'm sure he can take a look tomorrow (unless someone wants to add a patch to this PR in the meantime!)

@gnawux
Copy link
Copy Markdown
Member

gnawux commented May 11, 2018

I thought about this ("make this configurable") as well, but I'd like to accept this patch and have another patch on configurable things if @devimc do not have a slot on this.

@egernst
Copy link
Copy Markdown
Member

egernst commented May 11, 2018

@gnawux - sure this is fine. I think we shoudl get this in ASAP for sure. I want to wait until we can get the tests update (seems like a legit CI failure)

@gnawux
Copy link
Copy Markdown
Member

gnawux commented May 11, 2018

@egernst agree

// returns the maximum number of vCPUs supported
func maxQemuVCPUs() uint32 {
return uint32(240)
return uint32(runtime.NumCPU())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will be some problems if the physical machine has more than 240 physical CPUs, right?
I think we should choose MAX(runtime.NumCPU(), 240) here

@WeiZhang555
Copy link
Copy Markdown
Member

Sounds fair enough to merge this first, after fixing the CI problem.

@devimc devimc force-pushed the cpu/fixMemFootprint branch from ded5fad to b958d95 Compare May 11, 2018 12:22
@devimc
Copy link
Copy Markdown
Author

devimc commented May 11, 2018

CI is not happy because of Azure VM has 4 CPUs and CRIO tries to create a container with 5 vCPUs

# time="2018-05-11 13:06:20.495616418Z" level=error msg="Container creation error: Unable to hotplug 2 CPUs, currently this SB has 3 CPUs and the maximum amount of CPUs is 4

hence I need to make maxvcpus configurable in this patch

@devimc devimc force-pushed the cpu/fixMemFootprint branch 7 times, most recently from 5444864 to ff26a40 Compare May 13, 2018 17:32
@devimc
Copy link
Copy Markdown
Author

devimc commented May 13, 2018

please take a look, all these changes are needed to reduce memory footprint and make CRIO happy

cli/config.go Outdated
return uint32(numcpus)
}

if h.DefaultMaxVCPUs > int32(numcpus) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this comparison?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, in case of user value is greater than the actual number of physical CPUs, we need to check this values doesn't exceed the maximum number of vCPUs supported by QEMU/KVM.
Adding a comment in the code.
Thanks

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohhh wait.. docker and kubernetes don't allow you to create containers with a number of CPUs greater than the actual number of physical CPUs, hence we don't need this condition, thanks

$ docker run --cpus 10 -ti centos bash
docker: Error response from daemon: Range of CPUs is from 0.01 to 8.00, as there are only 8 CPUs available.

Copy link
Copy Markdown
Member

@egernst egernst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few queries here. Looks good in general, and like the additional commit.

// to reach out max vCPUs
if currentVCPUs+amount > q.config.DefaultMaxVCPUs {
return fmt.Errorf("Unable to hotplug %d CPUs, currently this SB has %d CPUs and the maximum amount of CPUs is %d",
q.Logger().Warnf("Cannot to hotplug %d CPUs, currently this SB has %d CPUs and the maximum amount of CPUs is %d",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit s/to//

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! thanks

cli/config.go Outdated
KernelParams string `toml:"kernel_params"`
MachineType string `toml:"machine_type"`
DefaultVCPUs int32 `toml:"default_vcpus"`
DefaultMaxVCPUs int32 `toml:"default_maxvcpus"`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why signed int? Is there a scenario where you'd have a negative number number of CPUs?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is part of configuration file, if this value is <= 0 then the actual number of vCPUs will be used

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my question is why negative needs to be supported? Seems == 0 should be enough here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, it should be enough, do you prefer to have an uint32 here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used an int32 to support negative values and be similar to default_vcpus

cli/config.go Outdated
numcpus := goruntime.NumCPU()
maxvcpus := vc.MaxQemuVCPUs()

if h.DefaultMaxVCPUs <= 0 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can the default be negative?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, same as DefaultVCPUs

return c.sandbox.agent.onlineCPUMem(uint32(vCPUs))
vcpusAdded, ok := data.(uint32)
if !ok {
return fmt.Errorf("Could not get the number of vCPUs added")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error is used multiple times so you could create a global for it maybe?

var errVCPUs = errors.New("Could not get the number of vCPUs added")

Having said that, it might be more useful to add the container ID to the returned error (which would justify using fmt.Errorf() rather than just errors.New(), which is all that is currently required).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll include data in the error, thanks

}

vCPUs := utils.ConstraintsToVCPUs(c.config.Resources.CPUQuota, c.config.Resources.CPUPeriod)
// fetch current configuration
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is almost identical to the one added in addResources(). Could you refactor to create a new function they can both call. Something like:

// if add is true, add the VCPUs in the container config, else remove them.
func (c *Container) handleVCPUs(add bool) error

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right, updating commit, thanks

@devimc devimc force-pushed the cpu/fixMemFootprint branch from ff26a40 to 2023f3c Compare May 14, 2018 14:33
@codecov
Copy link
Copy Markdown

codecov bot commented May 14, 2018

Codecov Report

Merging #296 into master will decrease coverage by <.01%.
The diff coverage is 46.72%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #296      +/-   ##
==========================================
- Coverage   64.35%   64.35%   -0.01%     
==========================================
  Files          86       86              
  Lines        8430     8469      +39     
==========================================
+ Hits         5425     5450      +25     
- Misses       2429     2441      +12     
- Partials      576      578       +2
Impacted Files Coverage Δ
virtcontainers/hypervisor.go 72.78% <ø> (ø) ⬆️
virtcontainers/hyperstart_agent.go 59.39% <ø> (+0.81%) ⬆️
virtcontainers/pkg/oci/utils.go 78.26% <0%> (+0.72%) ⬆️
virtcontainers/mock_hypervisor.go 100% <100%> (ø) ⬆️
virtcontainers/qemu_amd64.go 93.54% <100%> (ø) ⬆️
virtcontainers/qemu_arch_base.go 76.99% <100%> (ø) ⬆️
virtcontainers/container.go 47.5% <44.73%> (-0.57%) ⬇️
virtcontainers/qemu.go 12.02% <6.06%> (ø) ⬆️
virtcontainers/sandbox.go 67.35% <75%> (+0.05%) ⬆️
cli/config.go 88.5% <85.71%> (-0.34%) ⬇️
... and 2 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 90e3ba6...4527a80. Read the comment docs.

cli/config.go Outdated
return uint32(numcpus)
}

return uint32(h.DefaultMaxVCPUs)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is still a bit odd to me. What happens if default-provided is not greater than numcpus, but it is still greater than is greater than maxvcpus?
e.g., max-from-hypervisor is 100, number of physical cores is 120, but our configured default is 110?

Also I think you should s/QEMU/hypervisor?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g., max-from-hypervisor is 100, number of physical cores is 120, but our configured default is 110?

to avoid issues with KVM, the maximum number of CPUs supported by KVM is used, in this example 100

Also I think you should s/QEMU/hypervisor?

sure, I can do it, but in another PR, wdyt?

@devimc
Copy link
Copy Markdown
Author

devimc commented May 14, 2018

@egernst @jodh-intel @gnawux changes applied, thanks

@devimc devimc force-pushed the cpu/fixMemFootprint branch from 2023f3c to 92b57f7 Compare May 14, 2018 19:23
@devimc
Copy link
Copy Markdown
Author

devimc commented May 14, 2018

@egernst changes applied

@devimc devimc force-pushed the cpu/fixMemFootprint branch from 92b57f7 to e2db932 Compare May 14, 2018 20:25
cli/config.go Outdated

// Don't exceed the maximum number of vCPUs supported by hypervisor
if h.DefaultMaxVCPUs >= maxvcpus {
return maxvcpus
Copy link
Copy Markdown
Member

@egernst egernst May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if the default is also greater than numcpus? I think what you want is:

reqVCPUS := h.DefaultMaxVCPUS
//don't exceed the number of physical CPUs. If a default is not provided, use the
// numbers of physical CPUs
if reqVCPUS >= numcpus || reqVCPUS == 0 {
      reqVCPUS := numcpus
}

// Don't exceed the maximum number of vCPUs supported by hypervisor
if (reqVCPUS > maxvcpus )  {
   return maxvcpus
}
 
return reqVCPUS

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually is the same, but your code looks better, updating pr, thanks

Julio Montes added 2 commits May 14, 2018 17:33
There is a relation between the maximum number of vCPUs and the
memory footprint, if QEMU maxcpus option and kernel nr_cpus
cmdline argument are big, then memory footprint is big, this
issue only occurs if CPU hotplug support is enabled in the kernel,
might be because of kernel needs to allocate resources to watch all
sockets waiting for a CPU to be connected (ACPI event).

For example

```
+---------------+-------------------------+
|               | Memory Footprint (KB)   |
+---------------+-------------------------+
| NR_CPUS=240   | 186501                  |
+---------------+-------------------------+
| NR_CPUS=8     | 110684                  |
+---------------+-------------------------+
```

In order to do not affect CPU hotplug and allow to users to have containers
with the same number of physical CPUs, this patch tries to mitigate the
big memory footprint by using the actual number of physical CPUs as the
maximum number of vCPUs for each container if `default_maxvcpus` is <= 0 in
the runtime configuration file,  otherwise `default_maxvcpus` is used as the
maximum number of vCPUs.

Before this patch a container with 256MB of RAM

```
              total        used        free      shared  buff/cache   available
Mem:           195M         40M        113M         26M         41M        112M
Swap:            0B          0B          0B
```

With this patch

```
              total        used        free      shared  buff/cache   available
Mem:           236M         11M        188M         26M         36M        186M
Swap:            0B          0B          0B
```

fixes kata-containers#295

Signed-off-by: Julio Montes <julio.montes@intel.com>
Don't fail if a new container with a CPU constraint was added to
a POD and no more vCPUs are available, instead apply the constraint
and let kernel balance the resources.

Signed-off-by: Julio Montes <julio.montes@intel.com>
@devimc devimc force-pushed the cpu/fixMemFootprint branch from e2db932 to 4527a80 Compare May 14, 2018 22:33
@egernst egernst merged commit 90fc7e6 into kata-containers:master May 15, 2018
@devimc devimc deleted the cpu/fixMemFootprint branch August 3, 2018 17:52
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kata containers memory footprint

7 participants