Une image Docker PHP + Apache sur mesure pour la production
Tags: code, docker, php
Published: 2019-12-14 22:01:33

---
title: Une image Docker PHP + Apache sur mesure pour la production
published: true
date: 2019-02-25 14:53:00 UTC
tags: Code,docker,php
cover_image: https://blog.silarhi.fr/content/images/2019/07/Post-Social---L-image-Docker-PHP-Apache-Parfaite-1.png
canonical_url: https://blog.silarhi.fr/image-docker-php-apache-parfaite/
---

Créez l'image Docker PHP + Apache qui vous ressemble. Elle sera donc parfaite puisque vous êtes un lecteur de ce blog et que vous êtes abonnés aux nouveaux articles 💖.

## C'est quoi une image Docker parfaite ?

C'est avant tout une image établie selon **votre besoin et votre expérience**. Vous avez identifié des **patterns communs** de configuration qui se répètent au fil de vos développements. Et comme vous êtes quelqu'un de bien, vous voulez donc les regrouper dans une couche commune à toutes vos apps pour en faciliter la maintenance.



À partir de ma propre expérience et du type d'applications que je développe la plupart du temps, une image PHP Apache parfaite selon moi c'est :

- [Composer](https://getcomposer.org/) installé **par défaut** (qui n'utilise pas de gestionnaire de dépendances de nos jours ?).
- PHP et Apache configurés pour la **production**.
- Des modules PHP récurrents activés par défaut.
- De jolies page d'erreurs type 404 un peu plus silencieuses que celles par défaut d'Apache.
- Idéalement des variantes déjà prêtes pour des applications qui tournent avec Symfony.

## Créer son image parfaite

En premier lieu on va partir de [l'image officielle](https://hub.docker.com/_/php/) de php, parce qu'on veut gagner du temps et ne pas réinventer la roue. Vous savez quoi ? L'image est même fournie avec un serveur Apache intégré. **TOP.**

🤓 « _Pourquoi utiliser Apache plutôt que nginx ?_ »

Parce que cet article n'est pas neutre et qu'il faut trancher. Personnellement, je trouve la différence de performances négligeables entre les deux et Apache convient parfaitement pour la plupart des projets. C'est également plus serein pour les mises à jour de sécurité qui seront directement effectuées par la team PHP Docker. Il m'arrive par contre pour [des projets un peu plus importants](https://github.com/guillaume-sainthillier/ByNight) d'utiliser nginx. Encore une fois, cela reste vraiment **une question de besoin, d'expérience et de goûts**.

On va donc commencer notre Dockerfile avec la ligne suivante :


```
# Dockerfile
FROM php:7.4-apache
```


On build et on run tout ça pour vérifier que tout va bien :

```
docker build -t super/hero .
docker run -it -p 8080:80 super/hero
```

En vous rendant sur [http://127.0.0.1:8080](http://127.0.0.1:8080) vous devez tomber sur une belle page 403. Ça veut bien dire que tout fonctionne, il n'y a pas encore vos sources donc Apache vous répond gentiement !

### Composer par défaut

Rien de plus simple, on installe la dernière version de composer et on rend la commande accessible partout dans le conteneur.


```
# Dockerfile
ENV COMPOSER_ALLOW_SUPERUSER=1
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN composer --version
```

### Configuration d'Apache

Par défaut, l'image officielle est configurée pour avoir la racine du serveur Web dans /var/www/html. **Trop long !** On va ignorer tout ça et accueillir notre futur code source dans un dossier **/app** directement à la racine du conteneur.


```
# conf/vhost.conf
<VirtualHost *:80>
	ServerAdmin contact@monsupersite.fr

	DocumentRoot /app

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```

```
# conf/apache.conf
<Directory /app/>
	Options -Indexes +FollowSymLinks
	AllowOverride None
	Require all granted
</Directory>

ServerTokens Prod
ServerSignature Off
```

```
# Dockerfile
# ...
ADD conf/vhost.conf /etc/apache2/sites-available/000-default.conf
ADD conf/apache.conf /etc/apache2/conf-available/z-app.conf
RUN a2enconf z-app
```

On a changé ici la configuration du vhost de base d'Apache (000-default.conf), qui délivre maintenant le contenu du dossier /app. On configure aussi le dossier /app pour ne pas autoriser Apache de lister les fichiers d'un répertoire avec `Options -Indexes`.

Pour ma part, je n'aime pas configurer mes sites avec un .htaccess (pour des raisons de performance). C'est pourquoi j'ai placé la directive `AllowOverride None`. Comme l'image est destinée pour le staging / production, j'ai aussi supprimé les infos sur l'infrastructure utilisée avec les directives `ServerTokens Prod` et `ServerSignature Off`.

J'ai volontairement nommé la configuration `z-app` pour être sûr qu'elle soit chargée en dernier et qu'aucun autre module ne vienne surcharger les instructions.


### Configuration de PHP

On va configurer PHP selon les besoins récurrents de nos futures images. Ne mettez pas de valeurs trop importantes ici, si un de vos conteneurs en particulier a besoin d'une configuration plus véloce, vous pourrez override cette configuration de base dans le Dockerfile propre à cette app.


```
; conf/php.ini
date.timezone = Europe/Paris

opcache.enable = 1
opcache.enable_cli = 1
opcache.memory_consumption = 128
opcache.revalidate_freq = 0
apc.enable_cli = On

upload_max_filesize = 16M
post_max_size = 16M

realpath_cache_size = 4096k
realpath_cache_ttl = 7200

display_errors = Off
display_startup_errors = Off
```
```
# Dockerfile
# ...
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    libicu-dev
RUN docker-php-ext-install -j$(nproc) opcache pdo_mysql
ADD conf/php.ini /usr/local/etc/php/conf.d/app.ini
```


Adaptez les modules par défaut qui vous semblent essentiels. Pour moi, la quasi totalité de mes projets utilisent PDO et MySQL. OPCache est bien sûr par défaut également.

### Des jolies pages d'erreur par défaut

Qui ne s'est jamais retrouvé devant la page 404 par défaut d'Apache ? C'est l'assurance de perdre la crédibilité du **[ninja DevOp big data pro de baby-foot](https://www.mathieupassenaud.fr/un-developpeur-nest-pas-un-joueur/)** et autres buzzwords que vous êtes auprès de votre client. On va donc modifier la conf existante pour ajouter la gestion des pages d'erreurs :


```
# conf/apache.conf
# ...
<Directory /errors/>
	Options -Indexes
	AllowOverride None
	Require all granted
</Directory>

Alias /_errors/ /errors/
ErrorDocument 404 /_errors/404.html
ErrorDocument 403 /_errors/403.html
ErrorDocument 500 /_errors/500.html
```
```
# Dockerfile
# ...
ADD errors /errors
```


Pour ne pas causer de conflits avec les futures apps,  j'ai placé le contenu des pages d'erreur dans un dossier à part (/errors à la racine du conteneur) et j'isole les requêtes HTTP spécifiques à ces pages via le chemin /\_errors.

## Ça donne quoi au final ?

Le Dockerfile a été optimisé pour réduire la taille des couches.


```
# Dockerfile
FROM php:7.2-apache

ENV COMPOSER_ALLOW_SUPERUSER=1

EXPOSE 80
WORKDIR /app

RUN apt-get update -qy && \
    apt-get install -y \
    git \
    libicu-dev \
    unzip \
    zip && \
    curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# PHP Extensions
RUN docker-php-ext-install -j$(nproc) opcache pdo_mysql
ADD conf/php.ini /usr/local/etc/php/conf.d/app.ini

# Apache
ADD errors /errors
RUN a2enmod rewrite remoteip
ADD conf/vhost.conf /etc/apache2/sites-available/000-default.conf
ADD conf/apache.conf /etc/apache2/conf-available/z-app.conf
RUN a2enconf z-app
ADD index.php /app/index.php
```

```
# conf/apache.conf
<Directory /app/>
	Options -Indexes +FollowSymLinks
	AllowOverride None
	Require all granted

	SetEnvIf X_FORWARDED_PROTO https HTTPS=on
</Directory>

ServerTokens Prod
ServerSignature Off

<Directory /errors/>
	Options -Indexes
	AllowOverride None
	Require all granted
</Directory>

Alias /_errors/ /errors/
ErrorDocument 404 /_errors/404.html
ErrorDocument 403 /_errors/403.html
ErrorDocument 500 /_errors/500.html
```
```
# conf/vhost.conf
<VirtualHost *:80>
	ServerAdmin guillaume@silarhi.fr

	DocumentRoot /app

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```
```
; conf/php.ini
date.timezone = Europe/Paris

opcache.enable = 1
opcache.enable_cli = 1
opcache.memory_consumption = 128
opcache.revalidate_freq = 0
apc.enable_cli = On

upload_max_filesize = 16M
post_max_size = 16M

realpath_cache_size=4096k
realpath_cache_ttl=7200

display_errors = Off
display_startup_errors = Off
```

Curieux ? J'ai mis en place rien que pour vous :

- [La démo de l'image Docker avec un super phpinfo en guise de bonjour](https://labs.silarhi.fr/php)
- [Une démo plus concrète basée sur Symfony](https://labs.silarhi.fr) ([et son article de blog associé](https://blog.silarhi.fr/deploiement-continu-symfony-docker-circleci/))
- [Un exemple de page 404](https://labs.silarhi.fr/php/liencass%C3%A9)
- [Le code source qui gère tout ça avec Traefik](https://github.com/silarhi/labs.silarhi.fr/tree/master/php)

## Pour aller plus loin

- [L'intégralité du code source utilisé dans l'article sur GitHub](https://github.com/silarhi/docker-php)
- [Mettre en place vos images Docker sur votre serveur de production avec Traefik](https://blog.silarhi.fr/docker-compose-traefik-https/)
- [L'image dans le Hub Docker](https://hub.docker.com/r/silarhi/php-apache)