Skip to content

overlay2: use index=off if possible (fix EBUSY on mount)#37993

Merged
yongtang merged 2 commits intomoby:masterfrom
kolyshkin:ovr2-index
Oct 13, 2018
Merged

overlay2: use index=off if possible (fix EBUSY on mount)#37993
yongtang merged 2 commits intomoby:masterfrom
kolyshkin:ovr2-index

Conversation

@kolyshkin
Copy link
Copy Markdown
Contributor

As pointed out in #37970,
Docker overlay driver can't work with index=on feature of
the Linux kernel "overlay" filesystem. In case the global
default is set to "yes", Docker will fail with EBUSY when
trying to mount, like this:

error creating overlay mount to ...../merged: device or resource busy

and the kernel log should contain something like:

overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.

A workaround is to set index=off in overlay kernel module
parameters, or even recompile the kernel with
CONFIG_OVERLAY_FS_INDEX=n in .config. Surely this is not
always practical or even possible.

The solution, as pointed out my Amir Goldstein (as well as
the above kernel message 😄) is to use index=off option
when mounting.

NOTE since older (< 4.13rc1) kernels do not support index=
overlayfs parameter, try to figure out whether the option
is supported by the running kernel. In case it's not possible
to figure it out, assume the kernel is new enough to support it.

NOTE the default can be changed anytime (by writing to
/sys/module/overlay/parameters/index) so we need to always
use index=off.

Fixes: #37970

@kolyshkin
Copy link
Copy Markdown
Contributor Author

Comment thread daemon/graphdriver/overlay2/overlay.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.

No need to convert this from fmt.Sprint

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To me, it looks more readable that way (as the option arguments are right there next to options), and Sprintf() does nothing but string concatenating here.

Using fmt.Sprintf() in Golang to merely concatenate a few strings (without doing anything else like padding, quoting etc.) looks like a coding pattern that came straight from C, where you can't concatenate more than 2 strings at once, and should be discouraged unless necessary.

Concatenation is also quite faster (not that it matters in this particular case).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@dmcgowan please let me know what you think about ^^^ , and I'll surrender and obey :)

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 change is fine, neither has great readability but not worth making it an array and joining

@kolyshkin kolyshkin force-pushed the ovr2-index branch 2 times, most recently from 780ea19 to c685bdb Compare October 9, 2018 01:50
@kolyshkin
Copy link
Copy Markdown
Contributor Author

CI failures due to my careless typos ⌨️ should now be fixed

@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 9, 2018

Codecov Report

❗ No coverage uploaded for pull request base (master@1f48759). Click here to learn what that means.
The diff coverage is 34.78%.

@@            Coverage Diff            @@
##             master   #37993   +/-   ##
=========================================
  Coverage          ?   36.12%           
=========================================
  Files             ?      610           
  Lines             ?    45170           
  Branches          ?        0           
=========================================
  Hits              ?    16318           
  Misses            ?    26614           
  Partials          ?     2238

@kolyshkin
Copy link
Copy Markdown
Contributor Author

v2: moved the detection code to driver's Init() where it belongs.

Comment thread daemon/graphdriver/overlay2/overlay.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.

What's the result if it's not supported and we set this option? Will it fail?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it will most probably result in kernel returning EINVAL from mount() syscall. This will only happen if:

  • kernel is > 4.13rc1 (and the relevant feature is not backported);
    and
  • for some reason /sys/module/overlay/parameters/index is present but stat() returns an error.

The former is quite possible, say on older distros.

The latter is highly unlikely to happen, from the top of my head I can only think of a severe kernel memory shortage, or using any security mechanisms (say selinux) to disallow dockerd access to e.g. /sys/module/ directory.

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.

Right, so I was thinking if we should take the safe approach, and only add index=off if we're successfully able to detect that it's supported (i.e. os.Stat() was successfull), so:

  • os.Stat() was successful; add index=off
  • os.Stat() produces an IsNotExist: don't add index=off, and continue silently (as this is an expected situation)
  • os.Stat() produces any other error: log a warning ("unable to detect if index=off is supported: <original error message>"), but continue as usual (but don't add index=off)

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.

Logging a warning at least makes it visible that we were unable to detect, and may assist in troubleshooting why we failed to detect.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense; please see updated commit

This simplifies the code a lot.

Signed-off-by: Kir Kolyshkin <[email protected]>
As pointed out in moby#37970,
Docker overlay driver can't work with index=on feature of
the Linux kernel "overlay" filesystem. In case the global
default is set to "yes", Docker will fail with EBUSY when
trying to mount, like this:

> error creating overlay mount to ...../merged: device or resource busy

and the kernel log should contain something like:

> overlayfs: upperdir is in-use by another mount, mount with
> '-o index=off' to override exclusive upperdir protection.

A workaround is to set index=off in overlay kernel module
parameters, or even recompile the kernel with
CONFIG_OVERLAY_FS_INDEX=n in .config. Surely this is not
always practical or even possible.

The solution, as pointed out my Amir Goldstein (as well as
the above kernel message:) is to use 'index=off' option
when mounting.

NOTE since older (< 4.13rc1) kernels do not support "index="
overlayfs parameter, try to figure out whether the option
is supported. In case it's not possible to figure out,
assume it is not.

NOTE the default can be changed anytime (by writing to
/sys/module/overlay/parameters/index) so we need to always
use index=off.

[v2: move the detection code to Init()]
[v3: don't set index=off if stat() failed]

Signed-off-by: Kir Kolyshkin <[email protected]>
Copy link
Copy Markdown
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

defer func() {
if err := os.RemoveAll(td); err != nil {
logrus.WithField("storage-driver", "overlay2").Warnf("Failed to remove check directory %v: %v", td, err)
logger.Warnf("Failed to remove check directory %v: %v", td, err)
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.

We should also start using .WithError() for these;

logger.WithError(err).Warnf("Failed to remove check directory %v", td)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

right (also in this case there's no need to print filename, as it should be a part of error message returned from os.RemoveAll())

Copy link
Copy Markdown
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

@dmcgowan dmcgowan left a comment

Choose a reason for hiding this comment

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

LGTM

@yongtang yongtang merged commit ee6fc90 into moby:master Oct 13, 2018
fuweid pushed a commit to containerd/containerd that referenced this pull request Jun 9, 2020
kernel version > 4.13rc1 support index=on feature, it will be failed
with EBUSY when trying to mount.

Related: moby/moby#37993

Signed-off-by: Rudy Zhang <[email protected]>
fahedouch pushed a commit to fahedouch/containerd that referenced this pull request Aug 7, 2020
kernel version > 4.13rc1 support index=on feature, it will be failed
with EBUSY when trying to mount.

Related: moby/moby#37993

Signed-off-by: Rudy Zhang <[email protected]>
tussennet pushed a commit to tussennet/containerd that referenced this pull request Sep 11, 2020
kernel version > 4.13rc1 support index=on feature, it will be failed
with EBUSY when trying to mount.

Related: moby/moby#37993

Signed-off-by: Rudy Zhang <[email protected]>
@cody-c-lewis
Copy link
Copy Markdown

It looks like this change blocks a docker image from setting up an NFS export of a directory in the root file system (ie, what's enabled by this overlay Linux kernel module addition). Is there no way to export the filesystem with the overlay driver in Docker?

In the past I've used the AUFS driver, but that's deprecated and going away bit by bit.

@amir73il
Copy link
Copy Markdown
Contributor

@cody-c-lewis how were you planing to enable nfs_export in overlayfs? Without changing docker?
IMO, enabling NFS export should be controlled by a docker image config and/or runtime parameter.
When NFS export is requested, docker should not use index=off and add nfs_export=on mount option instead.
This will have at least two implications:

  1. If there are sill unfixed mount leaks in docker, that could lead to EBUSY errors on overlayfs mount
  2. Mounting overlayfs with index=on,nfs_export=on is recommended when upper layer is empty - it is not recommended to mount the same overlay with index=off, make changes and mount with index=on,nfs_export=on again
    At best case, NFS client may get ESTALE errors, at worst case, even local application may get EIO error when trying to access files in such an overlay mount

@cody-c-lewis
Copy link
Copy Markdown

@cody-c-lewis how were you planing to enable nfs_export in overlayfs? Without changing docker?

Since there isn't any docker setting for this then my next thought was to set it via the kernel module parameters (such as /sys/module/overlay/parameters). But I'm searching in the dark here and open to suggestions and working backwards from a desired end state (being able to run an NFS server inside of a docker container that serves a static part of the image).

IMO, enabling NFS export should be controlled by a docker image config and/or runtime parameter. When NFS export is requested, docker should not use index=off and add nfs_export=on mount option instead. This will have at least two implications...

It sounds like these two issues were the motivation behind this PR so I guess the real question is if they've been resolved in the intervening 3 years. If that's the case then maybe undoing this PR would be a solution, or adding first class support to docker as you describe.

@rdlarkins
Copy link
Copy Markdown

Since there isn't any docker setting for this then my next thought was to set it via the kernel module parameters (such as /sys/module/overlay/parameters).

The kernel module config gets overridden by the mount options set here. I rebuilt the kernel module to ignore these two mount options and just use the default parameters set, and starting a single container with a nfs export does seem to work at least superficially.

@cpuguy83
Copy link
Copy Markdown
Member

Just trying to understand, you are trying to create an nfs export on a path inside the container which is just part of the normal container rootfs (ie. overlay) and not a bind-mount from the host?

Have you considered bind-mounting a dir here (or just an anonymous volume) so that it bypasses overlay entirely? You can still create the nfs export in the container, just now it doesn't have to depend on the details of the container rootfs.

@rdlarkins
Copy link
Copy Markdown

That is correct. I originally posted about this on the docker forums, you might find the details there more interesting: https://forums.docker.com/t/nfs-export-disabled-with-overlay2-as-storage-driver/121325

I have considered other options for the source of the files in the nfs export, but as I would like the files to be versioned alongside the rest of the container content, updating them as a single unit is logistically easier.

@rdlarkins
Copy link
Copy Markdown

I've had some more time to work with this over the past couple days. So far it seems that for my specific use case, there aren't any issues with enabling index and nfs_export. I've created an issue so that discussion can happen there rather than a 4 year old PR. #43299

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Getting error with overlayfs: device or resource busy

10 participants