You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Philippe Caseiro ebabfe31d2 Mise à jour du README 3 years ago
bastion Docker ne fonctionne pas a ce moment la du reconfigure 3 years ago
diagnose Ajout d'un script de diagnose 3 years ago
dicos Ajout du (futur) support LDAP pour shipyard. 3 years ago
postservice Il faut relancer docker pour que les rêgles iptables soient en place. 3 years ago
preservice Modification du paquet docker a installer 3 years ago
scripts Correction du script dkmng, pour supporter plusieurs versions 3 years ago
tmpl Ajout du (futur) support LDAP pour shipyard. 3 years ago
LICENSE initial commit 3 years ago
Makefile eole-compose est un nom plus précis que eole-docker 3 years ago
README.md Mise à jour du README 3 years ago
apps.mk First commit 3 years ago
eole-compose.mk Ajout des rêgles de firewall statiques pour docker 3 years ago
eole.mk First commit 3 years ago

README.md

eole-compose

Using docker containers in an Eole server

Howto (French)

Conditions préalables :

  • Un serveur eolebase >= 2.5.2

L’espace disque

Docker utilise /var/lib/docker pour stocker les données qui lui sont utiles, y compris les images des conteneurs applicatifs. Il faut donc qu’il dispose d’un espace suffisant.

Sur un eolebase tout l’espace disque libre est disponible dans un LV keep_1. Pour l’exemple on récupère cet espace disque pour l’ajouter au LV root.

Récupérer le plus d’espace disque possible pour /var/lib/docker :

  • lvremove /dev/eolebase-vg/keep_1
  • lvresize -l +100%FREE -r /dev/eolebase-vg/root

Les dépendances

Le projet eole-compose est plutôt “auto-suffisant”, ce qui implique qu’il installe lui-même certaines dépendances. Ceci est valable uniquement pour l’expérimentation et il est évident que pour un passage en “production” il faut fournir les dépendances proprement.

Pour fonctionner, eole-compose a besoin des logiciels suivants :

  • docker
  • docker-compose
  • cgroup-bin
  • cgroup-lite
  • libcgroup1

Pour fournir les-dites dépendances nous utilisons les logiciels suivants :

  • python-pip
  • apt
  • make
  • git
  • eole-client-annuaire

Quelques détails sur le projet eole-compose

eole-compose est un projet plutôt simple : il repose sur l’utilisation de docker avec l’outil docker-compose. On pourrait très bien se passer de docker-compose mais il faudrait recoder la gestion du lancement des conteneurs et nous n’en avons pas le temps. Il faut garder en tête la possibilité de le faire pour avoir une plus grande intégration avec EOLE et ainsi pouvoir offrir plus de fonctionnalités avec la “pile” EOLE (tiramisu/creole/genconfig).

Voici l’arborescence commentée du projet :

.
├── LICENSE                 # GPL (what else?)
├── Makefile                # Makefile standard skeletor
├── README.md               # Readme
├── apps.mk                 # apps.mk standard eole-skeletor
├── diagnose                #
│   └── 90-docker           # Un script diagnose d'exemple
├── dicos                   #
│   ├── 07_docker.xml       # Le dictionnaire pour docker
│   └── 80_hastebin.xml     # Le dictionnaire d'exemple pour hastebin (fonctionnel)
├── eole.mk                 # eole.mk standard eole-skeletor
├── postservice             #
│   └── 07-docker           # Script de lancement des conteneurs docker
├── preservice              #
│   └── 99-docker           # Script d'arrêt des conteneurs docker
├── scripts		    #
│   └── dkmng               # Script de gestion des conteneurs utilisé par les autres
└── tmpl                    #
    └── dk-cmp-hastebin.yml # Template de l'exemple hastebin

Toute la gestion des conteneurs docker repose sur le script dkmng qui, lui-même, repose sur docker-compose.

Le principe est simple, fournir des images docker sur un hub (public ou privé) et fournir un paquet très simple avec juste des dicos et des templates pour les divers services.

Dans l’exemple hastebin, nous utilisons une image du hub publique qui prend sa configuration sous forme de variables d’environnement, le template sert à créer un fichier de configuration pour docker-compose et dkmng lance tout ça au bon moment via les scripts postservice et preservice

Les ports d’écoute et le firewall

Docker repose sur un principe simple, exposer des ports du conteneur sur les ports de l’hôte. Donc si, dans le conteneur, le service écoute sur le port 7777, nous avons la possibilité de faire un lien vers un port de l’hôte avec l’option -p passée lors du lancement du conteneur. Avec docker-compose il suffit de déclarer une section ports.

ports:
  - 8989:7777
  - 7878:80

Comme pour l’option -p, le port sur l’hôte est placé avant le port dans le conteneur. Dans l’exemple, il y a donc deux services qui écoutent respectivement sur les ports 7777 et 80.

Pour faire simple, le maitre écoute sur le port 8989 qui est redirigé vers le port 7777 du conteneur.

Pour ce faire docker utilise iptables directement.

Les données

Contrairement à lxc, docker ne stocke pas les images dans des répertoires mais avec diverses techniques (dites storage driver). Il est donc impossible d’avoir accès à l’arborescence des conteneurs comme pour lxc.

Par définition un conteneur docker est éphémère, comme une image non-persistante dans nebula. Pour avoir des données persistantes, il y a plusieurs solutions. La plus simple est de faire un lien entre un répertoire du maitre et un répertoire du conteneur. Ce lien se fait avec l’option “-v” lors du lancement du conteneur. Avec docker-compose, il suffit de déclarer une section volumes:

volumes:
  - /opt/data/MonConteneur/opt/data:/opt/data

Comme pour les ports, on place le répertoire sur l’hôte avant la cible dans le conteneur. Ici, on aura donc accès à tout ce que le conteneur écrit dans /opt/data dans le répertoire /opt/data/MonConteneur/opt/data du maitre.

Attention, le répertoire du maitre est monté dans le conteneur. Si l’image docker contient des fichiers dans le-dit répertoire, le conteneur n’y aura plus accès : il aura accès au contenu du répertoire de l’hôte.

Étape 1 : Installer eole-compose

Pour installer eole-compose sur un eolebase il faut exécuter les commandes suivantes en root :

apt-get update

apt-eole install git make eole-client-annuaire

git clone https://dev-eole.ac-dijon.fr/git/eole-compose.git

cd eole-compose

make install

Étape 2 : Configuration

Pas de surprise ici : il faut lancer gen_config. Dans l’onglet Services une nouvelle “question” est disponible: N Activer le système de gestion de conteneurs docker par défaut la réponse à cette question est oui. Cette question a pour but de permettre la désactivation de l’utilisation de docker et donc la désactivation de tous les services qui utilisent docker.

Dans ce projet nous livrons également un dictionnaire d’exemple pour un service qui utiliserait docker. Ici, le service s’appelle hastebin. Ce service est pleinement fonctionnel et fournit un exemple simple d’utilisation d’une image docker “externe”. En effet, ici, l’image utilisée est disponible sur le hub docker public. Nous reviendrons plus tard sur le mode de fonctionnement de cette image. C’est pour cette raison que la question Activer le service web de copier/coller hastebin est également présente dans l’onglet Service.

Le service docker est donc actif. Nous allons lancer une commande reconfigure avant d’aller plus avant. Ce premier lancement va nous permettre d’installer les dépendances qui manquent encore. Toutes les dépendances disponibles sous forme de paquets ubuntu sont installées via la balise package, les autres sont installés directement par un script.

Normalement, le reconfigure ne fait pas d’erreur et aucun docker ne tourne à son terme.

Étape 3 : Créer des conteneurs en masse (ou presque :)

Le principe

Pour déployer des services lors du reconfigure avec eole-compose, rien de plus simple. Il suffit de mettre un fichier docker-compose.yml dans un sous-répertoire de /etc/eole/docker. Par exemple, pour avoir un conteneur mysql, on peut créer le fichier /etc/eole/docker/00_mysql/docker-compose.yml.

Le répertoire /etc/eole/docker/ est structuré avec des sous-répertoires numérotés, afin de pouvoir “organiser” le lancement des conteneurs ainsi 00_mysql démarre avant 01_wordpress.

Si on reprend notre idée d’avoir un service mysql, voici un exemple de configuration pour avoir un conteneur mysql

db:
  image: mysql
  ports:
    - 3306:3306
  volumes:
    - /opt/docker/db/var/lib/mysql:/var/lib/mysql
  environment:
    MYSQL_ROOT_PASSWORD: 123456
    MYSQL_USER: dev
    MYSQL_PASSWORD: 123456
    MYSQL_DATABASE: myapp

Avec cette configuration on utilise l’image mysql. Si cette image n’est pas présente dans le “datastore” du docker local, il va la télécharger sur le hub de docker.

Le port 3306 du maitre est redirigé vers le port 3306 du conteneur.

Le répertoire du maitre /opt/docker/db/var/lib/mysql est monté dans le conteneur avec le point de montage /var/lib/mysql, ce qui devrait suffire pour que les bases de données soient “persistantes”.

L’image mysql permet de passer des variables d’environnement pour régler certaines configurations. Ici, par exemple, on lui passe des variables comme MYSQL_ROOT_PASSWORD ou MYSQL_DATABASE. Ces variables sont certainement traitées par un script lors du lancement pour mettre à jour la configuration.

Une fois ce fichier placé dans /etc/eole/docker/00_mysql/, il suffit de lancer reconfigure pour que eole-compose lance un nouveau conteneur.

L’exemple hastebin.

Pour montrer comment utiliser creole et genconfig pour créer des conteneurs, nous sommes partis d’un service simple “hastebin”. Dans le projet eole-compose on livre un dictionnaire eole et un template eole pour la gestion des services hastebin. Je dis des car nous offrons la possibilité d’instancier plusieurs services hastebin.

Hastebin est très simple et nous n’avons besoin que de deux informations pour le configurer : le titre du service et le port d’écoute sur le maitre.

L’image que nous utilisons permet de passer un titre dans la variable d’environnement HASTEBIN_TITLE lors du lancement.

Un oeil sur le dico

Nous avons donc créé un dico pour pouvoir paramétrer les conteneurs depuis genconfig. ce dictionnaire est très simple.

<?xml version="1.0" encoding="utf-8"?>
<creole>
        <files>
                <file filelist="fl_hastebin" name="/etc/eole/docker/01_hastebin/docker-compose.yml" source="dk-cmp-hastebin.yml" mkdir='True' rm='True'/>
        </files>
    <variables>
        <family name='services'>
                        <variable name='activer_hastebin' type='oui/non' description='Activer le service web de copier/coller hastebin'>
                <value>non</value>
            </variable>
        </family>
                <family name='hastebin'>
                        <variable name='hastebin' type='string' multi='True' description="Nom de l'instance hastebin"/>
                        <variable name='hastebin_title' type='string' multi='True' description="Titre du service web de copier/coller"/>
                        <variable name='hastebin_port' type='number' description="Port d'écoute du service"/>
                </family>
    </variables>
    <constraints>
        <condition name='disabled_if_in' source='activer_docker'>
            <param>non</param>
                        <target type='variable'>activer_hastebin</target>
                        <target type='family'>hastebin</target>
                        <target type='filelist'>fl_hastebin</target>
        </condition>
        <condition name='disabled_if_in' source='activer_hastebin'>
            <param>non</param>
                        <target type='family'>hastebin</target>
                        <target type='filelist'>fl_hastebin</target>
        </condition>
                <group master='hastebin'>
                        <slave>hastebin_title</slave>
                        <slave>hastebin_port</slave>
                </group>
    </constraints>
    <help>
                <variable name='activer_hastebin'>Hastebin est un logiciel libre qui permet de copier/coller du texte afin de le partager avec d'autres personnes avec un simple lien hypertexte</variable>
    </help>
</creole>

Nous voulons offrir plusieurs hastebin, alors en attendant la disponibilité de la 2.6 et Tiramisu 2, nous utilisons un groupe avec une multi. La master s’appelle hastebin et elle a deux slaveshastebin_title et hastebin_port la première pour le titre et la seconde pour le port. Du côté des conditions on désactive tout si activer_docker est à non ou si activer_hastebin est à non. Sans oublier la déclaration du template de configuration docker-compose.

<file filelist="fl_hastebin" name="/etc/eole/docker/01_hastebin/docker-compose.yml" source="dk-cmp-hastebin.yml" mkdir='True' rm='True'/>

Ici nous avons décidé que les conteneurs hastebin seront dans les premiers à se lancer, hastebin étant très simple et n’ayant besoin d’aucune base de données nous pouvons le lancer avant tous les autres.

Voici le dit template :

%if %%getVar('activer_hastebin', 'non') == 'oui'
   %for %%haste in %%hastebin
%%haste:
  image: cadoles/hastebin:latest
  ports:
    - %%haste.hastebin_port:7777
  volumes:
    - /opt/data/hastebin/%%haste:/opt/haste/data
  environment:
    HASTEBIN_TITLE: %%haste.hastebin_title
   %end for
%end if

Il est plutôt simple il va créer un fichier de configuration qui sera placé dans /etc/eole/docker/01_hastebin/ ce fichier de configuration aura une entrée du type :

haste01:
  image: cadoles/hastebin:latest
  ports:
    - 8989:7777
  volumes:
    - /opt/data/hastebin/haste01:/opt/haste/data
  environment:
    HASTEBIN_TITLE: Mon Hastebin

Pour chaque élément de la multi hastebin. Afin de bien distinguer les conteneurs, nous utilisons le contenu de la master pour nommer le conteneur.

Faire fonctionner les “hastebin”

Rien de plus simple : il suffit d’aller dans genconfig de passer Activer le service web de copier/coller hastebin à oui puis d’aller dans l’onglet Hastebin et de créer des nouvelles entrées pour la multi Nom de l'instance hastebin.

Une fois la configuration enregistrée, reconfigure puis vous pourrez vous rendre sur l’adresse dns ou ip de votre serveur eolebase sur les ports que vous avez déclarés.

ex: http://eolebase.ac-test.fr:8989

Ce qu’il manque :

  • Intégrer finement bastion et docker, aujourd’hui si on fait service bastion restart plus aucun conteneur n’est disponible car les rêgles sont détruites par bastion et non recrées ;
  • Avoir une gestion des external_link pour permettre l’utilisation de conteneurs communs comme mysql ou ldap.

Et la méthode et l’infra qui va avec ?

Il semble indispensable d’offrir une infrastructure et une méthode pour fournir des images docker compatibles.

Côté infra il faut mettre en place un hub docker eole pour que les dev puissent mettre à disposition des images spécifiques, car l’utilisation des images du hub docker va vite être limitante. Docker offre des images pour fournir ce genre de service il faut voir dans quelle mesure le projet a la possibilité de mettre ce service à disposition.

Pour la méthode de création d’image il faut statuer sur un moyen simple pour configurer les services à chaque lancement. Ici, nous utilisons les mécanismes utilisés généralement par les gens qui fournissent des images docker, les variables d’environnement. Pour l’image hastebin nous faisons un simple sed dans la configuration pour remplacer le titre, mais l’utilisation de envsubst semble être une bonne piste, cette commande de gettext permet de remplacer des chaines du type title=${HASTEBIN_TITLE} dans un fichier.

Avec cette méthode il suffit de copier des “templates” de configuration avec des ${VARIABLE} plutôt que des %%ma_variable. Cette technique permet d’utiliser n’importe quelle image comme base de travail sans forcément avoir des outils EOLE pour faire la configuration des applicatifs. Dans le Dockerfile, on copie les “templates” lors de la création de l’image et dans le entrypoint ou cmd, on lance un script qui utilise envsubst et qui exécute la commande de lancement du service, voici un exemple :

FROM gliderlabs/alpine:3.3
MAINTAINER william.petit@ptitcloud.fr

ENV BUILDPKG build-base git python openssl-dev pkgconfig curl
ENV SYSPKG ca-certificates nodejs gzip bash gettext

# Container installation
RUN apk add $SYSPKG $BUILDPKG --no-cache

# Grab the latest Git version
RUN mkdir /opt
RUN cd /opt && git clone git://github.com/ether/etherpad-lite.git etherpad

# Install node dependencies
RUN /opt/etherpad/bin/installDeps.sh

# Install process runner
RUN npm install pm2 -g

WORKDIR /opt/etherpad

# Add configuration template
ADD settings-template.json /opt/etherpad/settings-template.json

# Add startup script
ADD run.sh /opt/etherpad/
RUN chmod +x /opt/etherpad/run.sh

EXPOSE 9001
EXPOSE 8080

RUN apk del $BUILDPKG

CMD "/opt/etherpad/run.sh"

Et le run.sh qui va avec :

#!/usr/bin/env bash

# Generate session token
export ETHERPAD_SESSION=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)

# Generate configuration
envsubst < settings-template.json > settings.json

# Export PM2 env
export PM2_HOME=.
# Avoid Keymetrics banner
echo 1 > "touch"

# Start Etherpad
/usr/bin/pm2 start node_modules/ep_etherpad-lite/node/server.js || exit 1

# Stream logs
/usr/bin/pm2 logs
dd

Si vous vous demandez pourquoi on ne build pas directement l’image sur la machine cible, la réponse est simple pour moi c’est une très mauvaise idée, pour plusieurs raisons :

  • Le build prend du temps et donc le reconfigure serait beaucoup trop long ;
  • Le build prend de l’espace disque et donc l’utilisation disque serait trop importante et sans valeur ajoutée.

La question qui reste en suspend est “Quelle base pour les images ?”. Pour moi la réponse, également simple, est l’image la plus “light” possible. Il semble que le projet docker prenne le chemin d’abandonner les images à base ubuntu pour alpine linux. Je pense que c’est une très bonne idée car les images ne doivent contenir que le strict minimum pour rendre le service. Par exemple l’image hastebin qui sert dans l’exemple fait 55,49Mo, elle utilise une base Alpine. Avant de prendre cette option, j’utilisais l’image node basée sur l’image de base debian ; avec cette configuration l’image de mon service avait une taille de 65OMo,

Les autres services du projet eole-compose

Shipyard

Shipyard est une interface web a swarm (mode cluster de docker) il permet d’agir sur les conteneurs il est plus ou moins l’équivalent du Sunstone d’opennebula mais pour docker. Pour activer shipyard il suffit de passer la variable “activer_shipyard” à “oui” dans l’onglet Services de genconfig. Pour le moment shipyard écoute sur le port 1030 et ne dispose que d’un compte par défaut (admin/shipyard).

L’authentification LDAP est supportée dans eole-compose mais pour l’instant shipyard ne marche pas avec du LDAPs et ne cherche pas dans les branches de l’arbre LDAP, donc l’usage est plutôt limité.

Registry

Registry est “le” service pour mettre à disposition des images docker. Il fournis la même chose que le hub docker. Pour activer Registry il suffit de passer la variable “activer_registry” à “oui” dans l’onglet Service. Par défaut l’authentification n’est pas activée, pour l’activer il faut changer la valeur de la variable “reg_auth” dans l’onglet “Registry” de genconfig (l’onglet apparait après l’activation du service).

Pour utiliser LDAP il faut configurer l’onglet Annuaire de gen_config et choisir “ldap” dans la liste de la variable “reg_auth” de l’onglet “Registry”

Le mode “simple” crée un simple fichier de configuration qui contiens les comptes /opt/data/registry/auth/config/auth_config.yml. il faut stopper le conteneur et le relancer a chaque ajout de compte.

Le mode “htpasswd” vous permet de stocker les comptes dans un fichier au format htpasswd (Attention les mots de passe doivent être chiffrés en mode bcrypt) il faut placer le fichier dans le répertoire : /opt/data/registry/auth/