Maintenant que nous avons 2 serveurs tomcat, ça serait bien de pouvoir les arrêter un par un (pour effectuer une livraison par exemples) sans que cela n’ait d’impact pour l’utilisateur. Je pense notamment à la gestion de la session utilisateur. Je vais donc essayer de stocker (sérialiser) la session dans une base nosql. Vu qu’il faut juste stocker un couple clé/valeur, c’est beaucoup moins lourd qu’une base de données.
Préparation du code
Pour commencer, je vais reprendre le travail de James Coleman sur ce sujet https://github.com/jcoleman/tomcat-redis-session-manager. Tout d’abord comme je ne suis pas très à l’aise avec gradle (pour le moment), j’ai commencé par créer un pom.xml pour gérer les dépendances et plus tard créer une image docker. Comme expliqué dans le préambule du repository, la détection de la modification de la session pose des soucis si on modifie directement l’objet après avoir fait un getAttribute()
1 2 |
Produit p=(Produit)session.getAttribute("product"); p.setCount(p.getCount()+1); |
J’ai donc décidé (pour l’exemple) de forcer l’enregistrement de la session à la fin de chaque requête. Il faudra vérifier avec un test de charge que ça ne pose pas de soucis pour une mise en production. Pour utiliser cette libraire, il faut la builder et l’ajouter au répertoire lib de tomcat (ça ne fonctionne pas si la librairie est embarqué dans le projet. Pour faciliter un peu les choses, maven va créer une librairie qui contiendra ses propres dépendances:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<plugin> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> |
Ensuite, il suffit juste au niveau du projet de modifier le fichier META-INF/context.xml pour ajouter une valve et un manager:
1 2 3 4 5 6 |
<Valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve" /> <Manager className="com.radiadesign.catalina.session.RedisSessionManager" host="172.17.0.2" port="6379" database="0" maxInactiveInterval="180" /> |
On peut personnaliser la machine et le port de redis ainsi que la durée d’une session (ici j’ai réglé à 3 min). Maintenant que tout est configuré, voyons comment intégré cela dans docker.
Préparation des images
J’ai créé un fichier Dockerfile dans le projet tomcat-redis-session-manager pour créer une nouvelle image se basant sur jolokia/tomcat-6.0 et ajoutant notre librairie dans le répertoire lib.
1 2 3 4 5 |
FROM jolokia/tomcat-6.0 ENV TOMCAT_VERSION 6.0.39 ADD target/tomcat-redis-session-manager-jar-with-dependencies.jar /opt/apache-tomcat-${TOMCAT_VERSION}/lib/ |
D’ailleurs, il semble que docker est des soucis avec des répertoires symboliques target. Si je remplace la dernière ligne par
1 |
ADD target/tomcat-redis-session-manager-jar-with-dependencies.jar /opt/tomcat/lib/ |
j’obtiens l’erreur suivante (/opt/tomcat étant un lien symbolique vers opt/apache-tomcat-${TOMCAT_VERSION})
1 |
2014/05/25 17:18:25 mkdir /var/lib/docker/aufs/mnt/309ed27293f7af78691fbea762bfb364a3f6dd86fcec8c4a302d16cc91329708/opt/tomcat: file exists |
J’ai ensuite modifier le fichier Dockerfile du projet session-webapp pour se baser sur l’image qu’on vient de créer:
1 |
FROM benoitcharret/tomcat6-session |
Il ne reste plus qu’à lancer toutes les images pour voir le résultat
Lancement des images
Je commence par lancer redis:
1 |
docker run -d --name redis -p 6379:6379 -v /tmp/data:/data dockerfile/redis |
avec la même commande que dans le dernier article, je récupère son ip (à mettre dans le context.xml) Je lance ensuite un redis-cli pour tester la connectivité
1 2 3 4 |
docker run -d --name redis -p 6379:6379 -v /tmp/data:/data dockerfile/redis redis-cli -h 172.17.0.2 172.17.0.2:6379> ping PONG |
Mon serveur redis est donc opérationnel. Je lance maintenant 2 serveurs tomcat et haproxy. Je me connecte maintenant sur le site, je me loggue et fais des refresh sur la page index.jsp. Cela nous donne dans logs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
INFO: Server startup in 404 ms 917E8054D3BC7A4D9E4217136C415248 917E8054D3BC7A4D9E4217136C415248 value of product 1 917E8054D3BC7A4D9E4217136C415248 value of product 2 917E8054D3BC7A4D9E4217136C415248 value of product 3 917E8054D3BC7A4D9E4217136C415248 value of product 4 917E8054D3BC7A4D9E4217136C415248 value of product 5 917E8054D3BC7A4D9E4217136C415248 value of product 6 917E8054D3BC7A4D9E4217136C415248 value of product 7 ^CMay 25, 2014 3:26:33 PM org.apache.coyote.http11.Http11Protocol pause INFO: Pausing Coyote HTTP/1.1 on http-8080 May 25, 2014 3:26:34 PM org.apache.catalina.core.StandardService stop INFO: Stopping service Catalina |
Je suis bien resté sur le même serveur à chaque fois et j’ai bien récupéré ma donnée product. Si ne n’avais pas fait la modification dans RedisSessionManager, la valeur de product serait toujours de 1. J’ai ensuite arrêté le serveur tomcat et fais des refresh sur index.jsp. Cela nous donne:
1 2 3 4 5 6 7 8 9 10 11 |
INFO: Server startup in 392 ms 917E8054D3BC7A4D9E4217136C415248 value of product 8 917E8054D3BC7A4D9E4217136C415248 value of product 9 917E8054D3BC7A4D9E4217136C415248 value of product 10 917E8054D3BC7A4D9E4217136C415248 value of product 11 917E8054D3BC7A4D9E4217136C415248 value of product 12 |
J’ai donc bien récupérer ma session et la valeur de product. Pour voir le contenu des clés de redis, on peut utiliser la commande (a ne pas faire en production):
1 |
KEYS * |
Voila comment tester facilement la persistance de session avec redis et docker.
Les sources sont ici:
- session-webapp: https://github.com/BenoitCharret/session-webapp
- tomcat-redis-session-manager: https://github.com/BenoitCharret/tomcat-redis-session-manager/tree/tomcat-6
- image docker : https://index.docker.io/u/benoitcharret/tomcat6-session/