docker / for-mac

Bug reports for Docker Desktop for Mac

Home Page:https://www.docker.com/products/docker#/mac

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

File system performance improvements

yallop opened this issue · comments

Recent Docker versions (17.04 CE Edge onwards) add additional flags to the -v option for docker run that make it possible to specify the consistency requirements for a bind-mounted directory. The flags are

  • consistent: Full consistency. The container runtime and the host maintain an identical view of the mount at all times. This is the default.
  • cached: The host's view of the mount is authoritative. There may be delays before updates made on the host are visible within a container.
  • delegated: The container runtime's view of the mount is authoritative. There may be delays before updates made in a container are visible on the host.

In 17.04 cached is significantly faster than consistent for many workloads. However, delegated currently behaves identically to cached. The Docker for Mac team plans to release an improved implementation of delegated in the future, to speed up write-heavy workloads. We also plan to further improve the performance of cached and consistent.

We will post updates relating to Docker for Mac file sharing performance in comments to this issue. Users interested in news about performance improvements should subscribe here.

To keep the signal-to-noise ratio high we will actively moderate this issue, removing off-topic advertisements, etc. Questions about the implementation, the behaviour, and the design are very welcome, as are realistic benchmarks and real-world use cases.

A rudimentary real-worked example (Drupal 8):

Docker for Mac : Version 17.05.0-ce-rc1-mac8 (16582)
Mac-mini :: MacOS Sierra : 10.12.2 (16C67)

A simple command line curl test (taken average of 10 calls to URL) Drupal 8 clean install frontend:

old UNISON (custom synced container approach) Volume mount : 0.470s
standard Volume mount: 1.401s
new :cached Volume mount: 0.490

v.easy implementation to add to my compose.yaml files and happy with any delay between host/output using cached on host codebase.

@yallop Is there a rough/expected release date for 17.04 (stable) ?

@yallop Is there a rough/expected release date for 17.04 (stable)

The next stable version will be 17.06, and is likely to be released some time in June. There's been a change to the Docker version numbering scheme recently so that the numbers now indicate the release date, with stable releases every three months (March, June, September, December), and edge releases in the months in between. For example, 17.04 is the Edge release in April 2017 and 17.06 is the stable release in June 2017.

ok, thanks for that. do you expect 17.06 to contain at least the current edge implementation of :cached?

Yes, that's the plan.

How should this work in a docker-compose.yml file? I tried appending :cached to the existing volume setting:

volumes:
  - .:/srv:cached

... but that got me an error (on OSX):

ERROR: Cannot start service my_service: Mounts denied: 9p: Stack overflow

(Docker version 17.05.0-ce-rc1, build 2878a85)

Note: having a global setting or environment variable to switch my local default to "cached" would also be fine (or rather preferable).

The syntax looks correct @reinout
here's a stripped but working example docker-compose.yml

version: '2'
services:
  php:
    image: php:7.1-fpm
    ports:
      - 9000
    volumes:
      - .:/var/www/project:cached

tested on Docker version 17.05.0-ce-rc1, build 2878a85

Your example works. My own one still not (even after really making sure there were no left-over old mounted volumes). So I rebooted. Afterwards, it worked.

So: a reboot might be needed if you've run a docker-compose before upgrading docker to the latest version. Possibly related: I switched from docker stable to edge.


Is there a possibility of a global setting? I don't really want to add this option to the docker-compose.yml that all my linux colleagues are using.

not as far as I know. When you linux colleagues are running edge the flag should work.

Another workaround would be (that's what we do right now for separating linux and mac volume mounting )
to just put the mount settings for mac in a separate file, like docker-compose-mac.yml and then run docker-compose -f docker-compose.yml -f docker-compose.mac.yml up -d

See https://docs.docker.com/compose/extends/

@reinout You could use an additional compose file to override that of your colleagues? For instance, we have:

docker-compose.yml:

version: '2'

services:

  build_docs:
    image: docs/sphinx
    build: .
    environment:
      - DOCS_NAME='docs'
      - SRC_DIR=src
      - DST_DIR=build
    volumes:
      - "./../../docs/dev:/docs"
    command: /docs/source /docs/build

and volumes-cached.yml:

services:
  build_docs:
    volumes:
      - "./../../docs/dev:/docs:cached"

Which can be run with:

$ docker-compose -f docker-compose.yml -f volumes-cached.yml up

Yes, I could do that. But.... I'd have to do that for each of the 12 docker-compose projects. And I'd have to keep it in sync with changes to the "master" docker-compose.yml.

As an intermediary measure: fine. Long term: no, as it is not very don't-repeat-yourself :-)

If someone wants to enable the "cached" behaviour, that person probably wants to use it for all/most of the dockers. Would it make sense as a config setting in the Docker app itself? In the preferences' "File sharing" tab? (This should probably be its own ticket, I assume?)

@reinout: :cached is supported across platforms, so there should be no issue in adding it directly to compose files.

Provided that everybody uses the latest edge version, right? And it seems a bit strange to add an option that only has effect on osx to everybody's docker-compose.yml.

Anyway, it works for now. I won't drag the signal/noise ratio further down :-)

mtaylor(mjt)@mtaylor:~/tmp/docker-disk-perf-test$ time dd if=/dev/zero of=./output bs=8k count=40k; rm ./output
40960+0 records in
40960+0 records out
335544320 bytes transferred in 1.300857 secs (257941007 bytes/sec)

real	0m1.320s
user	0m0.012s
sys	0m0.564s
mtaylor(mjt)@mtaylor:~/tmp/docker-disk-perf-test$ docker run -it --rm -v "$(pwd):/host-disk" ubuntu /bin/bash 
root@4e9c8bc5e5c1:/# cd /host-disk/
root@4e9c8bc5e5c1:/host-disk# time dd if=/dev/zero of=./output bs=8k count=40k; rm ./output
40960+0 records in
40960+0 records out
335544320 bytes (336 MB, 320 MiB) copied, 10.7496 s, 31.2 MB/s

real	0m10.756s
user	0m0.050s
sys	0m1.090s
root@4e9c8bc5e5c1:/host-disk# exit
exit
mtaylor(mjt)@mtaylor:~/tmp/docker-disk-perf-test$ docker run -it --rm -v "$(pwd):/host-disk:cached" ubuntu /bin/bash 
root@597dc640bdeb:/# cd /host-disk/
root@597dc640bdeb:/host-disk# time dd if=/dev/zero of=./output bs=8k count=40k; rm ./output
40960+0 records in
40960+0 records out
335544320 bytes (336 MB, 320 MiB) copied, 11.1683 s, 30.0 MB/s

real	0m11.172s
user	0m0.060s
sys	0m1.080s
root@597dc640bdeb:/host-disk# exit
exit
mtaylor(mjt)@mtaylor:~/tmp/docker-disk-perf-test$ docker run -it --rm -v "$(pwd):/host-disk:delegated" ubuntu /bin/bash 
root@985e4143053b:/# cd /host-disk/
root@985e4143053b:/host-disk# time dd if=/dev/zero of=./output bs=8k count=40k; rm ./output
40960+0 records in
40960+0 records out
335544320 bytes (336 MB, 320 MiB) copied, 12.1589 s, 27.6 MB/s

real	0m12.165s
user	0m0.080s
sys	0m1.000s
root@985e4143053b:/host-disk# exit
exit
mtaylor(mjt)@mtaylor:~/tmp/docker-disk-perf-test$ docker run -it --rm -v "$(pwd):/host-disk:consistent" ubuntu /bin/bash 
root@3377ae356124:/# cd /host-disk/
root@3377ae356124:/host-disk# time dd if=/dev/zero of=./output bs=8k count=40k; rm ./output
40960+0 records in
40960+0 records out
335544320 bytes (336 MB, 320 MiB) copied, 12.5944 s, 26.6 MB/s

real	0m12.601s
user	0m0.060s
sys	0m0.980s
root@3377ae356124:/host-disk# exit
exit
mtaylor(mjt)@mtaylor:~/tmp/docker-disk-perf-test$ docker --version
Docker version 17.05.0-ce, build 89658be
mtaylor(mjt)@mtaylor:~/tmp/docker-disk-perf-test$ 

Perhaps my expectations on how this works are unreasonable, or I'm doing something wrong. Above are some simple disk performance tests and I'm not seeing any differences.

Would be interested in knowing if my expectations, or use of the flag is incorrect.

@matthewjosephtaylor ... :cached won't improve dd tests ... for this, you need to check for :delegated to rollout :)

The issue text says:

delegated: The container runtime's view of the mount is authoritative. There may be delays before updates made in a container are visible on the host.

Does this mean that if I were to use delegated (or cached for that matter) that syncing would strictly be a one-way affair?

In other words, to make my question clearer, let's say I have a codebase in a directory and I mount this directory inside a container using delegated. Does this mean that if I update the codebase on the host, the container will overwrite my changes?

What I understood until now was, that using for instance delegated, would keep syncing "two-way", but make it more efficient from the container to the host, but the text leads me to believe that I may have misunderstood, hence the question.

Just posting another data point:

  • Drupal codebase volume with rw,delegated on Docker Edge (for now, it's only using the cached option though): 37 requests/sec
  • Drupal codebase volume with rw (and no extra options): 2 requests/sec

Drupal 8 is ~18x faster if you're using a Docker volume to share a host codebase into a container, and it's pretty close to native filesystem performance.

With cached, Drupal and Symfony development are no longer insanely painful with Docker. With delegated, that's even more true, as operations like composer update (which results in many writes) will also be orders-of-magnitude faster!

I'm seeing good results for running a set of Behat tests on a Drupal 7 site:

On Mac OS, without :cached:

10:02 $ docker exec clientsite_php bin/behat -c tests/behat.yml --tags=~@failing -f progress
...................................................................... 70
...................................................................... 140
...................................................................... 210
...................................................................... 280
.................................................

69 scenarios (69 passed)
329 steps (329 passed)
11m26.20s (70.82Mb)

With :cached:

09:55 $ docker exec clientsite_php bin/behat -c tests/behat.yml --tags=~@failing -f progress
...................................................................... 70
...................................................................... 140
...................................................................... 210
...................................................................... 280
.................................................

69 scenarios (69 passed)
329 steps (329 passed)
4m33.77s (63.33Mb)

On Travis CI (without :cached):

$ docker exec clientsite_php bin/behat -c tests/behat.yml --tags=~@failing -f progress
...................................................................... 70
...................................................................... 140
...................................................................... 210
...................................................................... 280
.................................................
69 scenarios (69 passed)
329 steps (329 passed)
4m7.07s (55.01Mb)

On Travis CI (with :cached):

247.12s$ docker exec cliensite_php bin/behat -c tests/behat.yml --tags=~@failing -f progress
...................................................................... 70
...................................................................... 140
...................................................................... 210
...................................................................... 280
.................................................
69 scenarios (69 passed)
329 steps (329 passed)
4m6.71s (55.01Mb)

Nice work, Docker team! 👏

Is it expected that both :cached and :delegated can be combined or will they be mutually exclusive?

@ToonSpinISAAC both :cached and (when it lands) :delegated perform two-way "syncing". The text you cite is saying that, with :cached, the container may read stale data if it has changed on the host and the invalidation event hasn't propagated yet. With :cached, the container will write-through and no new write-write conflicts can occur (POSIX still allows multiple writers). Think of :cached as "read caching". With :delegated, if the container writes to a file that write will win even if an intermediate write has occurred on the host. Container writes can be delayed indefinitely but are guaranteed to persist after the container has successfully exited. flush and similar functionality will also guarantee persistence. Think of :delegated as "read-write caching". Even under :delegated, synchronization happens in both directions and updates may occur rapidly (but don't have to). Additionally, you may overlap :cached and :delegated and :cached semantics will override :delegated semantics. See https://docs.docker.com/docker-for-mac/osxfs-caching/#delegated guarantee 5. If you are using :delegated for source code but your container does not write to your code files (this seems unlikely but maybe it auto-formats or something?), there is nothing to worry about. :delegated is currently the same as :cached but will provide write caching in the future.

@carn1x :cached and :delegated (and :default and :consistent) form a partial order (see https://docs.docker.com/docker-for-mac/osxfs-caching/#semantics). They can't be combined but they do degrade to each other. This allows multiple containers with different requirements to share the same bind mount directories safely.

From within a container is there a way to tell which flag was applied for a volume?

I'm getting the same output from mount regardless of the flag used. Is there another way to check?

$ mount | grep osxfs
osxfs on /var/www/project type fuse.osxfs (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other,max_read=1048576)
Version 17.06.0-rc1-ce-mac13 (18169)
Channel: edge
2425473dc2

@lmakarov /Applications/Docker.app/Contents/MacOS/com.docker.osxfs state should show you the host directories that are mounted into containers and their mount options. For instance, after I run docker run --rm -it -v ~:/host:cached alpine ash, I then see:

$ /Applications/Docker.app/Contents/MacOS/com.docker.osxfs state
Exported directories:
 - /Users to /Users (nodes_/Users table size: 62)
 - /Volumes to /Volumes (nodes_/Volumes table size: 0)
 - /tmp to /tmp (nodes_/private/tmp table size: 9)
 - /private to /private (nodes_/private table size: 0)
Container-mounted directories:
 - /Users/dsheets into b8f7765665782501bc1a099f1898911b7eb393b08930be638545a55fd06e420e (state=cached)

Thanks for taking the time to clarify and explain @dsheets!

commented

Is there any indication from the high sierra dev beta of how this (and docker mac in general) will work under APFS?

Sorry if this is off-topic but it's a question I keep wanting to ask every time I see a new message here :)

Unable to get @DanielSchwiperich's example working? Retrieving the error:

invalid spec: .:/var/www/project:cached: unknown option: cached

It doesn't like version being set to 2 either. Perhaps I'm missing something? Currently running 17.06.0-rc2-ce-mac14

@WillSquire What version of docker-compose are you using?

@27Bslash6 Version 1.14.0-rc2. Believe this was installed automatically for me though, as per the docs: https://docs.docker.com/compose/install/

I'm running macOS

so if I understand this issue correct, :delegated is currently (in 17.04 and 17.05) the exact same as :cached?
Unfortunately https://docs.docker.com/docker-for-mac/osxfs-caching/ suggests that :delegated and :cached are implemented already different implementations.
Could we get some clarification what exactly is correct?

@Schnitzel: Yes, in 17.04, 17.05 and 17.06, :delegated behaves the same as cached.

The documentation is written in terms of the guarantees associated with each flag. :cached has all the guarantees of :delegated, plus some additional ones. (And :consistent has all the guarantees of :cached, plus some additional ones.) So the documentation allows Docker to perform more optimizations with :delegated than with :cached, but doesn't require it to do so.

Here's another way to think of it: switching on :cached or :delegated is a way of granting Docker permission to perform certain optimizations. Docker will never perform those optimizations without permission, and it won't always perform those optimizations even when you give it permission. But if you always give Docker permission to perform the optimizations you want then it'll optimize as well as it can within those constraints.

For a user the best approach is to grant the permissions that fit with your circumstances, and in return Docker will give you the best performance available at the time for those permissions. So if :delegated is the right setting for your application then it's reasonable for you to switch it on now, so that you'll immediately see better performance when Docker releases a more aggressive implementation of :delegated.

@yallop
Alright, thanks for that thorough explanation, makes sense now!

btw, here some performance comparison of Docker Machine vs Docker-for-Mac with the new flags: https://stories.amazee.io/docker-on-mac-performance-docker-machine-vs-docker-for-mac-4c64c0afdf99
(I'm updating it now to not differentiate between delegated and cached, blogpost was written while I still was in the impression that they are already different).

Will definitely redo the testing as soon as 17.06 is out! Do you already know what we can expect to land in 17.06 in terms of these improvements?

Thanks a lot for your effort in all of that. Super excited about the performance improvements :)

Does anyone have an example for a symfony application using :cached and :delegated do you just mount the whole project dir or mount app/cache vendor src etc separately to get the best performance?

I would think something like this would give the best performance?

volumes:
     - ./src:/builds/application/src:cached
     - ./app:/builds/application/app:cached
     - ./app/cache:/builds/application/app/cache:delegated
     - ./app/logs:/builds/application/app/logs:delegated
     - ./web:/builds/application/web:cached
     - ./vendor:/builds/application/vendor:delegated
     - ./node_modules:/builds/application/node_modules:delegated

Now on version 17.06.0-ce-rc5 for Docker and version 1.14.0 for Docker compose, but still recieving "unknown option" for cached and delegated. To give some background, Docker is being explored as a replacement for our current workflow (Vagrant + Ansible) due to the speed claims, so we might not be as 'au fait' to possible caveats. Any help would be appreciated. Thanks

Edit: Have now created a separate issue for this.

It's not clear whether these features made it into the 17.06 CE stable release today or not. Since they're not mentioned in the blog post or changelog I'm guessing not but can anyone confirm? Thank you

They did. I'm using the new :delegated flag on today's stable release.

@mattacular
in #1592 (comment) @yallop wrote that in 17.06 :delegated is the same as :cached

My own tests show that overall Docker got faster, but Docker-for-Mac is still slower then a Docker-Machine with NFS share (at least for Drupal)

I'm only seeing a slight improvement with running jest for unit tests, or even just a simple git status. I ran these several times and infrequently the delegated mount was actually slower than rw mount.

command: time jest -o

mode: rw:
run0: real 0m52.869s
run1: real 0m41.248s
run2: real 0m47.749s

mode: delegated:
run0: real 0m47.663s
run1: real 0m34.802s
run2: real 0m37.152s

no volume:
run0: real 0m16.811s
run1: real 0m10.429s
run2: real 0m10.200s

command: time git status

rw:
real 0m0.853

delegated:
real 0m0.370s

no volume:
real 0m0.016s

I've tried reinstalling docker4mac and rebooting the machine. Is there something else I'm missing or is this the performance increase I should expect?

@genei09 It depends on what the jest unit tests are doing. I can only assume they are doing a lot of writing to the volume if they are seeing such a performance deficit. As mentioned above,:delegated is currently the same as :cached, and :cached only provides performance benefits for read operations. Write operations are still very slow. I believe git status even performs writes (such as applying a git lock maybe?).

I've just got the 17.06 'update', and my performance over 17.04 is significantly worse (using 'delegated') I can only conclude that the 'delegated' flag didn't make it into the release....?
(which is a petty as I saw a significant speed improvement when using it on 17.04)

@markfoodyburton as written 10 comments back here, and repeated 3 comments back here, in 17.04, 17.05, and 17.06, :delegated was only ever going to behave the same as :cached. So in terms of 'making it into the release', I'm not sure what you are referring to. The flag simply allows Docker to make certain optimisations (which aren't yet developed/ready in the case of :delegated), but does not guarantee any.

As for your specific performance issue, I can't comment, but based on the available information I would expect the performance to be similar. If you're unable to resolve your specific issue, then I would suggest you open a new issue.

Have done, thanks.

@carn1x the jest unit tests perform no write operation. Unless there are some writes being hidden from inotify.

210 file Opens
1 file Access

git status has 2 Opens and 1 Create on the lock file

I read most of this issue. Why isn't the huge caveat around delegated in the documentation? There's a whole 8 point list of lies/wishes around it, as far as I can tell.

The 8 point list in the documentation is a specification for delegated, written in terms of guarantees about data integrity, not promises about performance. Specifications are often written in this way -- for example, C has a keyword restrict that allows a compiler to perform more aggressive optimzations, under the assumption that a particular object is not aliased; ignoring restrict altogether for optimization purposes is an entirely legitimate implementation.

The documentation, the blog post, and this issue all say that write caching is under development, not released. You can find the documentation at https://github.com/docker/docker.github.io/ if you'd like to propose improvements.

Is it fair to say that when a file is opened for writing the performance will still be sub-optimal as compared to strictly opened for read?

that would explain the jest test performance

Is it fair to say that when a file is opened for writing the performance will still be sub-optimal as compared to strictly opened for read?

Yes; at this time, delegated basically does the same thing as cached. Which means reads are much better optimized, but writes are just the same as earlier. If you have a write-heavy workflow (e.g. in my case, running something like composer install on a PHP project with hundreds of dependencies), then write-heavy operations will be very slow on mounted volumes.

Yes; at this time, delegated basically does the same thing as cached. Which means reads are much better optimized, but writes are just the same as earlier. If you have a write-heavy workflow (e.g. in my case, running something like composer install on a PHP project with hundreds of dependencies), then write-heavy operations will be very slow on mounted volumes.

I'm aware of the delegated and cached being functionally the same. My question is around a file which is OPENED but not actually MODIFIED.

@geerlingguy I believe @genei09 is asking this:

Suppose we have a file opened in read-only mode and we measure how long it takes to read the first 100 bytes from that file 100 times. We record this time as readonly_time.

Next, suppose we have this same file opened in read-write mode, and we measure how long it takes read the first 100 bytes from that file 100 times. We record this time as readwrite_time.

(Note that in both scenarios we perform no write operations.)

The questions is, can we reasonably expect that both scenarios would complete in the same amount of time, i.e., are readonly_time and readwrite_time equal?

I'm working on a fairly complex project with a dozen containers running on my Mac at the same time.
My CPU usage was rocket high until in started using :cached for all my source code volumes. This was a huge improvement for me: I went from 150% CPU usage by the hyperkit process to a tiny 10%.

I figured the main culprits were all the various watch processes monitoring file changes for live-reload and code hot swapping.

Anyway, since this is a huge improvement for most use-case, I would love to see a global option in docker to set all volume mount to :cached by default. People turning that option ON could still use :consistent for some volumes if needed.

Yeah, I really would like to see a configurable default so that delegated or cached is applied to all volume mounts. Adding these tags in every script or config where docker mounts a volume is proving to be painful

@mtibben delegated or cached can't be applied by default since they should either speed up reading container files on the host or the container reading files from the host that are mounted via a volume. I don't think we can apply both on the same time.

commented

No need to apply both at the same time. The ability to specify one of them as the default is enough.

Yes exactly, a single default is what I'm after. But you raise a good point, does it even make sense to tag individual volume mounts? What happens when there are multiple mounts with differing consistency tags?

@mtibben: the specification for consistency flags defines what happens when there are multiple mounts with different consistency flags:

  • If there is no overlap between the mounts, then each mount has the specified semantics (consistent, cached, or delegated), just as you'd expect.

  • If some portion of the mounts overlaps then the overlapping portion has all the guarantees of both flags. In practice, this means that the overlapping portion behaves with the more consistent semantics (e.g. if you mount the same directory with both consistent and cached then it behaves according to the consistent specification)

Thanks @yallop for the explanation, after reading the spec the behaviour is much clearer to me.

So what I'm effectively asking for then is the ability to change what the default configuration if no state flags are supplied.

Unfortunately the new settings are not enough to make Magento work with similar speed to Vagrant. It's still much slower.

@piotrekkaminski, which version of Magento are you using?

With Magento 1EE, my response time (on the homepage) is under 1s without page or blocks cache. And with Magento 2EE (on the homepage also), my response time is about 500ms with the same configuration.

That's so far better with the delegated mode that I left Docker Machine NFS almost immediately!

i'm using M2. I will check again, maybe the problem is elsewhere.

@piotrekkaminski could you post the dockerfile/docker-compose?

Any news on the :delegated implementation ? As far as can see (in the Changelog and in real life benchmarks), there is still no difference between :cachedand :delegated in Docker for Mac 17.09.0-ce-mac34 (edge) or 17.09.ce-mac35 (stable).

The day when fileshare will be solved on mac/windows will be great for Docker ... docker-sync is somekind of workaround but for example each time lot of files was changed macbook CPU goes crazy :/
So I second bpresles question - any news on :delegated?

@aegis123 I can confirm what @piotrekkaminski did earlier -- there is no improvement with shared files using option ":delegated" or ":cached" on Mac OS 10.13.1 running Docker v17.09.0-ce-mac35 (19611).

As you can see I am using v2 as shared drives are not working properly on v3:

version: '2'

services:
    db:
        image: mysql
        volumes:
            - "./.data/db:/var/lib/mysql:delegated"
        environment:
            MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
            MYSQL_DATABASE: ${MYSQL_DATABASE}
            MYSQL_USER: ${MYSQL_USER}
            MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    php:
        build:
            context: php7-fpm
            args:
                TIMEZONE: ${TIMEZONE}
        volumes:
            - ${MAGENTO_APP_PATH}:/var/www/magento:delegated
    nginx:
        build: nginx
        ports:
            - 80:80
        volumes_from:
            - php
        volumes:
            - ./logs/nginx/:/var/log/nginx:cached

For those using Rails, we have found that disabling the byebug remote debugging server reduces the impact of this issue enough to make development tolerable.

There is a known issue with byebug and remote debugging that substantially reduces performance. I'm not sure if it is file I/O related or not but it absolutely kills our load times on Docker. Meanwhile disabling byebug leads to near native speeds in dev despite using a reasonably large DB.

Is there a way to mount a subdirectory of a bind-mounted directory as cached? Let's say I have the following:

volumes:
      - ./static/stylesheets:/app/static/stylesheets:cached
      - .:/app

My understanding is that :cached will have no effect for the above mount because I mount the entire PWD, and overlapping portions of bind mounts obey the most consistent mount's mode. This makes sense in a lot of cases, but it can cause severe configuration bloat in others. To get the above example working, I would have to individually mount each top-level subdirectory / file rather than .:/app so that I can control the overlap. I have projects that have tens of folders and top-level files, with new ones being added at times. You can imagine the volumes config will become hard to maintain, and may get out of sync with what is actually in the pwd. Any ideas?

Is there any update on this with :cached and :delegate? Since this issue has been on the radar for docker for about 1.5 years now and we are coming up on a full version bump to 18.01

18.03 released, still no improvements?

Status would be appreciated. I'm using docker-sync as workaround, but it's confusing that blocker like this has no updates about progress (I'm not talking about fix - just information)
Recently docker-sync started to make troubles after mac42 so "native" fix like delegated would be awesome to replace 3rd party tools.

Maybe we should request to Apple if they can lend a hand?

I have noticed much improved performance with the delegated flags on my updated Magento builds - perhaps the addition has already been included in a recent version.

I think everyone needs to remember that this is 100% free software, and being mean to developers giving software away for free or making backhanded comments is a complete noop.

@markoshust D4M is not "free software", it's commercial software given away for no cost. It is not open source, nor is the OSXFS file system implementation whose performance is in question here. If they were open source, many of the people involved on this thread (myself included) would have already contributed to improving the perf of these components. Until Docker Inc. decides to open source these components, we have no choice but to whine and complain about the poor performance on Github and hope that Docker team members decide to fix them.

Open source isn't the only model that could work here, either, if this was a standalone piece of commercial software, I'd gladly pay for it, and the purchases could help fund a dev team that was dedicated to actually improving it. Right now D4M is in the worst of all possible places, closed-source commercial software with no income stream to fund improvements to it.

Bringing this back in focus, can we please look at a global setting for the default type of volume? When using a kubernetes setup on edge, you're forced to rely on the slowest volume mounts, and it makes development downright painful.

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale comment.
Stale issues will be closed after an additional 30d of inactivity.

Prevent issues from auto-closing with an /lifecycle frozen comment.

If this issue is safe to close now please do so.

Send feedback to Docker Community Slack channels #docker-for-mac or #docker-for-windows.
/lifecycle stale

commented

/remove-lifecycle stale

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale comment.
Stale issues will be closed after an additional 30d of inactivity.

Prevent issues from auto-closing with an /lifecycle frozen comment.

If this issue is safe to close now please do so.

Send feedback to Docker Community Slack channels #docker-for-mac or #docker-for-windows.
/lifecycle stale

/remove-lifecycle stale

Are there any updates in that matter? In my Nextcloud instance, the mounts makes everything very slow. When the same configuration with named volumes are very fast.

I'm not sure performance can really be vastly improved anymore without a significant overhaul in how the docker/host client communicates with the docker daemon. This would most likely be a paradigm shift in how we mount volumes completely.

That said, I'm able to achieve near-native performance now, after switching my approach to host bind mounts, and keeping as much data as possible within native Docker volume mounts. I wrote a blog post here on how to achieve this, I'm sure everyone subscribed to this issue will find this helpful. It's not a silver bullet, but it works (today).

https://markshust.com/2018/12/30/docker-mac-filesystem-volume-mount-approach-performance

So, is it possible to speed up read and write at the same time?

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale comment.
Stale issues will be closed after an additional 30d of inactivity.

Prevent issues from auto-closing with an /lifecycle frozen comment.

If this issue is safe to close now please do so.

Send feedback to Docker Community Slack channels #docker-for-mac or #docker-for-windows.
/lifecycle stale

/remove-lifecycle stale

/lifecycle frozen

Would just like to note that for a large subset of the projects I work on (often PHP or Node.js-related, but others as well), the number one cited reason for friction with switching to Docker for local development is this issue. This has remained consistent for something like 3 years now :(

The cached reads are greatly helpful in some use cases (and I love that), but the write slowness is crippling for certain use cases.

Seconding what @geerlingguy notes above. For this reason we are still on docker-machine in our local development setup.

This project solved problem for us very well https://mutagen.io (https://mutagen.io/documentation/transports/docker/)

Same for us! I implemented it on our Magento 2 environment which was very slow, actually really painful, and the improvement is awesome.

This project solved problem for us very well https://mutagen.io

There are multiple existing approaches to syncing the host's file system with the container's. Most are rsync/unison based (e.g. docker-sync, docker-bg-sync). Some use less known tools (e.g. syncthing).

While fine for smaller codebases, they all eventually choke on mass file operations (like git checkout <branch> or <package-manager> install) within large codebases. They can also be very resource intensive, since watching and syncing tens of thousands of files has a cost.

@radek-baczynski, @ajardin how well does mutagen handle that?

@lmakarov we used to use docker-sync and also self made custom unison solution.
Why we choose mutagen:

  • much faster for us for large repo
  • automatic conflict resolving
  • very solid, never hangs or stops synchronisation

@radek-baczynski how do you handle the volumens in docker-compose?
When I start my docker-compose, it need to have the e.g. public_html on the docker-container, which would be synced by mutagen later.

@lauer here is a link to the commit I made on a Docker environment to replace docker-sync by Mutagen. I think it will help you to understand how to configure it in your case.

EmakinaFR/docker-magento2@de4d3ec

The docker-sync gem solves this for me. My benchmarking has docker-sync outperforming the native implementation by nearly 450%. I wish Docker would follow EugenMayer's lead regarding sync strategies, it's absurd how big the delta is.

@ciekawy Exactly. Docker-sync does indeed work and I'm grateful for all the work that went into it, but it's just such a shame it's needed at all. It often falls over for one reason or another, often silently in my setup, with frustrating consequences.

I'm thinking of switching my code editor to Visual Studio Code simply because I'm hoping its new "remote containers extension" might allow me to avoid the need to sync at all. It does seem to work in my testing thus far.

Tried VSCode Remote Containers and it has the same issue: working folder is nothing but a mounted /workspace volume. Managed to add :delegated to it through docker-compose and IO performance is still terrible, it takes ages to yarn install for example.

Am I right thinking if you were willing to only ever access your code from inside the container (ie never from the native OS) then VSCode's Remote Containers would enable you to edit the code, without the usual performance degradation of any form of Docker shared filesystem?

Even if that's true, it demands a significant change in workflow, and not one I'm sure I'm ready for. For example, I rather like my macOS GIT GUI's 😆 not to mention per-file Time Machine backups etc etc

@deepsweet I think the main point of that feature is to use "Remote" containers, somewhere in a cloud on a linux machine - so no issues with mounting. But I agree, that :delegated thing does not help much.

Just curious about the status of this issue. Is it still open? ( as in "no real implementation differences exist between delegated and cached " )

I have speed up my magento2 environment mounting volumes using type nfs and excluding from my root directory all directories that need read-write operations and i don't need to persist on my host machine root directory:

volumes:
   magento:
       driver: local
       driver_opts:
         type: nfs
         device: ':${PWD}'
         o: addr=host.docker.internal,rw,nolock,hard,nointr,nfsvers=3
  ...
  vendor: {  }
  generated: {  }
  ...

locally performances are really improved.

This is a workaround of some tools like docker-sync or mutagen
Here a nice description about mounting nfs: https://medium.com/@sean.handley/how-to-set-up-docker-for-mac-with-native-nfs-145151458adc

@ridesoft Could you please describe your host setup? I suppose you've run NFS server there, probably with nfsd command, right? Have you modified your /etc/exports for this?

Could it please be advised how these new mounting options, consistent, cached, delegated, apply to the :ro modifier?

One use case to consider is a development workflow where code is edited on the host and is mounted into the container. Typically the intent in this use case is that the updates will be propagated quickly so that the containerized application will have the new code.

@prokher you are right, you need to start a nfs server locally (with a bash script) assigning your running user to all files you want to mount inside nfs: for this you need to modify /etc/exports.
Get inspired by this script, lines from 52 to 56.

LINE="/Users -alldirs -mapall=$(id -u):$(id -g) localhost"
FILE=/etc/exports
sudo cp /dev/null $FILE
grep -qF -- "$LINE" "$FILE" || sudo echo "$LINE" | sudo tee -a $FILE > /dev/null

LINE="nfs.server.mount.require_resv_port = 0"
FILE=/etc/nfs.conf
grep -qF -- "$LINE" "$FILE" || sudo echo "$LINE" | sudo tee -a $FILE > /dev/null

sudo nfsd restart

Then just mount your volumes like described above

@ridesoft Thank you very much. This is indeed useful. Let's see if this improves performance in my case.