Quand on est développeur, on a parfois besoin de travailler sur des versions de logicielles différentes, ou même avec des technologies différentes. Ceci ne concerne pas uniquement les développeurs fullstack mais, quand on veut bien faire les choses il est parfois intéressant d’avoir sur son poste le backend et le frontend, et ne pas travailler uniquement Postman pour faire les appels api.
Ceci implique d’installer toutes les versions et les logiciels nécessaires et certes oui il est possible de changer de versions par exemple pour PHP
sudo update-alternatives --config php
qui donnera
Il existe 3 choix pour l'alternative php (qui fournit /usr/bin/php).
Sélection Chemin Priorité État
------------------------------------------------------------
0 /usr/bin/php8.4 84 mode automatique
1 /usr/bin/php8.1 81 mode manuel
* 2 /usr/bin/php8.2 82 mode manuel
3 /usr/bin/php8.4 84 mode manuel
ou avec NodeJS
nvm use 20.19.1
sauf qu’il pourrait y avoir plusieurs projets avec des versions différentes de PHP ou NodeJS sur lesquels travailler et dans ce cas c’est assez compliqué.
Création de containers pour PHP
Disclaimers
Ce qui sera décrit ci-dessous, n’est qu’une suggestion d’architecture et de configuration et n’a pour objet que de montrer qu’un exemple.
Voici l’arborescence :
├── application
│ └── public
│ └── index.php
├── .docker
│ └── php
│ ├── custom.ini
│ └── Dockerfile-dev
├── docker-compose-dev.yml
├── .env.docker
└── Makefile
- application : dossier dans lequel la source sera placée
- .docker : toutes la configuration relative aux différents containters (exemple php)
- docker-compose-dev.yml : fichier de définition des services
- .env.docker : variables d’environnements utilisées pour la création des containers
- Makefile : défintion des commandes d’exécution afin de faciliter l’utilisation de docker
.env.docker
CONTAINER_PHP=my_container_php
PHP_IMAGE=php:8.2-alpine
PHP_PORT=9002
DOCKER_SUBNET=172.20.0.0/24
DOCKER_PHP_IP=172.20.0.5
DOCKER_USER=www-data
DOCKER_PROJECT_PATH=/var/www
docker-compose-dev.yml
networks:
mynetwork:
ipam:
config:
- subnet: ${DOCKER_SUBNET}
services:
php:
build:
context: ./.docker/php/
dockerfile: Dockerfile-dev
args:
UID: ${UID:-1000}
GID: ${GID:-1000}
PHP_IMAGE: ${PHP_IMAGE}
DOCKER_USER: ${DOCKER_USER:-app_user}
DOCKER_PROJECT_PATH: ${DOCKER_PROJECT_PATH}
container_name: '${CONTAINER_PHP}'
platform: linux/amd64
working_dir: ${DOCKER_PROJECT_PATH}
user: ${DOCKER_USER:-app_user}
tty: true
stdin_open: true
networks:
mynetwork:
ipv4_address: ${DOCKER_PHP_IP}
ports:
- '${PHP_PORT:-9000}:9000'
extra_hosts:
- 'host.docker.internal:host-gateway'
volumes:
- ${PWD:-./}/.docker/php/custom.ini:/usr/local/etc/php/php.ini:ro
- ${PWD:-./}/application:${DOCKER_PROJECT_PATH}:cached
restart: unless-stopped
Définition du network
Afin de ne pas se poser de question, et de maîtriser les containers, il semble utile de définir le réseau qui sera utilisé par les différents containers.
Bien sûr que ce n’est pas obligatoire et qu’un simple docker inspect <container_name>
permet de connaître l’ip utilisée par le container, mais le définir en
amont permet d’avoir toujours la même ip même si le container est détruit.
Défintion des services
Parmi les éléments importants il faut noter :
- Définition du
user
.
Il est courant de voir des exemples qui utilisent l’utilisateurroot
, c’est à ranger dans le même dossier « NE FAITES PAS ÇA CHEZ VOUS » quechmod 0777
.
Dans la configuration ci-dessus, on définit le user àwww-data
mais ça pourrait être n’importe quel nom.
.docker/php/Docker-dev
ARG PHP_IMAGE
FROM ${PHP_IMAGE:?"YOU_MUST_DEFINE_PHP_IMAGE_AS_ENV_VAR"}
ARG UID
ARG GID
ARG DOCKER_USER
ARG DOCKER_PROJECT_PATH
RUN docker-php-source extract \
# do important things \
&& docker-php-source delete \
&& apk --no-cache update && apk --no-cache upgrade
RUN apk --no-cache update && apk --no-cache upgrade && apk --no-cache add \
acl \
bash \
nano \
shadow \
sudo \
zip
# install gnu-libiconv and set LD_PRELOAD env to make iconv work fully on Alpine image.
# see https://github.com/docker-library/php/issues/240#issuecomment-763112749
ENV LD_PRELOAD=/usr/lib/preloadable_libiconv.so
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions \
@composer \
apcu \
bcmath \
bz2 \
intl \
memcached \
opcache \
xsl \
yaml \
zip
RUN groupmod -g $GID $DOCKER_USER && usermod -u $UID $DOCKER_USER
RUN mkdir -p /usr/local/var/run && \
mkdir -p $DOCKER_PROJECT_PATH/var && \
chown -R $DOCKER_USER: /usr/local/var/run $DOCKER_PROJECT_PATH
WORKDIR $DOCKER_PROJECT_PATH
USER $DOCKER_USER
Le Dockerfile
contient les éléments nécessaires à la création du container comme les dépendences au système bash par exemple,
ainsi que les librairies PHP et également composer qui permettra de créer plus simplement le projet, ses dépendences.
RUN groupmod -g $GID $DOCKER_USER && usermod -u $UID $DOCKER_USER
c’est ici qu’on crée l’utilisateur par défaut du container et ainsi on évite
d’utiliser root. On en profite pour créer les répertoires de l’application et on crée le bon propriétaire.
custom.ini
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
memory_limit=256M
max_execution_time=30
display_errors=Off
error_reporting=E_ALL & ~E_NOTICE
date.timezone=Europe/Paris
post_max_size=10M
upload_max_filesize=2M
short_open_tag=Off
custom.ini
permet de configurer PHP et sera utilisé dans le php.ini.
Makefile
make est un outil d’automatisation de tâches.
Installation sous linux
sudo apt-get install make
Installation sous MACOS
brew install make
Fichier Makefile
ifneq (,$(wildcard ./.env.docker))
include .env.docker
export
endif
ifeq (, $(which -v docker-compose))
DOCKER_COMPOSE = docker compose -f docker-compose-dev.yml
else
DOCKER_COMPOSE = docker-compose -f docker-compose-dev.yml
endif
MAKEFLAGS += --always-make
ARGS ?= $(strip $(subst ',\\',$(subst ",\\",$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)))))
# Misc
.DEFAULT_GOAL = help
.PHONY = help up down logs composer php sh console cc
## -- Docker Node Makefile --
help: ## Outputs this help screen
@grep -E '(^[a-zA-Z0-9_-]+:.*?##.*$$)|(^##)' $(firstword $(MAKEFILE_LIST)) | \
awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}'
## -- Docker containers --
.PHONY = up
up: ## Build the images and start the containers
@$(DOCKER_COMPOSE) up -d --build
.PHONY = down
down: ## Stop the docker hub
@$(DOCKER_COMPOSE) down --remove-orphans
.PHONY = start
start: ## Start containers
@$(DOCKER_COMPOSE) start
.PHONY = stop
stop: ## Stop containers
@$(DOCKER_COMPOSE) stop
## -- Composer --
.PHONY = composer
composer: ## Run composer
@docker exec -ti $(CONTAINER_PHP) composer $(ARGS)
## -- PHP --
.PHONY = php
php: ## Run php command line
@docker exec -ti $(CONTAINER_PHP) php $(ARGS)
.PHONY = server-start
server-start: ## Start PHP server
@docker exec -t $(CONTAINER_PHP) php -S 0.0.0.0:9000 -t public/ > /dev/null &
.PHONY = server-stop
server-stop: ## Stop PHP server
@docker exec -t $(CONTAINER_PHP) bash -c "ps auxw | grep 'php -S' | \
grep -v grep | awk '{print \$$1}' | xargs kill"
## -- Shell --
.PHONY = shell
shell: ## Connect to php container
@docker exec -ti $(CONTAINER_PHP) bash
.PHONY = shell-root
shell-root: ## Connect to php container
@docker exec -ti -u root $(CONTAINER_PHP) bash
## -- Logs --
.PHONY = logs
logs: ## Show live logs
@$(DOCKER_COMPOSE) logs --tail=0 --follow
# Avoid to build argument as a target
%::
@true
La commande make
affichera l’aide suivante :
## -- Docker Makefile --
help Outputs this help screen
## -- Docker containers --
up Build the images and start the containers
down Stop the docker hub
start Start containers
stop Stop containers
## -- Composer --
composer Run composer
## -- PHP --
php Run php command line
server-start Start PHP server
server-stop Stop PHP server
## -- Shell --
shell Connect to php container
shell-root Connect to php container
## -- Logs --
logs Show live logs
Then, you have to build the container.
make up
Once it is build, you can stop it
make stop
and start it again
make start
And now Start server
make server-start
and you will be able to see configuration at http://localhost:<PHP_PORT>
Code source : https://github.com/mrbinr/blog-resources/tree/post/learning-docker-php-example/learning-docker-php-example