Mon premier webservice
----------------------

C'est parti, nous allons déployer notre premier service !

Il s'agit d'un service montrant une image aléatoire à chaque chargement de
page :\
<https://you.p0m.fr/>.


Nous pouvons télécharger et lancer le service grâce à :

<div lang="en-US">
```bash
docker container run -i registry.nemunai.re/youp0m
```
</div>

Cette fois-ci, ce n'est pas un shell que nous obtenons[^defaultcmd] : il semblerait que le
service soit lancé et écoute sur le port 8080. Est-ce le cas ?

<http://localhost:8080>

[^defaultcmd]: Chaque conteneur dispose d'une commande par défaut : les images
	de base telles que les distributions vont lancer un shell, tandis que les
	conteneurs de service vont lancer leur service directement.

Non ! Car le service est contenerisé ! Il s'exécute dans son coin, sans
interférer avec son hôte.

![L'hôte n'arrive pas à joindre le port 8080](not-published-ports.png "L'hôte n'arrive pas à joindre le port 8080"){ width=70% }


### Redirection de ports

Nous pouvons rediriger le port avec l'argument <span lang="en-US">`--publish dst_host:src_cntr`</span> :

<div lang="en-US">
```bash
docker container run --publish 8080:8080 registry.nemunai.re/youp0m
```
</div>

Cet argument va faire effectuer à Docker une étape supplémentaire lorsqu'il
démarerra le conteneur : il va devoir mettre en place une redirection du port
de notre système local (8080) vers le port 8080 du conteneur.

![Le port 8080 est redirigé vers le conteneur](published-ports.png "Le port 8080 est redirigé vers le conteneur"){ width=70% }

::::: {.question}

#### Peut-on démarrer plusieurs conteurs utilisant le même port de notre système local ? {-}

Si l'on essai de lancer deux fois la commande de notre dernier `run`, nous
obtenons l'erreur suivante :

<div lang="en-US">
```bash
42sh$ docker container run --publish 8080:8080 registry.nemunai.re/youp0m
docker: Error response from daemon: driver failed programming external connectivity on endpoint: Bind for 0.0.0.0:8080 failed: port is already allocated.
```
</div>

En effet, un port de la pile réseau de notre système ne peut être associé qu'à
un seul et unique processus. Puisqu'il a déjà été attribué à notre précédent
conteneur, il ne peut pas être à nouveau alloué.

En revanche, nous pouvons tout-à-fait lancer deux conteneurs `youp0m` sans
rediriger leur port 8080, car le conteneur ne partage pas la pile réseau de
l'hôte. Une nouvelle pile réseau est créée pour chaque conteneur.

:::::

Cette fois, nous pouvons bien accéder au service depuis notre navigateur.

Pour le moment, le service ne dispose d'aucune image à afficher, vous pouvez
utiliser cette syntaxe pour ajouter une image :

<div lang="en-US">
```bash
base64 monimage.jpg | \
   curl --data @- http://localhost:8080/api/images/monimage
```
</div>

Si vous n'êtes pas particulièrement inspiré, vous pouvez ajouter ces images :

<div lang="en-US">
```bash
for IMG in lynx4 otters DNcrZ6u raccoons; do
    wget -O- https://you.p0m.fr/images/$IMG | base64 | \
	    curl --data @- http://localhost:8080/api/images/$IMG
done
```
</div>


### Prêt pour la production ?

Avec l'option `-i`, nous pouvons encore transmettre les signaux de terminaison
au conteneur. C'est pratique lorsque l'on développe, mais en production, notre
service ne s'exécutera pas dans notre terminal !

On utilise l'option `-d` pour lancer le conteneur en tâche de fond :

<div lang="en-US">
```bash
docker container run -d -p 8080:8080 registry.nemunai.re/youp0m
```
</div>

::::: {.question}

#### Où est la sortie standard ? {-}

À partir de l'identifiant renvoyé par cette commande (que l'on peut également
obtenir avec un `docker container ls`), nous pouvons consulter les logs du
service (en fait, les sorties standard et d'erreur) :

<div lang="en-US">
```bash
docker container logs 0a1b2c3d4e
```
</div>

:::::


### Une autre instance ?

Maintenant que nous avons un clone de <https://you.p0m.fr/>, nous voulons
absolument un clone de <https://food.p0m.fr/> !

Il s'agit du même service, mais ce ne sont pas les mêmes images.

On ne peut pas utiliser le même port sur la machine hôte, mais pour le reste,
il s'agit des mêmes options :

<div lang="en-US">
```bash
docker container run -d -p 8081:8080 registry.nemunai.re/youp0m
```
</div>

Voyons le résultat : <http://localhost:8081>

Nous avons réussi à lancer deux conteneurs à partir de la même image, et on
voit bien que ceux-ci ne partagent pas leur système de fichiers : notre
nouvelle instance est encore immaculée.


### Paramètres de lancement

`youp0m` dispose d'une interface d'administration minimaliste, pour modérer les
images soumises. Celle-ci est accessible par mot de passe (ou est désactivée si
aucun identifiant n'est trouvé). L'aide du programme indique que cela est géré
par l'argument `-htpasswd`, mais encore faut-il pouvoir recopier un fichier
créé à l'extérieur du conteneur...

Outre les arguments que l'on peut passer au premier processus du conteneur, la
plupart des images peuvent adapter leur comportement en fonction de variables
d'environnement que l'on passe en paramètre.

Cette bonne pratique est recommandée par
[`12factor.net`](https://12factor.net/), qui détaille les raisons qui devraient
pousser les développeurs à privilégier les variables d'environnements aux
arguments sur la ligne de commande.

Il se trouve que les conteneurs `youp0m` peuvent créer le fichier `htpasswd`,
s'ils sont démarrés avec les variables d'environnement :

  - `YOUP0M_USERNAME` : nom d'utilisateur pour l'administrateur (par défaut admin) ;
  - `YOUP0M_PASSWORD` : mot de passe de l'utilisateur.

Pour ajouter une variable d'environnement, cela se passe dans la commande
`run`, en ajoutant une ou plusieurs options `-e` :

<div lang="en-US">
```bash
docker container run -e YOUP0M_PASSWORD=foobar -p 8080:8080 registry.nemunai.re/youp0m
```
</div>

Une fois lancé, ce conteneur exposera une interface d'administration à cette
adresse :\
<http://localhost:8080/admin/>.


### Arrêt des conteneurs et persistance des données

Lorsque l'on souhaite stopper un conteneur lancé en tâche de fond, on utilise
son identifiant dans la commande suivante :

<div lang="en-US">
```bash
docker container stop 0123456789abcdef
```
</div>

Maintenant, si l'on relance un conteneur `youp0m`, il aura perdu toutes les
magnifiques images que l'on aura ajoutées. Nous allons voir dans la partie
suivante comment rendre les données persistantes entre deux lancements.
