Install Ubuntu 😉
Seriously: In a project all developers using a Mac had poor performance compared to those using a Ubuntu Linux system. While Docker per se is a Linux technology and obviously runs best on Linux the runtimes on Mac were way too slow to be acceptable. So we began research to optimize performance on Mac and ease our daily work.
During research i came across an article about tuning disk performance for Docker on Mac. All proposed steps seemed reasonable accept updating Docker to ‚edge‘ – this seems not necessary any more as the software is matured. Further insight gave an article about using NFS which we didn’t pursue.
First, we defined a disk heavy test task in our project to run on a container to measure runtimes for later comparison.
> time bin/console build real 41m34.257s
For comparison: The same task ran in 4 minute 25 seconds on a Linux system! That’s 89% faster!
> time bin/console build real 4m25s
1. Check disk image format
If you upgraded your Mac from an earlier MacOS version like Sierra or El Capitan to High Sierra or Mojave Docker might still be using its old disk format. If you’re still on .qcow2 then upgrade to .raw if system disk filesystem is APFS. Unfortunately this is easiest done by resetting Docker to factory defaults which deletes all images so all container images have to be downloaded and built again.
After resetting we build our images and measure 19% of speedup:
> time bin/console build real 33m48.665s
2. Use :cached: for volumes
Simply add :cached: to volumes with many files/much disk I/O. Use an override file so your platform specific changes don’t have to be put in project wide docker-compose.yml. So here is a snippet from docker-compose.override.yml:
version: '3' services: nginx: volumes: - .:/var/www/html:cached
We measure and achieve about of 44% of speedup:
> time bin/console build real 18m57.775s # a newer machine is even faster > time bin/console build real 16m20.238s
So all in we achieved 54% faster runtime for our test task! Unfortunately i didn’t find further improvements so we checked another approach.
3. Use Docker with Vagrant
We create a Vagrant machine based on Ubuntu 18.04 to run Docker. Vagrant’s provisioning is used to install latest Docker version. Key is to use NFS for good I/O performance. The Vagrantfile looks as follows:
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" config.vm.network :private_network, ip: "192.168.111.16" config.vm.provider :virtualbox do |v| v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] v.customize ["modifyvm", :id, "--memory", 2576] v.customize ["modifyvm", :id, "--name", "dockerdev"] v.customize ["modifyvm", :id, "--cpus", 2] end config.vm.synced_folder "../", "/vagrant", :type => "nfs", mount_options:['nolock,vers=3,udp,noatime,actimeo=1'] config.vm.provision "shell", inline: <<-SHELL sudo apt-get update && sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo apt-key fingerprint 0EBFCD88 sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" sudo apt-get update && sudo apt-get -y install docker-ce docker-compose sudo usermod -aG docker vagrant SHELL # config.vm.provision :shell, path: "bootstrap.sh" config.ssh.forward_agent = true end
After creating the box with vagrant up && vagrant ssh we start our Docker services. The newer machine reaches a pretty good time:
> time bin/console build real 6m3.552s
So this is a pretty impressive improvement – all in all 85,5% – which allows working with Docker on a Mac.
Surprisingly Docker in a VirtualBox managend by Vagrant is faster than Docker for Mac! We will keep exploring this topic.