Accueil
Le blog tech de Nicolas Steinmetz (Time Series, IoT, Web, Ops, Data)
.bashrc
sans que cela devienne une pagaille monstre : mettre tout dans un dossier et "sourcer" l'ensemble des fichiers s'y trouvant. Du coup, ça peut se versionner plus facilement/atomiquement ;-)Dockerfile
que l'on soit sur un serveur 64 bits ou un raspberry, cela va faciliter les chaines de développement et déploiement.immutable
pour l'entête Cache-Control
de sorte que le navigateur ne vérifie plus si la ressource a été modifiée ou pas (fini les 304) durant la période de cache qui a été définie pour cette ressource.Suite à une mission, synthèse sur la possibilité à date de gérer des conteneurs Linux et Windows avec Docker.
Les tests ont été réalisés, début mai 2017, sur les environnements suivants :
En début de mission, il était supposé qu'une application métier en C# pourrait migrer vers ASP.net Core (la version opensource de .Net et sa déclinaison ASP) et donc sur un conteneur Linux. La suite nous prouva que toutes les APIs ne sont pas (et ne seront pas forcément) dans la version opensource de .Net et qu'il nous fallait envisager des conteneurs Windows à court terme et une infrastructure hybride Windows & Linux, tant au niveau des hôtes que des conteneurs.
Avant d'aller plus loin, il faut bien avoir en tête la jeunesse du support de Windows dans le monde des conteneurs. Il faut distinguer deux niveaux de supports de l'OS Windows :
Support de Windows comme machine hôte :
Support de Windows comme OS de container :
Dans le cadre de la DockerCon d'Avril 2017, Docker et Microsoft ont annoncé le support des conteneurs Linux sous Microsoft Server 2016 via Hyper-V. Il devrait alors être possible de pouvoir lancer simultanément des conteneurs Linux ET Windows sous Windows Server 2016. Il n'y a pas de date connue pour la sortie de la version supportant cette nouveauté. Il faudra surveiller les prochaines releases trimestrielles de Docker pour savoir quand cela sera utilisable de façon "stable".
En somme, en continuant notre test plus loin, nous savons que nous entrons dans un monde immature avec un support incomplet.
La première surprise fut de voir qu'il était possible de créer un cluster Swarm avec un noeud Windows et un noeud Linux sans pré-requis particulier.
Il faut ensuite créer manuellement le réseau overlay pour son application (ce sera supporté dans docker-compose 1.13+, version 3.2 du format). Si vous utiliser docker-compose, il faudra alors spécifier ce réseau comme un réseau externe afin que l'application s'y attache à son lancement.
Afin de piloter le déploiement des conteneurs sur les bons hôtes, il convient d'ajouter des labels (ou de s'appuyer sur des labels existants comme node.platform.OS
). Ainsi, un conteneur Windows se déploira sur un hôte Windows et idem pour du Linux.
Jusque là, tout va bien mais c'est là que les limitations se font sentir :
Donc au final :
Bon à savoir également :
Une série de trois vidéos, publiées il y a quelques jours, montrent bien ce qu'il est possible de faire avec Swarm à ce jour.
Pour conclure, il est possible d'utiliser des conteneurs Windows mais que ce n'est pas encore la panacée. Cela reste néanmoins prometteur et les annonces de Microsoft vont dans le bon sens. Il va falloir attendre quelques mois pour envisager cela plus sereinement.
Update 16/8/2017 : La version 17.06 entreprise edition semble résoudre les limites évoquées précédemment.
zsh
via "Oh My ZSH" mais je n'étais pas allé bien loin, revenant au final au bon vieux bash
. L'article m'a donné envie d'essayer fish et pour le moment, je trouve ça plutôt pas mal. Ne pas se fier à la première impression du site :)JSON
et surtout qu'il n'y a pas de rotation des journaux par défaut :'( ; l'article montre ensuite les intérêts du format GELF (avoir un message sous la forme d'un dictionnaire/tableau au format clé/valeur) et les limites (connextion UDP) avec les solutions de contournement actuelles et les solutions à venir prochainement.Nouvelle année, nouveau format - au programme une édition mensuelle mixant brèves et des choses plus construites/élaborées (j'espère le mois prochain)
logger
et de ansible.cfg
\watch
ou jsonb_pretty
pour respectivement surveiller le résultat d'une requête et affichrer proprement une donnée au format JSON.Bonnes fêtes de fin d'année à tous !
npm
pour gérer le cycle de release de son application (génération du changelog, gestion des numéros de versions, création des tags git, etc).Pour un projet en cours de finalisation, j'ai utilisé le CMS Grav et j'ai décidé tant pour mon développement en local que pour l'environnement de production de déployer cela sous la forme de container docker et d'utiliser Traefik comme reverse-proxy et m'appuyer notamment sur son support natif et dynamique des containers docker.
J'avais initialement publié une image nsteinmetz/grav basée sur l'image officielle PHP:apache mais elle ne me convenait pas totalement :
PHP:apache
est basée sur Debian (taille plus importante qu'une image basée sur Alpine)Pour mémoire et à toutes fins utiles, le Dockerfile
- nsteinmetz/docker-grav :
FROM php:7-apache
ADD https://github.com/getgrav/grav/releases/download/1.1.8/grav-admin-v1.1.8.zip /tmp/grav-admin-v1.1.8.zip
RUN apt update && \
apt upgrade -y && \
apt install -y \
unzip \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng12-dev \
pkg-config && \
unzip /tmp/grav-admin-v1.1.8.zip -d /tmp/ && \
mv /tmp/grav-admin/* /var/www/html/ && \
mv /tmp/grav-admin/.htaccess /var/www/html/ && \
chown www-data:www-data -R /var/www/html && \
docker-php-ext-install -j$(nproc) mcrypt && \
docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ && \
docker-php-ext-install -j$(nproc) gd &&\
docker-php-ext-install -j$(nproc) zip &&\
a2enmod rewrite && \
rm -rf /var/lib/apt/lists/*
COPY php.conf /etc/apache2/conf-enabled/
COPY server-signature.conf /etc/apache2/conf-enabled/
Avec php.conf
:
php_admin_flag display_errors off
php_admin_flag expose_php off
php_admin_value post_max_size "40M"
php_admin_value upload_max_filesize "40M"
et server-signature.conf
:
ServerSignature Off
ServerTokens Prod
Les améliorations apportées :
docker-compose
pour avoir plus de flexibilité au moment de démarrer les containers en fonction des projets utilisant Grav,Cela donne :
├── app
│ ├── Dockerfile
│ ├── security.conf
│ └── uploads.conf
└── web
│ ├── Dockerfile
│ └── php-fpm.conf
├── docker-compose.yml
Petites précisions à ce niveau :
app
correspond au container php-fpm security.conf
contient des options de sécurité PHP (ne pas exposer la version de PHP, ne pas afficher les erreurs, etc)uploads.conf
contient des options liés aux uploads PHP (taille max de fichiers, etc)web
est le container nginx php-fpm.conf
est la configuration de mon virtualhost nginxOn a app/Dockerfile
:
FROM php:7-fpm-alpine
ADD https://github.com/getgrav/grav/releases/download/1.1.8/grav-admin-v1.1.8.zip /tmp/grav-admin-v1.1.8.zip
RUN apk update &&\
apk upgrade &&\
unzip /tmp/grav-admin-v1.1.8.zip -d /tmp/ && \
mv /tmp/grav-admin/* /var/www/html/ && \
mv /tmp/grav-admin/.htaccess /var/www/html/ && \
chown www-data:www-data -R /var/www/html &&\
apk add libjpeg libjpeg-turbo libjpeg-turbo-dev libpng libpng-dev freetype freetype-dev &&\
docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ && \
docker-php-ext-install mcrypt zip gd &&\
rm -rf /var/cache/apk/* &&\
rm -rf /tmp/grav-admin-v1.1.8.zip &&\
cd /var/www/html && ./bin/gpm update --no-interaction
COPY uploads.conf /usr/local/etc/php-fpm.d/uploads.conf
COPY security.conf /usr/local/etc/php-fpm.d/security.conf
VOLUME ["/var/www/html", "/var/www/html/assets", "/var/www/html/backup", "/var/www/html/cache", "/var/www/html/images", "/var/www/html/logs", "/var/www/html/tmp"]
A noter, même s'il n'y a rien d'exceptionnel :
RUN
va mettre à jour les plugins inclus nativement dans Gravbackups
et tmp
.Ensuite, app/security.conf
:
[www]
php_admin_flag[display_errors] = off
php_admin_flag[expose_php] = off
et app/uploads.conf
:
[www]
php_admin_value[post_max_size] = "40M"
php_admin_value[upload_max_filesize] = "40M"
J'ai fait le choix de surcharger l'instance php-fpm par défaut (ie: www
) et j'utilise php_admin_*
afin d'interdire toute surcharge de ces variables par l'application.
web/Dockerfile
:
FROM nginx:stable-alpine
RUN rm /etc/nginx/conf.d/default.conf
ADD php-fpm.conf /etc/nginx/conf.d/php-fpm.conf
Je supprime le virtualhost fourni par défaut et je fournis le mien en lieu et place.
web/php-fpm.conf
:
server {
listen 80;
server_name _;
charset utf-8;
root /var/www/html/;
index index.html index.php;
# Uploads to 100M
client_max_body_size 100m;
location / {
try_files $uri $uri/ /index.php?_url=$uri;
}
## Begin - Security
# don't send the nginx version number in error pages and Server header
server_tokens off;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Frame-Options SAMEORIGIN;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data: ; style-src 'self' 'unsafe-inline'; font-src 'self'; child-src; object-src 'none'";
# deny all direct access for these folders
location ~* /(.git|cache|bin|logs|backup|tests)/.*$ { return 403; }
# deny running scripts inside core system folders
location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
# deny running scripts inside user folder
location ~* /user/.*\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
# deny access to specific files in the root folder
location ~ /(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess) { return 403; }
## End - Security
## Begin - PHP
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
## End - PHP
location ~* ^.+\.(ico|js|gif|jpg|jpeg|png|bmp)$ {
expires 30d;
}
}
A noter :
X-Content-Type-Options
, X-XSS-Protection
, X-Frame-Options
et Content-Security-Policy
)app:9000
, car app
est le nom de mon service.version: '2'
services:
web:
build: ./web/
depends_on:
- app
volumes_from:
- app
ports:
- "80:80"
app:
build: ./app/
volumes:
- ./user:/var/www/html/user
- ./assets:/var/www/html/assets
- ./backup:/var/www/html/backup
- ./cache:/var/www/html/cache
- ./images:/var/www/html/images
- ./logs:/var/www/html/logs
- ./tmp:/var/www/html/tmp
Pour builder puis lancer vos containers en mode daemon :
docker-compse up -d --build
Traefik est un reverse-proxy moderne et il a le bon goût de s'interfacer notamment avec l'API de Docker. On peut alors déclarer dynamiquement nos containers à traefik et celui-ci les prend en compte dynamiquement.
Pour mon poste en local, j'ai décidé de lancer Traefik de la façon suivante :
docker run -d -v /dev/null:/traefik.toml -v /var/run/docker.sock:/var/run/docker.sock -p 80:80 -p 443:443 -p 8080:8080 --name traefik traefik:camembert --web --docker --docker.domain=docker.localhost --logLevel=DEBUG
Pour les détails :
-v /dev/null:/traefik.toml
permet de démarrer Traefik sans fichier de configuration-v /var/run/docker.sock:/var/run/docker.sock
permert au container Traefik de communiquer avec Docker et son API--web
: lance l'interface web (dashboard)--docker --docker.domain=docker.localhost
: active le support de docker et fourni un domaine par défaut à tous les containers docker.--logLevel=DEBUG
: permet d'avoir des logs verbeux si problème via docker logs -f traefik
Il "suffit" d'indiquer des labels à vos services :
version: '2'
services:
web:
build: ./web/
depends_on:
- app
volumes_from:
- app
labels:
- "traefik.backend=grav-project"
- "traefik.frontend.rule=Host:project.grav"
- "traefik.port=80"
- "traefik.protocol=http"
- "traefik.frontend.entryPoints=http"
- "traefik.docker.network=nom_du_reseau"
app:
build: ./app/
volumes:
- ./user:/var/www/html/user
- ./assets:/var/www/html/assets
- ./backup:/var/www/html/backup
- ./cache:/var/www/html/cache
- ./images:/var/www/html/images
- ./logs:/var/www/html/logs
- ./tmp:/var/www/html/tmp
labels:
- "traefik.enable=false"
Explications pour le service web
:
entryPoint
indique que je vais me connecter à Traefik sur le port 80 pour me connecter ensuite à mon conteneur.docker network ls
.En gros on a :
Vous <=> Votre navigateur <=> Traefik Frontend (
Host:project.grav
(frontend.rule) + protocolehttp
(EntryPoint)) <=> Traefik Backend (grav-project
+ port80
+ protocolehttp
) <=> ContainerWeb
Explications pour le service app
:
Il ne me reste plus qu'à :
/etc/hosts
pour project.grav
http://project.grav/
Sauf que cela ne marche pas à ce stade (si vous utilisez traefik dans un container docker). En effet, lors du docker-compose up -d
, docker a créé un réseau pour votre application. Or par défaut, Traefik n'y a pas accès. Il vous faut faire:
docker network connect <nom_du_reseau> <container-traefik>
Et ce coup-ci, votre site propulsé par Grav s'affiche.
Vous pouvez retrouver les fichiers et remonter vos commentaires sur cerenit/docker-grav
Une fois prochaine; je vous parlerais d'Ansible, Docker et Traefik pour déployer vos projets aisément.