lindev.fr

Aller au contenu | Aller au menu | Aller à la recherche

07 juin 2017

Environnement de développement complet avec Docker

Qu'allons nous voir

docker-logo.jpg Nous allons voir comment mettre sur pied avec Docker un environnement de développement Web complet ( LAMP ) en quelques minutes ( si l'on compte uniquement le temps de déploiement, et non la lecture de ce post. ).

Mais surtout , rendre cet environnement facilement configurable pour qu'il réponde à vos attentes.

Au programme, nous aurons donc 3 conteneurs


Petit plus, nous aurons à disposition, un fichier de configuration permettant l'utilisation de la fonction mail() de php.

Pour lancer facilement ces conteneurs Docker, nous utiliseront l'outil docker-compose, qui permet en un seul fichier de déclarer notre environnements et bien plus encore.

Pour les plus pressés : Si vous ne souhaitez pas créer les répertoires et fichiers de conf à la main en suivant tout le billet, vous pouvez directement cloner le projet puis lancer ( Après avoir installé Docker sur votre machine évidemment ). Rendez-vous tout en bas de cette page, dans la section Go go go.

Installation de Docker et docker-compose

Franchement, il n'y a rien de compliqué, je ne vais pas détailler spécialement la méthode , juste vous donner les liens pour les différents systèmes.
Après, il suffit de suivre.

Docker CE

Docker-Compose

Bonne nouvelle pour les utilisateurs de Mac & Windows, l'outil est automatiquement installé avec Docker.

Pour les utilisateurs Linux, je conseille la commande pip

sudo pip install docker-compose

Le projet LAMP

Parfait, nous avons tout les outils nécessaires pour atteindre notre objectif.
Commençons par créer un répertoire qui contiendra l’ensemble des fichiers de configuration et le code source de nos futurs projets Web .

mkdir -p dockerlamp/www dockerlamp/docker/apache/vhosts dockerlamp/docker/mysql dockerlamp/docker/php

Voici en résumé ce que ça donne :

dockerlamp
├── docker
│   ├── apache
│   │   └── vhosts
│   ├── mysql
│   ├── php
└── www

Nous aurons donc les fichiers de configuration dans le répertoire docker/ et le code source sera dans le répertoire www/

docker-compose.yml

Je vais pas tourner autour du pot, je vais tout de suite vous donner le contenu complet du fichier docker-compose.yml, nous verrons ensuite les différents points à compléter.

Créez donc le fichier docker-compose.yml à la racine du répertoire dockerlamp/

# Adopt version 2 syntax:
#   https://docs.docker.com/compose/compose-file/#/versioning
version: '2'

volumes:
    database_data2:
        driver: local

services:
###########################
# Setup the Apache container
###########################
    httpd:
        container_name: dockerapache_httpd_1
        restart: always
        image: httpd:2.4.25
        ports:
            - 80:80
        volumes:
            - ./docker/apache/httpd.conf:/usr/local/apache2/conf/httpd.conf
            - ./docker/apache/vhosts/:/usr/local/apache2/conf/vhosts
        volumes_from:
            - php

###########################
# Setup the PHP container
###########################
    php:
        container_name: dockerapache_php_1
        restart: always
        build: ./docker/php/
        expose:
            - 9000
        volumes:
            - ./www/:/usr/local/apache2/htdocs
            - ./docker/php/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro
            - ./docker/php/php-mail.conf:/usr/local/etc/php/conf.d/mail.ini:ro


###########################
# Setup the Database (MySQL) container
###########################
    mysql:
        container_name: dockerapache_mysql_1
        restart: always
        image: mysql:5.6.36
        expose:
            - 3306
        volumes:
            - database_data2:/var/lib/mysql
            - ./docker/mysql/conf-mysql.cnf:/etc/mysql/mysql.conf.d/conf-mysql.cnf:ro
        environment:
            MYSQL_ROOT_PASSWORD: secret
            MYSQL_DATABASE: project
            MYSQL_USER: project
            MYSQL_PASSWORD: project

Configuration Apache

Afin de configurer finement le service Apache, nous allons récupérer le fichier httpd.conf et le déposer dans le répertoire dockerlamp/docker/apache/

Voici le lien de téléchargement httpd.conf

La seule modification faite à ce fichier ( comparé à la version d'origine ) est la prise en charge des virtualhosts.

Nous allons par conséquent également récupérer le virtualhost par défaut et le placer dans le répertoire dockerlamp/docker/apache/vhosts/

Voici le lien de téléchargement 000-default.conf


Pour les curieux, si vous éditez le fichier 000-default.conf, vous verrez que l'on utilise le module proxy d'apache pour gérer les sources php . Nous passons ces sources au service nommé "php" sur le port 9000.

Nous en avons terminé avec le service apache . Passons au service php.

Configuration php-fpm

Contrairement au service apache, le conteneur php-fpm doit être modifié, pour ne serait-ce qu'installer les extensions.
Nous allons donc créer un fichier Dockerfile, qui contiendra toutes les instructions pour finaliser notre container .

La bonne pratique

J'aurais pu simplement créer une image du container sur dockerHub et la mettre à disposition, mais pour partager une image modifiée, la bonne pratique consiste à utiliser comme base l'image officielle ( php ), puis d'ajouter explicitement les changements à apporter dans un DockerFile.

Ce qui vous permet d'être certain que l'image finale n'est pas corrompue.

Dockerfile

créez le fichier Dockerfile dans le répertoire dockerlamp/docker/php/, comme précisé dans docker-compose.yml.

FROM php:5.6-fpm

# install the PHP extensions we need
RUN set -ex; \
	\
	apt-get update; \
	apt-get install -y \
		libjpeg-dev \
		libpng12-dev \
                libmcrypt-dev \
                ssmtp \
	; \
	rm -rf /var/lib/apt/lists/*; \
	\
	docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr; \
	docker-php-ext-install gd mysqli opcache pdo_mysql json mcrypt; \
        pecl install -o -f xdebug redis \
        && rm -rf /tmp/pear \
        && echo "extension=redis.so" > /usr/local/etc/php/conf.d/redis.ini \
        && echo "zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20131226/xdebug.so" > /usr/local/etc/php/conf.d/xdebug.ini; 
# TODO consider removing the *-dev deps and only keeping the necessary lib* packages

# set recommended PHP.ini settings
# see https://secure.php.net/manual/en/opcache.installation.php
RUN { \
		echo 'opcache.memory_consumption=128'; \
		echo 'opcache.interned_strings_buffer=8'; \
		echo 'opcache.max_accelerated_files=4000'; \
		echo 'opcache.revalidate_freq=2'; \
		echo 'opcache.fast_shutdown=1'; \
		echo 'opcache.enable_cli=1'; \
	} > /usr/local/etc/php/conf.d/opcache-recommended.ini

RUN { \
                echo '[xdebug]'; \
                echo 'xdebug.default_enable = 0'; \
        } >> /usr/local/etc/php/conf.d/xdebug.ini


RUN { \
        echo 'error_reporting  =  E_ALL'; \
        echo 'log_errors = On'; \
        echo 'display_errors = Off'; \
    } > /usr/local/etc/php/php.ini

CMD ["php-fpm"]
EXPOSE 9000

php7-fpm : Si vous préférez une autre version de php, il vous suffit de la changer sur la première ligne ( ex : FROM php:7.1.5-fpm ).
Toutes les versions sont listées ici.

fonction mail()

Puisque nous utilisons le service php à l'intérieur d'un container, seul ce service est disponible ( bien qu'il soit possible de lancer plusieurs services dans un même conteneur, cette méthode n'est pas conseillée ), le service sendmail n'est pas disponible.

Pour palier à ce problème, nous allons utiliser ssmtp. Cet utilitaire ne fait que rediriger les mails vers un serveur SMTP externe.
Pour le configurer ( l'activer au sein de php et définir quel serveur SMTP utiliser ), nous allons ajouter deux fichiers dans le répertoire dockerlamp/docker/php/

  • php-mail.conf
  • ssmtp.conf
php-mail.conf

Ce fichier de configuration ( chargé comme un php.ini supplémentaire ) ne fait que surcharger le chemin de l’exécutable sendmail afin d'utiliser ssmtp.

[mail function]
sendmail_path = "/usr/sbin/ssmtp -t"
ssmtp.conf

Ce fichier vous permet de définir quel serveur SMTP utiliser pour envoyer vos mails.
Par exemple, pour utiliser un compte Gmail:

mailhub=smtp.gmail.com:587
AuthUser=me@mydomain.net
AuthPass=mypassword

FromLineOverride=YES
hostname=dockerdev.foo.net
UseSTARTTLS=YES

Seules les 3 premières lignes sont à adapter selon votre cas.

Nous en avons terminé avec la configuration de php-fpm. Passons à Mysql

Configuration Mysql

C'est presque le service le plus simple... dans le fichier docker-compose, nous avons défini des variables d'environnements, qui spécifient le mot de passe du compte root, une base de données à créer avec un utilisateur associé (login et mot de passe) .

Par la suite, si vous devez ajouter une nouvelle base de données, deux possibilités .

  1. Ajouter les informations dans le fichier docker-compose, et relancer le conteneur
  2. Se connecter en root sur le service mysql et créer manuellement la base de données et son utilisateur .

Nous allons créer le fichier conf-mysql.cnf, dans le répertoire dockerlamp/docker/mysql/ ce fichier est automatiquement monté dans le conteneur Mysql à son démarrage et permet donc de modifier simplement la configuration de mysql .

Par exemple, si l'on souhaite modifier la variable sql_mode

[mysqld]  
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

Note : Vous pouvez laisser ce fichier vide si vous n'en avez pas l'utilité pour le moment. Mais il doit être présent dans le répertoire mysql !

Notre environnement LAMP est maintenant prêt !

Lancer l'environnement LAMP

Puisque nous avons configuré notre environnement via un fichier docker-compose.yml, nous allons évidemment utiliser la commande docker-compose pour le lancer.
Placez-vous à la racine du projet dockerlamp/

docker-compose up -d

Cette commande va construire les volumes, les interfaces réseaux, télécharger les images nécessaires, construire les conteneurs si besoin ( via le Dockerfile ) et enfin ... les exécuter en tache de fond . Oui oui .. tout ça en une seule commande .. le pied!

Le test

Nous allons tester notre stack, apache et php.

Créez deux fichiers dans le répertoire www/

  • info.php
  • index.html
info.php
<?php 
echo phpinfo();
index.html
<h1>Hello World</h1>

Enfin ouvrez un navigateur et entrez ces URLs :

Vous devriez normalement voir un magnifique Hello World, et la page d'infos de php ! Selection_999_057_.png

Un vrai projet

Je ne vais pas vous laisser avec un simple phpinfo() et un Hello World !!
Nous allons aller au bout des choses, en installant par exemple un petit Drupal.

L'objectif est d'installer Drupal et qu'il soit accessible ( sur votre machine ) depuis l'url http://drupal.local/



Le Vhost

Afin de pouvoir utiliser un nom de domaine locale, ( drupal.local ), il faut auparavant renseigner le fichier hosts, pour le déclarer .

Linux et Mac
sudo sh -c "echo '127.0.0.1   drupal.local' >> /etc/hosts"
Windows

Pour Windows vous devez ouvrir le fichier c:\Windows\System32\Drivers\etc\hosts en administrateur avec NotePad et y ajouter la ligne :

127.0.0.1   drupal.local

Maintenant, nous allons créer notre vhost afin qu'Apache sache quoi faire lorsqu'il reçoit les requêtes avec le nom de domaine drupal.local.

Editer le fichier dockerlamp/docker/apache/vhosts/drupal.conf

<VirtualHost *:80>
        ServerAdmin you@example.com
        ServerName drupal.local

        <Directory />
            AllowOverride All
            Require all granted
        </Directory>

        DocumentRoot "/usr/local/apache2/htdocs/drupal"
        <Directory "/usr/local/apache2/htdocs/drupal">
                Options Indexes FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>


    <FilesMatch \.php$>
        # 2.4.10+ can proxy to unix socket
        # SetHandler "proxy:unix:/var/run/php5-fpm.sock|fcgi://localhost/"

        # Else we can just use a tcp socket:
        SetHandler "proxy:fcgi://php:9000"
    </FilesMatch>

</VirtualHost>

On a donc déclaré le nom de domaine à Apache, en lui spécifiant de pointer sur le répertoire drupal/

Les sources

Puisque nous avons demandé à Apache de pointer sur le répertoire drupal/, nous allons télécharger les sources de Drupal dans ce répertoire qui lui-même se trouve dans le répertoire dockerlamp/www/

cd www
wget https://ftp.drupal.org/files/projects/https://ftp.drupal.org/files/projects/drupal-8.3.2.tar.gz
tar xzf drupal-8.3.2.tar.gz 
mv drupal-8.3.2 drupal
mkdir -p drupal/sites/default/files/translations && chmod -R 777 sites/default/files/translations
chmod -R 777 drupal/sites/default/files
touch drupal/sites/default/settings.php && chmod 777 drupal/sites/default/settings.php

Enfin, pour que le conteneur Apache prenne en considération notre nouveau vhost, nous allons redémarrer notre stack

docker-compose restart

L'installation

Pour l'installation, il suffit de pointer sur notre nom de domaine local et suivre les instructions

http://drupal.local/

Arrivé à l'installation de la base de données, remplissez le formulaire comme ceci ( Base de données déclarée dans le fichier docker-compose.yml ),

  • Nom de la base de données : project
  • Utilisateur : project
  • Mot de passe : project
  • Et dans options avancés > Hôte : mysql


Selection_999_058_.png

Après cette étape, Drupal s'installe tranquillement sur votre environnement
Selection_999_059_.png
Remplissez les formulaires durant l'installation, vous devriez rapidement arriver sur la page d'accueil de votre Drupal fraîchement installé .

Selection_999_060_.png Selection_999_061_.png

Conclusion

Vous avez à disposition un environnement de développement complet comme démontré avec Drupal.

Pour chaque projet vous devez donc

  1. Créer un domaine local dans votre fichier hosts
  2. Créer la configuration du virtualHost pour apache
  3. Récupérer les sources et les placer dans le répertoire www/
  4. Relancer la stack LAMP via docker-compose

Voici quelques outils / lignes de commandes pour vous aider à travailler, "debugger".

Vous trouverez ces petits scripts dans le répertoire tools/ du dépôt dockerlamp.

Ces scripts ne représentent qu'une base pour vous aider à travailler, je vous laisse maintenant parcourir la documentation de Docker pour comprendre les différentes commandes / configurations utilisées dans ce billet.

N’hésitez pas à poser des questions si besoin.

Créer un dump d'une base de données

dump_sql.sh

docker exec dockerapache_mysql_1 /usr/bin/mysqldump --databases -uroot -psecret $DATABASE > $DATABASE.sql

Restaurer/Importer une base de donnée depuis un dump sql

restore_sql.sh

cat $DUMP | docker exec -i dockerapache_mysql_1 /usr/bin/mysql -uroot -psecret

Afficher les logs Apache en temps réel ( Pour débugger )

logs.sh

docker logs --tail 100 -f dockerapache_httpd_1 

Afficher les ressources utilisées par les conteneurs sur votre machine

stats.sh

docker stats --format "table {{.Container}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"

Selection_999_062_.png

Go go go

On clone l'environnement

git clone https://cdesaintleger@gitlab.com/cdesaintleger/dockerlamp.git && cd dockerlamp

On Configure le smtp

cp docker/php/ssmtp.conf.dist docker/php/ssmtp.conf && vim docker/php/ssmtp.conf

On lance la stack

docker-compose up -d

07 fév. 2016

Vagrant, environnement de développement unifié

Vagrant.png Je souhaite proposer aux différents développeurs d'un projet, un moyen simple d'avoir un environnement le plus proche possible du serveur de production, que ce soit au niveau de la distribution, mais également des versions des outils installés ( Php, Mysql MariaDb, MongoDb, python, uwsgi, etc ... ). Le but étant de mettre en place l'environnement en un minimum de temps possible, mais aussi de permettre aux développeurs de rester sur leur système d'exploitation préféré. (Linux Mac ou même Windows) pour développer.

Première approche

Peut être (certainement) dû à l'effet de mode, j'ai commencé mes tests avec Docker. Ce dernier permet ce genre de chose, mais je trouve la façon de faire un peu complexe ( Liaisons entre les conteneurs, maintenabilité de l’ensemble etc ) , juste pour avoir un environnement de dev ! Il est vrai que cette techno est vraiment intéressante, le partage de conteneurs avec les autres utilisateurs, le versionning grâce à l'union FS et tout ça sans exploser la conso d'espace disque.

Plus simple, vagrant

Puis un ami m'a parlé de Vagrant, une techno que je n'avais pas encore étudié plus que ça. Pour résumer, il n'y a rien de magique, c'est "juste" un écosystème de gestion de VMs , basé sur virtualbox, qui permet de créer/manager des VMs à partir de systèmes de bases disponibles sur le net ( catalogue Vagrant Boxes ) et surtout de provisionner ces VMs.

C'est là que ça devient intéressant, on a donc un système de gestion de VMs compatible Linux,Windows,Mac, permettant une gestion de VMs très efficace, start, stop, freeze, resume, reload etc ... mais surtout, vous permet de monter une VMs configurée selon vos souhaits et ce de plusieurs façons.

Testons avec un simple environnement LAMP

Je vais volontairement aller vite sur les options disponibles, la doc en ligne de Vagrant étant extrêmement clair, je ne vais pas la copier/coller ici

Commencez par installer Vagrant ( pas besoin d'explication pour cette étape triviale )

Système de base

Nous allons travailler sur une Debian Jessie . Commençons par créer un répertoire pour notre projet ( je le nomme lamp )

mkdir lamp && cd lamp
vagrant init debian/jessie64

Dans notre répertoire lamp, nous trouvons maintenant un fichier de configuration de notre environnement nommé Vagrantfile. Nous allons bientôt mettre les mains dedans .

Capture_d_e_cran_2016-02-13_a__14.53.05.png

Le téléchargement est terminé ? oui ? très bien lançons notre "machine" et allons faire un tour dedans .

Lancer la VM et tour du propriétaire

vagrant up

Une fois la machine lancée, prenons la main en ssh

vagrant ssh

Vous voilà connecté à notre machine. Le système est nu. Avez-vous remarqué au lancement de la vm, ces deux lignes :

==> default: Installing rsync to the VM...
==> default: Rsyncing folder: /Users/moi/lamp/ => /vagrant

Par défaut, vagrant synchronise le répertoire local ( dans notre cas /Users/moi/lamp/ ) avec le répertoire /vagrant présent à la racine du système de fichiers de la VM.
Nous pouvons bien entendu ajouter d'autres répertoires ( voir le fichier de configuration Vagrantfile ligne: config.vm.synced_folder "../data", "/vagrant_data" ).

Retournons sur notre machine hôte ( Cmd + D )

LAMP

Linux ( Ok ça c'est fait ), Apache ( à installer ), Mysql/MariaDB ( à installer ), Php ( à installer ).
Bon on sait ce qu'il nous reste à faire. Mettons les quelques lignes nécessaires à l'installation de ces derniers dans un script bash. Éditons donc dans le répertoire lamp, un fichier que nous nommerons bootstrap.sh

Voici le contenu:

#!/usr/bin/env bash

#Login par defaut : root et pwd : rootpass ( à modifier bien évidemment )
debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password rootpass'
debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password rootpass'

apt-get update
apt-get install -y apache2
apt-get install -y mysql-server
apt-get install -y php5 php5-mysql php-pear


if ! [ -L /var/www ]; then
  rm -rf /var/www
  ln -fs /vagrant /var/www
fi

Éditons ensuite le fichier Vagrantfile, et en dessous de la ligne config.vm.box = "debian/jessie64" ajouter :

config.vm.provision :shell, path: "bootstrap.sh"

Ne reste plus qu'à provisionner la VM

vagrant reload --provision

Capture_d_e_cran_2016-02-13_a__14.55.18.png

Vous voilà avec un lamp de base, mais comment communiquer facilement avec ?

Le réseau

Il y a plusieurs solutions selon votre besoin.

  • Natter un port de votre machine hôte vers la VM
  • Créer un bridge avec une des vos interfaces pour que la VM soit présente comme n'importe quel autre poste sur votre réseau local
  • Créer un réseau privé permettant à la machine hôte d’accéder à la VM , tout en restant inaccessible depuis l'extérieur pour les autres postes ( solution retenu dans mon cas ).

Éditons le fichier de configuration pour activer ce mode. dé-commenter/compléter cette ligne :

config.vm.network "private_network", ip: "192.168.33.10"

Relançons notre vm

vagrant reload

Testons notre serveur Apache, ouvrez un navigateur, et entrez l'ip de votre vm : http://192.168.33.10

Et là ... vous avez un magnifique "404 not found" !!!.
Normal, le vhost par défaut d'apache cherche ses sources dans le répertoire /var/www/html . et si vous avez bien observé, dans le fichier bootstrap.sh, nous avons ces lignes :

if ! [ -L /var/www ]; then
  rm -rf /var/www
  ln -fs /vagrant /var/www
fi

Qui a pour but de mettre le répertoire "vagrant" ( partagé entre l'hôte et la vm ) accessible ( via un lien symbolique ) vers /var/www . Il nous suffit donc de créer un répertoire nommé html dans notre répertoire de projet, lamp ( sur la machine hôte ).

Donc, sur notre machine hôte, dans notre répertoire lamp :

mkdir html && cd html
echo '<?php echo phpinfo();' > index.php

Relançons notre vm

vagrant reload

Maintenant allons rafraichir notre navigateur . Vous devriez avoir la belle page de phpinfo qui s'affiche . Capture_d_e_cran_2016-02-13_a__13.54.36.png

NFS

C'est magnifique, mais si vous ajoutez des fichiers depuis la machine hôte dans le répertoire html, ces derniers ne seront pas accessibles ( synchronisés ) sur la VM. Il faudra pour celà lancer la commande

vagrant rsync-auto

Pas très pratique. Je me suis donc tourné vers le protocole NFS qui est pris en charge en natif avec vagrant ( attention.. ne fonctionne pas avec Windows ).
Éditons notre fichier Vagrantfile et ajoutons ces quelques lignes avant de relancer notre vm

  config.vm.synced_folder ".", "/vagrant",
    :nfs => true

Relançons la VM

vagrant reload

Vagrant va vous demander le mot de passe sudo, afin de modifier pour le fichier /etc/exports.
Afin que le mot de passe ne soit pas demandé à chaque démarrage, vous pouvez soit:

  • Ajouter des règles dans le fichier de config sudo ou ...
  • Renseigner une bonne fois pour toute votre fichier exports et indiquer à vagrant qu'il n'a rien à faire . ( voir les options NFS ).

Conclusion

Voilà pour le premier tour d'horizon de Vagrant, nous avons dans nos main un système de gestion de VM bien ficelé, doté de nombreuses autres options, ( snapshots/restauration, share, ... ) je ne peux que vous conseiller d'aller faire un tour sur le site officiel pour découvrir les différentes options qui s'offrent à vous. Enfin, pour gérer vos environnements, je vous conseil de versionner votre projet ( dossier lamp dans notre exemple ) avec Git par exemple, car c'est grâce aux fichiers de ce répertoire que vous serez en mesure de remonter/partager une VM configurée à l'identique sans aucun effort et en un minimum de temps.