lindev.fr

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

28 déc. 2016

HAProxy LetsEncrypt et Varnish en front .. rien que ça

L'objectif final

Avoir l'ensemble de ses sites accessibles en https, avec des certificats letsencrypt ( gratuits et surtout renouvelés automatiquement ) et un serveur de cache ( varnish ) pour augmenter la capacité de charge ( bref optimiser les perfs des sites ) pour enfin passer le relais au backend ( Apache, Nginx .. peu importe ).

network_diagram_user_haproxy_varnish_apache.png

le hic

Il y en a toujours un ... et pas des moindres cette fois, varnish ne gère pas l'https !

La solution - TLS Termination Proxy

L'utilisation d'un proxy ( HAProxy ) pour gérer les transactions Https, puis relayer en clair les requêtes vers varnish qui à son tour si nécessaire va relayer les requêtes au(x) serveur(s) ( Apache/Nginx/... )

Plusieurs avantages à cette solution ( Terminaison SSL ), soulager les serveurs finaux ( Apache/Nginx ... ) de la gestion du SSL et surtout pour nous admins, nous simplifier la tâche pour ce qui est de la gestion des certificats.

Dans ce billet

Dans ce billet, je ne vais parler que de HAProxy et letsencrypt pour commencer, varnish étant un assez gros morceau, il fera l'objet d'un second billet.

Mise en place de la situation

Vous pouvez utiliser un seul et même serveur pour héberger l'ensemble de ces services ... mais franchement ça ne devrait jamais être le cas ! Donc pour ce billet nous allons partir du principe que nous possédons 2 serveurs .

Le premier pour héberger HAProxy et Varnish ( 10.0.0.1 ) et le second pour héberger le serveur HTTP ( 10.0.0.2 )

Les deux serveurs sont sous Debian 8 (Jessie).

Serveur HAProxy

Commençons par Installer HAProxy ( plus quelque trucs utils dans ce "tuto" ) ... qui se fait simplement par

apt install haproxy bc vim 

Commençons par copier le fichier de configuration par defaut

cd /etc/haproxy/
cp haproxy.cfg haproxy.cfg.orig


Maintenant nous pouvons éditer le fichier /etc/haproxy/haproxy.cfg

vim /etc/haproxy/haproxy.cfg

Remplacer tout le contenu par :

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Default ciphers to use on SSL-enabled listening sockets.
        # For more information, see ciphers(1SSL). This list is from:
        #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3

        maxconn 2048
        tune.ssl.default-dh-param 2048

defaults
        #log    global
        log     /dev/log local0
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

        option forwardfor
        option http-server-close


#################################

frontend www-http
        bind *:80
        mode http

        #option httpclose
        option forwardfor
        option httplog
        option dontlognull

        reqadd X-Forwarded-Proto:\ http
        default_backend www-backend


frontend www-https
        bind *:443 ssl crt /etc/haproxy/certs/
        mode http
        reqadd X-Forwarded-Proto:\ https

        #option httpclose
        option forwardfor
        option httplog
        option dontlognull

        acl letsencrypt-acl path_beg /.well-known/acme-challenge/
        use_backend letsencrypt-backend if letsencrypt-acl

        default_backend www-backend


backend www-backend

        #option httpclose
        option forwardfor

        http-request set-header X-Forwarder-Port %[dst_port]
        redirect scheme https if !{ ssl_fc }
        server www-1 10.0.0.2:80 check

backend letsencrypt-backend
        server letsencrypt 127.0.0.1:54321


listen stats *:1936
        stats enable
        stats uri /
        stats hide-version
        stats auth username:password

Comme vous l'avez certainement remarqué, nous allons chercher les certificats dans le répertoire /etc/haproxy/certs/ qui n'éxiste pas encore.
Créons le

sudo mkdir /etc/haproxy/certs
sudo chmod -R go-rwx /etc/haproxy/certs

Voilà pour HAProxy, nous verrons ensuite dans l'interface de statistics si tout est ok .

Letsencrypt et certbot

Pour la génération des certificats, nous allons utiliser l'outil certbot .
Pour l'installer nous allons devoir activer ( si ce n'est déjà fait ) le dépôt BlackPorts de Debian

sudo echo 'deb http://ftp.debian.org/debian jessie-backports main' > /etc/apt/sources.list.d/blackports.list
apt update

On install certbot depuis ce dépôt

apt-get install certbot -t jessie-backports

Afin d'initialiser letsencrypt, nous allons le lancer une première fois

certbot certonly

Celà ne va pas aboutir, c'est normal, c'est juste pour qu'il crée les répertoires de base.

Celà ne vous a pas échappé, le port 80 est déjà utilisé par HAProxy, et ne peu donc être utilisé par Letsencrypt pour générer ou renouveler un certificat.
L'idée de couper HAProxy le temps de cette action est inconcevable, nous allons simplement utiliser un autre port. ( ici : 54321 )

Pour ne pas avoir à le spécifier à chaque commande, nous allons mettre en place un petit fichier de configuration pour définir quelques paramètres qui ne changent pas . ( ce qui va nous permettre de raccourcir nos lignes de commandes plus tard ).

sudo cd /etc/letsencrypt/ && vim cli.ini

Voici le contenu

# This is an example of the kind of things you can do in a configuration file.
# All flags used by the client can be configured here. Run Certbot with
# "--help" to learn more about the available options.

# Use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

# Uncomment and update to register with the specified e-mail address
email = admin@your.net

# Uncomment to use a text interface instead of ncurses
text = True

standalone-supported-challenges = http-01

Afin de tester l'ensemble de notre configuration, nous allons générer notre premier certificat ( manuellement ) .
Imaginons que le domaine soit "lindev.fr" :

sudo certbot certonly --standalone --http-01-port 54321 -d lindev.fr -d www.lindev.fr
sudo cat /etc/letsencrypt/live/lindev.fr/fullchain.pem /etc/letsencrypt/live/lindev.fr/privkey.pem > /etc/haproxy/certs/lindev.fr.pem
sudo service haproxy restart

Bien entendu, il faut que le nom de domaine "lindev.fr" et "www.lindev.fr" pointent sur l'ip de mon serveur ( HAProxy ).

HAProxy est maintenant prêt . Je suppose que le backend ( Nginx ou Apache ) est déjà configuré avec un vhost écoutant sur le port 80 pour le domaine "lindev.fr et www.lindev.fr".

Et voilà le résultat. Capture_d_e_cran_2017-01-03_a__21.15.37.png

Renouvellement automatique

Nous allons maintenant faire en sorte que les certificats LetsEncrypt se régénèrent automatiquement avant expiration et ce tant qu'à faire sans intervention nécessaire d'une admin sys sur le serveur HAProxy.

Le script

Pour celà j'ai concocter un petit script batch générique qui fait très bien le travail.

sudo mkdir /root/scripts & cd /root/scripts
vim /root/scripts/renew_all.sh

Le voici

#!/bin/bash

#Configuration variables
certbot_bin="/usr/bin/certbot"
haproxy_pem_path="/etc/haproxy/certs"
http_01_port='54321'
exp_limit=30

#Then, create domain.pem containing fullchain et privkey for haproxy
for domainconf in $(ls /etc/letsencrypt/renewal/); do
        domain=${domainconf%.conf}

        cert_file="${haproxy_pem_path}/${domain}.pem"
        exp=$(date -d "`openssl x509 -in $cert_file -text -noout|grep "Not After"|cut -c 25-`" +%s)
        datenow=$(date -d "now" +%s)
        days_exp=$(echo \( $exp - $datenow \) / 86400 |bc)

        if [ "$days_exp" -gt "$exp_limit" ] ; then
                echo "[${domain}] : The certificate is up to date, no need for renewal ($days_exp days left)."
                #exit 0;
        else
                echo "The certificate for $domain is about to expire soon. Starting Let's Encrypt (HAProxy:$http_01_port) renewal script..."
                $certbot_bin certonly --standalone --renew-by-default --http-01-port $http_01_port -d ${domain} -d www.${domain}
                cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > ${haproxy_pem_path}/${domain}.pem
        fi
done

# At the end, reload haproxy
echo "$(date +%c) Reload haproxy"
/usr/sbin/service haproxy reload

Les deux seuls paramètres que vous pourriez vouloir changer sont :

  • http_01_port='54321'
  • exp_limit=30



A savoir, le port utiliser pour la génération des certificats, et la limite (en jours) à partir de laquelle vous souhaitez régénérer un certificat .

sudo chmod +x /root/scripts/renew_all.sh
sudo -s
crontab -e

On ajoute cette ligne dans la crontab ( afin de vérifier chaque jour les certificats à régénérer )

@daily /bin/bash /root/scripts/renew_all.sh 2>&1 | mail -s "[SSL Lindev] Vérification des certificats SSL HAProxy" admin@lindev.fr

Et voilà chaque jour, le script sera exécuté et vous recevrez un petit mail résumant le temps restant pour chaque certificat et potentiellement les logs de régénération s'il y en a eu.

Nouveau domaine à gérer

Imaginons que vous souhaitez gérer un autre nom de domaine, vous devrez la première fois créer le nouveau certificat.
Voici un petit script pour vous simplifier la vie.

sudo vim /root/script/new_cert.sh

le voici

#!/bin/bash

#Configuration variables
certbot_bin="/usr/bin/certbot"
haproxy_pem_path="/etc/haproxy/certs"
http_01_port='54321'
domain=$1

$certbot_bin certonly --standalone --renew-by-default --http-01-port $http_01_port -d ${domain} -d www.${domain}
cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > ${haproxy_pem_path}/${domain}.pem

# At the end, reload haproxy
echo "$(date +%c) Reload haproxy"
service haproxy restart

On le rend exécutable

sudo chmod +x /root/scripts/new_cert.sh

A partir de là pour générer un nouveau certificat (qui sera par la suite automatiquement renouvelé), il vous suffit (en root) d'executer ce script comme ceci

cd /root/scripts
./new_cert.sh mon_nouveau_domaine.com

Et voilà , le certificat sera généré, renouvelé automatiquement et pris en charge par HAProxy .

Conclusion

Nous avons mis en place notre front (HAProxy) qui se charge de la partie SSL puis redirige les flus vers un ou plusieurs backends .
L'avantage de cette configuration et la simplicité de gestion des certificats, même avec plusieurs backends physiquement différents ( load balancing, différents services etc ... ).

Prochaine étape intercaler entre HAProxy et le backend, un serveur de cache ( Varnish ) .

Dans un prochain billet... N'hésitez pas à commenter.

Ch.

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.

26 janv. 2016

Php7, installation/compilation ... et rollback

Les articles sur Php7 ne cessent de culpabiliser les développeurs sous php5.x .. , il est plus .. performant, rapide, des nouvelles fonctionnalités qui se rapprochent de python ( ok ... ça c'est un troll ), il est ... bref .. il faut le tester pour se faire une idée, et surtout vérifier si les projets tournent dessus dans devoir faire face à une horde de logs de type Notice , Deprecated, ou encore pire .. Fatal Error

On fait quoi

Vous avez de beaux projets qui tournent sous une version de Php stable 5.X et .. malgré l'adage .. on ne change pas quelque chose qui marche, vous trépigniez d'impatience de voir ce qu'ils donnent avec la dernière monture de la société Zend .

Ok .. mais sans tout casser s'il vous plait.

On va donc voir comment installer ( par compilation ) php7 afin de pouvoir l'utiliser (ou pas) projet par projet. Ou plutôt vhost par vhost .

Installer PHP7

J'ai pris comme base, un serveur tout neuf .. à poil .. Debian Jessie sur la plateforme Google.

Commençons par installer quelques paquets nécessaires pour le bon déroulement de cet article ( et de la compilation de php ).

sudo apt install vim bzip2 build-essential libxml2-dev libc-client-dev libbz2-dev libkrb5-dev libmcrypt-dev librecode-dev

Puis classiquement nous téléchargeons les sources de php

cd /usr/local/src
wget http://us2.php.net/distributions/php-7.0.2.tar.bz2
tar xjf php-7.0.2.tar.bz2
cd php-7.0.2

Nous allons créer un petit fichier de config pour notre version de php ( activer ou pas certains modules, définir l'emplacement de php... )

vim myConfig

Voici le contenu ( nous allons l'installer dans /usr/local/php7 )

#!/bin/sh

mkdir -p /usr/local/php7

export OPTIM=-02
./configure --prefix=/usr/local/php7 \
        --enable-fpm \
        --with-xsl \
        --with-gettext \
        --enable-mbstring \
        --enable-mbregex \
        --disable-debug \
        --enable-ftp \
        --with-mcrypt \
        --enable-zip \
        --enable-calendar \
        --enable-exif \
        --with-pdo-mysql \
        --with-gettext \
        --with-zlib \
        --with-bz2 \
        --enable-inline-optimization \
        --enable-pcntl \
        --enable-opcache \
        --with-mysqli \
        --with-zlib \
        --with-jpeg-dir \
        --with-gd \
        --with-freetype-dir=DIR \
        --with-imap-ssl \
        --with-imap \
        --with-curl \
        --enable-bcmath \
        --with-kerberos \
        --enable-sysvsem \
        --enable-sysvshm \
        --enable-sockets \
        --enable-gd-native-ttf \
        --with-openssl \

Rendons ce script exécutable et ... c'est parti

chmod +x myConfig
./myConfig

Si tout ce passe bien, nous pouvons lancer les deux dernières commandes pour installer php7

make -j `cat /proc/cpuinfo | grep processor | wc -l` 
sudo make install

Configuration

Et bien oui .. vous ne passez pas par les paquets de la distrib .. vous avez donc un poil plus de boulot . Commençons par récupérer le configuration par défaut ( vous 'adapterez au besoin plus tard, ce point sort du cadre de cet article )

cd /usr/local/php7/etc/
cp php-fpm.conf.default php-fpm.conf

Malgré tout, vérifiez bien à dé-commenter la ligne suivante ( vous verrez plus tard pourquoi )

pid = run/php7-fpm.pid
Pools

Enfin il nous faut également créer le pool par défaut de php-fpm

cd php-fpm.d
cp www.conf.default www.conf

Idem il nous faut dé-commencter/éditer/modifier deux trois lignes ( vous pouvez reprendre les valeurs ci-dessous, si vous avez une installation de base . Notamment pour l'utilisateur utilisé pour interpréter vos scripts php )

user = www-data
group = www-data
listen = /var/run/php7-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
php.ini

Aller .. encore un fichier de config à gérer.
On va prendre la version "prod" disponible dans les sources que nous avons téléchargées .

cp /usr/local/src/php-7.0.2/php.ini-production /usr/local/php7/lib/php.ini

Ajoutons le module opcache dans la foulé

vim /usr/local/lib/php.ini

Ajouter en fin de fichier

zend_extension=opcache.so

Ready ! ? ... non pas encore

Lancer php-fpm au démarrage

Commençons par créer notre fichier d'init ( modèle dispo dans les sources également. Je précise ce point car certains articles semblent sortir ces scripts de leur chapeau, sans explication ! )
Les modèles se trouvent dans : /usr/local/src/php-7.0.2/sapi/fpm

vim /etc/init.d/php7-fpm

Le contenu :

#! /bin/sh
### BEGIN INIT INFO
# Provides:          php7-fpm
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts php7-fpm
# Description:       starts the PHP FastCGI Process Manager daemon
### END INIT INFO
php_fpm_BIN=/usr/local/php7/sbin/php-fpm
php_fpm_CONF=/usr/local/php7/etc/php-fpm.conf
php_fpm_PID=/usr/local/php7/var/run/php7-fpm.pid
php_opts="--fpm-config $php_fpm_CONF"
wait_for_pid () {
        try=0
        while test $try -lt 35 ; do
                case "$1" in
                        'created')
                        if [ -f "$2" ] ; then
                                try=''
                                break
                        fi
                        ;;
                        'removed')
                        if [ ! -f "$2" ] ; then
                                try=''
                                break
                        fi
                        ;;
                esac
                echo -n .
                try=`expr $try + 1`
                sleep 1
        done
}
case "$1" in
        start)
                echo -n "Starting php-fpm "
                $php_fpm_BIN $php_opts
                if [ "$?" != 0 ] ; then
                        echo " failed"
                        exit 1
                fi
                wait_for_pid created $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed"
                        exit 1
                else
                        echo " done"
                fi
        ;;
        stop)
                echo -n "Gracefully shutting down php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -QUIT `cat $php_fpm_PID`
                wait_for_pid removed $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed. Use force-exit"
                        exit 1
                else
                        echo " done"
                       echo " done"
                fi
        ;;
        force-quit)
                echo -n "Terminating php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -TERM `cat $php_fpm_PID`
                wait_for_pid removed $php_fpm_PID
                if [ -n "$try" ] ; then
                        echo " failed"
                        exit 1
                else
                        echo " done"
                fi
        ;;
        restart)
                $0 stop
                $0 start
        ;;
        reload)
                echo -n "Reload service php-fpm "
                if [ ! -r $php_fpm_PID ] ; then
                        echo "warning, no pid file found - php-fpm is not running ?"
                        exit 1
                fi
                kill -USR2 `cat $php_fpm_PID`
                echo " done"
        ;;
        *)
                echo "Usage: $0 {start|stop|force-quit|restart|reload}"
                exit 1
        ;;
esac

On le rend exécutable et crée les liens de démarrage

sudo chmod 755 /etc/init.d/php7-fpm
insserv php7-fpm

Puis on crée le fichier pour initd

vim /lib/systemd/system/php7-fpm.service

Voici le contenu

[Unit]
Description=The PHP 7 FastCGI Process Manager
After=network.target

[Service]
Type=simple
PIDFile=/usr/local/php7/var/run/php7-fpm.pid
ExecStart=/usr/local/php7/sbin/php-fpm --nodaemonize --fpm-config /usr/local/php7/etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

On active le service

systemctl enable php7-fpm.service
systemctl daemon-reload
systemctl enable php7-fpm

On lance ENFIN php7-fpm

service php7-fpm start

Petit check

ps -xa | grep php-fpm

qui doit donner un truc du genre:

  861 ?        Ss     0:00 php-fpm: master process (/usr/local/php5/etc/php-fpm.conf)                
  862 ?        S      0:00 php-fpm: pool www                                                         
  863 ?        S      0:00 php-fpm: pool www

Et voilà, vous avez la toute dernière monture de php disponible .
Ok mais ... comment l'utiliser dans le vhost.

Nginx

Installons rapidement Nginx

sudo apt-get install nginx

éditons le vhost par défaut pour prendre en compte notre installation de php

vim /etc/nginx/sites-enabled/default

Et on dé-commente pour avoir ceci

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
        #
        #       # With php5-cgi alone:
        #       fastcgi_pass 127.0.0.1:9000;
        #       # With php5-fpm:
                fastcgi_pass unix:/var/run/php7-fpm.sock;
        }

On demande à Nginx de passer les requêtes pointant les fichiers php au socket : /var/run/php7-fpm.sock

Test

Un rapide script de test ...LE phpinfo() :)

vim /var/www/html/test.php

Puis on colle le code suivant

<?php

echo phpinfo();

Rendez-vous sur l'url de votre serveur http://x.x.x.x/test.php

Et là ... victoire ..

Capture_d_e_cran_2016-01-26_a__22.00.30.png

Conclusion

Vous avez compris comment ajouter une version de php à votre serveur ( en plus de la version des dépôts ). Vous pouvez comme celà installer n'importe quelle version de php et utiliser l'une ou l'autre au besoin, en spécifiant le bon socket dans votre vhost .

N'hésitez pas à laisser un commentaire si vous avez des questions .

Bonne nuit.

27 août 2014

Django déploiement avec Nginx Gunicorn et Supervisord

Je vois souvent passer des questions sur le forums ou autres fils de discussion concernant la mise en production d'un projet Django.
Ici je vais présenter une façon de faire, utilisant Nginx Gunicorn et Supervisord .

Prérequis

Sur une Débian toute fraiche,

Nginx

sudo apt-get install nginx

Virtualenv

Car nous travaillons toujours avec un environnement virtuel, dans lequel nous allons y installer les dépendances de notre projet.
Ce qui permet de ne pas avoir de conflit de version avec d'autres éventuels projets déjà en prod.

pip install virtualenv virtualenvwrapper

Note : Si vous n'avez pas l'outil pip de disponible, installez-le comme ceci

sudo apt-get install python-setuptools
sudo easy_install pip

Puis dans votre fichier ~/.bashrc , ajoutez les lignes suivantes, pour l'autocompletion

export VIRTUALENVWRAPPER_VIRTUALENV_ARGS='--no-site-package'
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh

Gunicorn

Pourquoi pas utiliser uWSGI ? humm ... car je préfère Gunicorn qui me donne toute satisfaction sur mes projets en prod. donc ... pourquoi pas !

sudo apt-get install libevent-dev
pip install gunicorn

Supervisor

Installation simple :

sudo pip install supervisor

Récupération du fichier de conf par défaut

echo_supervisord_conf > /etc/supervisord.conf

Dans le fichier de conf, afin de pouvoir manager votre projet via l'interface web, il faut dé-commenter et paramétrer les lignes suivantes

[inet_http_server]         ; inet (TCP) server disabled by default
port=*:9001        ; (ip_address:port specifier, *:port for all iface)
username=user              ; (default is no username (open server))
password=123               ; (default is no password (open server))

Note: Changer le username et password évidemment

L'interface web sera disponible à l'adresse : http://ipduserveur:9001

Pour le lancement automatique au démarrage du système, vous pouvez utiliser ces scripts d'init:

  1. debian init supervisor
  2. debian init supervisor

Installer votre projet

Notre environnement est prêt, nous allons commencer par installer le projet Django dans l’environnement virtuel.

Création du virtualenv

mkvirtualenv monprojet

Si vous utilisez git, cloner votre projet, sinon, copier le à l'endroit que vous souhaitez, ( pour l'exemple ce sera /var/www/monprojet )

Installation des dépendances du projet

si vous utilisez également les environnements virtuels pour développer ( ce que je conseille ) vous pouvez alors enregistrer la liste des libs python installées dans cette environnement, afin de pouvoir également les installer ( avec la même version ) sur un autre serveur.

Pour avoir la liste des libs :

pip freeze > requirements.txt

puis sur votre serveur, pour installer les libs depuis un export freeze :

pip install -r requirements.txt

Configuration de Django

Il vous faudra peut-être toucher un peu à votre fichier settings.py pour le passer du mode debug au mode production.
Personnellement j'utilise une autre subtilité qui me permet de ne pas avoir à toucher au fichier settings.py ( j'expliquerai celà dans un autre billet ) .

N'oubliez pas de vérifier le paramètre STATIC_ROOT

STATIC_ROOT = '/var/www/static_monprojet/'

C'est le répertoire ou seront copiés les fichiers statiques du projet, pour ensuite être servis par Nginx

Ce répertoire DOIT EXISTER

Une fois le répertoire créé, nous allons y "placer/lier" les fichiers statiques du projet

python manage.py collectstatic --link

Perso je préfère y mettre des liens symboliques .. ( les gouts et les couleurs ... )

Liaison du projet avec Gunicorn

Nous allons créer un fichier dans notre projet qui sera utilisé pour le lancement du/des process Gunicorn ( vous devrez adapter les valeurs dans ce script )

vim /var/www/monprojet/monprojet/gunicorn.sh

Et voici le contenu

#!/bin/bash
  set -e
  NUM_WORKERS=2
  USER=www-data
  GROUP=www-data
  ADDRESS=127.0.0.1:5002
  cd /var/www/monprojet
  source /home/monuser/.virtualenvs/monprojet/bin/activate
  exec gunicorn monprojet.wsgi:application -w $NUM_WORKERS --bind=$ADDRESS \
    --user=$USER --group=$GROUP --log-level=debug

Puis on le rend exécutable

chmod 777 /var/www/monprojet/monprojet/gunicorn.sh

Configuration Supervisord

Notre projet est prêt, afin de lancer Gunicorn automatiquement, nous allons utiliser supervisord, qui en plus de s'occuper de démarrer le projet automatiquement au démarrage du système, va aussi le relancer en cas de crash, gérer les logs et vous donner la main pour arrêter ré-demarrer les process, via l'interface web ou en mode console.

supervisord.png

Ajoutez à la fin du fichier de configuration /etc/supervisord.conf les lignes suivantes ( paramètres à adapter selon votre cas )

[program:guni_monprojet]
directory=/var/www/monprojet/monprojet
user = www-data
autostart=true
autorestart=true
stdout_logfile=/var/log/monprojet.log
redirect_stderr=true
stopsignal=QUIT
command = /var/www/monprojet/monprojet/monprojet.sh

Ne reste plus qu'à redémarrer supervisor pour prendre en compte la nouvelle config.

Pour le lancer manuellement

sudo supervisord -c /etc/supervisord.conf

Vhost Nginx

Dernier point, la création du vhost de Nginx, pour diriger les requêtes vers Gunicorn qui écoute sur le port 5002 ( il est aussi possible d'utiliser un fichier socket à la place )

vim /etc/nginx/sites-enabled/monprojet

Et voilà le contenu ( fonctionnel, mais vous pouvez l'adapter )

upstream us_monprojet {
        server 127.0.0.1:5002;
}


server {
        listen 80;

        root /var/www/monprojet;

        gzip             on;
        gzip_min_length  1000;
        gzip_proxied     expired no-cache no-store private auth;
        gzip_types       text/plain application/xml text/css text/javascript application/x-javascript application/x-shockwave-flash video/x-flv;
        gzip_disable     "MSIE [1-6]\.";


        server_name monprojet.com;
        charset utf-8;

        client_max_body_size 75M;


        location ~ /\.ht {
            deny  all;
        }

        location /favicon.ico {
                alias /var/www/monprojet/monprojet/static/favicon.ico;

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }

        }


        location /media {
                alias /var/www/monprojet/monprojet/media;

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }

        }

        location /static {
                alias /var/www/static_monprojet/;

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }

        }
location / {

                if (-f $request_filename) {
                        access_log off;
                        expires max;
                }       

                #gunicornParams
                if (!-f $request_filename) {
                        proxy_pass         http://us_monprojet;
                        break;
                }       
                proxy_redirect     off;
                proxy_set_header   Host             $host;
                proxy_set_header   X-Real-IP        $remote_addr;
                proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

        }       
}       

Ne reste plus qu'à activer le vhost, et relancer nginx

ln -s /etc/nginx/sites-available/monprojet /etc/nginx/sites-enabled/
service nginx reload

15 sept. 2013

Mezzanine CMS

mezzanine.png je vais présenter dans ce post, le CMS Mezzanine, qui se veut être une alternative à Wordpress.
Mezzanine est un projet bâti sur l'excellent framework Django, aujoud'hui avec plus de 250k téléchargements, il est devenu une référence dans les solutions CMS basées sur python.

Présentation

Mezzanine possède des aspects intéressants se rapprochant de Wordpress, une interface intuitive permettant de gérer des pages statiques avec une édition simplifiée et rapide grâce au WYSIWYG, directement depuis les pages en ligne.
Une programmation de mise en ligne / hors ligne, une gestion multi-langues, la personnalisation des thèmes avec une granularité élevée ( un thème par page si vous le souhaitez ). Sans oublier la compatibilité multi-sites, vous permettant de gérer plusieurs sites avec une seule instance de Mezzanine.

Et encore bien d'autres aspects intéressants, comme:

  • Une bonne documentation
  • Un moteur de recherche intégré et directement fonctionnel
  • Le coté modulaire de Django ( modules mis à disposition par la communauté )
  • Une gestion de galeries Photos à 100% intégré
  • Détection des types de clients ( smartphones, tablettes ... )
  • Une gestion de migration depuis d'autres Blogs
  • L'intégration de Disqus
  • Intégration de Gravatar
  • Google analitycs
  • Fils de discutions Twitter
  • Intégration de bit.ly
  • Intégration d'Akismet ( Filtre anti-spam des commentaires )

...

Liste de sites utilisant Mezzanine

Installation

Super Mezzanine non ?
On va le tester.
Ce qui est bien avec les projets basés sous Django, c'est qu'il n'y a pas besoin de passer par un "setup" sur un serveur web, vhost BDD etc ... non pas besoin !
Il faut juste avoir python d'installé sur le poste .

VirtualEnv

Commençons par créer un environnement virtuel ( un minimum quand même ).

mkvirtualenv mezzanine

Puis installons mezzanine

pip install mezzanine

Nous voilà avec les librairies mezzanine de disponibles, nous pouvons donc créer notre site, appelé "monsite" pour l'occasion ( innovant comme nom .. non ? )

mezzanine-project monsite
cd monsite

Nous allons maintenant initialiser la base de données, ( sqlitle par défaut .. )

python manage.py createdb --noinput

Fin du setup de l'environnement de développement.

Premiers Tests

Bien notre site est prêt, nos allons pouvoir le tester et se familiariser avec la partie admin et l'édition en ligne via le WYSIWYG

python manage.py runserver

Et voilà ne vous reste plus qu'à vous rendre à l'adresse suivante : http://127.0.0.1:8000
Et pour la partie Admin : http://127.0.0.1:8000/admin

Le login et mot de passe par défaut pour la partie Admin sont :

  • Login : admin
  • Mot de passe: default

Conclusion

Voilà pour la première présentation de mezzanine, je n'ai pas encore décidé du sujet du prochain article ... "personnaliser le thème de Mezzanine", "installer des modules tiers" ou encore "mise en production d'un projet Mezzanine" ou ... vous avez envie de quoi ? ( laissez un commentaire si vous avez une envie particulière. ).

Bonne découverte,

Ch.

Référence : Mezzanine

- page 1 de 2