What can we build today?

Random notes of a Webdeveloper called Mario.

Intro

Since years, this is one of the topics that comes back every now and then - and until now it was always a choice out of many ways that all had their good sides but also their drawbacks. Until now, I used several things to develop PHP based projects:

  • local on Mac, with MAMP
  • local on Mac, within a Vagrant-Box
  • local on Mac, with Homebrew-based installations of Nginx, PHP-FPM and MySQL
  • on a shared Development-Server

But now I learned about ddev and how it solves many of the issues the above solutions had. I've now tested it for a few days and for now this seems to be a very cool solution:

  • based on Docker
  • config per project can be versioned along with the code
  • brings 99% of the things I need just "out of the box"

What was not out ouf the box, could be fixed. Thanks a lot to Kay Strobach for some insights and tipps! I like to share my setup for anyone that wants to use it.

Install Docker and ddev

As ddev is based on Docker, you definitely need to install Docker to your computer. Just follow the ddev documentation about Docker and install it.

Then install ddev itself. Since I'm running a Mac right now, I chose to install ddev via HomeBrew with the following command:

➜  ~ brew tap drud/ddev && brew install ddev

This will install ddev globally on your computer - once done, you're ready to start to configure ddev for your first project.

Prepare your project

To run a PHP project within the ddev Docker-Containers, you have to configure it once. I've now done that to multiple projects (including this website btw, just to easily preview this blog post). As soon as you've done that more than once, it's a matter of seconds to get up to speed... Very neat!

The steps are easy:

  • change to your project's main directory with the shell of your choice (e.g. where the composer.json is located)
  • start the ddev config wizard
  • answer 3 questions (it basically just asks about the name of the project, the desired directory where the document-root for the webserver config shall point to and the type of the project)
  • start ddev

The commands for this are:

ddev config
ddev start

For several kinds of applications (Drupal, TYPO3, Wordpress, ...) ddev ships with more specific presets or configurations (don't know in detail, not tested). As of now, the Flow Framework doesn't have such presets in ddev - maybe this will change. Until then, just choose the project-type php.

Depending on the PHP-Version you need or want - just have a look into the .ddev/config.yaml file. As of now, new setups are configured to run PHP 7.1 but you can change this in that file.

As soon as you start ddev with ddev start it takes a moment to initially download the docker containers (this happens only once). As soon as the containers are up, ddev tells you, where to access your application:

➜  rimann-org git:(master) ✗ ddev start
Starting rimann-org...
Starting ddev-rimann-org-db ... done
Starting ddev-rimann-org-dba ... done
Starting ddev-rimann-org-web ... done

Network ddev_default is external, skipping
Creating ddev-router ... done

Successfully started rimann-org
Project can be reached at http://rimann-org.ddev.local, https://rimann-org.ddev.local, http://127.0.0.1:32774

Configure Flow

Starting ddev also starts an own container for the database "server" and it's credentials are pretty simple to remember:

Configuration/Development/Settings.yaml:

Neos:
  Flow:
    persistence:
      backendOptions:
        host:     'db'
        dbname:   'db'
        user:     'db'
        password: 'db'
      doctrine:
        sqlLogger: 'Neos\Flow\Persistence\Doctrine\Logging\SqlLogger'
  SwiftMailer:
    transport:
      type: 'Swift_SendmailTransport'

Besides the database access credentials, this also instructs your application to

  • log SQL Queries to Data/Logs/Query_Development.log - can be omitted if you don't want to use that
  • send mails that are created/sent via the SwiftMailer Package to use the sendmail transport (to make sure sent mails get caught to Mailhog for testing)

Test-Drive

Now fire up your Browser and visit https://PROJECTNAME.ddev.local/ and it should work :-)

Additional Features

By default, ddev starts multiple Docker containers. Basically everything that is needed to run the PHP based application including PHP-FPM, Nginx Webserver and a MySQL database. But there are some hidden magic features I like to highlight:

As a super Bonus, if you're a Mac user and have SequelPro installed, just execute ddev sequelpro and it will magically start SequelPro, including the config to access the MySQL-Database within the container.

Work with the container

As your application now runs in the browser, you can continue to develop by editing the local files in your project directory. Don't forget to version your the .ddev/config.yaml file.

If needed, you can access the container via ddev ssh and it will bring you directly to your project directory within the container.

Optimize the configuration

As said before, ddev runs well just out of the box. But I ran into minor details that bogged me and that I wanted to optimize to get happy with ddev:

Performance, Part 1

Somehow, the Applications seemed to run slow - and reloading the browser after a code change resulted in waiting for >2s which is well, just is not so funny... Add the file below and restart ddev to see an effect.

.ddev/docker-compose.yaml:

version: '3.6'

services:
  web:
    environment:
      - FLOW_CONTEXT=Development
      - FLOW_PATH_TEMPORARY_BASE=/tmp/Flow

This basically sets two environment variables to instruct the Flow-Framework to

  • run in Development Context (well, this is the default anyway, but you could tweak this to FLOW_CONTEXT=Development/Ddev if you'd like to use different settings for within ddev (aka a sub-context)
  • put the temporary stuff into /tmp/Flow - this results in storing the many little files within the container - instead of your project directory (which is just mounted into the container and thus slower on each read/write operation)

Performance, Part 2

Flow does a walk through all files in the installation to check if they changed since the last run - and if so, it issues a recompile of the application's code. This is nice in Development Context as the Code-Cache doesn't need to be flushed manually to actually see/test the new version of the code. But it seems to be rather slow within a Docker-Container - again because it has to access dozens (ok, thousands?) of files that are mounted into the container.

My solution for now is to instruct Flow to freeze almost all packages. With this, Flow just "ignores" those packages and just re-scans the Packages that I'm actually working on - and that's then just a fraction of the number of files to check on each request compared to before.

Just look at ./flow help to find more details on freezing, re-freezing and un-freezing packages.

./flow package:freeze --package-key all

This freezes all packages. After that, you might need to unfreeze those packages you're actually working on:

./flow package:unfreeze --package-key Vendor.PackageKey

To check what's frozen, you can get this information from the list of packages, just run ./flow package:list

Flow Rescan

To make sure the container runs the most-current version of the code upon starting the ddev containers, we can instruct ddev to execute a rescan of all packages - and warmup all code caches when starting ddev.

.ddev/config.yaml:

hooks:
  post-start:
  - exec: ./flow flow:package:rescan
  - exec: ./flow flow:cache:warmup

After adding the above section to the ddev-config file (and a restart of the containers), the listed commands are automatically executed and result in rescanning all (also freezed) packages and building the code caches.

Results

The above mentioned configuration options solved my initial performance issue. From >2s load time in the browser for a simple login form or a list of <10 entries, it went down to somewhat beween 600ms and 750ms which I think is pretty good and in the range I'd like it to be. (Faster would be nice as usual - but that's well acceptable for me)

Power consumption

Now to the only unsolved and not so satisfying point:
Running the Docker containers drains my MacBook's battery way faster than without Docker (device gets hot, fans making noise almost all the time). I have to admit, it's not a brand-new but rather 3-year old notebook and I've not looked into optimizing Docker for power-saving or the like.

This means: If one of you has a good idea to optimize this situation - let me know :-)

What else

Not mentioned so far, but ddev has a great documentation - have a look there if you need more input or want to customize your setup further.