identidock
: une application Flask qui se connecte à redis
identidock
et chargez-le avec la fonction Add folder to workspace)app
, ajoutez une petite application python en créant ce fichier identidock.py
:from flask import Flask, Response, request, abort
import requests
import hashlib
import redis
import os
import logging
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
logging.basicConfig(level=LOGLEVEL)
app = Flask(__name__)
cache = redis.StrictRedis(host='redis', port=6379, db=0)
salt = "UNIQUE_SALT"
default_name = 'toi'
@app.route('/', methods=['GET', 'POST'])
def mainpage():
name = default_name
if request.method == 'POST':
name = request.form['name']
salted_name = salt + name
name_hash = hashlib.sha256(salted_name.encode()).hexdigest()
header = '<html><head><title>Identidock</title></head><body>'
body = '''<form method="POST">
Salut <input type="text" name="name" value="{0}"> !
<input type="submit" value="submit">
</form>
<p>Tu ressembles à ça :
<img src="/monster/{1}"/>
'''.format(name, name_hash)
footer = '</body></html>'
return header + body + footer
@app.route('/monster/<name>')
def get_identicon(name):
found_in_cache = False
try:
image = cache.get(name)
redis_unreachable = False
if image is not None:
found_in_cache = True
logging.info("Image trouvée dans le cache")
except:
redis_unreachable = True
logging.warning("Cache redis injoignable")
if not found_in_cache:
logging.info("Image non trouvée dans le cache")
try:
r = requests.get('http://dnmonster:8080/monster/' + name + '?size=80')
image = r.content
logging.info("Image générée grâce au service dnmonster")
if not redis_unreachable:
cache.set(name, image)
logging.info("Image enregistrée dans le cache redis")
except:
logging.critical("Le service dnmonster est injoignable !")
abort(503)
return Response(image, mimetype='image/png')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
uWSGI
est un serveur python de production très adapté pour servir notre serveur intégré Flask, nous allons l’utiliser.
Dockerisons maintenant cette nouvelle application avec le Dockerfile suivant :
FROM python:3.7
RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
RUN pip install Flask uWSGI requests redis
WORKDIR /app
COPY app/identidock.py /app
EXPOSE 5000 9191
USER uwsgi
CMD ["uwsgi", "--http", "0.0.0.0:5000", "--wsgi-file", "/app/identidock.py", \
"--callable", "app", "--stats", "0.0.0.0:9191"]
USER
, pourquoi ?.identidock
(à côté du Dockerfile), créez un fichier de déclaration de notre application appelé docker-compose.yml
avec à l’intérieur :services:
identidock:
build: .
ports:
- "5000:5000"
Plusieurs remarques :
services
déclare le conteneur de notre applicationbuild: .
indique que l’image d’origine de notre conteneur est le résultat de la construction d’une image à partir du répertoire courant (équivaut à docker build -t identidock .
)Lancez le service (pour le moment mono-conteneur) avec docker compose up
(cette commande sous-entend docker compose build
)
Visitez la page web de l’app.
Ajoutons maintenant un deuxième conteneur. Nous allons tirer parti d’une image déjà créée qui permet de récupérer une “identicon”. Ajoutez à la suite du fichier Compose (attention aux indentations !) :
dnmonster:
image: amouat/dnmonster:1.0
Le docker-compose.yml
doit pour l’instant ressembler à ça :
services:
identidock:
build: .
ports:
- "5000:5000"
dnmonster:
image: amouat/dnmonster:1.0
Enfin, nous déclarons aussi un réseau appelé identinet
pour y mettre les deux conteneurs de notre application.
networks:
identinet:
driver: bridge
identidock
et dnmonster
sur le même réseau en ajoutant deux fois ce bout de code où c’est nécessaire (attention aux indentations !) :networks:
- identinet
redis
(attention aux indentations !). Cette base de données sert à mettre en cache les images et à ne pas les recalculer à chaque fois.redis:
image: redis
networks:
- identinet
docker-compose.yml
final :
services:
identidock:
build: .
ports:
- "5000:5000"
- "9191:9191" # port pour les stats
networks:
- identinet
dnmonster:
image: amouat/dnmonster:1.0
networks:
- identinet
redis:
image: redis
networks:
- identinet
networks:
identinet:
driver: bridge
Lancez l’application et vérifiez que le cache fonctionne en cherchant les messages dans les logs de l’application.
N’hésitez pas à passer du temps à explorer les options et commandes de docker-compose
, ainsi que la documentation officielle du langage des Compose files.
En s’inspirant des exercices sur les volumes (TP3) et du fichier boot.sh
de l’app microblog (TP2), modifions le docker-compose.yml
pour y inclure des instructions pour lancer le serveur python en mode debug : la modification du code source devrait immédiatement être répercutée dans les logs d'identidock
, et recharger la page devrait nous montrer la nouvelle version du code de l’application.
microblog
Créons un fichier Docker Compose pour faire fonctionner l’application Microblog du TP précédent avec Postgres.
On se propose ici d’essayer de déployer plusieurs services pré-configurés comme Wordpress, Nextcloud, Sentry ou votre logiciel préféré.
Récupérez (et adaptez si besoin) à partir d’Internet un fichier docker-compose.yml
permettant de lancer un pad HedgeDoc ou autre avec sa base de données. Je vous conseille de toujours chercher dans la documentation officielle ou le repository officiel (souvent sur Github) en premier.
Vérifiez que le service est bien accessible sur le port donné.
Si besoin, lisez les logs en quête bug et adaptez les variables d’environnement.
L’utilité d’Elasticsearch est que, grâce à une configuration très simple de son module Filebeat, nous allons pouvoir centraliser les logs de tous nos conteneurs Docker. Pour ce faire, il suffit d’abord de télécharger une configuration de Filebeat prévue à cet effet :
curl -L -O https://raw.githubusercontent.com/elastic/beats/7.10/deploy/docker/filebeat.docker.yml
Renommons cette configuration et rectifions qui possède ce fichier pour satisfaire une contrainte de sécurité de Filebeat :
mv filebeat.docker.yml filebeat.yml
sudo chown root filebeat.yml
sudo chmod go-w filebeat.yml
Enfin, créons un fichier docker-compose.yml
pour lancer une stack Elasticsearch :
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.5.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
networks:
- logging-network
filebeat:
image: docker.elastic.co/beats/filebeat:7.5.0
user: root
depends_on:
- elasticsearch
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock
networks:
- logging-network
environment:
- -strict.perms=false
kibana:
image: docker.elastic.co/kibana/kibana:7.5.0
depends_on:
- elasticsearch
ports:
- 5601:5601
networks:
- logging-network
networks:
logging-network:
driver: bridge
Il suffit ensuite de :
5601
)*
dans le champ indiqué, de valider@timestamp
, puis de valider.L’index nécessaire à Kibana est créé, vous pouvez vous rendre dans la partie Discover à gauche (l’icône boussole 🧭) pour lire vos logs.
Il est temps de faire un petit docker stats
pour découvrir l’utilisation du CPU et de la RAM de vos conteneurs !
Puis, à l’aide de la documentation Elasticsearch et/ou en adaptant de bouts de code Docker Compose trouvés sur internet, ajoutez et configurez un nœud Elastic. Toujours à l’aide de la documentation Elasticsearch, vérifiez que ce nouveau nœud communique bien avec le premier.
microblog
Dans la dernière version de l’app microblog
, Elasticsearch est utilisé pour fournir une fonctionnalité de recherche puissante dans les posts de l’app.
Avec l’aide du tutoriel de Miguel Grinberg, écrivez le docker-compose.yml
qui permet de lancer une stack entière pour microblog
. Elle devra contenir un conteneur microblog
, un conteneur mysql
, un conteneur elasticsearch
et un conteneur kibana
.
Vous pouvez désormais faire l’exercice 1 du TP7 pour configurer un serveur web qui permet d’accéder à vos services via des domaines.