Communication avec l’extérieur

Réseau

La théorie

Ce qui vous a été présenté :

Un container =

  • une paire de « veth » : deux interfaces virtuelles, connectées entre elle (tout ce qui rentre dans l’une ressort dans l’autre et inversement)

  • une des veth est dans le namespace réseau standard de l’hote

  • l’autre est dans un namespace isolé

  • La veth du namespace standard est connectée au « bridge » docker0

  • Le bridge docker0 fait du DNAT : il permet l’accès à l’extérieur

(Un bon article pour aller plus loin : https://iximiuz.com/en/posts/container-networking-is-simple/ )

Observer le réseau des conteneurs

Regardez les interfaces réseau et le routage d’un conteneur :

docker run --rm -it alpine sh
ip link
ip route

De la même manière, regardez ce qu’il se passe en choissant de ne pas faire de namespace séparé pour le conteneur :

docker run --network host --rm -it alpine sh
ip link
ip route

SNAT

A la création d’un conteneur, on crée une redirection de port en ajoutant l’option -p PORTHOTE:PORTCONTENEUR à docker run. Cela ouvre le port PORTHOTE sur votre machine et redirige tout ce qui y arrive sur le port PORTCONTENEUR du conteneur.

Par exemple, en partant de l’image nginx qui lance un serveur web qui écoute sur le port 80 du conteneur, nous allons pouvoir exposer le port 8000 de notre machine : docker run -p 8000:80 nginx.

C’est aussi simple que ça !

Variables d’environnement

Créez un fichier affiche_variable_essai.sh contenant le code suivant :

#!/bin/bash

echo Le contenu de la variable ESSAI :
echo $ESSAI

Ce script affiche la valeur de la variable d’environnement ESSAI. Pour le constater, il faut faire :

export ESSAI="Bonjour"
./affiche_variable_essai.sh
  • Écrivez un dockerfile sur la base de alpine dont l’entrypoint est ce fichier afficher_essai.sh (pensez à donner les droits d’exécution à affiche_variable_essai.sh)

  • Exécutez ce conteneur après avoir exporté la variable d’environnement ESSAI. Qu’en pensez vous ?

  • Pour passer une valeur à l’environnement du conteneur, deux possibilités :

    • À la création de l’image via l’instruction ENV. Par exemple en ajoutant la ligne ENV ESSAI=Bonjour au Dockerfile.

    • À l’exécution du conteneur, via l’option -e de docker run : docker run -e "ESSAI=Bonjour" -it --rm IMAGE

Essayez !

Bind mount

Un bind mount est un montage d’un dossier de l’hote dans un dossier du conteneur. A la création d’un conteneur, on crée un bind mount en ajoutant l’option -v CHEMINHOTE:CHEMINCONTENEUR à docker run.

Essayez en lançant une image alpine avec la commande sh et montant le répertoire courant dans le dossier /opt/ .

Volumes

Un volume est un peu comme un bind mount, à la différence que le volume est un espace de stockage géré par docker (par exemple en utilisant un répertoire quelque part sur le disque, ou encore en utilisant un stockage réseau en NFS, …)

Pour créer un volume on fera : docker volume create nom_volume . Ensuite, au lancement d’un conteneur en ajoutant l’option -v nom_volume:CHEMINCONTENEUR à docker run.

Essayez : créez un volume “essai_volume” et lancez une image alpine avec la commande sh et montant le volume dans le dossier /opt/ . Créez des fichiers dans le répertoire opt, tuez le conteneur puis lancez en un nouveau. Constatez que les fichiers sont toujours là !

A l’aide de docker volume inspect, retrouvez l’endroit où sont stockés vos fichiers.

Note: une chose intéressante avec les volumes et qui diffère du Bind mount : si le volume est vide, les fichiers présents dans l’image sont recopiés dans le volume. C’est utile pour récupérer ce qui est par défaut dans l’image.

Applications

Serveur de fichiers statiques avec nginx

Utilisez l’image nginx pour servir un fichier index.html disant « bonjour ». La documentation de l’image nginx, présente sur dockerhub (https://hub.docker.com/_/nginx) indique que les fichiers statics sont à mettre dans le dossier /usr/share/nginx/html.

Serveur postgresql

L’image postgres lance un serveur postgres qui écoute sur le port 5432, stocke les données dans le dossier /var/lib/postgresql/data et est accessible avec l’utilisateur postgres et le mot de passe défini dans la variable d’environnement POSTGRES_PASSWORD.

Lancez un serveur postgresql et testez qu’il fonctionne :

psql
# lister les base de données
\l
# Creer la base exemple
CREATE DATABASE exemple
# se connecter sur la base exemple
\c exemple
# lister les tables
\dt
# Créer une table
CREATE TABLE machin (id INTEGER PRIMARY KEY, nom VARCHAR);

Si vous n’avez pas fait de bind-mount de dossier, lorsque vous quittez et supprimez votre conteneur vous perdrez vos données. Modifiez la création du conteneur pour que vos données ne soient jamais perdues.

Serveur ftp

Créez/trouvez une image permettant de servir des fichiers via ftp.

Springboot

Lors d’un développement web certains paramètres du code ne doivent pas être « en dur » dans le code et seront fournis uniquement au moment du déploiement. Par exemple un serveur web connecté à une base de données sera paramétré par l’adresse du serveur de base de données, le nom de l’utilisateur et son mot de passe.

Dans cet exercice, on va voir comment envoyer une valeur à springboot depuis notre fichier compose ou depuis la ligne de commande au moment de lancer un conteneur. On choisira ici d’appeler cette variable « nom du serveur de base de données » (qui contiendra par exemple « mon_postgres.exemple.com »), même si dans notre exemple minimal notre serveur n’aura pas de base de données.

L’objectif est de créer un serveur web qui affiche à la route « / » le nom du serveur de base de données.

Ecrivez un serveur springboot minimal en suivant les instructions suivantes en vous arrêtant à « Containerize it » : https://spring.io/guides/gs/spring-boot-docker/̀

Ajoutez à votre classe Application un attribut « nom_base_de_donnees » récupéré depuis le fichier application.properties et modifiez la route « / » pour qu’on puisse voir son contenu :

package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }


      @Value("${test.nom_base_de_donnees}")
      private String nom_base_de_donnees;

    @RequestMapping("/")
    public String home() {
        return "Serveur de base de données : " + nom_base_de_donnees;
      }


}

Modifiez le fichier application.properties pour que la propriété test.nom_base_de_donnees ait la valeur de la variable d’environnement DATABASE_SERVER_NAME :

test.nom_base_de_donnees=${DATABASE_SERVER_NAME:default_server_name_from_springboot.com}

Lancez votre code java pour vérifier que jusqu’ici cela fait ce que vous voulez:

DATABASE_SERVER_NAME=mon_postgres.exemple.com ./mvnw spring-boot:run

Vous pouvez aussi le packager :

DATABASE_SERVER_NAME='pour_les_tests' ./mvnw package

Créez un fichier Dockerfile pour containeriser votre application :

FROM openjdk:11-jdk
ENV DATABASE_SERVER_NAME default_from_docker.com
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Lancez votre conteneur et observez ce que le serveur affiche à la route / .

Relancez votre conteneur en spécifiant une autre valeur pour la variable DATABASE_SERVER_NAME .

Développement d’un serveur quarkus

  1. Écrivez un dockerfile permettant d’avoir une image :

    • qui est créée à partir de openjdk

    • avec quarkus installé (en suivant la documentation ici : https://quarkus.io/get-started/)

    • qui lance bash -c avec comme options par defaut bash

  2. Compilez cette image en lui donnant le nom « dev_quarkus »

  3. Créez un dossier hello_world_quarkus et lancez un bash dans votre image docker en ayant monté le dossier hello_world_quarkus de la machine hôte dans le dossier /src/ du conteneur.

  4. Suivez la documentation de quarkus pour créer le squelette d’application.

  5. Utilisez votre image en exposant le port 8080 du conteneur sur le port 5000 de votre machine. Cela ne doit pas être encore fonctionnel : en effet, par défaut quarkus ne répond qu’aux requetes provenant de localhost, mais ici elles proviennent de l’hote et non directement du conteneur.

  6. Modifiez le fichier /src/main/resources/application.properties pour y ajouter la ligne : quarkus.http.host = 0.0.0.0 et constatez que maintenant ça fonctionne.

  7. Vous avez un environnement de développement pour quarkus fonctionnel et dockerisé !