Skip to content

Windows: don't override size storage-option if it's set#40256

Closed
thaJeztah wants to merge 1 commit intomoby:masterfrom
thaJeztah:windows_dont_override_custom_size
Closed

Windows: don't override size storage-option if it's set#40256
thaJeztah wants to merge 1 commit intomoby:masterfrom
thaJeztah:windows_dont_override_custom_size

Conversation

@thaJeztah
Copy link
Copy Markdown
Member

@thaJeztah thaJeztah commented Nov 26, 2019

Splitting this from #39846

Partially addresses #34947 (#34947 (comment))

Only set the size option if no custom size was configured on the daemon.

Previously, the storage-driver size for hyper-v containers used during build was always overridden with a fixed 127GB value. While this made sense to allow for building images that required more space than the default (40GB) size, it also caused a custom size configured on the daemon to be ignored (for example, situations where the size is explicitly limited to a lower size, or situations where a size larger than 127GB was needed).

This patch only increases the default (40GB) size for containers used during build, if no custom value was configured on the daemon.

- Description for the changelog

* Windows: respect custom storage-driver size for hyper-v containers used during build

@thaJeztah thaJeztah added area/builder Build platform/windows status/2-code-review kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. impact/changelog labels Nov 26, 2019
@thaJeztah thaJeztah force-pushed the windows_dont_override_custom_size branch from cb3e322 to dccecf5 Compare January 7, 2020 10:58
Comment thread builder/dockerfile/internals.go Outdated
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.

Suggested change
if isWCOW && (len(hc.StorageOpt) == 0 || len(hc.StorageOpt) > 0 && hc.StorageOpt["size"] == "") {
if isWCOW && hc.StorageOpt["size"] == {""

Unless I'm missing something this seems like what we'd want.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Forgot about this one; yes, looks like all the extra checks are not needed

@thaJeztah
Copy link
Copy Markdown
Member Author

rebased to get a fresh run of CI

@cpuguy83 @tonistiigi PTAL

// are started during build, as that's the default size of
// a VHD in Hyper-V.
if isWCOW && hc.StorageOpt["size"] == "" {
if hc.StorageOpt == nil {
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.

hc.StorageOpt can't be nil here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Had to double-check, but yes, it can be; https://play.golang.org/p/bD7H35U2wdZ

package main

import (
	"fmt"
)

type HostConfig struct {
	StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container.
}

func main() {
	hc := &HostConfig{}

	if hc.StorageOpt["size"] == "" {
		if hc.StorageOpt == nil {
			fmt.Println("StorageOpt is nil")
		}

		hc.StorageOpt["size"] = "127GB"
	}
}

Running that will panic;

StorageOpt is nil
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
	/tmp/sandbox898184674/prog.go:19 +0xa8

Only set the size option if no custom size was configured
on the daemon.

Previously, the storage-driver size for hyper-v containers
used during build was always overridden with a fixed 127GB
value. While this made sense to allow for building images
that required more space than the default (40GB) size, it
also caused a _custom_ size configured on the daemon to be
ignored (for example, situations where the size is explicitly
limited to a _lower_ size, or situations where a size larger
than 127GB was needed).

This patch only increases the default (40GB) size for containers
used during build, if no custom value was configured on the daemon.

Signed-off-by: Sebastiaan van Stijn <[email protected]>
@thaJeztah thaJeztah force-pushed the windows_dont_override_custom_size branch from 0bbd7eb to 315101e Compare November 5, 2020 11:11
@thaJeztah
Copy link
Copy Markdown
Member Author

@cpuguy83 @AkihiroSuda rebased; ptal

// If no custom size is set, use 127GB for containers that
// are started during build, as that's the default size of
// a VHD in Hyper-V.
if isWCOW && hc.StorageOpt["size"] == "" {
Copy link
Copy Markdown
Contributor

@TBBle TBBle Nov 5, 2020

Choose a reason for hiding this comment

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

While you're at it, you might want to look at the definition of isWCOW on line 451. Currently, it's true if you run docker build . --platform windows but false if you run docker build .. I was just now looking into whether it should be inverted to be

isWCOW := runtime.GOOS == "windows" && (b.platform == nil || b.platform.OS == "windows")

Edit: Confirmed that with the above change, the "127GB overrides storage-opts" now replicates both with and without --platform windows.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh! Comments crossed with the last comment I left on #37352 (comment)

Ah, interesting; sounds like more fun because of the LCOW paths (I guess it doesn't set WCOW if it doesn't know if the image is gonna be Linux or Windows?) haven't checked all paths

Should we remove the isWCOW there? It would be a somewhat closer match to "linux" (where by default size is only restricted by physical disk space)and replace with if runtime.GOOS == "windows"

WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LCOW has its own option, lcow.sandboxsize, so size would be ignored by LCOW. We can't validate that because the config is shared between WCOW and LCOW. -_-

I would expect that if runtime.GOOS == "windows", then LCOW would only happen if b.platform.OS == "linux", which I assume is what this check was intended to do.

Or perhaps at the time, b.platform would not reasonably be nil, but now we have things that check platform == nil and fallback to runtime.GOOS, e.g., createCopyInstruction in builder/dockerfile/copy.go.

Anyway, given that, a simple check for runtime.GOOS would be okay, since we know the only graphdrivers that run on Windows are lcow and windowsfilter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LCOW is also a 20GB default, it might make sense that we set both size= and lcow.sandboxsize= to 127GB here.

// If no custom size is set, use 127GB for containers that
// are started during build, as that's the default size of
// a VHD in Hyper-V.
if isWCOW && hc.StorageOpt["size"] == "" {
Copy link
Copy Markdown
Contributor

@TBBle TBBle Nov 5, 2020

Choose a reason for hiding this comment

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

How can hc.StorageOpt ever be set here? hc is defined 14 lines earlier, and doesn't set the StorageOpt member, so won't it always be nil here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think maybe this fix is in the wrong place. The values set here are merged with the daemon's config values later, in create in daemon/create.go, which is called with the output of this method. See line 190 of daemon/create.go. There, the logic is for the StorageOpts created here to override the daemon config, but it should be the other way 'round since the StorageOpts here are defaults.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🤦 You are absolutely right. Thinking how I came to this change at the time, as it's a while, but looks like the daemon config is merged in

moby/daemon/create.go

Lines 195 to 205 in 8a4671f

if isWindows {
if ctr.HostConfig.StorageOpt == nil {
ctr.HostConfig.StorageOpt = make(map[string]string)
}
for _, v := range daemon.configStore.GraphOptions {
opt := strings.SplitN(v, "=", 2)
if _, ok := ctr.HostConfig.StorageOpt[opt[0]]; !ok {
ctr.HostConfig.StorageOpt[opt[0]] = opt[1]
}
}
}

🤔

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Alternatively, move this entire chunk of logic to set a default sandbox size, into daemon/create.go after the merge. It makes now different now, but if we ever find a way to get docker build to pass storage opts, then we'd want those to win over the daemon.json and the default 127GB sandbox.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Perhaps we should hold this change until after we cut the 20.10 branch, and when we start removing the LCOW paths (I'd also personally be ok with increasing the size to a higher value if 127GB is still too low for some scenarios); IIRC, setting a higher value shouldn't affect performance when creating the container (and would only make a difference if someone actually used the given amount of space)

Copy link
Copy Markdown
Contributor

@TBBle TBBle Nov 5, 2020

Choose a reason for hiding this comment

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

The advantage of moving this default-size logic into moby/daemon/create.go is that I think we know if it's LCOW or WCOW by then, because we have os set with reference to the image.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

On the other hand, taking it out of builder/dockerfile means that we can't as-easily reuse the number for the copy mount, as part of fixing #37352.

Copy link
Copy Markdown
Contributor

@TBBle TBBle Nov 5, 2020

Choose a reason for hiding this comment

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

Easiest approach might be to leave builder/dockerfile/internals.go as-is, or with only the isWCOW test fixed, and just change daemon/create.go from

 		if _, ok := ctr.HostConfig.StorageOpt[opt[0]]; !ok { 
 			ctr.HostConfig.StorageOpt[opt[0]] = opt[1] 
 		} 

to just

 		ctr.HostConfig.StorageOpt[opt[0]] = opt[1] 

i.e.. daemon.configStore.GraphOptions always overrides ctr.HostConfig.StorageOpt, and today the only thing that this affects is size=127GB from builder/dockerfile/internals.go.

Ignore that, I'm dumb. Running containers will hit this, not just builders, and that would blat docker run --storage-opts.

@TBBle
Copy link
Copy Markdown
Contributor

TBBle commented Nov 5, 2020

Side-note for the PR description: The default Windows container volume size is 20GB, not 40GB, as far as I can tell.

@TBBle
Copy link
Copy Markdown
Contributor

TBBle commented Nov 5, 2020

Would it make sense for docker run default sandboxes to also be 127GB? Or do we only want this for builder sandboxes?

@TBBle
Copy link
Copy Markdown
Contributor

TBBle commented Nov 6, 2020

Just to inject some more movement/shifts into this PR, for #37352/#41636, I'm thinking of moving the parsing of the "storage-opts" "size" option into the WindowsFilter graphdriver (which receives but ignores the options currently), rather than having higher level layers parse it and send it down via the various APIs. That would reserve the APIs that take StorageOpts for the --build-opts command-line (i.e. not docker build), and remove the code that this PR currently touches.

For purposes of this, it makes sense to me that if "storage-opts" is not specified, the WindowsFilter itself should default to 127GB, which would remove the complexity this PR is trying to fix, but also be applied to executing containers (docker run if --storage-opts is not passed) not just building containers (docker build).

I'd appreciate feedback on that change, since it has the risk that docker run with 40GB free on the underlying volume would now allow the container to fill the volume, where currently the default 20GB limit will prevent that. That seems like a peril-fraught behaviour to have been relying on though. AFAIK Linux does not enforce any such size protections, so I don't think it's bad if the default is larger, but there may be other concerns that don't occur to me.

I do know that this won't allow over-commit, as the free space in the container will be the lesser of the sandbox size and the free space on the underlying volume.

Edit: 56d378a and db7b7f6 is what I mean:

  • Move the 127GB value down from docker build into WindowsFilter, and override it on startup with the daemon storage-opts parameter if provided.
  • When the container provides storage-opts (only docker run, never docker build), override the settings in Windows Filter for that container.

Anyway, #41636 is ready for review, and I believe it supplants this PR as well as solving #37352 and simplifying the code we were discussing here.

@TBBle
Copy link
Copy Markdown
Contributor

TBBle commented Jan 25, 2021

I believe #41636 being merged has made this PR unnecessary.

@thaJeztah
Copy link
Copy Markdown
Member Author

Ah, yes; closing

@thaJeztah thaJeztah closed this Jan 25, 2021
@thaJeztah thaJeztah deleted the windows_dont_override_custom_size branch January 25, 2021 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/builder Build impact/changelog kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. platform/windows status/2-code-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants