Vous vous rappelez de cet article qui parlait d’une application révolutionnaire en perl et utilisant le framework web Mojolicious ? Super. Voici la démo plus ou moins liée, à savoir comment utiliser Docker pour déployer mon application Mojolicious.

Note : je ne présente plus Docker.

Pour que mon application puisse tourner en conteneur, il faut d’abord construire une image de laquelle sera lancée ledit conteneur. Je ne saurais trop vous conseiller de lire les bests practices en matière de rédaction de Dockerfile, au passage.

Je pars d’une image légère, alpine linux. C’est une sorte de système linux ultra-léger, parfait pour construire des images optimisées. C’est beaucoup plus léger que de partir d’une Ubuntu 20.04, par exemple. Je rajoute quelques labels au passage pour décrire mon image et donc mon application.

FROM alpine:latest
LABEL maintainer="cazzaniga.sandro@gmail.com" \ 
description="A simple quote API written in perl"

J’installe ensuite les dépendances liées à mon application avec apk, le gestionnaire de paquets sous Alpine.

Note : lorsque vous êtes en train de construire votre image comme moi ici, c’est bien plus pratique d’avoir un conteneur alpine pas loin pour tester vos commandes, chercher vos paquets, etc. Vous pouvez en lancer un avec docker run -it alpine sh, qui vous donnera directement un shell et le conteneur se stoppera à la déconnexion/sortie.

Un peu de magie noire : j’ai deux types de dépendances, les build-deps et les dépendances classiques. La différence est que les build-deps ne sont utiles que lors du temps de compilation/installation d’une application, alors que les dépendances sont nécessaires au bon fonctionnement quotidien de l’application. On les désinstalle donc avant de boucler l’image pour l’optimiser/l’alléger encore.

Et pour les localiser simplement et éviter de se retaper la ligne, ce qui au passage peut-être source de manquement ou d’erreur, on peut ajouter une sorte de tags à la liste de paquets installés par une ligne de commande apk, ici c’est .build-deps.

Dans mes build-deps, j’ai besoin de cpan qui me permettra d’installer mes modules perl, de git pour cloner mon repository et enfin de make pour builder Mojolicious. Donc oui, j’aurais pu les désinstaller à la main mais le but c’est quand même de vous montrer des trucs cools.

RUN apk add --update --no-cache --virtual .build-deps git make perl-dev \                 
    && apk add perl \ 

Le paquet perl-dev installe déjà perl, donc pourquoi la seconde ligne ? Parce que quand l'apk del va tomber sur les build-deps, il supprimera perl en même temps que perl-dev puisqu’il n’a pas été expressément installé, chose que je fais justement sur cette deuxième ligne.

Je configure ensuite cpan par défaut et clone le repo git de mon application. Comme je fais les choses bien, mon application et les scripts liés (j’y reviendrais) sont versionnés dans un git. Ça évite aussi de copier les fichiers à la main et de multiplier les instructions.

    && echo | cpan \ 
    && git clone https://github.com/Kharec/quote-api.git /quote-api \

Une fois cloné, je vais installer les dépendances perl avec cpan :

    && cpan Mojolicious::Lite Fortune Mojo::Server::Daemon \

Et pour finir, je nettoie mes build-deps et certains répertoires afin d’optimiser l’image. Et comme j’ai mis un tag, c’est propre et rapide.

    && apk del .build-deps \
    && rm -rf /var/cache/apk/* /root/.cpan*

J’en ai fini avec l’instruction RUN, la voici en entière :

RUN apk add --update --no-cache --virtual .build-deps git make perl-dev \
    && apk add perl \ 
    && echo | cpan \ 
    && git clone https://github.com/Kharec/quote-api.git /quote-api \
    && cpan Mojolicious::Lite Fortune Mojo::Server::Daemon \
    && apk del .build-deps \
    && rm -rf /var/cache/apk/* /root/.cpan*

A ce stade, l’application a tout ce qu’il faut pour tourner, mais ne tourne pas. C’est normal, je ne lui ai donné aucune instruction autre qu’installer des paquets et des modules.

En premier lieu, je vais exposer un port de mon conteneur, ainsi l’utilisateur pourra choisir de binder ce port au port de son choix sur son hôte :

EXPOSE 8080

Je positionne ensuite mon script de démarrage dans un répertoire décent par une instruction RUN :

RUN cp /quote-api/startup.sh /usr/local/bin/startup

Je ne m’étends pas sur le script, il est assez simple et lance simplement le script perl principal :

#!/bin/sh
cd /quote-api && perl quote-api

Mais le jour où le script ou l’application évolueront, je ne modifierais pas mon image mais juste ce script. C’est aussi une best practice.

Et enfin, je lance le script et donc l’api :

CMD ["/usr/local/bin/startup"]

J’en ai fini avec le Dockerfile, le voici complet :

FROM alpine:latest
LABEL maintainer="cazzaniga.sandro@gmail.com" \
      description="A simple quote API written in perl"

RUN apk add --update --no-cache --virtual .build-deps git make perl-dev \
    && apk add perl \ 
    && echo | cpan \ 
    && git clone https://github.com/Kharec/quote-api.git /quote-api \
    && cpan Mojolicious::Lite Fortune Mojo::Server::Daemon \
    && apk del .build-deps \
    && rm -rf /var/cache/apk/* /root/.cpan*

EXPOSE 8080

RUN cp /quote-api/startup.sh /usr/local/bin/startup

CMD ["/usr/local/bin/startup"]

Il ne reste qu’à builder l’image, et la tagguer à mon nom ainsi que celui de l’application. Ainsi, si un jour je la publie sur le Docker hub, les utilisateurs pourront la retrouver facilement. Peu de chances que je publie celle-ci, cela dit.

➜  quote-api git:(master) docker build . -t kharec/quote-api
...
➜  quote-api git:(master) docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
kharec/quote-api    latest              3837b4ebdc88        About a minute ago   43.7MB

Mon image s’est buildée sans erreurs et est bien disponible. Je peux la lancer en bindant le port exposé via -p 8080:8080:

➜  quote-api git:(master) docker run --name dummy-api -p 8080:8080 -d kharec/quote-api 
f8d9fe6342665894221647c8a709bd6c8cc72b32ecd2868c79ecc9d91ca6492b

Mon API est maintenant up and running en conteneur :

➜  quote-api git:(master) docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
f8d9fe634266        kharec/quote-api    "/usr/local/bin/star…"   7 seconds ago       Up 7 seconds        0.0.0.0:8080->8080/tcp   dummy-api

Et je le prouve !

➜  quote-api git:(master) http http://127.0.0.1:8080
HTTP/1.1 200 OK
Content-Length: 42
Content-Type: application/json;charset=UTF-8
Date: Wed, 06 May 2020 04:44:10 GMT
Server: Mojolicious (Perl)

"The probability of someone watching you is directly proportional to the stupidity of your action."

➜  quote-api git:(master) http http://127.0.0.1:8080
HTTP/1.1 200 OK
Content-Length: 76
Content-Type: application/json;charset=UTF-8
Date: Wed, 06 May 2020 04:44:29 GMT
Server: Mojolicious (Perl)

"In great matters men show themselves as they wish to be seen; in small matters, as they are."

➜  quote-api git:(master) http http://127.0.0.1:8080
HTTP/1.1 200 OK
Content-Length: 79
Content-Type: application/json;charset=UTF-8
Date: Wed, 06 May 2020 04:44:29 GMT
Server: Mojolicious (Perl)

"A fanatic is a person who can't change his mind and won't change the subject."

Comme d’habitude, vous retrouverez tout le code sur mon github. Non pas que cette API vous soit indispensable mais vous pourrez vous inspirer du Dockerfile pour construire vos conteneurs, et c’était un peu le but de cet article.