17

I try to run a simple Symfony 4 project on a docker container. I have tested regular PHP scripts, and they work very well. But, with Symfony project, the execution gets ridiculously slow. For example, a page without any significant content takes 5-6 seconds.

I have attached the screenshots from Symfony's performance profiler.

Screenshot1 Screenshot2 Screenshot3 Screenshot4

Do you have any idea what how to reduce this execution time to an acceptable level?

Tom Jowitt
  • 5,686
  • 11
  • 42
  • 60
user3429660
  • 1,597
  • 3
  • 16
  • 29
  • 2
    What are your host machine details? Docker for Mac + filesystem sync has been a known performance bottleneck in the past. – Aken Roberts Feb 17 '18 at 23:43
  • What environment are you running this in (i.e., what's the Docker host)? What are the times for the same config outside Docker? What does your Docker config look like, particularly how you are handling volumes? – ldg Feb 17 '18 at 23:43
  • 2
    Same goes for docker on windows. Docker on windows runs in a VM and the way the file system is mounted makes a filesystem heavy application like symfony painfully slow – JimL Feb 17 '18 at 23:44
  • 1
    It is running in Docker for Mac. Why I am concerned is that I run (with the same setup) other PHP projects build on top of another framework and it is much faster: all pages run in under 250ms, including ones that produce considerable amount of reports. – user3429660 Feb 18 '18 at 09:48

5 Answers5

15

It seems that changing the consistency level greatly increases Symfony performance. (see Docker docs)

Here is my new docker-compose.yml file. Note the ":cached" after the volumne.

version: '3'
services:
  web:
    image: apache-php7
    ports:
     - "80:80"
    volumes:
      - .:/app:cached
    tty: true

Note from manual:

For directories mounted with cached, the host’s view of the file system is authoritative; writes performed by containers are immediately visible to the host, but there may be a delay before writes performed on the host are visible within containers.

Sven van Zoelen
  • 5,948
  • 4
  • 30
  • 45
user3429660
  • 1,597
  • 3
  • 16
  • 29
12

Since the provided answer is working with macOSX, only, but performance issues exist with Docker for Windows as well the preferred answer didn't help in my case. I was following different approach partially described in answers to similar questions here on SO.

According to Performance Best Practices folders with heavy load such as vendor and var in a Symfony application shouldn't be part of a shared mount. If you require to persist those folders you should use volumes instead.

To prevent interferences with shared volume in /app I was relocating those two folders to separate folder /symfony in container. In Dockerfile folders /symfony/var and /symfony/vendor are created in addition.

The script run on start of container is setting symbolic links from /app/var to /symfony/var and from /app/vendor to /symfony/vendor. These two new folders are then mounted to volumes e.g. in a docker-compose.yml file.

Here is what I was adding to my Dockerfile:

RUN mkdir /app && mkdir /symfony/{var,vendor}

COPY setup-symfony.sh /setup-symfony.sh

VOLUME /symfony/var
VOLUME /symfony/vendor

Here is what I was adding to my startup script right before invoking composer update or any task via bin/console:

[ -e /app/var ] || ln -s /symfony/var /app/var
[ -e /app/vendor ] || ln -s /symfony/vendor /app/vendor

This is what my composition looks like eventually:

version: "3.5"
services:
  database:
    build:
      context: docker/mysql
    volumes:
      - "dbdata:/var/lib/mysql"
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 1

  application:
    depends_on:
      - database
    build:
      context: docker/lamps
    ports:
      - "8000:8000"
    volumes:
      - ".:/app:cached"
      - "var:/symfony/var"
      - "vendor:/symfony/vendor"
    environment:
      DATABASE_URL: mysql://dbuser:dbuser@database/dbname

volumes:
  dbdata:
  var:
  vendor:

Using this setup Symfony is responding within 500ms rather than taking 4000ms and more.

UPDATE: When using an IDE for developing Symfony-based application like PhpStorm you might need the files in vendor/ for code assist or similar. In my case I was able to take a snapshot of those files and put them into a different folder which is shared with host as well, but isn't actively used by Symfony/PSR, e.g. vendor.dis/. This snapshot is taken manually once per install/upgrade e.g. by entering the running container with a shell like so:

docker exec -it IDofContainer /bin/sh

Then in shell invoke

cp -Lr vendor vendor.dis

Maybe you have to fix the pathnames or make sure to switch into folder containing the your app first.

In my case using PhpStorm the vendor.dis/ was picked up by background indexing and obeyed by code inspection and code assist. Visual Studio code was having issues with the great number of untracked changes with regards to git so I had to explicitly make this snapshot ignored by git, adding its name in .gitignore file.

UPDATE 2020: More recent setups may have issues with accessing folders like /symfony/templates or /symfony/public e.g. on warming up the cache. This is obviously due to using relative folders in auto-loading code now existing in /symfony/vendor due to relocation described above. As an option, you could directly mount extra volumes in /app/var and /app/vendor instead of /symfony/var and /symfony/vendor. Creating deep copies of those folders in /app/var.dis and /app/vendor.dis keeps enabling code assist and inspections in host filesystem.

Thomas Urban
  • 3,321
  • 21
  • 29
10
  1. do not sync the vendor folder

In your docker file, you can prevent the vendor folder to sync with the container. This has the biggest impact on performance because the folder gets very huge:

#DockerFile:

  volumes:
    - /local/app:/var/www/html/app
    - /var/www/html/app/vendor # ignore vendor folder

This will have the effect that you will need to copy the vendor folder manuelly to the container once after the build and when you update your composer dependencies:

docker cp  /local/app/vendor <CONTAINER_ID>:/var/www/html/app/
  1. do not sync the cache folder

in your src/Kernel.php:

public function getCacheDir()
{
    // for docker performance
    if ($this->getEnvironment() === 'test' || $this->getEnvironment() === 'dev') {
        return '/tmp/'.$this->environment;
    } else {
        return $this->getProjectDir().'/var/cache/'.$this->environment;
    }

}
  1. sync the app folders in delegated mode

use delegated mode for volume mounts on development environments:

delegated: the container’s view is authoritative (permit delays before updates on the container appear in the host) Docker Performance tuning for volume mounts

This makes sense for dev envrionemtns, because normally you change your code with your IDE on the host not in the container and sync into the container. #DockerFile:

  volumes:
    - /local/app:/var/www/html/app:delegated
  1. disable Docker debug mode

check if Docker is NOT in debug mode:

docker info
# It Should display: Debug Mode: false

Disable in docker-config:

{
  
  "debug": false,
} 
  1. do not use a file cache

this is extra slow in a docker box, use for examle a SQLITE cache: Symfony Sqlite Cache

  1. for Windows 10 users: Use Docker Desktop with WSL 2 support

Use Docker Desktop with WSL 2 support, whichs incredibley boosts performance in general:

https://docs.docker.com/docker-for-windows/wsl/

Sebastian Viereck
  • 4,233
  • 36
  • 46
0

One more very important thing for container's performances. It's essential to check if a Dockerfile contain build of unnecessary layers.

For example,

Bad Practice -> use multiple unnecessary chained RUN

Best Practice -> use && from shell for chianed command as often as possible

e.g. , for example

We might write in our Dockerfile:

RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
    && apt-get update &&  apt-get install -y --no-install-recommends \
        locales apt-utils git \
    \
    && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen  \
    && echo "fr_FR.UTF-8 UTF-8" >> /etc/locale.gen \
    &&  locale-gen \

Instead of :

RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf 
RUN apt-get update &&  apt-get install -y --no-install-recommends \
    locales apt-utils git 

RUN  echo "en_US.UTF-8 UTF-8" > /etc/locale.gen  \
     && echo "fr_FR.UTF-8 UTF-8" >> /etc/locale.gen 
RUN  locale-gen

More layers improve container's slowness... Check your Server Dockerfiles friends !

I hope this comment help someone somewhere !

0

You can avoid using bind mounts which are extremely slow on Mac or Windows when they contain a big amount of files.

So, instead you can sync files between the host and the container volumes by using Mutagen, it's almost as fast as native with Linux. A benchmark is available here.

Here is a basic configuration of Mutagen:

sync:
    defaults:
      ignore:
        vcs: true
      permissions:
        defaultFileMode: 644
        defaultDirectoryMode: 755
    codebase:
      alpha: "./app" # dir of your app
      beta: "docker://project_container_1/var/www" # targets an absolute path in the container named project_container_1
      mode: "two-way-resolved"

This repository shows a full configuration with a simple PHP project (Symfony 5) but it can be used for any type of project in any language.

Kwadz
  • 1,755
  • 1
  • 17
  • 33