Evgenii Goryaev
Development, support and optimization

My perfect php-fpm docker image

Poster for the article My perfect php-fpm docker image

For everyone who uses docker in their web projects on php, the question arises of building a container for your project. Someone uses Apache-based containers with php as a module, but I, like most developers, prefer to build my container for a php-fpm-based application.

The main problem is that [the original php-fpm] (https://hub.docker.com/_/php) does not contain many php modules for full work, as well as auxiliary software, which is regularly required.

The situation is aggravated if it is necessary to support several projects on the same stack. This is just my case, as I support and refine [usually more than 10k projects] (/ projects.html) on the Yii2 framework. They all use approximately the same stack: [Yii2 framework] (https://www.yiiframework.com/), php-fpm, nginx, memcached and the mysql or postgres database.

So, in the base container with php-fpm, you need to add the following php libraries:

  • GD - necessarily compiled with WEBP support;
  • IMAP - for sending mail;
  • Zip - at least for working with backups;
  • Intl - a necessary module for internationalization;
  • Pdo_mysql - for working with Mysql;
  • Pdo_pgsq l - for working with Postgresql;
  • Memcached - for organizing caching;
  • Xdebug - I disable it by default and activate it only during development.

In addition to these libraries, I put additional software:

  • git
  • composer
  • zip
  • ffmpeg
  • mysql-client
  • postgresql-client

Git ended up on this list because it is needed for composer. I install zip, since I use the [Yii2 backup module] (https://floor12.net/packages/yii2-module-backup.html), which uses zip for compression. [FFmpeg] (https://www.ffmpeg.org/) I install so that the [file handling module for Yii2] (https://floor12.net/packages/yii2-module-files.html) converts video files. I install console clients for mysql and postgresql for universality, since my applications work with both databases, and in some cases with two at the same time.

As a result, I get a universal and quite flexible php-container, which has everything you need to develop, launch a project in production or create a final image based on it.

For the convenience of supporting the two php branches that I need, I store them in different branches of the repository. I build them through gitlab CI and store them in the public domain in the same place, in [gitlab registry] (https://gitlab.com/floor12/images/container_registry/) .

After the development phase, when it is necessary to assemble the project into a separate image for launching in production, I usually create it based on the images mentioned above. Copy to container:

  • project files necessary for work;
  • php.ini config, which contains the optimal parameters for the project (at least increase upload_max_filesize and post_max_size, set the time zone);
  • config for nginx, which in the future will be shared with a container running nearby;
  • config for the database, if it needs any additional settings.

Then I start the installation of dependencies through composer install --no-scripts --prefer-dist --optimize-autoloader. But at the same time, in the final assembly I also have packages from dev-dependencies ([codeception] (https://codeception.com/), debug and other components necessary for testing). Since when building applications in CI, I am testing a container that will go directly to production, it needs dev dependencies in it. After the tests, they can be removed from the container. I will talk about this in other articles.

If you want to slightly reduce the size of the final production image, it is possible to build it not on the basis of these images, but edit their original [dockerfile] (https://gitlab.com/floor12/images/-/blob/basic74/Dockerfile-basic) under yourself, removing the extra software from there. In addition, you can remove all unnecessary software from the container even after the application is built. But I do not use this approach. It’s more convenient for me that the images do not differ from each other for the convenience of supporting projects: sometimes you need to connect to the container and already have in it all the necessary tools for debugging and useful utilities.

At the time of this writing, the image is being built using the dockerfile below. However, its current version can be found in the [repository on hitlab] (https://gitlab.com/floor12/images).

FROM php:7.4-fpm

RUN apt-get update
RUN apt-get install -y \
            git \
            libzip-dev \
            libc-client-dev \
            libkrb5-dev \
            libpng-dev \
            libjpeg-dev \
            libwebp-dev \
            libfreetype6-dev \
            libkrb5-dev \
            libicu-dev \
            zlib1g-dev \
            zip \
            ffmpeg \
            libmemcached11 \
            libmemcachedutil2 \
            build-essential \
            libmemcached-dev \
            gnupg2 \
            libpq-dev \
            libpq5 \
            libz-dev

RUN echo 'deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main 9.5' > /etc/apt/sources.list.d/pgdg.list

RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8

RUN apt-get update && apt-get install -y postgresql-client-9.5

RUN docker-php-ext-configure gd \
    --with-webp=/usr/include/ \
    --with-freetype=/usr/include/ \
    --with-jpeg=/usr/include/
RUN docker-php-ext-install gd

RUN docker-php-ext-configure imap \
    --with-kerberos \
    --with-imap-ssl
RUN docker-php-ext-install imap

RUN docker-php-ext-configure zip

RUN docker-php-ext-install zip

RUN docker-php-ext-configure intl
RUN docker-php-ext-install intl

RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install pdo_pgsql
RUN docker-php-ext-install exif
RUN docker-php-ext-install fileinfo

RUN pecl install xdebug

RUN curl --silent --show-error https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

RUN pecl install memcached

RUN echo extension=memcached.so >> /usr/local/etc/php/conf.d/memcached.ini

ENV COMPOSER_ALLOW_SUPERUSER 1

WORKDIR /app