Makefile et Docker en Dev

Objectif : Découvrir quelques astuces pour mieux gérer l'environnement de développement de son équipe. Thèmes : Développement, Docker-Compose, Makefile, PHP.

Docker-Compose

On débute avec seulement notre projet PHP. La première chose à faire, c'est de mettre en place l'outil qui nous permettra de créer, de recréer, à volonté, la stack qui est utilisée pour développer sur le logiciel.

On va vouloir définir l'état actif normal de notre stack de développement. Et uniquement cet état. Quelles parties du logiciel doivent tourner obligatoirement ? Lesquelles sont à contrario temporaires ? Cet état actif normal, on va vouloir l'écrire dans un fichier, et laisser notre outil gérer lui même les actions à effectuer pour l'atteindre.

Pour ça, il y a Docker-Compose. C'est un ensemble de commandes qui vont encapsuler l'accès à Docker – qu'on utilise, lui, pour gérer toutes les problématiques de portabilité –. On écrit dans un fichier descriptif, conventionnellement docker-compose.yml, l'état de la stack qu'on souhaitera faire tourner via Docker.

Un exemple de docker-compose.yml, utilisé pour faire tourner l'infrastructure de développement en local de green.vanoix.com :

version: '3.7'

services:
    application:
        build: ./docker/build/application
        volumes:
            - .:/var/www/app
        ports:
            - "80:80"
        networks:
            - backend
        secrets:
            -
                source: ssh_key
                target: /root/.ssh/id_rsa
    database:
        image: mariadb:latest
        volumes:
            - ./docker/data/database:/var/lib/mysql
        environment:
            - 'MYSQL_ALLOW_EMPTY_PASSWORD=yes'
            - 'MYSQL_DATABASE=application'
        networks:
            - backend

secrets:
    ssh_key:
        file: ~/.ssh/id_rsa

networks:
    backend: ~

On décrit : – D'abord les services qu'on va vouloir faire tourner, les volumes qui leur seront rattachés, etc. – Puis les secrets qu'on va vouloir utiliser. Qu'on va ensuite ajouter à chaque service où c'est nécessaire. – Enfin les réseaux qui devront être créés. Qu'on renseigne, de la même manière, au niveau des services qui les utiliseront.

Makefile

Maintenant que notre cible est définie, on va vouloir la rendre accessible, rapidement. D'une part, on cherche à diminuer la difficulté à mettre en route cette stack. D'une autre, on cherche à diminuer le temps pour ce faire.

Il va donc falloir réduire au maximum le nombre d'étapes, et automatiser chacune le plus possible.

Pour sa portabilité, le fait qu'il soit un standard, et qu'il permet de gérer tout ce qu'un shell peut gérer – donc, tout –, on va utiliser make. De manière assez conventionnelle, la règle principale par défaut est souvent nommée install. On retrouve aussi la règle run (ou start), qui permet de lancer le logiciel. Souvent la règle clean pour nettoyer l'environnement local. Et parfois, stop dans le cas d'un service en arrière-plan.

Dans notre cas, un fichier Makefile contenant ceci devrait suffir. Moins il y a de règles, plus le projet est simple à aborder :

.PHONY: start stop

start:
    docker-compose build
    docker-compose run application composer install
    docker-compose up -d

stop:
    docker-compose down

Rappel sur le .PHONY : chaque règle (ou cible) est normalement associée au fichier du même nom. Pour décorréler les deux, on renseigne la règle dans l'énumération .PHONY.

Makefile et Docker-Compose

Le principal problème avec ce que l'on a fait, c'est qu'on va essayer de rebuild les containers et réinstaller les dépendances Composer, à chaque lancement du projet. On perd beaucoup de temps. D'un autre côté, si on souhaite seulement réinstaller les containers, on démarre aussi l'application. On perd beaucoup en ressources. Cela traduit un problème de conception, sur les différents cas d'utilisation de nos automatismes.

Pour corriger cela, on va définir les états actifs normaux de l'environnement de développement. Puis les décrire, dans un fichier, sous forme de règles idempotentes. Puis – comme avec docker-compose – on considérera que le travail de notre outil n'est que de mettre le système dans l'état souhaité.

Ici, on définit trois états pour un environnement local : – l'état installé, lorsque les containers sont construits et les dépendances logicielles installées, – l'état démarré, lorsque le logiciel a été lancé, – et l'état stoppé, lorsque le logiciel a été arrêté.

make et docker se combinent à merveille, afin de définir les règles de passage à un état spécifique. Voici l'exemple du Makefile utilisé pour le projet green.vanoix.com :

.PHONY: install start stop

install:
	docker-compose build
	docker-compose run --no-deps --rm application composer install

start:
	docker-compose up -d

stop:
	docker-compose down

On définit une règle par état ciblé, et une série d'action pour chaque règle. Sur un plan plus technique, l'astuce consiste à profiter de la capacité de Docker-Compose à éxécuter seul (run --no-deps) un container temporaire (--rm), basé sur un service à l'arrêt (application), pour installer les dépendances PHP sur le système de fichiers (composer install).

README

Dans le cas d'applications plus complexes, il reste encore une chose à paufiner : la gestion du contexte. Souvent, on va considérer que le développeur devra avoir tel prérequis installé sur sa machine. Tel service lancé en fond. Telle configuration SSH. Tel dossier à tel emplacement. Telle connaissance acquise.

On va vouloir gérer tous ces cas particuliers. Mais sans sacrifier les performances et la simplicité de notre système. On peut encadrer notre environnement de développement en utilisant des outils de communication.

Le fichier README.md est classique et efficace, et un excellent liant avec tous les autres outils. Un exemple de template minimal qui permet de gérer la configuration du host et clef SSH sur un projet :

## Dev Environment

This project uses Docker (docker-compose version >=3.5) to manage the local development environment.

### Requirements

#### Hosts Configuration

You must have this entry in your `/etc/hosts` file:

```
127.0.0.1 myproject.localhost
```

#### SSH key forwarding

The project uses Docker Secrets to handle the SSH key forwarding to your container (mainly used to access private repositories).

Your local SSH key must be located at `~/.ssh/id_rsa`.

### Installation

```
make install && make start
```

The project should be now successfully running.

Liens