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 ca :
<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 trouvee dans le cache")
except:
redis_unreachable = True
logging.warning("Cache redis injoignable")
if not found_in_cache:
logging.info("Image non trouvee dans le cache")
try:
r = requests.get('http://dnmonster:8080/monster/' + name + '?size=80')
image = r.content
logging.info("Image generee grace au service dnmonster")
if not redis_unreachable:
cache.set(name, image)
logging.info("Image enregistree 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 pip3 install Flask uWSGI requests redis
WORKDIR /app
COPY app/identidock.py /app
ENV FLASK_APP identidock.py
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 !) un service dnmonster
utilisant l’image amouat/dnmonster:1.0
.
identinet
pour y mettre les deux conteneurs de notre application.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.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.
Modifions le docker-compose.yml
pour y inclure des instructions pour lancer le serveur python en mode debug.
Notre image est codée pour lancer le serveur de production appelé uWSGI (CMD ["uwsgi", "--http", "0.0.0.0:5000", "--wsgi-file", "/app/identidock.py", \ "--callable", "app", "--stats", "0.0.0.0:9191"]
). Nous voulons plutôt lancer le serveur de debug qui se lance avec :
FLASK_ENV=development
flask run -h 0.0.0.0
En réfléchissant à comment utiliser les volumes, le but est de trouver comment la modification du code source devrait immédiatement être répercutée dans les logs d'identidock
: recharger la page devrait nous montrer la nouvelle version du code de l’application.
En vous inspirant de ce fichier, créez un script d’entrypoint et déclarez un volume pour l’utiliser.
#!/bin/sh
# cette partie sert à effectuer des opérations sur la base de données si nécessaires
while true; do
if flask db upgrade; then
break
fi
echo Deploy command failed, retrying in 5 secs...
sleep 5
done
# cette partie permet de faire varier l'environnement du container
set -e
if [ "$CONTEXT" = 'DEV' ]; then
echo "Running Development Server"
FLASK_ENV=development exec flask run -h 0.0.0.0
else
echo "Running Production Server"
exec gunicorn -b :5000 --access-logfile - --error-logfile - microblog:app
fi
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.